@emailens/engine 0.10.2 → 0.10.4
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/README.md +7 -4
- package/dist/index.cjs +345 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -1
- package/dist/index.d.ts +14 -1
- package/dist/index.js +343 -8
- package/dist/index.js.map +1 -1
- package/package.json +7 -3
package/README.md
CHANGED
|
@@ -7,9 +7,10 @@
|
|
|
7
7
|
|
|
8
8
|
**The rendering linter for email**
|
|
9
9
|
|
|
10
|
+
[](https://github.com/emailens/engine/actions/workflows/ci.yml)
|
|
10
11
|
[](https://www.npmjs.com/package/@emailens/engine)
|
|
11
12
|
[](./LICENSE)
|
|
12
|
-
[]()
|
|
13
14
|
[](https://nodejs.org/)
|
|
14
15
|
[](https://github.com/emailens/mcp)
|
|
15
16
|
[](https://github.com/emailens/engine/stargazers)
|
|
@@ -20,9 +21,11 @@
|
|
|
20
21
|
|
|
21
22
|
**Your email looks perfect in Apple Mail. Gmail strips half the CSS. Outlook renders it in Word.**
|
|
22
23
|
|
|
23
|
-
`@emailens/engine` analyzes your
|
|
24
|
+
`@emailens/engine` analyzes your email against 255 CSS and HTML features across 21 email clients, scores compatibility, and shows you exactly what to fix — before you hit send.
|
|
24
25
|
|
|
25
|
-
|
|
26
|
+
Write it however you like. **HTML, MJML, Maizzle and React Email** all go in the front: the engine compiles the template with your own compiler and lints the HTML that actually gets sent, which is the only version your reader sees.
|
|
27
|
+
|
|
28
|
+
Our data says you need this: of those 255 features, **only 6 are fully supported in every major email client**. See [The State of Email CSS](https://emailens.dev/email-css/report).
|
|
26
29
|
|
|
27
30
|

|
|
28
31
|
|
|
@@ -105,7 +108,7 @@ const { code } = await generateAiFix({
|
|
|
105
108
|
|
|
106
109
|
10 analysis engines, one `auditEmail()` call.
|
|
107
110
|
|
|
108
|
-
- **CSS compatibility** —
|
|
111
|
+
- **CSS compatibility** — 255 CSS and HTML features tested across 21 email clients, with fix snippets and AI-powered auto-fix
|
|
109
112
|
- **Content overflow** — fixed widths wider than the email frame and unbreakable strings that force horizontal scrolling
|
|
110
113
|
- **Visual bugs** — gradients/background images with no color fallback (invisible content in Outlook) and fonts with no web-safe fallback, each with a concrete fix
|
|
111
114
|
- **Spam scoring** — 45+ signals modeled after SpamAssassin, CAN-SPAM, and GDPR
|
package/dist/index.cjs
CHANGED
|
@@ -68,6 +68,7 @@ __export(index_exports, {
|
|
|
68
68
|
CompileError: () => CompileError,
|
|
69
69
|
EMAIL_CLIENTS: () => EMAIL_CLIENTS,
|
|
70
70
|
EMPTY_DELIVERABILITY: () => EMPTY_DELIVERABILITY,
|
|
71
|
+
FEATURE_URLS: () => FEATURE_URLS,
|
|
71
72
|
GENERIC_LINK_TEXT: () => GENERIC_LINK_TEXT,
|
|
72
73
|
HTML_ELEMENT_FEATURES: () => HTML_ELEMENT_FEATURES,
|
|
73
74
|
MAX_HTML_SIZE: () => MAX_HTML_SIZE,
|
|
@@ -92,6 +93,7 @@ __export(index_exports, {
|
|
|
92
93
|
errorWarnings: () => errorWarnings,
|
|
93
94
|
estimateAiFixTokens: () => estimateAiFixTokens,
|
|
94
95
|
extractInboxPreview: () => extractInboxPreview,
|
|
96
|
+
featureUrl: () => featureUrl,
|
|
95
97
|
formatRgb: () => formatRgb,
|
|
96
98
|
generateAiFix: () => generateAiFix,
|
|
97
99
|
generateCompatibilityScore: () => generateCompatibilityScore,
|
|
@@ -11021,6 +11023,54 @@ function locOfAttr(el, attr) {
|
|
|
11021
11023
|
const attrLoc = (_d = (_a = raw.attrs) == null ? void 0 : _a[attr]) != null ? _d : (_c = (_b = raw.startTag) == null ? void 0 : _b.attrs) == null ? void 0 : _c[attr];
|
|
11022
11024
|
return attrLoc ? toLoc(attrLoc) : locOfElement(el);
|
|
11023
11025
|
}
|
|
11026
|
+
function locInAttr(attrLoc, source, property, occurrence = 0) {
|
|
11027
|
+
if (!attrLoc || !source) return void 0;
|
|
11028
|
+
const raw = source.slice(attrLoc.offset, attrLoc.offset + attrLoc.length);
|
|
11029
|
+
const open = raw.search(/["']/);
|
|
11030
|
+
const close = raw.lastIndexOf(raw[open]);
|
|
11031
|
+
if (open === -1 || close <= open) return void 0;
|
|
11032
|
+
const found = declarationsIn(raw.slice(open + 1, close), property);
|
|
11033
|
+
const hit = found[occurrence];
|
|
11034
|
+
if (!hit) return void 0;
|
|
11035
|
+
const start = attrLoc.offset + open + 1 + hit.start;
|
|
11036
|
+
const end = attrLoc.offset + open + 1 + hit.end;
|
|
11037
|
+
const from = positionOf(source, start);
|
|
11038
|
+
const to = positionOf(source, end);
|
|
11039
|
+
return {
|
|
11040
|
+
line: from.line,
|
|
11041
|
+
column: from.column,
|
|
11042
|
+
endLine: to.line,
|
|
11043
|
+
endColumn: to.column,
|
|
11044
|
+
offset: start,
|
|
11045
|
+
length: end - start
|
|
11046
|
+
};
|
|
11047
|
+
}
|
|
11048
|
+
function declarationsIn(value, property) {
|
|
11049
|
+
const wanted = property.toLowerCase();
|
|
11050
|
+
const found = [];
|
|
11051
|
+
let depth = 0;
|
|
11052
|
+
let start = 0;
|
|
11053
|
+
const consider = (from, to) => {
|
|
11054
|
+
const text = value.slice(from, to);
|
|
11055
|
+
const colon = text.indexOf(":");
|
|
11056
|
+
if (colon === -1) return;
|
|
11057
|
+
if (text.slice(0, colon).trim().toLowerCase() !== wanted) return;
|
|
11058
|
+
const lead = text.length - text.trimStart().length;
|
|
11059
|
+
const trail = text.length - text.trimEnd().length;
|
|
11060
|
+
if (from + lead < to - trail) found.push({ start: from + lead, end: to - trail });
|
|
11061
|
+
};
|
|
11062
|
+
for (let i = 0; i < value.length; i++) {
|
|
11063
|
+
const c = value[i];
|
|
11064
|
+
if (c === "(") depth++;
|
|
11065
|
+
else if (c === ")") depth = Math.max(0, depth - 1);
|
|
11066
|
+
else if (c === ";" && depth === 0) {
|
|
11067
|
+
consider(start, i);
|
|
11068
|
+
start = i + 1;
|
|
11069
|
+
}
|
|
11070
|
+
}
|
|
11071
|
+
consider(start, value.length);
|
|
11072
|
+
return found;
|
|
11073
|
+
}
|
|
11024
11074
|
function locOfFirst($, selector) {
|
|
11025
11075
|
const el = $(selector).first()[0];
|
|
11026
11076
|
return el ? locOfElement(el) : void 0;
|
|
@@ -11286,7 +11336,7 @@ function describeSelector($, el) {
|
|
|
11286
11336
|
if (cls) return `${tag}.${cls.split(/\s+/)[0]}`;
|
|
11287
11337
|
return tag;
|
|
11288
11338
|
}
|
|
11289
|
-
function checkDarkModeFromDom(
|
|
11339
|
+
function checkDarkModeFromDom($, source) {
|
|
11290
11340
|
var _a, _b;
|
|
11291
11341
|
const darkBlock = collectDarkBlocks($);
|
|
11292
11342
|
if (!darkBlock) return [];
|
|
@@ -11313,7 +11363,7 @@ function checkDarkModeFromDom($) {
|
|
|
11313
11363
|
const coveredByAny = matchedElements($, darkBlock.any);
|
|
11314
11364
|
let uncovered = 0;
|
|
11315
11365
|
$("[bgcolor], [style]").each((_, el) => {
|
|
11316
|
-
var _a2;
|
|
11366
|
+
var _a2, _b2;
|
|
11317
11367
|
if (uncovered >= MAX_UNCOVERED_ELEMENTS) return false;
|
|
11318
11368
|
const $el = $(el);
|
|
11319
11369
|
const style = parseInlineStyle($el.attr("style") || "");
|
|
@@ -11323,7 +11373,8 @@ function checkDarkModeFromDom($) {
|
|
|
11323
11373
|
if (inline ? coveredByImportant.has(el) : coveredByAny.has(el)) return;
|
|
11324
11374
|
uncovered++;
|
|
11325
11375
|
const selector = describeSelector($, el);
|
|
11326
|
-
const
|
|
11376
|
+
const attrLoc = locOfAttr(el, inline ? "style" : "bgcolor");
|
|
11377
|
+
const loc = inline ? (_b2 = locInAttr(attrLoc, source, style.get("background-color") !== void 0 ? "background-color" : "background")) != null ? _b2 : attrLoc : attrLoc;
|
|
11327
11378
|
for (const clientId of PREFERS_COLOR_SCHEME_CLIENTS) {
|
|
11328
11379
|
warnings.push(__spreadValues({
|
|
11329
11380
|
severity: "warning",
|
|
@@ -11588,18 +11639,37 @@ function analyzeEmailFromDom($, framework, source) {
|
|
|
11588
11639
|
const style = $(el).attr("style") || "";
|
|
11589
11640
|
const props = parseStyleProperties(style);
|
|
11590
11641
|
const selector = describeSelector2(el);
|
|
11591
|
-
const
|
|
11642
|
+
const attrLoc = locOfAttr(el, "style");
|
|
11643
|
+
const declarationLocs = (prop, occurrence = 0) => {
|
|
11644
|
+
var _a;
|
|
11645
|
+
return (_a = elementLocs(locInAttr(attrLoc, source, prop, occurrence))) != null ? _a : elementLocs(attrLoc);
|
|
11646
|
+
};
|
|
11647
|
+
const locs = elementLocs(attrLoc);
|
|
11592
11648
|
for (const prop of props) {
|
|
11593
11649
|
for (const det of COMPOUND_DETECTORS) {
|
|
11594
11650
|
if (prop === det.property) {
|
|
11595
11651
|
const value2 = getStyleValue(style, prop);
|
|
11596
11652
|
if (value2 == null ? void 0 : value2.toLowerCase().includes(det.valueIncludes)) {
|
|
11597
|
-
checkPropertySupport(
|
|
11653
|
+
checkPropertySupport(
|
|
11654
|
+
det.key,
|
|
11655
|
+
addWarning,
|
|
11656
|
+
framework,
|
|
11657
|
+
selector,
|
|
11658
|
+
void 0,
|
|
11659
|
+
void 0,
|
|
11660
|
+
declarationLocs(prop)
|
|
11661
|
+
);
|
|
11598
11662
|
}
|
|
11599
11663
|
}
|
|
11600
11664
|
}
|
|
11601
11665
|
if (cssPropertiesToCheck.includes(prop)) {
|
|
11602
11666
|
const declared = getStyleValues(style, prop);
|
|
11667
|
+
const placed = [];
|
|
11668
|
+
declared.forEach((value2, i) => {
|
|
11669
|
+
const at = locInAttr(attrLoc, source, prop, i);
|
|
11670
|
+
if (at) placed.push({ value: value2, loc: at });
|
|
11671
|
+
});
|
|
11672
|
+
const occurrences = placed.length === declared.length && placed.length > 0 ? { locs: placed.map((p) => p.loc), values: placed.map((p) => p.value) } : locs;
|
|
11603
11673
|
checkPropertySupport(
|
|
11604
11674
|
prop,
|
|
11605
11675
|
addWarning,
|
|
@@ -11607,14 +11677,22 @@ function analyzeEmailFromDom($, framework, source) {
|
|
|
11607
11677
|
selector,
|
|
11608
11678
|
void 0,
|
|
11609
11679
|
declared.length ? declared : void 0,
|
|
11610
|
-
|
|
11680
|
+
occurrences
|
|
11611
11681
|
);
|
|
11612
11682
|
}
|
|
11613
11683
|
const value = getStyleValue(style, prop);
|
|
11614
11684
|
if (value) {
|
|
11615
11685
|
for (const fn of CSS_FUNCTION_DETECTORS) {
|
|
11616
11686
|
if (value.includes(fn.pattern)) {
|
|
11617
|
-
checkPropertySupport(
|
|
11687
|
+
checkPropertySupport(
|
|
11688
|
+
fn.key,
|
|
11689
|
+
addWarning,
|
|
11690
|
+
framework,
|
|
11691
|
+
selector,
|
|
11692
|
+
void 0,
|
|
11693
|
+
void 0,
|
|
11694
|
+
declarationLocs(prop)
|
|
11695
|
+
);
|
|
11618
11696
|
}
|
|
11619
11697
|
}
|
|
11620
11698
|
}
|
|
@@ -11653,7 +11731,7 @@ function analyzeEmailFromDom($, framework, source) {
|
|
|
11653
11731
|
for (const fn of detectedCssFunctions) {
|
|
11654
11732
|
checkPropertySupport(fn, addWarning, framework, void 0, propertyLines.get(fn), void 0, propertyLocs.get(fn));
|
|
11655
11733
|
}
|
|
11656
|
-
for (const w of checkDarkModeFromDom(
|
|
11734
|
+
for (const w of checkDarkModeFromDom($, source)) addWarning(w);
|
|
11657
11735
|
const severityOrder = { error: 0, warning: 1, info: 2 };
|
|
11658
11736
|
warnings.sort((a, b) => severityOrder[a.severity] - severityOrder[b.severity]);
|
|
11659
11737
|
return warnings;
|
|
@@ -12083,6 +12161,263 @@ function extractCode(response) {
|
|
|
12083
12161
|
return response.trim();
|
|
12084
12162
|
}
|
|
12085
12163
|
|
|
12164
|
+
// src/rules/feature-urls.ts
|
|
12165
|
+
var FEATURE_URLS = {
|
|
12166
|
+
"!important": "https://www.caniemail.com/features/css-important/",
|
|
12167
|
+
"::after": "https://www.caniemail.com/features/css-pseudo-element-after/",
|
|
12168
|
+
"::before": "https://www.caniemail.com/features/css-pseudo-element-before/",
|
|
12169
|
+
"::first-letter": "https://www.caniemail.com/features/css-pseudo-element-first-letter/",
|
|
12170
|
+
"::first-line": "https://www.caniemail.com/features/css-pseudo-element-first-line/",
|
|
12171
|
+
"::marker": "https://www.caniemail.com/features/css-pseudo-element-marker/",
|
|
12172
|
+
"::placeholder": "https://www.caniemail.com/features/css-pseudo-element-placeholder/",
|
|
12173
|
+
":adjacent-sibling": "https://www.caniemail.com/features/css-selector-adjacent-sibling/",
|
|
12174
|
+
":attribute-selector": "https://www.caniemail.com/features/css-selector-attribute/",
|
|
12175
|
+
":chaining": "https://www.caniemail.com/features/css-selector-chaining/",
|
|
12176
|
+
":checked": "https://www.caniemail.com/features/css-pseudo-class-checked/",
|
|
12177
|
+
":child-combinator": "https://www.caniemail.com/features/css-selector-child/",
|
|
12178
|
+
":class-selector": "https://www.caniemail.com/features/css-selector-class/",
|
|
12179
|
+
":descendant-combinator": "https://www.caniemail.com/features/css-selector-descendant/",
|
|
12180
|
+
":first-child": "https://www.caniemail.com/features/css-pseudo-class-first-child/",
|
|
12181
|
+
":first-of-type": "https://www.caniemail.com/features/css-pseudo-class-first-of-type/",
|
|
12182
|
+
":focus": "https://www.caniemail.com/features/css-pseudo-class-focus/",
|
|
12183
|
+
":focus-visible": "https://www.caniemail.com/features/css-pseudo-class-focus-visible/",
|
|
12184
|
+
":focus-within": "https://www.caniemail.com/features/css-pseudo-class-focus-within/",
|
|
12185
|
+
":general-sibling": "https://www.caniemail.com/features/css-selector-general-sibling/",
|
|
12186
|
+
":grouping": "https://www.caniemail.com/features/css-selector-grouping/",
|
|
12187
|
+
":has": "https://www.caniemail.com/features/css-pseudo-class-has/",
|
|
12188
|
+
":hover": "https://www.caniemail.com/features/css-pseudo-class-hover/",
|
|
12189
|
+
":id-selector": "https://www.caniemail.com/features/css-selector-id/",
|
|
12190
|
+
":lang": "https://www.caniemail.com/features/css-pseudo-class-lang/",
|
|
12191
|
+
":last-child": "https://www.caniemail.com/features/css-pseudo-class-last-child/",
|
|
12192
|
+
":last-of-type": "https://www.caniemail.com/features/css-pseudo-class-last-of-type/",
|
|
12193
|
+
":link": "https://www.caniemail.com/features/css-pseudo-class-link/",
|
|
12194
|
+
":not": "https://www.caniemail.com/features/css-pseudo-class-not/",
|
|
12195
|
+
":nth-child": "https://www.caniemail.com/features/css-pseudo-class-nth-child/",
|
|
12196
|
+
":nth-last-child": "https://www.caniemail.com/features/css-pseudo-class-nth-last-child/",
|
|
12197
|
+
":nth-of-type": "https://www.caniemail.com/features/css-pseudo-class-nth-of-type/",
|
|
12198
|
+
":only-child": "https://www.caniemail.com/features/css-pseudo-class-only-child/",
|
|
12199
|
+
":only-of-type": "https://www.caniemail.com/features/css-pseudo-class-only-of-type/",
|
|
12200
|
+
":target": "https://www.caniemail.com/features/css-pseudo-class-target/",
|
|
12201
|
+
":type-selector": "https://www.caniemail.com/features/css-selector-type/",
|
|
12202
|
+
":universal-selector": "https://www.caniemail.com/features/css-selector-universal/",
|
|
12203
|
+
":visited": "https://www.caniemail.com/features/css-pseudo-class-visited/",
|
|
12204
|
+
"<abbr>": "https://www.caniemail.com/features/html-abbr/",
|
|
12205
|
+
"<acronym>": "https://www.caniemail.com/features/html-acronym/",
|
|
12206
|
+
"<address>": "https://www.caniemail.com/features/html-address/",
|
|
12207
|
+
"<audio>": "https://www.caniemail.com/features/html-audio/",
|
|
12208
|
+
"<base>": "https://www.caniemail.com/features/html-base/",
|
|
12209
|
+
"<bdi>": "https://www.caniemail.com/features/html-bdi/",
|
|
12210
|
+
"<blockquote>": "https://www.caniemail.com/features/html-blockquote/",
|
|
12211
|
+
"<body>": "https://www.caniemail.com/features/html-body/",
|
|
12212
|
+
"<code>": "https://www.caniemail.com/features/html-code/",
|
|
12213
|
+
"<del>": "https://www.caniemail.com/features/html-del/",
|
|
12214
|
+
"<dfn>": "https://www.caniemail.com/features/html-dfn/",
|
|
12215
|
+
"<dialog>": "https://www.caniemail.com/features/html-dialog/",
|
|
12216
|
+
"<div>": "https://www.caniemail.com/features/html-div/",
|
|
12217
|
+
"<form>": "https://www.caniemail.com/features/html-form/",
|
|
12218
|
+
"<h1>": "https://www.caniemail.com/features/html-h1-h6/",
|
|
12219
|
+
"<hr>": "https://www.caniemail.com/features/html-hr/",
|
|
12220
|
+
"<img>": "https://www.caniemail.com/features/html-img/",
|
|
12221
|
+
"<link>": "https://www.caniemail.com/features/html-link/",
|
|
12222
|
+
"<marquee>": "https://www.caniemail.com/features/html-marquee/",
|
|
12223
|
+
"<meter>": "https://www.caniemail.com/features/html-meter/",
|
|
12224
|
+
"<object>": "https://www.caniemail.com/features/html-object/",
|
|
12225
|
+
"<p>": "https://www.caniemail.com/features/html-p/",
|
|
12226
|
+
"<picture>": "https://www.caniemail.com/features/html-picture/",
|
|
12227
|
+
"<pre>": "https://www.caniemail.com/features/html-pre/",
|
|
12228
|
+
"<progress>": "https://www.caniemail.com/features/html-progress/",
|
|
12229
|
+
"<rp>": "https://www.caniemail.com/features/html-rp/",
|
|
12230
|
+
"<rt>": "https://www.caniemail.com/features/html-rt/",
|
|
12231
|
+
"<ruby>": "https://www.caniemail.com/features/html-ruby/",
|
|
12232
|
+
"<select>": "https://www.caniemail.com/features/html-select/",
|
|
12233
|
+
"<small>": "https://www.caniemail.com/features/html-small/",
|
|
12234
|
+
"<span>": "https://www.caniemail.com/features/html-span/",
|
|
12235
|
+
"<strike>": "https://www.caniemail.com/features/html-strike/",
|
|
12236
|
+
"<strong>": "https://www.caniemail.com/features/html-strong/",
|
|
12237
|
+
"<style>": "https://www.caniemail.com/features/html-style/",
|
|
12238
|
+
"<svg>": "https://www.caniemail.com/features/html-svg/",
|
|
12239
|
+
"<table>": "https://www.caniemail.com/features/html-table/",
|
|
12240
|
+
"<textarea>": "https://www.caniemail.com/features/html-textarea/",
|
|
12241
|
+
"<ul>": "https://www.caniemail.com/features/html-lists/",
|
|
12242
|
+
"<video>": "https://www.caniemail.com/features/html-video/",
|
|
12243
|
+
"<wbr>": "https://www.caniemail.com/features/html-wbr/",
|
|
12244
|
+
"@font-face": "https://www.caniemail.com/features/css-at-font-face/",
|
|
12245
|
+
"@import": "https://www.caniemail.com/features/css-at-import/",
|
|
12246
|
+
"@keyframes": "https://www.caniemail.com/features/css-at-keyframes/",
|
|
12247
|
+
"@media": "https://www.caniemail.com/features/css-at-media/",
|
|
12248
|
+
"@supports": "https://www.caniemail.com/features/css-at-supports/",
|
|
12249
|
+
"accent-color": "https://www.caniemail.com/features/css-accent-color/",
|
|
12250
|
+
"align-items": "https://www.caniemail.com/features/css-align-items/",
|
|
12251
|
+
"animation": "https://www.caniemail.com/features/css-animation/",
|
|
12252
|
+
"aspect-ratio": "https://www.caniemail.com/features/css-aspect-ratio/",
|
|
12253
|
+
"backdrop-filter": "https://www.caniemail.com/features/css-backdrop-filter/",
|
|
12254
|
+
"background": "https://www.caniemail.com/features/css-background/",
|
|
12255
|
+
"background-blend-mode": "https://www.caniemail.com/features/css-background-blend-mode/",
|
|
12256
|
+
"background-clip": "https://www.caniemail.com/features/css-background-clip/",
|
|
12257
|
+
"background-color": "https://www.caniemail.com/features/css-background-color/",
|
|
12258
|
+
"background-image": "https://www.caniemail.com/features/css-background-image/",
|
|
12259
|
+
"background-origin": "https://www.caniemail.com/features/css-background-origin/",
|
|
12260
|
+
"background-position": "https://www.caniemail.com/features/css-background-position/",
|
|
12261
|
+
"background-repeat": "https://www.caniemail.com/features/css-background-repeat/",
|
|
12262
|
+
"background-size": "https://www.caniemail.com/features/css-background-size/",
|
|
12263
|
+
"bimi": "https://www.caniemail.com/features/bimi/",
|
|
12264
|
+
"block-size": "https://www.caniemail.com/features/css-block-inline-size/",
|
|
12265
|
+
"border": "https://www.caniemail.com/features/css-border/",
|
|
12266
|
+
"border-collapse": "https://www.caniemail.com/features/css-border-collapse/",
|
|
12267
|
+
"border-image": "https://www.caniemail.com/features/css-border-image/",
|
|
12268
|
+
"border-inline": "https://www.caniemail.com/features/css-border-inline-block/",
|
|
12269
|
+
"border-inline-individual": "https://www.caniemail.com/features/css-border-inline-block-individual/",
|
|
12270
|
+
"border-inline-longhand": "https://www.caniemail.com/features/css-border-inline-block-longhand/",
|
|
12271
|
+
"border-radius": "https://www.caniemail.com/features/css-border-radius/",
|
|
12272
|
+
"border-radius-logical": "https://www.caniemail.com/features/css-border-radius-logical/",
|
|
12273
|
+
"border-spacing": "https://www.caniemail.com/features/css-border-spacing/",
|
|
12274
|
+
"box-shadow": "https://www.caniemail.com/features/css-box-shadow/",
|
|
12275
|
+
"box-sizing": "https://www.caniemail.com/features/css-box-sizing/",
|
|
12276
|
+
"calc": "https://www.caniemail.com/features/css-unit-calc/",
|
|
12277
|
+
"caption-side": "https://www.caniemail.com/features/css-caption-side/",
|
|
12278
|
+
"clamp": "https://www.caniemail.com/features/css-function-clamp/",
|
|
12279
|
+
"clear": "https://www.caniemail.com/features/css-clear/",
|
|
12280
|
+
"clip-path": "https://www.caniemail.com/features/css-clip-path/",
|
|
12281
|
+
"color-scheme": "https://www.caniemail.com/features/css-color-scheme/",
|
|
12282
|
+
"column-count": "https://www.caniemail.com/features/css-column-count/",
|
|
12283
|
+
"columns": "https://www.caniemail.com/features/css-column-layout-properties/",
|
|
12284
|
+
"conic-gradient": "https://www.caniemail.com/features/css-conic-gradient/",
|
|
12285
|
+
"css-comments": "https://www.caniemail.com/features/css-comments/",
|
|
12286
|
+
"cursor": "https://www.caniemail.com/features/css-cursor/",
|
|
12287
|
+
"custom-properties": "https://www.caniemail.com/features/css-variables/",
|
|
12288
|
+
"direction": "https://www.caniemail.com/features/css-direction/",
|
|
12289
|
+
"display": "https://www.caniemail.com/features/css-display/",
|
|
12290
|
+
"display:flex": "https://www.caniemail.com/features/css-display-flex/",
|
|
12291
|
+
"display:grid": "https://www.caniemail.com/features/css-display-grid/",
|
|
12292
|
+
"display:none": "https://www.caniemail.com/features/css-display-none/",
|
|
12293
|
+
"empty-cells": "https://www.caniemail.com/features/css-empty-cells/",
|
|
12294
|
+
"filter": "https://www.caniemail.com/features/css-filter/",
|
|
12295
|
+
"fit-content": "https://www.caniemail.com/features/css-intrinsic-size/",
|
|
12296
|
+
"flex-direction": "https://www.caniemail.com/features/css-flex-direction/",
|
|
12297
|
+
"flex-wrap": "https://www.caniemail.com/features/css-flex-wrap/",
|
|
12298
|
+
"float": "https://www.caniemail.com/features/css-float/",
|
|
12299
|
+
"font": "https://www.caniemail.com/features/css-font/",
|
|
12300
|
+
"font-kerning": "https://www.caniemail.com/features/css-font-kerning/",
|
|
12301
|
+
"font-size": "https://www.caniemail.com/features/css-font-size/",
|
|
12302
|
+
"font-size-adjust": "https://www.caniemail.com/features/css-font-size-adjust/",
|
|
12303
|
+
"font-stretch": "https://www.caniemail.com/features/css-font-stretch/",
|
|
12304
|
+
"font-weight": "https://www.caniemail.com/features/css-font-weight/",
|
|
12305
|
+
"gap": "https://www.caniemail.com/features/css-gap/",
|
|
12306
|
+
"grid-template-columns": "https://www.caniemail.com/features/css-grid-template/",
|
|
12307
|
+
"height": "https://www.caniemail.com/features/css-height/",
|
|
12308
|
+
"hyphenate-character": "https://www.caniemail.com/features/css-hyphenate-character/",
|
|
12309
|
+
"hyphenate-limit-chars": "https://www.caniemail.com/features/css-hyphenate-limit-chars/",
|
|
12310
|
+
"hyphens": "https://www.caniemail.com/features/css-hyphens/",
|
|
12311
|
+
"inert": "https://www.caniemail.com/features/css-inert-attribute/",
|
|
12312
|
+
"inline-size": "https://www.caniemail.com/features/css-inline-size/",
|
|
12313
|
+
"inset": "https://www.caniemail.com/features/css-inset/",
|
|
12314
|
+
"justify-content": "https://www.caniemail.com/features/css-justify-content/",
|
|
12315
|
+
"left": "https://www.caniemail.com/features/css-left-right-top-bottom/",
|
|
12316
|
+
"letter-spacing": "https://www.caniemail.com/features/css-letter-spacing/",
|
|
12317
|
+
"light-dark": "https://www.caniemail.com/features/css-function-light-dark/",
|
|
12318
|
+
"line-height": "https://www.caniemail.com/features/css-line-height/",
|
|
12319
|
+
"linear-gradient": "https://www.caniemail.com/features/css-linear-gradient/",
|
|
12320
|
+
"list-style": "https://www.caniemail.com/features/css-list-style/",
|
|
12321
|
+
"list-style-image": "https://www.caniemail.com/features/css-list-style-image/",
|
|
12322
|
+
"list-style-position": "https://www.caniemail.com/features/css-list-style-position/",
|
|
12323
|
+
"list-style-type": "https://www.caniemail.com/features/css-list-style-type/",
|
|
12324
|
+
"margin": "https://www.caniemail.com/features/css-margin/",
|
|
12325
|
+
"margin-block-start-end": "https://www.caniemail.com/features/css-margin-block-start-end/",
|
|
12326
|
+
"margin-inline-block": "https://www.caniemail.com/features/css-margin-inline-block/",
|
|
12327
|
+
"margin-inline-start-end": "https://www.caniemail.com/features/css-margin-inline-start-end/",
|
|
12328
|
+
"mask-image": "https://www.caniemail.com/features/css-mask-image/",
|
|
12329
|
+
"max": "https://www.caniemail.com/features/css-function-max/",
|
|
12330
|
+
"max-block-size": "https://www.caniemail.com/features/css-max-block-size/",
|
|
12331
|
+
"max-height": "https://www.caniemail.com/features/css-max-height/",
|
|
12332
|
+
"max-inline-size": "https://www.caniemail.com/features/css-max-inline-size/",
|
|
12333
|
+
"max-width": "https://www.caniemail.com/features/css-max-width/",
|
|
12334
|
+
"min": "https://www.caniemail.com/features/css-function-min/",
|
|
12335
|
+
"min-block-size": "https://www.caniemail.com/features/css-min-block-size/",
|
|
12336
|
+
"min-height": "https://www.caniemail.com/features/css-min-height/",
|
|
12337
|
+
"min-inline-size": "https://www.caniemail.com/features/css-min-inline-size/",
|
|
12338
|
+
"min-width": "https://www.caniemail.com/features/css-min-width/",
|
|
12339
|
+
"mix-blend-mode": "https://www.caniemail.com/features/css-mix-blend-mode/",
|
|
12340
|
+
"modern-color": "https://www.caniemail.com/features/css-modern-color/",
|
|
12341
|
+
"nesting": "https://www.caniemail.com/features/css-nesting/",
|
|
12342
|
+
"object-fit": "https://www.caniemail.com/features/css-object-fit/",
|
|
12343
|
+
"object-position": "https://www.caniemail.com/features/css-object-position/",
|
|
12344
|
+
"opacity": "https://www.caniemail.com/features/css-opacity/",
|
|
12345
|
+
"orphans": "https://www.caniemail.com/features/css-orphans/",
|
|
12346
|
+
"outline": "https://www.caniemail.com/features/css-outline/",
|
|
12347
|
+
"outline-offset": "https://www.caniemail.com/features/css-outline-offset/",
|
|
12348
|
+
"overflow": "https://www.caniemail.com/features/css-overflow/",
|
|
12349
|
+
"overflow-wrap": "https://www.caniemail.com/features/css-word-wrap/",
|
|
12350
|
+
"padding": "https://www.caniemail.com/features/css-padding/",
|
|
12351
|
+
"padding-block-start-end": "https://www.caniemail.com/features/css-padding-block-start-end/",
|
|
12352
|
+
"padding-inline-block": "https://www.caniemail.com/features/css-padding-inline-block/",
|
|
12353
|
+
"padding-inline-start-end": "https://www.caniemail.com/features/css-padding-inline-start-end/",
|
|
12354
|
+
"position": "https://www.caniemail.com/features/css-position/",
|
|
12355
|
+
"pseudo-class-active": "https://www.caniemail.com/features/css-pseudo-class-active/",
|
|
12356
|
+
"pseudo-class-default": "https://www.caniemail.com/features/css-pseudo-class-default/",
|
|
12357
|
+
"pseudo-class-nth-last-of-type": "https://www.caniemail.com/features/css-pseudo-class-nth-last-of-type/",
|
|
12358
|
+
"radial-gradient": "https://www.caniemail.com/features/css-radial-gradient/",
|
|
12359
|
+
"resize": "https://www.caniemail.com/features/css-resize/",
|
|
12360
|
+
"rgb": "https://www.caniemail.com/features/css-rgb/",
|
|
12361
|
+
"rgba": "https://www.caniemail.com/features/css-rgba/",
|
|
12362
|
+
"scroll-snap": "https://www.caniemail.com/features/css-scroll-snap/",
|
|
12363
|
+
"shape-margin": "https://www.caniemail.com/features/css-shape-margin/",
|
|
12364
|
+
"shape-outside": "https://www.caniemail.com/features/css-shape-outside/",
|
|
12365
|
+
"system-ui": "https://www.caniemail.com/features/css-sytem-ui/",
|
|
12366
|
+
"tab-size": "https://www.caniemail.com/features/css-tab-size/",
|
|
12367
|
+
"table-layout": "https://www.caniemail.com/features/css-table-layout/",
|
|
12368
|
+
"text-align": "https://www.caniemail.com/features/css-text-align/",
|
|
12369
|
+
"text-align-last": "https://www.caniemail.com/features/css-text-align-last/",
|
|
12370
|
+
"text-decoration": "https://www.caniemail.com/features/css-text-decoration/",
|
|
12371
|
+
"text-decoration-color": "https://www.caniemail.com/features/css-text-decoration-color/",
|
|
12372
|
+
"text-decoration-line": "https://www.caniemail.com/features/css-text-decoration-line/",
|
|
12373
|
+
"text-decoration-skip-ink": "https://www.caniemail.com/features/css-text-decoration-skip-ink/",
|
|
12374
|
+
"text-decoration-style": "https://www.caniemail.com/features/css-text-decoration-style/",
|
|
12375
|
+
"text-decoration-thickness": "https://www.caniemail.com/features/css-text-decoration-thickness/",
|
|
12376
|
+
"text-emphasis": "https://www.caniemail.com/features/css-text-emphasis/",
|
|
12377
|
+
"text-emphasis-position": "https://www.caniemail.com/features/css-text-emphasis-position/",
|
|
12378
|
+
"text-indent": "https://www.caniemail.com/features/css-text-indent/",
|
|
12379
|
+
"text-justify": "https://www.caniemail.com/features/css-text-justify/",
|
|
12380
|
+
"text-orientation": "https://www.caniemail.com/features/css-text-orientation/",
|
|
12381
|
+
"text-overflow": "https://www.caniemail.com/features/css-text-overflow/",
|
|
12382
|
+
"text-shadow": "https://www.caniemail.com/features/css-text-shadow/",
|
|
12383
|
+
"text-transform": "https://www.caniemail.com/features/css-text-transform/",
|
|
12384
|
+
"text-underline-offset": "https://www.caniemail.com/features/css-text-underline-offset/",
|
|
12385
|
+
"text-underline-position": "https://www.caniemail.com/features/css-text-underline-position/",
|
|
12386
|
+
"text-wrap": "https://www.caniemail.com/features/css-text-wrap/",
|
|
12387
|
+
"transform": "https://www.caniemail.com/features/css-transform/",
|
|
12388
|
+
"transition": "https://www.caniemail.com/features/css-transition/",
|
|
12389
|
+
"unit-ch": "https://www.caniemail.com/features/css-unit-ch/",
|
|
12390
|
+
"unit-cm": "https://www.caniemail.com/features/css-unit-cm/",
|
|
12391
|
+
"unit-em": "https://www.caniemail.com/features/css-unit-em/",
|
|
12392
|
+
"unit-ex": "https://www.caniemail.com/features/css-unit-ex/",
|
|
12393
|
+
"unit-in": "https://www.caniemail.com/features/css-unit-in/",
|
|
12394
|
+
"unit-initial": "https://www.caniemail.com/features/css-unit-initial/",
|
|
12395
|
+
"unit-mm": "https://www.caniemail.com/features/css-unit-mm/",
|
|
12396
|
+
"unit-pc": "https://www.caniemail.com/features/css-unit-pc/",
|
|
12397
|
+
"unit-percent": "https://www.caniemail.com/features/css-unit-percent/",
|
|
12398
|
+
"unit-pt": "https://www.caniemail.com/features/css-unit-pt/",
|
|
12399
|
+
"unit-px": "https://www.caniemail.com/features/css-unit-px/",
|
|
12400
|
+
"unit-rem": "https://www.caniemail.com/features/css-unit-rem/",
|
|
12401
|
+
"unit-vh": "https://www.caniemail.com/features/css-unit-vh/",
|
|
12402
|
+
"unit-vmax": "https://www.caniemail.com/features/css-unit-vmax/",
|
|
12403
|
+
"unit-vmin": "https://www.caniemail.com/features/css-unit-vmin/",
|
|
12404
|
+
"unit-vw": "https://www.caniemail.com/features/css-unit-vw/",
|
|
12405
|
+
"user-select": "https://www.caniemail.com/features/css-user-select/",
|
|
12406
|
+
"vertical-align": "https://www.caniemail.com/features/css-vertical-align/",
|
|
12407
|
+
"visibility": "https://www.caniemail.com/features/css-visibility/",
|
|
12408
|
+
"white-space": "https://www.caniemail.com/features/css-white-space/",
|
|
12409
|
+
"white-space-collapse": "https://www.caniemail.com/features/css-white-space-collapse/",
|
|
12410
|
+
"widows": "https://www.caniemail.com/features/css-widows/",
|
|
12411
|
+
"width": "https://www.caniemail.com/features/css-width/",
|
|
12412
|
+
"word-break": "https://www.caniemail.com/features/css-word-break/",
|
|
12413
|
+
"word-spacing": "https://www.caniemail.com/features/css-word-spacing/",
|
|
12414
|
+
"writing-mode": "https://www.caniemail.com/features/css-writing-mode/",
|
|
12415
|
+
"z-index": "https://www.caniemail.com/features/css-z-index/"
|
|
12416
|
+
};
|
|
12417
|
+
function featureUrl(code) {
|
|
12418
|
+
return FEATURE_URLS[code];
|
|
12419
|
+
}
|
|
12420
|
+
|
|
12086
12421
|
// src/spam-scorer.ts
|
|
12087
12422
|
var SPAM_TRIGGER_PHRASES = [
|
|
12088
12423
|
"act now",
|
|
@@ -14058,6 +14393,7 @@ var CompileError = class extends Error {
|
|
|
14058
14393
|
CompileError,
|
|
14059
14394
|
EMAIL_CLIENTS,
|
|
14060
14395
|
EMPTY_DELIVERABILITY,
|
|
14396
|
+
FEATURE_URLS,
|
|
14061
14397
|
GENERIC_LINK_TEXT,
|
|
14062
14398
|
HTML_ELEMENT_FEATURES,
|
|
14063
14399
|
MAX_HTML_SIZE,
|
|
@@ -14082,6 +14418,7 @@ var CompileError = class extends Error {
|
|
|
14082
14418
|
errorWarnings,
|
|
14083
14419
|
estimateAiFixTokens,
|
|
14084
14420
|
extractInboxPreview,
|
|
14421
|
+
featureUrl,
|
|
14085
14422
|
formatRgb,
|
|
14086
14423
|
generateAiFix,
|
|
14087
14424
|
generateCompatibilityScore,
|