@emailens/engine 0.12.0 → 0.12.2
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/{chunk-UFSYNSDP.js → chunk-KKWC2C3R.js} +3 -3
- package/dist/chunk-KKWC2C3R.js.map +1 -0
- package/dist/{chunk-PW6EUG2G.js → chunk-OA6VNA5L.js} +16 -4
- package/dist/chunk-OA6VNA5L.js.map +1 -0
- package/dist/compile/index.cjs +18 -6
- package/dist/compile/index.cjs.map +1 -1
- package/dist/compile/index.d.cts +2 -21
- package/dist/compile/index.d.ts +2 -21
- package/dist/compile/index.js +4 -4
- package/dist/index.cjs +388 -29
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +98 -5
- package/dist/index.d.ts +98 -5
- package/dist/index.js +387 -29
- package/dist/index.js.map +1 -1
- package/dist/maizzle-R5KU3HHZ.js +11 -0
- package/dist/{react-email-CVZZNVZ2.js → react-email-2DGTHWTN.js} +2 -2
- package/dist/{react-email-BzyXjPrH.d.ts → react-email-BTIw8QV8.d.ts} +9 -4
- package/dist/{react-email-DHkCZ7TG.d.cts → react-email-Dmn1QpXz.d.cts} +9 -4
- package/dist/server.d.cts +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/{types-edFeUest.d.cts → types-DdFHwMtT.d.cts} +23 -88
- package/dist/{types-edFeUest.d.ts → types-DdFHwMtT.d.ts} +23 -88
- package/package.json +1 -1
- package/dist/chunk-PW6EUG2G.js.map +0 -1
- package/dist/chunk-UFSYNSDP.js.map +0 -1
- package/dist/maizzle-KKJF7XY7.js +0 -9
- /package/dist/{maizzle-KKJF7XY7.js.map → maizzle-R5KU3HHZ.js.map} +0 -0
- /package/dist/{react-email-CVZZNVZ2.js.map → react-email-2DGTHWTN.js.map} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -99,6 +99,7 @@ __export(index_exports, {
|
|
|
99
99
|
checkMobileContrast: () => checkMobileContrast,
|
|
100
100
|
checkOverflow: () => checkOverflow,
|
|
101
101
|
checkSize: () => checkSize,
|
|
102
|
+
checkStyleSurvival: () => checkStyleSurvival,
|
|
102
103
|
checkTemplateVariables: () => checkTemplateVariables,
|
|
103
104
|
checkVisual: () => checkVisual,
|
|
104
105
|
checkVml: () => checkVml,
|
|
@@ -137,7 +138,7 @@ __export(index_exports, {
|
|
|
137
138
|
module.exports = __toCommonJS(index_exports);
|
|
138
139
|
|
|
139
140
|
// src/clients.ts
|
|
140
|
-
var
|
|
141
|
+
var CLIENTS = [
|
|
141
142
|
{
|
|
142
143
|
id: "gmail-web",
|
|
143
144
|
name: "Gmail",
|
|
@@ -312,6 +313,7 @@ var EMAIL_CLIENTS = [
|
|
|
312
313
|
icon: "monitor"
|
|
313
314
|
}
|
|
314
315
|
];
|
|
316
|
+
var EMAIL_CLIENTS = [...CLIENTS];
|
|
315
317
|
function getClient(id) {
|
|
316
318
|
return EMAIL_CLIENTS.find((c) => c.id === id);
|
|
317
319
|
}
|
|
@@ -8751,7 +8753,9 @@ var GRACEFUL_FEATURES = /* @__PURE__ */ new Set([
|
|
|
8751
8753
|
"[aria-labelledby]",
|
|
8752
8754
|
"[aria-live]",
|
|
8753
8755
|
"[lang]",
|
|
8754
|
-
"[dir]"
|
|
8756
|
+
"[dir]",
|
|
8757
|
+
"[width]",
|
|
8758
|
+
"[height]"
|
|
8755
8759
|
]);
|
|
8756
8760
|
var OUTLOOK_WORD_UNSUPPORTED = /* @__PURE__ */ new Set([
|
|
8757
8761
|
"border-radius",
|
|
@@ -11068,9 +11072,74 @@ var MAIZZLE_SUGGESTION_DATABASE = {
|
|
|
11068
11072
|
"opacity::maizzle": "Use solid Tailwind color classes instead of opacity."
|
|
11069
11073
|
};
|
|
11070
11074
|
|
|
11075
|
+
// src/fix-snippets/framework-markup.ts
|
|
11076
|
+
var FRAMEWORK_MARKUP_SUGGESTIONS = {
|
|
11077
|
+
// ── The doctype ───────────────────────────────────────────────────────
|
|
11078
|
+
// Every client that "does not support" it simply rewrites it, and none of
|
|
11079
|
+
// the three frameworks lets you omit it. There is nothing to do.
|
|
11080
|
+
"doctype::mjml": "MJML writes the doctype itself, and the clients that replace it do so without changing how the email renders. Nothing to change in your MJML.",
|
|
11081
|
+
"doctype::maizzle": "Your Maizzle layout writes the doctype, and the clients that replace it do so without changing how the email renders. Nothing to change.",
|
|
11082
|
+
"doctype::jsx": "React Email's <Html> renders the doctype, and the clients that replace it do so without changing how the email renders. Nothing to change.",
|
|
11083
|
+
// ── lang and dir ──────────────────────────────────────────────────────
|
|
11084
|
+
// Dropped, these cost screen-reader language and bidi hints, not layout.
|
|
11085
|
+
"[lang]::mjml": "MJML sets lang on <html> from the lang attribute on <mjml>. Where a client drops it only assistive technology notices, so leave it: removing it helps nobody.",
|
|
11086
|
+
"[lang]::maizzle": "Your Maizzle layout sets lang on <html>. Where a client drops it only assistive technology notices, so leave it.",
|
|
11087
|
+
"[lang]::jsx": "React Email renders this from <Html lang=\u2026>. Where a client drops it only assistive technology notices, so leave it.",
|
|
11088
|
+
"[dir]::mjml": "MJML sets dir from the dir attribute on <mjml>. For a right-to-left email, also set align on mj-text and mj-section, which those clients do honour.",
|
|
11089
|
+
"[dir]::maizzle": "Your Maizzle layout sets dir. For a right-to-left email, also set align on the cells, which these clients do honour.",
|
|
11090
|
+
"[dir]::jsx": "React Email renders this from <Html dir=\u2026>. For a right-to-left email, also set textAlign on the components, which these clients do honour.",
|
|
11091
|
+
// ── role ──────────────────────────────────────────────────────────────
|
|
11092
|
+
// All three add it to their layout tables. Dropping it loses the
|
|
11093
|
+
// screen-reader hint and nothing visual.
|
|
11094
|
+
"[role]::mjml": 'MJML adds role="presentation" to the tables it generates. A client that strips it announces the layout table to screen readers, which is worth knowing but is not something you can change from your MJML.',
|
|
11095
|
+
"[role]::maizzle": "Maizzle's markup transformer adds role to layout tables. A client that strips it announces the table to screen readers; you can turn the transformer off, but the attribute is better present than absent.",
|
|
11096
|
+
"[role]::jsx": "React Email's layout components add role to their tables. A client that strips it announces the table to screen readers, which is not something to fix in your component.",
|
|
11097
|
+
// ── align ─────────────────────────────────────────────────────────────
|
|
11098
|
+
"[align]::mjml": "MJML writes align onto the cells it generates. Set it through the align attribute on mj-section, mj-column, mj-image or mj-text; for the clients that ignore it, add text-align through mj-style and a css-class.",
|
|
11099
|
+
"[align]::maizzle": "Maizzle writes align onto your table cells. For the clients that ignore it, add a text-align utility as well: those survive inlining.",
|
|
11100
|
+
"[align]::jsx": "React Email components render align. For the clients that ignore it, pass textAlign in the style prop too, which is inlined and survives.",
|
|
11101
|
+
// ── width and height ──────────────────────────────────────────────────
|
|
11102
|
+
"[width]::mjml": "MJML writes width from mj-image's or mj-section's width attribute. A client that drops it sizes the element by its content, so also set width through mj-style and a css-class.",
|
|
11103
|
+
"[width]::maizzle": "Maizzle writes width onto the table it generates. A client that drops it sizes by content, so keep a width utility on the element as well: inlined CSS survives where the attribute does not.",
|
|
11104
|
+
"[width]::jsx": "React Email renders width from the component's width prop. A client that drops it sizes by content, so set width in the style prop too.",
|
|
11105
|
+
"[height]::mjml": "MJML writes height from mj-image's height attribute. A client that drops it sizes by content, which for an image means reflow while it loads; set height through mj-style and a css-class as well.",
|
|
11106
|
+
"[height]::maizzle": "Maizzle writes height onto the element. A client that drops it sizes by content, so keep a height utility on it as well.",
|
|
11107
|
+
"[height]::jsx": "React Email renders height from the component's height prop. Set it in the style prop too, so the clients that drop the attribute still reserve the space.",
|
|
11108
|
+
// ── <body> ────────────────────────────────────────────────────────────
|
|
11109
|
+
"<body>::mjml": "MJML styles <body> from mj-body's background-color. Clients that replace <body> with a <div> lose it, so set background-color on mj-section too and the page still fills.",
|
|
11110
|
+
"<body>::maizzle": "Your layout styles <body>. Clients that replace it with a <div> lose those styles, so put the background on a full-width wrapper table as well.",
|
|
11111
|
+
"<body>::jsx": "React Email's <Body> carries your style prop. Clients that replace <body> with a <div> lose it, so put the background on a wrapping <Section> too."
|
|
11112
|
+
};
|
|
11113
|
+
var STYLE_SURVIVAL_NOTES = {
|
|
11114
|
+
"gmail-space-separated-color::mjml": "The block comes from mj-style; write the colour with commas there.",
|
|
11115
|
+
"gmail-space-separated-color::maizzle": "Maizzle rewrites colours it inlines, but a rule left in a <style> block passes through untouched. Fix it in your CSS source.",
|
|
11116
|
+
"gmail-space-separated-color::jsx": "The block comes from a <style> element in your component; write the colour with commas.",
|
|
11117
|
+
"gmail-space-separated-color-inline::mjml": "MJML builds inline styles from component attributes, so the colour came from one of them: write it with commas.",
|
|
11118
|
+
"gmail-space-separated-color-inline::maizzle": "This survived inlining, so it was written inline in your template rather than in a utility class. Write it with commas.",
|
|
11119
|
+
"gmail-space-separated-color-inline::jsx": "The colour is in a style prop object; write it with commas.",
|
|
11120
|
+
"outlook-double-brace::mjml": "The stylesheet is assembled from mj-style. Put a newline before the closing brace of the media query, or stop minifying that block.",
|
|
11121
|
+
"outlook-double-brace::maizzle": "Maizzle's minifier produces this. Leave a space between the braces in the source, or exclude the <style> block from minification.",
|
|
11122
|
+
"outlook-double-brace::jsx": "The stylesheet comes from a <style> element in your component. Put a newline before the media query's closing brace.",
|
|
11123
|
+
"yahoo-attribute-selector-semicolon::mjml": "The selector is in mj-style; split it into [style^=\u2026][style$=\u2026] there.",
|
|
11124
|
+
"yahoo-attribute-selector-semicolon::maizzle": "The selector is in your CSS source; split it into [style^=\u2026][style$=\u2026] there.",
|
|
11125
|
+
"yahoo-attribute-selector-semicolon::jsx": "The selector is in a <style> element in your component; split it into [style^=\u2026][style$=\u2026].",
|
|
11126
|
+
"outlook-web-chained-class::mjml": "css-class takes several names, and MJML passes them through as written. Give the element one class carrying both rules instead.",
|
|
11127
|
+
"outlook-web-chained-class::maizzle": "A compound like .a.b usually comes from @apply or an arbitrary variant. Flatten it into a single class in your component layer.",
|
|
11128
|
+
"outlook-web-chained-class::jsx": "Give the element one class carrying both rules, or move the declarations into the style prop, which is inlined and unaffected.",
|
|
11129
|
+
"outlook-first-class-only::mjml": "The classes come from css-class. Merge the conflicting ones into a single class in mj-style.",
|
|
11130
|
+
"outlook-first-class-only::maizzle": "Inlining sidesteps this entirely, and Maizzle inlines by default. If you turned it off, merge the conflicting utilities into one component class.",
|
|
11131
|
+
"outlook-first-class-only::jsx": "Merge the conflicting classes, or move the declarations into the style prop, which is inlined and unaffected by this.",
|
|
11132
|
+
"outlook-first-class-descendants::mjml": "The rule is in mj-style. Target the child directly with its own css-class rather than through a descendant selector.",
|
|
11133
|
+
"outlook-first-class-descendants::maizzle": "Inlining sidesteps this, and Maizzle inlines by default. Otherwise put the rule on the child element rather than on an ancestor's class.",
|
|
11134
|
+
"outlook-first-class-descendants::jsx": "Put the declarations on the child component, through its own class or its style prop, rather than through a descendant selector."
|
|
11135
|
+
};
|
|
11136
|
+
|
|
11071
11137
|
// src/fix-snippets/index.ts
|
|
11072
11138
|
var FIX_DATABASE = __spreadValues(__spreadValues(__spreadValues(__spreadValues({}, HTML_FIX_DATABASE), JSX_FIX_DATABASE), MJML_FIX_DATABASE), MAIZZLE_FIX_DATABASE);
|
|
11073
|
-
var SUGGESTION_DATABASE = __spreadValues(__spreadValues(__spreadValues(__spreadValues({}, HTML_SUGGESTION_DATABASE), JSX_SUGGESTION_DATABASE), MJML_SUGGESTION_DATABASE), MAIZZLE_SUGGESTION_DATABASE);
|
|
11139
|
+
var SUGGESTION_DATABASE = __spreadValues(__spreadValues(__spreadValues(__spreadValues(__spreadValues({}, HTML_SUGGESTION_DATABASE), JSX_SUGGESTION_DATABASE), MJML_SUGGESTION_DATABASE), MAIZZLE_SUGGESTION_DATABASE), FRAMEWORK_MARKUP_SUGGESTIONS);
|
|
11140
|
+
function getStyleSurvivalNote(rule, framework) {
|
|
11141
|
+
return framework ? STYLE_SURVIVAL_NOTES[`${rule}::${framework}`] : void 0;
|
|
11142
|
+
}
|
|
11074
11143
|
function getCodeFix(property, clientId, framework) {
|
|
11075
11144
|
const clientPrefix = getClientPrefix(clientId);
|
|
11076
11145
|
if (framework && clientPrefix) {
|
|
@@ -12171,6 +12240,8 @@ var GENERIC_LINK_TEXT = /* @__PURE__ */ new Set([
|
|
|
12171
12240
|
]);
|
|
12172
12241
|
var GMAIL_CLIP_THRESHOLD = 102 * 1024;
|
|
12173
12242
|
var GMAIL_CLIP_WARNING_THRESHOLD = 90 * 1024;
|
|
12243
|
+
var GMAIL_STYLE_LIMIT = 16 * 1024;
|
|
12244
|
+
var GMAIL_STYLE_WARNING_THRESHOLD = 14 * 1024;
|
|
12174
12245
|
var CLIENT_DISPLAY_LIMITS = [
|
|
12175
12246
|
{ client: "Gmail (Web)", subjectLimit: 70, preheaderLimit: 90 },
|
|
12176
12247
|
{ client: "Gmail (Mobile)", subjectLimit: 40, preheaderLimit: 45 },
|
|
@@ -12205,7 +12276,8 @@ var EMPTY_LINKS = {
|
|
|
12205
12276
|
var EMPTY_ACCESSIBILITY = { score: 100, issues: [] };
|
|
12206
12277
|
var EMPTY_IMAGES = { total: 0, totalDataUriBytes: 0, issues: [], images: [] };
|
|
12207
12278
|
var EMPTY_INBOX_PREVIEW = { subject: null, preheader: null, subjectLength: 0, preheaderLength: 0, truncation: [], issues: [] };
|
|
12208
|
-
var EMPTY_SIZE = { htmlBytes: 0, humanSize: "0 B", clipped: false, issues: [] };
|
|
12279
|
+
var EMPTY_SIZE = { htmlBytes: 0, styleBytes: 0, humanSize: "0 B", clipped: false, issues: [] };
|
|
12280
|
+
var EMPTY_STYLE_SURVIVAL = { issues: [] };
|
|
12209
12281
|
var EMPTY_TEMPLATE = { unresolvedCount: 0, issues: [] };
|
|
12210
12282
|
var EMPTY_OVERFLOW = { hasOverflow: false, issues: [] };
|
|
12211
12283
|
var EMPTY_VML = { hasVml: false, issues: [] };
|
|
@@ -14744,6 +14816,274 @@ function extractCode(response) {
|
|
|
14744
14816
|
return response.trim();
|
|
14745
14817
|
}
|
|
14746
14818
|
|
|
14819
|
+
// src/style-survival.ts
|
|
14820
|
+
var csstree6 = __toESM(require("css-tree"), 1);
|
|
14821
|
+
var WORD_ENGINE = ["outlook-windows-legacy"];
|
|
14822
|
+
var OUTLOOK_WEB = ["outlook-web", "outlook-windows"];
|
|
14823
|
+
var OUTLOOK_WEB_AND_MOBILE = [
|
|
14824
|
+
"outlook-web",
|
|
14825
|
+
"outlook-windows",
|
|
14826
|
+
"outlook-ios",
|
|
14827
|
+
"outlook-android"
|
|
14828
|
+
];
|
|
14829
|
+
var GMAIL = ["gmail-web", "gmail-android", "gmail-ios"];
|
|
14830
|
+
var YAHOO_AOL = [
|
|
14831
|
+
"yahoo-mail",
|
|
14832
|
+
"yahoo-mail-android",
|
|
14833
|
+
"yahoo-mail-ios",
|
|
14834
|
+
"aol"
|
|
14835
|
+
];
|
|
14836
|
+
var MAX_LOCS = 20;
|
|
14837
|
+
var MAX_COLOR_FUNCTION_LENGTH = 256;
|
|
14838
|
+
function hasSpaceSeparatedColor(css) {
|
|
14839
|
+
const fn = /\b(?:rgba?|hsla?)\(/gi;
|
|
14840
|
+
let m;
|
|
14841
|
+
while (m = fn.exec(css)) {
|
|
14842
|
+
const start = m.index + m[0].length;
|
|
14843
|
+
const limit = Math.min(css.length, start + MAX_COLOR_FUNCTION_LENGTH);
|
|
14844
|
+
let depth = 1;
|
|
14845
|
+
let comma = false;
|
|
14846
|
+
let i = start;
|
|
14847
|
+
for (; i < limit && depth > 0; i++) {
|
|
14848
|
+
const c = css[i];
|
|
14849
|
+
if (c === "(") depth++;
|
|
14850
|
+
else if (c === ")") depth--;
|
|
14851
|
+
else if (c === "," && depth === 1) comma = true;
|
|
14852
|
+
}
|
|
14853
|
+
if (depth > 0) continue;
|
|
14854
|
+
const args = css.slice(start, i - 1);
|
|
14855
|
+
if (!comma && /\S\s+\S/.test(args)) return true;
|
|
14856
|
+
}
|
|
14857
|
+
return false;
|
|
14858
|
+
}
|
|
14859
|
+
var ATTR_SELECTOR_SEMICOLON = /\[[^\]]*;[^\]]*\]/;
|
|
14860
|
+
var DOUBLE_BRACE = /\}\}/;
|
|
14861
|
+
function readSheet(ast) {
|
|
14862
|
+
const propsByClass = /* @__PURE__ */ new Map();
|
|
14863
|
+
const classesWithDescendants = /* @__PURE__ */ new Set();
|
|
14864
|
+
const chainedClassSelectors = [];
|
|
14865
|
+
csstree6.walk(ast, {
|
|
14866
|
+
visit: "Rule",
|
|
14867
|
+
enter(rule) {
|
|
14868
|
+
var _a;
|
|
14869
|
+
if (rule.type !== "Rule" || rule.prelude.type !== "SelectorList") return;
|
|
14870
|
+
const props = /* @__PURE__ */ new Set();
|
|
14871
|
+
csstree6.walk(rule.block, {
|
|
14872
|
+
visit: "Declaration",
|
|
14873
|
+
enter(d) {
|
|
14874
|
+
if (d.type === "Declaration") props.add(d.property.toLowerCase());
|
|
14875
|
+
}
|
|
14876
|
+
});
|
|
14877
|
+
for (const selector of rule.prelude.children) {
|
|
14878
|
+
if (selector.type !== "Selector") continue;
|
|
14879
|
+
const parts = selector.children.toArray();
|
|
14880
|
+
const classes = parts.filter((p) => p.type === "ClassSelector");
|
|
14881
|
+
if (!classes.length) continue;
|
|
14882
|
+
let run = 0;
|
|
14883
|
+
for (const part of parts) {
|
|
14884
|
+
if (part.type === "ClassSelector" || part.type === "IdSelector") {
|
|
14885
|
+
run++;
|
|
14886
|
+
if (run === 2) {
|
|
14887
|
+
chainedClassSelectors.push(csstree6.generate(selector));
|
|
14888
|
+
break;
|
|
14889
|
+
}
|
|
14890
|
+
} else if (part.type !== "PseudoClassSelector" && part.type !== "PseudoElementSelector") {
|
|
14891
|
+
run = 0;
|
|
14892
|
+
}
|
|
14893
|
+
}
|
|
14894
|
+
const hasCombinator = parts.some(
|
|
14895
|
+
(p) => p.type === "Combinator" || p.type === "WhiteSpace"
|
|
14896
|
+
);
|
|
14897
|
+
if (hasCombinator) {
|
|
14898
|
+
const head = parts[0];
|
|
14899
|
+
if ((head == null ? void 0 : head.type) === "ClassSelector") classesWithDescendants.add(head.name);
|
|
14900
|
+
continue;
|
|
14901
|
+
}
|
|
14902
|
+
if (parts.length === classes.length && classes.length === 1) {
|
|
14903
|
+
const name = classes[0].name;
|
|
14904
|
+
const seen = (_a = propsByClass.get(name)) != null ? _a : /* @__PURE__ */ new Set();
|
|
14905
|
+
for (const p of props) seen.add(p);
|
|
14906
|
+
propsByClass.set(name, seen);
|
|
14907
|
+
}
|
|
14908
|
+
}
|
|
14909
|
+
}
|
|
14910
|
+
});
|
|
14911
|
+
return { propsByClass, classesWithDescendants, chainedClassSelectors };
|
|
14912
|
+
}
|
|
14913
|
+
function checkStyleSurvivalFromDom($, source, framework) {
|
|
14914
|
+
const issues = [];
|
|
14915
|
+
const add2 = (rule, severity, clients, message, locs, detail) => {
|
|
14916
|
+
const frameworkNote = getStyleSurvivalNote(rule, framework);
|
|
14917
|
+
issues.push(__spreadValues(__spreadValues(__spreadValues(__spreadValues({
|
|
14918
|
+
rule,
|
|
14919
|
+
severity,
|
|
14920
|
+
clients: [...clients],
|
|
14921
|
+
message
|
|
14922
|
+
}, frameworkNote ? { frameworkNote } : {}), detail ? { detail } : {}), locs.length ? { loc: locs[0], locs: locs.slice(0, MAX_LOCS) } : {}), locs.length > MAX_LOCS ? { locsTruncated: true } : {}));
|
|
14923
|
+
};
|
|
14924
|
+
const sheets = [];
|
|
14925
|
+
const hits = { spaceColor: 0, doubleBrace: 0, attrSemicolon: 0, chained: 0 };
|
|
14926
|
+
const spaceColorLocs = [];
|
|
14927
|
+
const doubleBraceLocs = [];
|
|
14928
|
+
const attrSemicolonLocs = [];
|
|
14929
|
+
const chainedLocs = [];
|
|
14930
|
+
let chainedExample = "";
|
|
14931
|
+
$("style").each((_, el) => {
|
|
14932
|
+
const cssText = $(el).text();
|
|
14933
|
+
if (!cssText.trim()) return;
|
|
14934
|
+
const anchor = cssBlockAnchor(el, cssText, source);
|
|
14935
|
+
const at = (index) => {
|
|
14936
|
+
const before = cssText.slice(0, index);
|
|
14937
|
+
const line = before.split("\n").length;
|
|
14938
|
+
const column = index - before.lastIndexOf("\n");
|
|
14939
|
+
return locInCssBlock(anchor, {
|
|
14940
|
+
start: { line, column, offset: index },
|
|
14941
|
+
end: { line, column: column + 1, offset: index + 1 }
|
|
14942
|
+
});
|
|
14943
|
+
};
|
|
14944
|
+
const push = (into, index) => {
|
|
14945
|
+
var _a;
|
|
14946
|
+
const loc = (_a = at(index)) != null ? _a : locOfElement(el);
|
|
14947
|
+
if (loc) into.push(loc);
|
|
14948
|
+
};
|
|
14949
|
+
if (hasSpaceSeparatedColor(cssText)) {
|
|
14950
|
+
hits.spaceColor++;
|
|
14951
|
+
push(spaceColorLocs, cssText.search(/\b(?:rgba?|hsla?)\(/i));
|
|
14952
|
+
}
|
|
14953
|
+
const brace = cssText.search(DOUBLE_BRACE);
|
|
14954
|
+
if (brace >= 0) {
|
|
14955
|
+
hits.doubleBrace++;
|
|
14956
|
+
push(doubleBraceLocs, brace);
|
|
14957
|
+
}
|
|
14958
|
+
const attr2 = cssText.search(ATTR_SELECTOR_SEMICOLON);
|
|
14959
|
+
if (attr2 >= 0) {
|
|
14960
|
+
hits.attrSemicolon++;
|
|
14961
|
+
push(attrSemicolonLocs, attr2);
|
|
14962
|
+
}
|
|
14963
|
+
try {
|
|
14964
|
+
const sheet = readSheet(csstree6.parse(cssText, { positions: true }));
|
|
14965
|
+
sheets.push(sheet);
|
|
14966
|
+
if (sheet.chainedClassSelectors.length) {
|
|
14967
|
+
hits.chained++;
|
|
14968
|
+
chainedExample || (chainedExample = sheet.chainedClassSelectors[0]);
|
|
14969
|
+
push(chainedLocs, 0);
|
|
14970
|
+
}
|
|
14971
|
+
} catch (e) {
|
|
14972
|
+
}
|
|
14973
|
+
});
|
|
14974
|
+
const inlineColorEls = $("[style]").toArray().filter((el) => {
|
|
14975
|
+
var _a;
|
|
14976
|
+
return hasSpaceSeparatedColor((_a = $(el).attr("style")) != null ? _a : "");
|
|
14977
|
+
});
|
|
14978
|
+
if (hits.spaceColor) {
|
|
14979
|
+
add2(
|
|
14980
|
+
"gmail-space-separated-color",
|
|
14981
|
+
"error",
|
|
14982
|
+
GMAIL,
|
|
14983
|
+
"Gmail removes an entire <style> block containing a space-separated colour like `rgb(0 0 0)`. Write it with commas, `rgb(0, 0, 0)`, and the block survives.",
|
|
14984
|
+
spaceColorLocs
|
|
14985
|
+
);
|
|
14986
|
+
}
|
|
14987
|
+
if (inlineColorEls.length) {
|
|
14988
|
+
add2(
|
|
14989
|
+
"gmail-space-separated-color-inline",
|
|
14990
|
+
"error",
|
|
14991
|
+
GMAIL,
|
|
14992
|
+
`Gmail drops every declaration in a style attribute that uses a space-separated colour like \`rgb(0 0 0)\`, not only the colour. ${inlineColorEls.length} element${inlineColorEls.length > 1 ? "s are" : " is"} affected; write the colour with commas.`,
|
|
14993
|
+
inlineColorEls.map((el) => locOfElement(el)).filter((l) => !!l)
|
|
14994
|
+
);
|
|
14995
|
+
}
|
|
14996
|
+
if (hits.doubleBrace) {
|
|
14997
|
+
add2(
|
|
14998
|
+
"outlook-double-brace",
|
|
14999
|
+
"error",
|
|
15000
|
+
OUTLOOK_WEB_AND_MOBILE,
|
|
15001
|
+
"Outlook.com and Outlook on iOS/Android discard every style rule after `}}`. Minified CSS closing a media query produces it. Put a space or newline between the two braces and the rest of the sheet survives.",
|
|
15002
|
+
doubleBraceLocs
|
|
15003
|
+
);
|
|
15004
|
+
}
|
|
15005
|
+
if (hits.attrSemicolon) {
|
|
15006
|
+
add2(
|
|
15007
|
+
"yahoo-attribute-selector-semicolon",
|
|
15008
|
+
"error",
|
|
15009
|
+
YAHOO_AOL,
|
|
15010
|
+
'Yahoo and AOL discard every style rule after an attribute selector whose value contains a semicolon, such as `div[style="margin: 16px;"]`. Split it into `[style^=\u2026][style$=\u2026]` and the rest of the sheet survives.',
|
|
15011
|
+
attrSemicolonLocs
|
|
15012
|
+
);
|
|
15013
|
+
}
|
|
15014
|
+
if (hits.chained) {
|
|
15015
|
+
add2(
|
|
15016
|
+
"outlook-web-chained-class",
|
|
15017
|
+
"warning",
|
|
15018
|
+
OUTLOOK_WEB,
|
|
15019
|
+
`Outlook.com rewrites class names to namespace them and only rewrites the first in a compound selector, so \`${chainedExample}\` stops matching. Give the element one class that carries both rules.`,
|
|
15020
|
+
chainedLocs
|
|
15021
|
+
);
|
|
15022
|
+
}
|
|
15023
|
+
const conflicting = [];
|
|
15024
|
+
const shadowed = [];
|
|
15025
|
+
let conflictCount = 0;
|
|
15026
|
+
let shadowCount = 0;
|
|
15027
|
+
let conflictExample = "";
|
|
15028
|
+
$("[class]").each((_, el) => {
|
|
15029
|
+
var _a, _b;
|
|
15030
|
+
const names = ((_a = $(el).attr("class")) != null ? _a : "").split(/\s+/).filter(Boolean);
|
|
15031
|
+
if (names.length < 2) return;
|
|
15032
|
+
const loc = locOfElement(el);
|
|
15033
|
+
const seen = /* @__PURE__ */ new Map();
|
|
15034
|
+
for (const sheet of sheets) {
|
|
15035
|
+
for (const name of names) {
|
|
15036
|
+
for (const prop of (_b = sheet.propsByClass.get(name)) != null ? _b : []) {
|
|
15037
|
+
const first = seen.get(prop);
|
|
15038
|
+
if (first && first !== name) {
|
|
15039
|
+
conflictExample || (conflictExample = `.${first} and .${name} both set ${prop}`);
|
|
15040
|
+
conflictCount++;
|
|
15041
|
+
if (loc) conflicting.push(loc);
|
|
15042
|
+
return;
|
|
15043
|
+
}
|
|
15044
|
+
if (!first) seen.set(prop, name);
|
|
15045
|
+
}
|
|
15046
|
+
}
|
|
15047
|
+
if (names.some((n) => sheet.classesWithDescendants.has(n))) {
|
|
15048
|
+
shadowCount++;
|
|
15049
|
+
if (loc) shadowed.push(loc);
|
|
15050
|
+
return;
|
|
15051
|
+
}
|
|
15052
|
+
}
|
|
15053
|
+
});
|
|
15054
|
+
if (conflictCount) {
|
|
15055
|
+
add2(
|
|
15056
|
+
"outlook-first-class-only",
|
|
15057
|
+
"warning",
|
|
15058
|
+
WORD_ENGINE,
|
|
15059
|
+
`Outlook on Windows applies only the first class on an element, so a second class setting the same property never takes effect (${conflictExample}). Merge them into one class.`,
|
|
15060
|
+
conflicting
|
|
15061
|
+
);
|
|
15062
|
+
}
|
|
15063
|
+
if (shadowCount) {
|
|
15064
|
+
add2(
|
|
15065
|
+
"outlook-first-class-descendants",
|
|
15066
|
+
"warning",
|
|
15067
|
+
WORD_ENGINE,
|
|
15068
|
+
"Outlook on Windows ignores a descendant rule like `.text td` once the element carries a second class. Move the rule onto the child, or give the element a single class.",
|
|
15069
|
+
shadowed
|
|
15070
|
+
);
|
|
15071
|
+
}
|
|
15072
|
+
return { issues };
|
|
15073
|
+
}
|
|
15074
|
+
function checkStyleSurvival(html, options) {
|
|
15075
|
+
return fromHtml(
|
|
15076
|
+
html,
|
|
15077
|
+
EMPTY_STYLE_SURVIVAL,
|
|
15078
|
+
($) => checkStyleSurvivalFromDom(
|
|
15079
|
+
$,
|
|
15080
|
+
(options == null ? void 0 : options.positions) ? html : void 0,
|
|
15081
|
+
options == null ? void 0 : options.framework
|
|
15082
|
+
),
|
|
15083
|
+
options
|
|
15084
|
+
);
|
|
15085
|
+
}
|
|
15086
|
+
|
|
14747
15087
|
// src/rules/feature-urls.ts
|
|
14748
15088
|
var FEATURE_URLS = {
|
|
14749
15089
|
"!important": "https://www.caniemail.com/features/css-important/",
|
|
@@ -15693,7 +16033,7 @@ function validateLinks(html, options) {
|
|
|
15693
16033
|
|
|
15694
16034
|
// src/accessibility-checker.ts
|
|
15695
16035
|
var cheerio5 = __toESM(require("cheerio"), 1);
|
|
15696
|
-
var
|
|
16036
|
+
var csstree7 = __toESM(require("css-tree"), 1);
|
|
15697
16037
|
var RULE_PENALTY_CAPS = {
|
|
15698
16038
|
"img-missing-alt": 3,
|
|
15699
16039
|
"link-generic-text": 3,
|
|
@@ -15937,7 +16277,7 @@ function specificityFromTokens(selector) {
|
|
|
15937
16277
|
}
|
|
15938
16278
|
function specificityOf(selector) {
|
|
15939
16279
|
try {
|
|
15940
|
-
const ast =
|
|
16280
|
+
const ast = csstree7.parse(selector, { context: "selector" });
|
|
15941
16281
|
return specificityOfSelector(ast);
|
|
15942
16282
|
} catch (e) {
|
|
15943
16283
|
return specificityFromTokens(selector);
|
|
@@ -15982,14 +16322,14 @@ function computeCascade($, ctx) {
|
|
|
15982
16322
|
});
|
|
15983
16323
|
});
|
|
15984
16324
|
const handleRule = (node) => {
|
|
15985
|
-
const selectorList =
|
|
16325
|
+
const selectorList = csstree7.generate(node.prelude).trim();
|
|
15986
16326
|
if (!selectorList) return;
|
|
15987
16327
|
const decls = [];
|
|
15988
16328
|
node.block.children.forEach((child) => {
|
|
15989
16329
|
if (child.type !== "Declaration") return;
|
|
15990
16330
|
const prop = child.property.toLowerCase();
|
|
15991
16331
|
if (!CASCADE_PROPS.has(prop)) return;
|
|
15992
|
-
decls.push({ prop, value:
|
|
16332
|
+
decls.push({ prop, value: csstree7.generate(child.value), important: !!child.important });
|
|
15993
16333
|
});
|
|
15994
16334
|
if (!decls.length) return;
|
|
15995
16335
|
for (const selector of splitTopLevel(selectorList).map((x) => x.trim()).filter(Boolean)) {
|
|
@@ -16019,7 +16359,7 @@ function computeCascade($, ctx) {
|
|
|
16019
16359
|
return;
|
|
16020
16360
|
}
|
|
16021
16361
|
if (node.type !== "Atrule" || node.name.toLowerCase() !== "media" || !node.block) return;
|
|
16022
|
-
const prelude = node.prelude ?
|
|
16362
|
+
const prelude = node.prelude ? csstree7.generate(node.prelude) : "";
|
|
16023
16363
|
if (!mediaApplies(prelude, ctx)) return;
|
|
16024
16364
|
visitBlock(node.block.children);
|
|
16025
16365
|
});
|
|
@@ -16027,7 +16367,7 @@ function computeCascade($, ctx) {
|
|
|
16027
16367
|
$("style").each((_, styleEl) => {
|
|
16028
16368
|
let ast;
|
|
16029
16369
|
try {
|
|
16030
|
-
ast =
|
|
16370
|
+
ast = csstree7.parse($(styleEl).text());
|
|
16031
16371
|
} catch (e) {
|
|
16032
16372
|
return;
|
|
16033
16373
|
}
|
|
@@ -16808,8 +17148,9 @@ function humanizeBytes(bytes) {
|
|
|
16808
17148
|
const mb = kb / 1024;
|
|
16809
17149
|
return `${mb.toFixed(2)} MB`;
|
|
16810
17150
|
}
|
|
16811
|
-
function checkSizeFromDom(
|
|
17151
|
+
function checkSizeFromDom($, html) {
|
|
16812
17152
|
const htmlBytes = new TextEncoder().encode(html).length;
|
|
17153
|
+
const styleBytes = $("style").toArray().reduce((n, el) => n + new TextEncoder().encode($(el).text()).length, 0);
|
|
16813
17154
|
const humanSize = humanizeBytes(htmlBytes);
|
|
16814
17155
|
const issues = [];
|
|
16815
17156
|
let clipped = false;
|
|
@@ -16829,7 +17170,22 @@ function checkSizeFromDom(_$, html) {
|
|
|
16829
17170
|
detail: `${htmlBytes} bytes is within ${GMAIL_CLIP_THRESHOLD - htmlBytes} bytes of the clip threshold.`
|
|
16830
17171
|
});
|
|
16831
17172
|
}
|
|
16832
|
-
|
|
17173
|
+
if (styleBytes > GMAIL_STYLE_LIMIT) {
|
|
17174
|
+
issues.push({
|
|
17175
|
+
rule: "gmail-style-truncated",
|
|
17176
|
+
severity: "error",
|
|
17177
|
+
message: `${humanizeBytes(styleBytes)} of CSS is past Gmail's 16 KB ceiling for <style>. Gmail keeps the rules before the limit and drops the rest, so the email renders with part of its CSS and no error anywhere.`,
|
|
17178
|
+
detail: `${styleBytes} bytes of CSS across all <style> elements exceeds the ${GMAIL_STYLE_LIMIT} byte limit.`
|
|
17179
|
+
});
|
|
17180
|
+
} else if (styleBytes > GMAIL_STYLE_WARNING_THRESHOLD) {
|
|
17181
|
+
issues.push({
|
|
17182
|
+
rule: "gmail-style-warning",
|
|
17183
|
+
severity: "warning",
|
|
17184
|
+
message: `${humanizeBytes(styleBytes)} of CSS, approaching Gmail's 16 KB <style> ceiling. Past it, later rules are dropped silently.`,
|
|
17185
|
+
detail: `${styleBytes} bytes is within ${GMAIL_STYLE_LIMIT - styleBytes} bytes of the limit.`
|
|
17186
|
+
});
|
|
17187
|
+
}
|
|
17188
|
+
return { htmlBytes, styleBytes, humanSize, clipped, issues };
|
|
16833
17189
|
}
|
|
16834
17190
|
function checkSize(html) {
|
|
16835
17191
|
return fromHtml(html, EMPTY_SIZE, checkSizeFromDom);
|
|
@@ -16943,7 +17299,7 @@ function checkTemplateVariables(html, options) {
|
|
|
16943
17299
|
}
|
|
16944
17300
|
|
|
16945
17301
|
// src/overflow-checker.ts
|
|
16946
|
-
var
|
|
17302
|
+
var csstree8 = __toESM(require("css-tree"), 1);
|
|
16947
17303
|
var RESPONSIVE_MIN_WIDTH = 320;
|
|
16948
17304
|
function fixedPxWidth($el) {
|
|
16949
17305
|
const style = $el.attr("style") || "";
|
|
@@ -17020,11 +17376,11 @@ function checkOverflowFromDom($, source) {
|
|
|
17020
17376
|
const anchor = cssBlockAnchor(el, cssText, source);
|
|
17021
17377
|
let ast;
|
|
17022
17378
|
try {
|
|
17023
|
-
ast =
|
|
17379
|
+
ast = csstree8.parse(cssText, { positions: true });
|
|
17024
17380
|
} catch (e) {
|
|
17025
17381
|
return;
|
|
17026
17382
|
}
|
|
17027
|
-
|
|
17383
|
+
csstree8.walk(ast, {
|
|
17028
17384
|
visit: "Rule",
|
|
17029
17385
|
enter(node) {
|
|
17030
17386
|
if (node.type !== "Rule") return;
|
|
@@ -17034,7 +17390,7 @@ function checkOverflowFromDom($, source) {
|
|
|
17034
17390
|
node.block.children.forEach((child) => {
|
|
17035
17391
|
if (child.type !== "Declaration") return;
|
|
17036
17392
|
const prop = child.property.toLowerCase();
|
|
17037
|
-
const val =
|
|
17393
|
+
const val = csstree8.generate(child.value);
|
|
17038
17394
|
if (prop === "width") {
|
|
17039
17395
|
const m = val.match(/^(\d+)px$/);
|
|
17040
17396
|
if (m) {
|
|
@@ -17047,7 +17403,7 @@ function checkOverflowFromDom($, source) {
|
|
|
17047
17403
|
}
|
|
17048
17404
|
});
|
|
17049
17405
|
if (widthPx !== null && widthPx > EMAIL_MAX_WIDTH && !fluid) {
|
|
17050
|
-
const selector =
|
|
17406
|
+
const selector = csstree8.generate(node.prelude).trim().slice(0, 40);
|
|
17051
17407
|
addWidthIssue(widthPx, selector || "rule", issues, seen, widthLoc);
|
|
17052
17408
|
}
|
|
17053
17409
|
}
|
|
@@ -17372,7 +17728,7 @@ function checkVml(html, options) {
|
|
|
17372
17728
|
}
|
|
17373
17729
|
|
|
17374
17730
|
// src/visual-checker.ts
|
|
17375
|
-
var
|
|
17731
|
+
var csstree9 = __toESM(require("css-tree"), 1);
|
|
17376
17732
|
var CSS_WIDE_KEYWORDS = /* @__PURE__ */ new Set(["inherit", "initial", "unset", "revert", "revert-layer"]);
|
|
17377
17733
|
var GRADIENT_RE = /(?:linear|radial|conic)-gradient\(/i;
|
|
17378
17734
|
function isSolidColor(value) {
|
|
@@ -17478,7 +17834,7 @@ function ruleToMap(node) {
|
|
|
17478
17834
|
const map = /* @__PURE__ */ new Map();
|
|
17479
17835
|
node.block.children.forEach((child) => {
|
|
17480
17836
|
if (child.type === "Declaration") {
|
|
17481
|
-
map.set(child.property.toLowerCase(),
|
|
17837
|
+
map.set(child.property.toLowerCase(), csstree9.generate(child.value));
|
|
17482
17838
|
}
|
|
17483
17839
|
});
|
|
17484
17840
|
return map;
|
|
@@ -17497,11 +17853,11 @@ function checkVisualFromDom($, source) {
|
|
|
17497
17853
|
const anchor = cssBlockAnchor(el, cssText, source);
|
|
17498
17854
|
let ast;
|
|
17499
17855
|
try {
|
|
17500
|
-
ast =
|
|
17856
|
+
ast = csstree9.parse(cssText, { positions: true });
|
|
17501
17857
|
} catch (e) {
|
|
17502
17858
|
return;
|
|
17503
17859
|
}
|
|
17504
|
-
|
|
17860
|
+
csstree9.walk(ast, {
|
|
17505
17861
|
visit: "Rule",
|
|
17506
17862
|
enter(node) {
|
|
17507
17863
|
if (node.type !== "Rule") return;
|
|
@@ -17527,7 +17883,7 @@ function checkVisual(html, options) {
|
|
|
17527
17883
|
}
|
|
17528
17884
|
|
|
17529
17885
|
// src/design-consistency.ts
|
|
17530
|
-
var
|
|
17886
|
+
var csstree10 = __toESM(require("css-tree"), 1);
|
|
17531
17887
|
var SAME_COLOUR_DISTANCE = 0.02;
|
|
17532
17888
|
var LIMITS = {
|
|
17533
17889
|
fontSize: 8,
|
|
@@ -17581,15 +17937,15 @@ function collect($) {
|
|
|
17581
17937
|
$("style").each((_, el) => {
|
|
17582
17938
|
let ast;
|
|
17583
17939
|
try {
|
|
17584
|
-
ast =
|
|
17940
|
+
ast = csstree10.parse($(el).text());
|
|
17585
17941
|
} catch (e) {
|
|
17586
17942
|
return;
|
|
17587
17943
|
}
|
|
17588
|
-
|
|
17944
|
+
csstree10.walk(ast, {
|
|
17589
17945
|
visit: "Declaration",
|
|
17590
17946
|
enter(node) {
|
|
17591
17947
|
if (node.type !== "Declaration") return;
|
|
17592
|
-
record(node.property,
|
|
17948
|
+
record(node.property, csstree10.generate(node.value));
|
|
17593
17949
|
}
|
|
17594
17950
|
});
|
|
17595
17951
|
});
|
|
@@ -17698,6 +18054,7 @@ var EMPTY_AUDIT = {
|
|
|
17698
18054
|
images: EMPTY_IMAGES,
|
|
17699
18055
|
inboxPreview: EMPTY_INBOX_PREVIEW,
|
|
17700
18056
|
size: EMPTY_SIZE,
|
|
18057
|
+
styleSurvival: EMPTY_STYLE_SURVIVAL,
|
|
17701
18058
|
templateVariables: EMPTY_TEMPLATE,
|
|
17702
18059
|
overflow: EMPTY_OVERFLOW,
|
|
17703
18060
|
visual: EMPTY_VISUAL,
|
|
@@ -17726,6 +18083,7 @@ function runAudit($, html, framework, options) {
|
|
|
17726
18083
|
const images = skip.has("images") ? EMPTY_IMAGES : analyzeImagesFromDom($);
|
|
17727
18084
|
const inboxPreview = skip.has("inboxPreview") ? EMPTY_INBOX_PREVIEW : extractInboxPreviewFromDom($);
|
|
17728
18085
|
const size = skip.has("size") ? EMPTY_SIZE : checkSizeFromDom($, html);
|
|
18086
|
+
const styleSurvival = skip.has("styleSurvival") ? EMPTY_STYLE_SURVIVAL : checkStyleSurvivalFromDom($, source, framework);
|
|
17729
18087
|
const templateVariables = skip.has("templateVariables") ? EMPTY_TEMPLATE : checkTemplateVariablesFromDom($, source);
|
|
17730
18088
|
const overflow = skip.has("overflow") ? EMPTY_OVERFLOW : checkOverflowFromDom($, source);
|
|
17731
18089
|
const visual = skip.has("visual") ? EMPTY_VISUAL : checkVisualFromDom($, source);
|
|
@@ -17736,7 +18094,7 @@ function runAudit($, html, framework, options) {
|
|
|
17736
18094
|
]);
|
|
17737
18095
|
const design = skip.has("design") ? EMPTY_DESIGN : checkDesignConsistencyFromDom($);
|
|
17738
18096
|
const mobileContrast = skip.has("mobileContrast") || skip.has("accessibility") ? [] : checkMobileContrastFromDom($, accessibility.issues);
|
|
17739
|
-
return { compatibility: { warnings, scores }, spam, links, accessibility, images, inboxPreview, size, templateVariables, overflow, visual, vml, darkContrast, mobileContrast, design };
|
|
18097
|
+
return { compatibility: { warnings, scores }, spam, links, accessibility, images, inboxPreview, size, styleSurvival, templateVariables, overflow, visual, vml, darkContrast, mobileContrast, design };
|
|
17740
18098
|
}
|
|
17741
18099
|
function auditEmail(html, options) {
|
|
17742
18100
|
return fromHtml(html, EMPTY_AUDIT, ($, h) => runAudit($, h, options == null ? void 0 : options.framework, options), options);
|
|
@@ -17780,7 +18138,7 @@ function toPlainText(html) {
|
|
|
17780
18138
|
if (trimmed) lines.push(trimmed);
|
|
17781
18139
|
currentLine = "";
|
|
17782
18140
|
}
|
|
17783
|
-
function
|
|
18141
|
+
function walk10(node) {
|
|
17784
18142
|
var _a;
|
|
17785
18143
|
if (node.type === "text") {
|
|
17786
18144
|
const text = node.data.replace(/\s+/g, " ");
|
|
@@ -17829,7 +18187,7 @@ function toPlainText(html) {
|
|
|
17829
18187
|
currentLine = "- ";
|
|
17830
18188
|
}
|
|
17831
18189
|
for (const child of el.children) {
|
|
17832
|
-
|
|
18190
|
+
walk10(child);
|
|
17833
18191
|
}
|
|
17834
18192
|
if (isBlock) flushLine();
|
|
17835
18193
|
}
|
|
@@ -17837,7 +18195,7 @@ function toPlainText(html) {
|
|
|
17837
18195
|
const root = body.length ? body[0] : $.root()[0];
|
|
17838
18196
|
if (root && "children" in root) {
|
|
17839
18197
|
for (const child of root.children) {
|
|
17840
|
-
|
|
18198
|
+
walk10(child);
|
|
17841
18199
|
}
|
|
17842
18200
|
}
|
|
17843
18201
|
flushLine();
|
|
@@ -17979,6 +18337,7 @@ var CompileError = class extends Error {
|
|
|
17979
18337
|
checkMobileContrast,
|
|
17980
18338
|
checkOverflow,
|
|
17981
18339
|
checkSize,
|
|
18340
|
+
checkStyleSurvival,
|
|
17982
18341
|
checkTemplateVariables,
|
|
17983
18342
|
checkVisual,
|
|
17984
18343
|
checkVml,
|