@emailens/engine 0.10.0 → 0.10.1

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/index.cjs CHANGED
@@ -10477,351 +10477,25 @@ function transformForAllClients(html, framework) {
10477
10477
  }
10478
10478
 
10479
10479
  // src/analyze.ts
10480
- var cheerio3 = __toESM(require("cheerio"), 1);
10481
- var csstree3 = __toESM(require("css-tree"), 1);
10482
- var HTML_ELEMENT_SELECTORS = {
10483
- "<style>": "style",
10484
- "<link>": "link[rel='stylesheet']",
10485
- "<svg>": "svg",
10486
- "<video>": "video",
10487
- "<form>": "form, input, button[type='submit']",
10488
- "<audio>": "audio",
10489
- "<picture>": "picture",
10490
- "<dialog>": "dialog",
10491
- "<meter>": "meter",
10492
- "<progress>": "progress",
10493
- "<select>": "select",
10494
- "<textarea>": "textarea",
10495
- "<marquee>": "marquee",
10496
- "<object>": "object",
10497
- "<base>": "base"
10498
- };
10499
- var HTML_ELEMENT_SEVERITY = {
10500
- "<style>": "error",
10501
- "<link>": "error",
10502
- "<svg>": "error",
10503
- "<form>": "error",
10504
- "<video>": "warning",
10505
- "<audio>": "warning",
10506
- "<picture>": "warning",
10507
- "<dialog>": "warning",
10508
- "<marquee>": "warning",
10509
- "<meter>": "warning",
10510
- "<progress>": "warning",
10511
- "<select>": "warning",
10512
- "<textarea>": "warning",
10513
- "<object>": "warning",
10514
- "<base>": "warning"
10515
- };
10516
- var HTML_ELEMENT_MESSAGES = {
10517
- "<style>": (n) => `${n} strips <style> blocks. Styles must be inlined.`,
10518
- "<link>": (n) => `${n} does not support external stylesheets.`,
10519
- "<svg>": (n) => `${n} does not support inline SVG.`,
10520
- "<video>": (n) => `${n} does not support <video> elements.`,
10521
- "<form>": (n) => `${n} strips form elements.`
10522
- };
10523
- var COMPOUND_DETECTORS = [
10524
- { key: "display:flex", property: "display", valueIncludes: "flex" },
10525
- { key: "display:grid", property: "display", valueIncludes: "grid" },
10526
- { key: "display:none", property: "display", valueIncludes: "none" }
10527
- ];
10528
- var CSS_FUNCTION_DETECTORS = CSS_FUNCTION_FEATURES.map((fn) => ({
10529
- key: fn,
10530
- pattern: `${fn}(`
10531
- // require opening paren — matches "min(" but not "Minion"
10532
- }));
10533
- function analyzeEmailFromDom($, framework) {
10534
- const warnings = [];
10535
- const seenWarnings = /* @__PURE__ */ new Set();
10536
- function addWarning(w) {
10537
- const key = `${w.client}:${w.property}:${w.severity}:${w.selector || ""}`;
10538
- if (!seenWarnings.has(key)) {
10539
- seenWarnings.add(key);
10540
- warnings.push(w);
10541
- }
10542
- }
10543
- function describeSelector(el) {
10544
- var _a;
10545
- const $el = $(el);
10546
- const tag = ((_a = el.tagName) == null ? void 0 : _a.toLowerCase()) || "";
10547
- const cls = $el.attr("class");
10548
- const id = $el.attr("id");
10549
- if (id) return `${tag}#${id}`;
10550
- if (cls) return `${tag}.${cls.split(/\s+/)[0]}`;
10551
- const href = $el.attr("href");
10552
- if (href) return `${tag}[href]`;
10553
- return tag;
10554
- }
10555
- for (const feature of HTML_ELEMENT_FEATURES) {
10556
- const selector = HTML_ELEMENT_SELECTORS[feature];
10557
- if (!selector) continue;
10558
- if ($(selector).length === 0) continue;
10559
- const supportData = CSS_SUPPORT[feature];
10560
- if (!supportData) continue;
10561
- const baseSeverity = HTML_ELEMENT_SEVERITY[feature] || "warning";
10562
- for (const client of EMAIL_CLIENTS) {
10563
- const support = supportData[client.id];
10564
- if (support === "unsupported") {
10565
- const msgFn = HTML_ELEMENT_MESSAGES[feature];
10566
- const message = msgFn ? msgFn(client.name) : `${client.name} does not support ${feature}.`;
10567
- const sug = getSuggestion(feature, client.id, framework);
10568
- const fix = getCodeFix(feature, client.id, framework);
10569
- addWarning(__spreadValues({
10570
- severity: baseSeverity,
10571
- client: client.id,
10572
- property: feature,
10573
- message,
10574
- suggestion: sug.text,
10575
- fix,
10576
- fixType: getFixType(feature)
10577
- }, framework && (sug.isGenericFallback || fix && isCodeFixGenericFallback(feature, client.id, framework)) ? { fixIsGenericFallback: true } : {}));
10578
- } else if (support === "partial" && feature === "<style>") {
10579
- const sug = getSuggestion("<style>:partial", client.id, framework);
10580
- const fix = getCodeFix("<style>", client.id, framework);
10581
- addWarning(__spreadValues({
10582
- severity: "warning",
10583
- client: client.id,
10584
- property: "<style>",
10585
- message: `${client.name} has partial <style> support (head only, with limitations). Inline styles recommended.`,
10586
- suggestion: sug.text,
10587
- fix,
10588
- fixType: getFixType("<style>")
10589
- }, framework && (sug.isGenericFallback || fix && isCodeFixGenericFallback("<style>", client.id, framework)) ? { fixIsGenericFallback: true } : {}));
10590
- }
10591
- }
10592
- }
10593
- const parsedAtRules = /* @__PURE__ */ new Set();
10594
- const parsedProperties = /* @__PURE__ */ new Set();
10595
- const propertyLines = /* @__PURE__ */ new Map();
10596
- const propertyValues = /* @__PURE__ */ new Map();
10597
- const detectedCssFunctions = /* @__PURE__ */ new Set();
10598
- const detectedPseudoClasses = /* @__PURE__ */ new Set();
10599
- const detectedPseudoElements = /* @__PURE__ */ new Set();
10600
- $("style").each((_, el) => {
10601
- const cssText = $(el).text();
10602
- try {
10603
- const ast = csstree3.parse(cssText, { parseCustomProperty: true, positions: true });
10604
- csstree3.walk(ast, {
10605
- enter(node) {
10606
- if (node.type === "Atrule") {
10607
- parsedAtRules.add(`@${node.name}`);
10608
- }
10609
- if (node.type === "PseudoClassSelector") {
10610
- detectedPseudoClasses.add(`:${node.name}`);
10611
- }
10612
- if (node.type === "PseudoElementSelector") {
10613
- detectedPseudoElements.add(`::${node.name}`);
10614
- }
10615
- if (node.type === "Declaration") {
10616
- const prop = node.property.toLowerCase();
10617
- parsedProperties.add(prop);
10618
- if (node.loc && !propertyLines.has(prop)) {
10619
- propertyLines.set(prop, node.loc.start.line);
10620
- }
10621
- const valueStr = csstree3.generate(node.value);
10622
- const seenValues = propertyValues.get(prop);
10623
- if (seenValues) seenValues.push(valueStr);
10624
- else propertyValues.set(prop, [valueStr]);
10625
- for (const det of COMPOUND_DETECTORS) {
10626
- if (prop === det.property && valueStr.includes(det.valueIncludes)) {
10627
- parsedProperties.add(det.key);
10628
- if (node.loc && !propertyLines.has(det.key)) {
10629
- propertyLines.set(det.key, node.loc.start.line);
10630
- }
10631
- }
10632
- }
10633
- for (const fn of CSS_FUNCTION_DETECTORS) {
10634
- if (valueStr.includes(fn.pattern)) {
10635
- detectedCssFunctions.add(fn.key);
10636
- if (node.loc && !propertyLines.has(fn.key)) {
10637
- propertyLines.set(fn.key, node.loc.start.line);
10638
- }
10639
- }
10640
- }
10641
- }
10642
- }
10643
- });
10644
- } catch (e) {
10645
- }
10646
- });
10647
- for (const atRule of AT_RULE_FEATURES) {
10648
- if (!parsedAtRules.has(atRule)) continue;
10649
- checkPropertySupport(atRule, addWarning, framework);
10650
- }
10651
- const cssPropertiesToCheck = Object.keys(CSS_SUPPORT).filter(
10652
- (k) => !k.startsWith("<") && !k.startsWith("@")
10653
- );
10654
- $("[style]").each((_, el) => {
10655
- var _a;
10656
- const style = $(el).attr("style") || "";
10657
- const props = parseStyleProperties(style);
10658
- const selector = describeSelector(el);
10659
- for (const prop of props) {
10660
- for (const det of COMPOUND_DETECTORS) {
10661
- if (prop === det.property) {
10662
- const value2 = getStyleValue(style, prop);
10663
- if (value2 == null ? void 0 : value2.includes(det.valueIncludes)) {
10664
- checkPropertySupport(det.key, addWarning, framework, selector);
10665
- }
10666
- }
10667
- }
10668
- if (cssPropertiesToCheck.includes(prop)) {
10669
- checkPropertySupport(prop, addWarning, framework, selector, void 0, (_a = getStyleValue(style, prop)) != null ? _a : void 0);
10670
- }
10671
- const value = getStyleValue(style, prop);
10672
- if (value) {
10673
- for (const fn of CSS_FUNCTION_DETECTORS) {
10674
- if (value.includes(fn.pattern)) {
10675
- checkPropertySupport(fn.key, addWarning, framework, selector);
10676
- }
10677
- }
10678
- }
10679
- }
10680
- });
10681
- for (const prop of parsedProperties) {
10682
- if (prop.includes(":")) continue;
10683
- if (!cssPropertiesToCheck.includes(prop)) continue;
10684
- const values = propertyValues.get(prop);
10685
- checkPropertySupport(
10686
- prop,
10687
- addWarning,
10688
- framework,
10689
- void 0,
10690
- propertyLines.get(prop),
10691
- values ? values.join(" ") : void 0
10692
- );
10693
- }
10694
- for (const compound of COMPOUND_VALUE_FEATURES) {
10695
- if (compound.startsWith(":") || compound.startsWith("::")) continue;
10696
- if (parsedProperties.has(compound)) {
10697
- checkPropertySupport(compound, addWarning, framework, void 0, propertyLines.get(compound));
10698
- }
10699
- }
10700
- for (const pseudo of detectedPseudoClasses) {
10701
- if (CSS_SUPPORT[pseudo]) {
10702
- checkPropertySupport(pseudo, addWarning, framework);
10703
- }
10704
- }
10705
- for (const pseudo of detectedPseudoElements) {
10706
- if (CSS_SUPPORT[pseudo]) {
10707
- checkPropertySupport(pseudo, addWarning, framework);
10708
- }
10709
- }
10710
- for (const fn of detectedCssFunctions) {
10711
- checkPropertySupport(fn, addWarning, framework, void 0, propertyLines.get(fn));
10712
- }
10713
- const severityOrder = { error: 0, warning: 1, info: 2 };
10714
- warnings.sort((a, b) => severityOrder[a.severity] - severityOrder[b.severity]);
10715
- return warnings;
10716
- }
10717
- function analyzeEmail(html, framework) {
10718
- if (!html || !html.trim()) {
10719
- return [];
10720
- }
10721
- if (html.length > MAX_HTML_SIZE) {
10722
- throw new Error(`HTML input exceeds ${MAX_HTML_SIZE / 1024}KB limit.`);
10723
- }
10724
- const $ = cheerio3.load(html);
10725
- return analyzeEmailFromDom($, framework);
10726
- }
10727
- function getFixType(prop) {
10728
- return STRUCTURAL_FIX_PROPERTIES.has(prop) ? "structural" : "css";
10729
- }
10730
- var VALUE_CAVEAT_PROPS = /* @__PURE__ */ new Set(["margin", "position", "overflow"]);
10731
- var POSITION_KEYWORDS = ["relative", "absolute", "fixed", "sticky"];
10732
- function valueTriggersCaveat(prop, value, notes) {
10733
- const note = (notes != null ? notes : []).join(" ");
10734
- const noteLc = note.toLowerCase();
10735
- if (prop === "margin") {
10736
- if (/(?:^|[\s:(])-\.?\d/.test(value) && noteLc.includes("negative")) return true;
10737
- if (/\bauto\b/.test(value) && noteLc.includes("auto")) return true;
10738
- return false;
10739
- }
10740
- if (prop === "position") {
10741
- const used = POSITION_KEYWORDS.find((k) => new RegExp(`\\b${k}\\b`).test(value));
10742
- if (!used) return false;
10743
- const m = note.match(/supports\s+.+?\s+but not\s+([^.]+)/i);
10744
- if (m) return m[1].toLowerCase().includes(used);
10745
- return used === "fixed" || used === "sticky";
10746
- }
10747
- if (prop === "overflow") {
10748
- if (!/\b(?:auto|scroll)\b/.test(value)) return false;
10749
- return noteLc.includes("cannot scroll");
10750
- }
10751
- return true;
10752
- }
10753
- function noteSuffix(notes) {
10754
- if (!(notes == null ? void 0 : notes.length)) return "";
10755
- const cleaned = notes.map((n) => n.replace(/^(?:Partial|Buggy|Not supported)\.\s*/i, "").trim()).filter(Boolean);
10756
- return cleaned.length ? ` ${cleaned.join(" ")}` : "";
10757
- }
10758
- function checkPropertySupport(prop, addWarning, framework, selector, line, value) {
10759
- var _a;
10760
- const supportData = CSS_SUPPORT[prop];
10761
- if (!supportData) return;
10762
- const fixType = getFixType(prop);
10763
- const valueGated = VALUE_CAVEAT_PROPS.has(prop);
10764
- for (const client of EMAIL_CLIENTS) {
10765
- const support = supportData[client.id] || "unknown";
10766
- const notes = (_a = CSS_SUPPORT_NOTES[prop]) == null ? void 0 : _a[client.id];
10767
- if (support === "unsupported") {
10768
- const sug = getSuggestion(prop, client.id, framework);
10769
- const fix = getCodeFix(prop, client.id, framework);
10770
- addWarning(__spreadValues(__spreadValues(__spreadValues({
10771
- severity: "warning",
10772
- client: client.id,
10773
- property: prop,
10774
- message: `${client.name} does not support "${prop}".${noteSuffix(notes)}`,
10775
- suggestion: sug.text,
10776
- fix,
10777
- fixType
10778
- }, selector ? { selector } : {}), line !== void 0 ? { line } : {}), framework && (sug.isGenericFallback || fix && isCodeFixGenericFallback(prop, client.id, framework)) ? { fixIsGenericFallback: true } : {}));
10779
- } else if (support === "partial") {
10780
- if (valueGated && value !== void 0 && !valueTriggersCaveat(prop, value, notes)) continue;
10781
- const sug = getSuggestion(prop, client.id, framework);
10782
- const fix = getCodeFix(prop, client.id, framework);
10783
- addWarning(__spreadValues(__spreadValues(__spreadValues({
10784
- severity: "info",
10785
- client: client.id,
10786
- property: prop,
10787
- message: `${client.name} has partial support for "${prop}".${noteSuffix(notes)}`,
10788
- suggestion: sug.text,
10789
- fix,
10790
- fixType
10791
- }, selector ? { selector } : {}), line !== void 0 ? { line } : {}), framework && (sug.isGenericFallback || fix && isCodeFixGenericFallback(prop, client.id, framework)) ? { fixIsGenericFallback: true } : {}));
10792
- }
10793
- }
10794
- }
10795
- function generateCompatibilityScore(warnings) {
10796
- const result = {};
10797
- for (const client of EMAIL_CLIENTS) {
10798
- const clientWarnings = warnings.filter((w) => w.client === client.id);
10799
- const errorProps = new Set(clientWarnings.filter((w) => w.severity === "error").map((w) => w.property));
10800
- const warnProps = new Set(clientWarnings.filter((w) => w.severity === "warning").map((w) => w.property));
10801
- const infoProps = new Set(clientWarnings.filter((w) => w.severity === "info").map((w) => w.property));
10802
- const errors = errorProps.size;
10803
- const warns = warnProps.size;
10804
- const info = infoProps.size;
10805
- const score = Math.max(0, Math.min(100, 100 - errors * 10 - warns * 3));
10806
- result[client.id] = { score, errors, warnings: warns, info };
10807
- }
10808
- return result;
10809
- }
10810
- function warningsForClient(warnings, clientId) {
10811
- return warnings.filter((w) => w.client === clientId);
10812
- }
10813
- function errorWarnings(warnings) {
10814
- return warnings.filter((w) => w.severity === "error");
10815
- }
10816
- function structuralWarnings(warnings) {
10817
- return warnings.filter((w) => w.fixType === "structural");
10818
- }
10819
-
10820
- // src/dark-mode.ts
10821
10480
  var cheerio4 = __toESM(require("cheerio"), 1);
10481
+ var csstree5 = __toESM(require("css-tree"), 1);
10482
+
10483
+ // src/dark-mode-checker.ts
10822
10484
  var csstree4 = __toESM(require("css-tree"), 1);
10485
+
10486
+ // src/dark-mode.ts
10487
+ var cheerio3 = __toESM(require("cheerio"), 1);
10488
+ var csstree3 = __toESM(require("css-tree"), 1);
10823
10489
  var LIGHT_THRESHOLD = 0.7;
10824
10490
  var DARK_THRESHOLD = 0.15;
10491
+ var PREFERS_COLOR_SCHEME_CLIENTS = [
10492
+ "apple-mail-macos",
10493
+ "apple-mail-ios",
10494
+ "samsung-mail",
10495
+ "thunderbird",
10496
+ "hey-mail",
10497
+ "superhuman"
10498
+ ];
10825
10499
  function simulateDarkMode(html, clientId) {
10826
10500
  var _a, _b;
10827
10501
  if (!html || !html.trim()) {
@@ -10830,7 +10504,7 @@ function simulateDarkMode(html, clientId) {
10830
10504
  if (html.length > MAX_HTML_SIZE) {
10831
10505
  throw new Error(`HTML input exceeds ${MAX_HTML_SIZE / 1024}KB limit.`);
10832
10506
  }
10833
- const $ = cheerio4.load(html);
10507
+ const $ = cheerio3.load(html);
10834
10508
  const warnings = [];
10835
10509
  $("img").each((_, el) => {
10836
10510
  const src = $(el).attr("src") || "";
@@ -11032,54 +10706,531 @@ function applyColorInversion($, mode) {
11032
10706
  }
11033
10707
  }
11034
10708
  }
11035
- });
11036
- if (changed) {
11037
- $(el).attr("style", serializeStyle(props));
10709
+ });
10710
+ if (changed) {
10711
+ $(el).attr("style", serializeStyle(props));
10712
+ }
10713
+ });
10714
+ $("style").each((_, el) => {
10715
+ const cssText = $(el).text();
10716
+ try {
10717
+ const ast = csstree3.parse(cssText, { parseCustomProperty: true });
10718
+ let modified = false;
10719
+ csstree3.walk(ast, {
10720
+ enter(node) {
10721
+ if (node.type !== "Declaration") return;
10722
+ const prop = node.property.toLowerCase();
10723
+ if (!COLOR_PROPS.has(prop) && prop !== "background") return;
10724
+ const valueStr = csstree3.generate(node.value);
10725
+ if (prop === "background") {
10726
+ const bgColor = extractBackgroundColor(valueStr);
10727
+ if (bgColor) {
10728
+ const inverted = invertColor(bgColor, mode);
10729
+ if (inverted) {
10730
+ const newValue = valueStr.replace(bgColor, inverted);
10731
+ node.value = csstree3.parse(newValue, { context: "value" });
10732
+ modified = true;
10733
+ }
10734
+ }
10735
+ } else {
10736
+ const inverted = invertColor(valueStr, mode);
10737
+ if (inverted) {
10738
+ node.value = csstree3.parse(inverted, { context: "value" });
10739
+ modified = true;
10740
+ }
10741
+ }
10742
+ }
10743
+ });
10744
+ if (modified) {
10745
+ $(el).text(csstree3.generate(ast));
10746
+ }
10747
+ } catch (e) {
10748
+ }
10749
+ });
10750
+ $("[bgcolor]").each((_, el) => {
10751
+ const bgcolor = $(el).attr("bgcolor") || "";
10752
+ const inverted = invertColor(bgcolor, mode);
10753
+ if (inverted) {
10754
+ $(el).attr("bgcolor", inverted);
10755
+ }
10756
+ });
10757
+ }
10758
+
10759
+ // src/dark-mode-checker.ts
10760
+ var DARK_MEDIA_RE = /\(\s*prefers-color-scheme\s*:\s*dark\s*\)/i;
10761
+ var MAX_UNCOVERED_ELEMENTS = 3;
10762
+ function backgroundShorthandColor(value) {
10763
+ if (!value) return null;
10764
+ const trimmed = value.trim();
10765
+ if (parseColor(trimmed)) return trimmed;
10766
+ for (const token of trimmed.split(/\s+/)) {
10767
+ if (token.includes("(")) continue;
10768
+ if (parseColor(token)) return token;
10769
+ }
10770
+ return null;
10771
+ }
10772
+ function isLight(value) {
10773
+ const c = parseColor(value);
10774
+ if (!c || c.a < 0.5) return false;
10775
+ return relativeLuminance(c.r, c.g, c.b) > LIGHT_THRESHOLD;
10776
+ }
10777
+ function collectDarkBlocks($) {
10778
+ const block = { any: [], important: [], rules: 0 };
10779
+ let found = false;
10780
+ $("style").each((_, el) => {
10781
+ const cssText = $(el).text();
10782
+ if (!DARK_MEDIA_RE.test(cssText)) return;
10783
+ let ast;
10784
+ try {
10785
+ ast = csstree4.parse(cssText);
10786
+ } catch (e) {
10787
+ found = true;
10788
+ return;
10789
+ }
10790
+ csstree4.walk(ast, {
10791
+ visit: "Atrule",
10792
+ enter(node) {
10793
+ if (node.type !== "Atrule" || node.name.toLowerCase() !== "media") return;
10794
+ if (!node.prelude || !DARK_MEDIA_RE.test(csstree4.generate(node.prelude))) return;
10795
+ found = true;
10796
+ if (!node.block) return;
10797
+ csstree4.walk(node.block, {
10798
+ visit: "Rule",
10799
+ enter(rule) {
10800
+ if (rule.type !== "Rule") return;
10801
+ block.rules++;
10802
+ let setsBackground = false;
10803
+ let important = false;
10804
+ rule.block.children.forEach((child) => {
10805
+ if (child.type !== "Declaration") return;
10806
+ const prop = child.property.toLowerCase();
10807
+ if (prop !== "background" && prop !== "background-color") return;
10808
+ setsBackground = true;
10809
+ if (child.important) important = true;
10810
+ });
10811
+ if (!setsBackground) return;
10812
+ const selector = csstree4.generate(rule.prelude).trim();
10813
+ if (!selector) return;
10814
+ block.any.push(selector);
10815
+ if (important) block.important.push(selector);
10816
+ }
10817
+ });
10818
+ }
10819
+ });
10820
+ });
10821
+ return found ? block : null;
10822
+ }
10823
+ function matchedElements($, selectors) {
10824
+ const matched = /* @__PURE__ */ new Set();
10825
+ for (const selector of selectors) {
10826
+ try {
10827
+ $(selector).each((_, el) => {
10828
+ matched.add(el);
10829
+ });
10830
+ } catch (e) {
10831
+ }
10832
+ }
10833
+ return matched;
10834
+ }
10835
+ function describeSelector($, el) {
10836
+ var _a;
10837
+ const $el = $(el);
10838
+ const tag = ((_a = el.tagName) == null ? void 0 : _a.toLowerCase()) || "element";
10839
+ const id = $el.attr("id");
10840
+ if (id) return `${tag}#${id}`;
10841
+ const cls = $el.attr("class");
10842
+ if (cls) return `${tag}.${cls.split(/\s+/)[0]}`;
10843
+ return tag;
10844
+ }
10845
+ function checkDarkModeFromDom($) {
10846
+ var _a, _b;
10847
+ const darkBlock = collectDarkBlocks($);
10848
+ if (!darkBlock) return [];
10849
+ const warnings = [];
10850
+ const hasOptIn = $("meta").toArray().some((el) => {
10851
+ const name = ($(el).attr("name") || "").trim().toLowerCase();
10852
+ return name === "color-scheme" || name === "supported-color-schemes";
10853
+ });
10854
+ if (!hasOptIn) {
10855
+ for (const clientId of PREFERS_COLOR_SCHEME_CLIENTS) {
10856
+ warnings.push({
10857
+ severity: "warning",
10858
+ client: clientId,
10859
+ property: "dark-mode-opt-in",
10860
+ message: `The email has @media (prefers-color-scheme: dark) styles but no dark-mode opt-in meta tag. ${(_b = (_a = getClient(clientId)) == null ? void 0 : _a.name) != null ? _b : clientId} may keep the email in light mode, so the dark styles never activate.`,
10861
+ suggestion: 'Add both opt-in tags to <head>: <meta name="color-scheme" content="light dark"> and <meta name="supported-color-schemes" content="light dark">.',
10862
+ fixType: "structural"
10863
+ });
10864
+ }
10865
+ }
10866
+ if (darkBlock.rules === 0) return warnings;
10867
+ const coveredByImportant = matchedElements($, darkBlock.important);
10868
+ const coveredByAny = matchedElements($, darkBlock.any);
10869
+ let uncovered = 0;
10870
+ $("[bgcolor], [style]").each((_, el) => {
10871
+ var _a2;
10872
+ if (uncovered >= MAX_UNCOVERED_ELEMENTS) return false;
10873
+ const $el = $(el);
10874
+ const style = parseInlineStyle($el.attr("style") || "");
10875
+ const inline = (_a2 = style.get("background-color")) != null ? _a2 : backgroundShorthandColor(style.get("background"));
10876
+ const color = inline != null ? inline : $el.attr("bgcolor");
10877
+ if (!color || !isLight(color)) return;
10878
+ if (inline ? coveredByImportant.has(el) : coveredByAny.has(el)) return;
10879
+ uncovered++;
10880
+ const selector = describeSelector($, el);
10881
+ for (const clientId of PREFERS_COLOR_SCHEME_CLIENTS) {
10882
+ warnings.push({
10883
+ severity: "warning",
10884
+ client: clientId,
10885
+ property: "dark-mode-coverage",
10886
+ message: `<${selector}> keeps its hardcoded light background (${color}) in dark mode \u2014 the dark block never overrides it. The rest of the email inverts around it, producing a half-inverted render (e.g. light text left on a still-white background).`,
10887
+ suggestion: inline ? `Override it inside @media (prefers-color-scheme: dark) with a dark background-color and !important (an inline style beats a plain rule), or move the colour onto a class the dark block already targets.` : `Override it inside @media (prefers-color-scheme: dark) with a dark background-color, or give the element a class the dark block already targets.`,
10888
+ fixType: "css",
10889
+ selector
10890
+ });
10891
+ }
10892
+ });
10893
+ return warnings;
10894
+ }
10895
+
10896
+ // src/analyze.ts
10897
+ var HTML_ELEMENT_SELECTORS = {
10898
+ "<style>": "style",
10899
+ "<link>": "link[rel='stylesheet']",
10900
+ "<svg>": "svg",
10901
+ "<video>": "video",
10902
+ "<form>": "form, input, button[type='submit']",
10903
+ "<audio>": "audio",
10904
+ "<picture>": "picture",
10905
+ "<dialog>": "dialog",
10906
+ "<meter>": "meter",
10907
+ "<progress>": "progress",
10908
+ "<select>": "select",
10909
+ "<textarea>": "textarea",
10910
+ "<marquee>": "marquee",
10911
+ "<object>": "object",
10912
+ "<base>": "base"
10913
+ };
10914
+ var HTML_ELEMENT_SEVERITY = {
10915
+ "<style>": "error",
10916
+ "<link>": "error",
10917
+ "<svg>": "error",
10918
+ "<form>": "error",
10919
+ "<video>": "warning",
10920
+ "<audio>": "warning",
10921
+ "<picture>": "warning",
10922
+ "<dialog>": "warning",
10923
+ "<marquee>": "warning",
10924
+ "<meter>": "warning",
10925
+ "<progress>": "warning",
10926
+ "<select>": "warning",
10927
+ "<textarea>": "warning",
10928
+ "<object>": "warning",
10929
+ "<base>": "warning"
10930
+ };
10931
+ var HTML_ELEMENT_MESSAGES = {
10932
+ "<style>": (n) => `${n} strips <style> blocks. Styles must be inlined.`,
10933
+ "<link>": (n) => `${n} does not support external stylesheets.`,
10934
+ "<svg>": (n) => `${n} does not support inline SVG.`,
10935
+ "<video>": (n) => `${n} does not support <video> elements.`,
10936
+ "<form>": (n) => `${n} strips form elements.`
10937
+ };
10938
+ var COMPOUND_DETECTORS = [
10939
+ { key: "display:flex", property: "display", valueIncludes: "flex" },
10940
+ { key: "display:grid", property: "display", valueIncludes: "grid" },
10941
+ { key: "display:none", property: "display", valueIncludes: "none" }
10942
+ ];
10943
+ var CSS_FUNCTION_DETECTORS = CSS_FUNCTION_FEATURES.map((fn) => ({
10944
+ key: fn,
10945
+ pattern: `${fn}(`
10946
+ // require opening paren — matches "min(" but not "Minion"
10947
+ }));
10948
+ function analyzeEmailFromDom($, framework) {
10949
+ const warnings = [];
10950
+ const seenWarnings = /* @__PURE__ */ new Set();
10951
+ function addWarning(w) {
10952
+ const key = `${w.client}:${w.property}:${w.severity}:${w.selector || ""}`;
10953
+ if (!seenWarnings.has(key)) {
10954
+ seenWarnings.add(key);
10955
+ warnings.push(w);
10956
+ }
10957
+ }
10958
+ function describeSelector2(el) {
10959
+ var _a;
10960
+ const $el = $(el);
10961
+ const tag = ((_a = el.tagName) == null ? void 0 : _a.toLowerCase()) || "";
10962
+ const cls = $el.attr("class");
10963
+ const id = $el.attr("id");
10964
+ if (id) return `${tag}#${id}`;
10965
+ if (cls) return `${tag}.${cls.split(/\s+/)[0]}`;
10966
+ const href = $el.attr("href");
10967
+ if (href) return `${tag}[href]`;
10968
+ return tag;
10969
+ }
10970
+ for (const feature of HTML_ELEMENT_FEATURES) {
10971
+ const selector = HTML_ELEMENT_SELECTORS[feature];
10972
+ if (!selector) continue;
10973
+ if ($(selector).length === 0) continue;
10974
+ const supportData = CSS_SUPPORT[feature];
10975
+ if (!supportData) continue;
10976
+ const baseSeverity = HTML_ELEMENT_SEVERITY[feature] || "warning";
10977
+ for (const client of EMAIL_CLIENTS) {
10978
+ const support = supportData[client.id];
10979
+ if (support === "unsupported") {
10980
+ const msgFn = HTML_ELEMENT_MESSAGES[feature];
10981
+ const message = msgFn ? msgFn(client.name) : `${client.name} does not support ${feature}.`;
10982
+ const sug = getSuggestion(feature, client.id, framework);
10983
+ const fix = getCodeFix(feature, client.id, framework);
10984
+ addWarning(__spreadValues({
10985
+ severity: baseSeverity,
10986
+ client: client.id,
10987
+ property: feature,
10988
+ message,
10989
+ suggestion: sug.text,
10990
+ fix,
10991
+ fixType: getFixType(feature)
10992
+ }, framework && (sug.isGenericFallback || fix && isCodeFixGenericFallback(feature, client.id, framework)) ? { fixIsGenericFallback: true } : {}));
10993
+ } else if (support === "partial" && feature === "<style>") {
10994
+ const sug = getSuggestion("<style>:partial", client.id, framework);
10995
+ const fix = getCodeFix("<style>", client.id, framework);
10996
+ addWarning(__spreadValues({
10997
+ severity: "warning",
10998
+ client: client.id,
10999
+ property: "<style>",
11000
+ message: `${client.name} has partial <style> support (head only, with limitations). Inline styles recommended.`,
11001
+ suggestion: sug.text,
11002
+ fix,
11003
+ fixType: getFixType("<style>")
11004
+ }, framework && (sug.isGenericFallback || fix && isCodeFixGenericFallback("<style>", client.id, framework)) ? { fixIsGenericFallback: true } : {}));
11005
+ }
11038
11006
  }
11039
- });
11007
+ }
11008
+ const parsedAtRules = /* @__PURE__ */ new Set();
11009
+ const parsedProperties = /* @__PURE__ */ new Set();
11010
+ const propertyLines = /* @__PURE__ */ new Map();
11011
+ const propertyValues = /* @__PURE__ */ new Map();
11012
+ const detectedCssFunctions = /* @__PURE__ */ new Set();
11013
+ const detectedPseudoClasses = /* @__PURE__ */ new Set();
11014
+ const detectedPseudoElements = /* @__PURE__ */ new Set();
11040
11015
  $("style").each((_, el) => {
11041
11016
  const cssText = $(el).text();
11042
11017
  try {
11043
- const ast = csstree4.parse(cssText, { parseCustomProperty: true });
11044
- let modified = false;
11045
- csstree4.walk(ast, {
11018
+ const ast = csstree5.parse(cssText, { parseCustomProperty: true, positions: true });
11019
+ csstree5.walk(ast, {
11046
11020
  enter(node) {
11047
- if (node.type !== "Declaration") return;
11048
- const prop = node.property.toLowerCase();
11049
- if (!COLOR_PROPS.has(prop) && prop !== "background") return;
11050
- const valueStr = csstree4.generate(node.value);
11051
- if (prop === "background") {
11052
- const bgColor = extractBackgroundColor(valueStr);
11053
- if (bgColor) {
11054
- const inverted = invertColor(bgColor, mode);
11055
- if (inverted) {
11056
- const newValue = valueStr.replace(bgColor, inverted);
11057
- node.value = csstree4.parse(newValue, { context: "value" });
11058
- modified = true;
11021
+ if (node.type === "Atrule") {
11022
+ parsedAtRules.add(`@${node.name}`);
11023
+ }
11024
+ if (node.type === "PseudoClassSelector") {
11025
+ detectedPseudoClasses.add(`:${node.name}`);
11026
+ }
11027
+ if (node.type === "PseudoElementSelector") {
11028
+ detectedPseudoElements.add(`::${node.name}`);
11029
+ }
11030
+ if (node.type === "Declaration") {
11031
+ const prop = node.property.toLowerCase();
11032
+ parsedProperties.add(prop);
11033
+ if (node.loc && !propertyLines.has(prop)) {
11034
+ propertyLines.set(prop, node.loc.start.line);
11035
+ }
11036
+ const valueStr = csstree5.generate(node.value);
11037
+ const seenValues = propertyValues.get(prop);
11038
+ if (seenValues) seenValues.push(valueStr);
11039
+ else propertyValues.set(prop, [valueStr]);
11040
+ for (const det of COMPOUND_DETECTORS) {
11041
+ if (prop === det.property && valueStr.includes(det.valueIncludes)) {
11042
+ parsedProperties.add(det.key);
11043
+ if (node.loc && !propertyLines.has(det.key)) {
11044
+ propertyLines.set(det.key, node.loc.start.line);
11045
+ }
11059
11046
  }
11060
11047
  }
11061
- } else {
11062
- const inverted = invertColor(valueStr, mode);
11063
- if (inverted) {
11064
- node.value = csstree4.parse(inverted, { context: "value" });
11065
- modified = true;
11048
+ for (const fn of CSS_FUNCTION_DETECTORS) {
11049
+ if (valueStr.includes(fn.pattern)) {
11050
+ detectedCssFunctions.add(fn.key);
11051
+ if (node.loc && !propertyLines.has(fn.key)) {
11052
+ propertyLines.set(fn.key, node.loc.start.line);
11053
+ }
11054
+ }
11066
11055
  }
11067
11056
  }
11068
11057
  }
11069
11058
  });
11070
- if (modified) {
11071
- $(el).text(csstree4.generate(ast));
11072
- }
11073
11059
  } catch (e) {
11074
11060
  }
11075
11061
  });
11076
- $("[bgcolor]").each((_, el) => {
11077
- const bgcolor = $(el).attr("bgcolor") || "";
11078
- const inverted = invertColor(bgcolor, mode);
11079
- if (inverted) {
11080
- $(el).attr("bgcolor", inverted);
11062
+ for (const atRule of AT_RULE_FEATURES) {
11063
+ if (!parsedAtRules.has(atRule)) continue;
11064
+ checkPropertySupport(atRule, addWarning, framework);
11065
+ }
11066
+ const cssPropertiesToCheck = Object.keys(CSS_SUPPORT).filter(
11067
+ (k) => !k.startsWith("<") && !k.startsWith("@")
11068
+ );
11069
+ $("[style]").each((_, el) => {
11070
+ var _a;
11071
+ const style = $(el).attr("style") || "";
11072
+ const props = parseStyleProperties(style);
11073
+ const selector = describeSelector2(el);
11074
+ for (const prop of props) {
11075
+ for (const det of COMPOUND_DETECTORS) {
11076
+ if (prop === det.property) {
11077
+ const value2 = getStyleValue(style, prop);
11078
+ if (value2 == null ? void 0 : value2.includes(det.valueIncludes)) {
11079
+ checkPropertySupport(det.key, addWarning, framework, selector);
11080
+ }
11081
+ }
11082
+ }
11083
+ if (cssPropertiesToCheck.includes(prop)) {
11084
+ checkPropertySupport(prop, addWarning, framework, selector, void 0, (_a = getStyleValue(style, prop)) != null ? _a : void 0);
11085
+ }
11086
+ const value = getStyleValue(style, prop);
11087
+ if (value) {
11088
+ for (const fn of CSS_FUNCTION_DETECTORS) {
11089
+ if (value.includes(fn.pattern)) {
11090
+ checkPropertySupport(fn.key, addWarning, framework, selector);
11091
+ }
11092
+ }
11093
+ }
11081
11094
  }
11082
11095
  });
11096
+ for (const prop of parsedProperties) {
11097
+ if (prop.includes(":")) continue;
11098
+ if (!cssPropertiesToCheck.includes(prop)) continue;
11099
+ const values = propertyValues.get(prop);
11100
+ checkPropertySupport(
11101
+ prop,
11102
+ addWarning,
11103
+ framework,
11104
+ void 0,
11105
+ propertyLines.get(prop),
11106
+ values ? values.join(" ") : void 0
11107
+ );
11108
+ }
11109
+ for (const compound of COMPOUND_VALUE_FEATURES) {
11110
+ if (compound.startsWith(":") || compound.startsWith("::")) continue;
11111
+ if (parsedProperties.has(compound)) {
11112
+ checkPropertySupport(compound, addWarning, framework, void 0, propertyLines.get(compound));
11113
+ }
11114
+ }
11115
+ for (const pseudo of detectedPseudoClasses) {
11116
+ if (CSS_SUPPORT[pseudo]) {
11117
+ checkPropertySupport(pseudo, addWarning, framework);
11118
+ }
11119
+ }
11120
+ for (const pseudo of detectedPseudoElements) {
11121
+ if (CSS_SUPPORT[pseudo]) {
11122
+ checkPropertySupport(pseudo, addWarning, framework);
11123
+ }
11124
+ }
11125
+ for (const fn of detectedCssFunctions) {
11126
+ checkPropertySupport(fn, addWarning, framework, void 0, propertyLines.get(fn));
11127
+ }
11128
+ for (const w of checkDarkModeFromDom($)) addWarning(w);
11129
+ const severityOrder = { error: 0, warning: 1, info: 2 };
11130
+ warnings.sort((a, b) => severityOrder[a.severity] - severityOrder[b.severity]);
11131
+ return warnings;
11132
+ }
11133
+ function analyzeEmail(html, framework) {
11134
+ if (!html || !html.trim()) {
11135
+ return [];
11136
+ }
11137
+ if (html.length > MAX_HTML_SIZE) {
11138
+ throw new Error(`HTML input exceeds ${MAX_HTML_SIZE / 1024}KB limit.`);
11139
+ }
11140
+ const $ = cheerio4.load(html);
11141
+ return analyzeEmailFromDom($, framework);
11142
+ }
11143
+ function getFixType(prop) {
11144
+ return STRUCTURAL_FIX_PROPERTIES.has(prop) ? "structural" : "css";
11145
+ }
11146
+ var VALUE_CAVEAT_PROPS = /* @__PURE__ */ new Set(["margin", "position", "overflow"]);
11147
+ var POSITION_KEYWORDS = ["relative", "absolute", "fixed", "sticky"];
11148
+ function valueTriggersCaveat(prop, value, notes) {
11149
+ const note = (notes != null ? notes : []).join(" ");
11150
+ const noteLc = note.toLowerCase();
11151
+ if (prop === "margin") {
11152
+ if (/(?:^|[\s:(])-\.?\d/.test(value) && noteLc.includes("negative")) return true;
11153
+ if (/\bauto\b/.test(value) && noteLc.includes("auto")) return true;
11154
+ return false;
11155
+ }
11156
+ if (prop === "position") {
11157
+ const used = POSITION_KEYWORDS.find((k) => new RegExp(`\\b${k}\\b`).test(value));
11158
+ if (!used) return false;
11159
+ const m = note.match(/supports\s+.+?\s+but not\s+([^.]+)/i);
11160
+ if (m) return m[1].toLowerCase().includes(used);
11161
+ return used === "fixed" || used === "sticky";
11162
+ }
11163
+ if (prop === "overflow") {
11164
+ if (!/\b(?:auto|scroll)\b/.test(value)) return false;
11165
+ return noteLc.includes("cannot scroll");
11166
+ }
11167
+ return true;
11168
+ }
11169
+ function noteSuffix(notes) {
11170
+ if (!(notes == null ? void 0 : notes.length)) return "";
11171
+ const cleaned = notes.map((n) => n.replace(/^(?:Partial|Buggy|Not supported)\.\s*/i, "").trim()).filter(Boolean);
11172
+ return cleaned.length ? ` ${cleaned.join(" ")}` : "";
11173
+ }
11174
+ function checkPropertySupport(prop, addWarning, framework, selector, line, value) {
11175
+ var _a;
11176
+ const supportData = CSS_SUPPORT[prop];
11177
+ if (!supportData) return;
11178
+ const fixType = getFixType(prop);
11179
+ const valueGated = VALUE_CAVEAT_PROPS.has(prop);
11180
+ for (const client of EMAIL_CLIENTS) {
11181
+ const support = supportData[client.id] || "unknown";
11182
+ const notes = (_a = CSS_SUPPORT_NOTES[prop]) == null ? void 0 : _a[client.id];
11183
+ if (support === "unsupported") {
11184
+ const sug = getSuggestion(prop, client.id, framework);
11185
+ const fix = getCodeFix(prop, client.id, framework);
11186
+ addWarning(__spreadValues(__spreadValues(__spreadValues({
11187
+ severity: "warning",
11188
+ client: client.id,
11189
+ property: prop,
11190
+ message: `${client.name} does not support "${prop}".${noteSuffix(notes)}`,
11191
+ suggestion: sug.text,
11192
+ fix,
11193
+ fixType
11194
+ }, selector ? { selector } : {}), line !== void 0 ? { line } : {}), framework && (sug.isGenericFallback || fix && isCodeFixGenericFallback(prop, client.id, framework)) ? { fixIsGenericFallback: true } : {}));
11195
+ } else if (support === "partial") {
11196
+ if (valueGated && value !== void 0 && !valueTriggersCaveat(prop, value, notes)) continue;
11197
+ const sug = getSuggestion(prop, client.id, framework);
11198
+ const fix = getCodeFix(prop, client.id, framework);
11199
+ addWarning(__spreadValues(__spreadValues(__spreadValues({
11200
+ severity: "info",
11201
+ client: client.id,
11202
+ property: prop,
11203
+ message: `${client.name} has partial support for "${prop}".${noteSuffix(notes)}`,
11204
+ suggestion: sug.text,
11205
+ fix,
11206
+ fixType
11207
+ }, selector ? { selector } : {}), line !== void 0 ? { line } : {}), framework && (sug.isGenericFallback || fix && isCodeFixGenericFallback(prop, client.id, framework)) ? { fixIsGenericFallback: true } : {}));
11208
+ }
11209
+ }
11210
+ }
11211
+ function generateCompatibilityScore(warnings) {
11212
+ const result = {};
11213
+ for (const client of EMAIL_CLIENTS) {
11214
+ const clientWarnings = warnings.filter((w) => w.client === client.id);
11215
+ const errorProps = new Set(clientWarnings.filter((w) => w.severity === "error").map((w) => w.property));
11216
+ const warnProps = new Set(clientWarnings.filter((w) => w.severity === "warning").map((w) => w.property));
11217
+ const infoProps = new Set(clientWarnings.filter((w) => w.severity === "info").map((w) => w.property));
11218
+ const errors = errorProps.size;
11219
+ const warns = warnProps.size;
11220
+ const info = infoProps.size;
11221
+ const score = Math.max(0, Math.min(100, 100 - errors * 10 - warns * 3));
11222
+ result[client.id] = { score, errors, warnings: warns, info };
11223
+ }
11224
+ return result;
11225
+ }
11226
+ function warningsForClient(warnings, clientId) {
11227
+ return warnings.filter((w) => w.client === clientId);
11228
+ }
11229
+ function errorWarnings(warnings) {
11230
+ return warnings.filter((w) => w.severity === "error");
11231
+ }
11232
+ function structuralWarnings(warnings) {
11233
+ return warnings.filter((w) => w.fixType === "structural");
11083
11234
  }
11084
11235
 
11085
11236
  // src/diff.ts
@@ -12781,7 +12932,7 @@ function checkTemplateVariables(html) {
12781
12932
  }
12782
12933
 
12783
12934
  // src/overflow-checker.ts
12784
- var csstree5 = __toESM(require("css-tree"), 1);
12935
+ var csstree6 = __toESM(require("css-tree"), 1);
12785
12936
  function fixedPxWidth($el) {
12786
12937
  const style = $el.attr("style") || "";
12787
12938
  const styleMatch = style.match(/(?:^|[;\s])width\s*:\s*(\d+)px/i);
@@ -12818,11 +12969,11 @@ function checkOverflowFromDom($) {
12818
12969
  $("style").each((_, el) => {
12819
12970
  let ast;
12820
12971
  try {
12821
- ast = csstree5.parse($(el).text());
12972
+ ast = csstree6.parse($(el).text());
12822
12973
  } catch (e) {
12823
12974
  return;
12824
12975
  }
12825
- csstree5.walk(ast, {
12976
+ csstree6.walk(ast, {
12826
12977
  visit: "Rule",
12827
12978
  enter(node) {
12828
12979
  if (node.type !== "Rule") return;
@@ -12831,7 +12982,7 @@ function checkOverflowFromDom($) {
12831
12982
  node.block.children.forEach((child) => {
12832
12983
  if (child.type !== "Declaration") return;
12833
12984
  const prop = child.property.toLowerCase();
12834
- const val = csstree5.generate(child.value);
12985
+ const val = csstree6.generate(child.value);
12835
12986
  if (prop === "width") {
12836
12987
  const m = val.match(/^(\d+)px$/);
12837
12988
  if (m) widthPx = parseInt(m[1], 10);
@@ -12841,7 +12992,7 @@ function checkOverflowFromDom($) {
12841
12992
  }
12842
12993
  });
12843
12994
  if (widthPx !== null && widthPx > EMAIL_MAX_WIDTH && !fluid) {
12844
- const selector = csstree5.generate(node.prelude).trim().slice(0, 40);
12995
+ const selector = csstree6.generate(node.prelude).trim().slice(0, 40);
12845
12996
  addWidthIssue(widthPx, selector || "rule", issues, seen);
12846
12997
  }
12847
12998
  }
@@ -12875,7 +13026,7 @@ function checkOverflow(html) {
12875
13026
  }
12876
13027
 
12877
13028
  // src/visual-checker.ts
12878
- var csstree6 = __toESM(require("css-tree"), 1);
13029
+ var csstree7 = __toESM(require("css-tree"), 1);
12879
13030
  var CSS_WIDE_KEYWORDS = /* @__PURE__ */ new Set(["inherit", "initial", "unset", "revert", "revert-layer"]);
12880
13031
  var GRADIENT_RE = /(?:linear|radial|conic)-gradient\(/i;
12881
13032
  function isSolidColor(value) {
@@ -12962,7 +13113,7 @@ function ruleToMap(node) {
12962
13113
  const map = /* @__PURE__ */ new Map();
12963
13114
  node.block.children.forEach((child) => {
12964
13115
  if (child.type === "Declaration") {
12965
- map.set(child.property.toLowerCase(), csstree6.generate(child.value));
13116
+ map.set(child.property.toLowerCase(), csstree7.generate(child.value));
12966
13117
  }
12967
13118
  });
12968
13119
  return map;
@@ -12976,11 +13127,11 @@ function checkVisualFromDom($) {
12976
13127
  $("style").each((_, el) => {
12977
13128
  let ast;
12978
13129
  try {
12979
- ast = csstree6.parse($(el).text());
13130
+ ast = csstree7.parse($(el).text());
12980
13131
  } catch (e) {
12981
13132
  return;
12982
13133
  }
12983
- csstree6.walk(ast, {
13134
+ csstree7.walk(ast, {
12984
13135
  visit: "Rule",
12985
13136
  enter(node) {
12986
13137
  if (node.type === "Rule") inspectDeclarations(ruleToMap(node), issues, seen);
@@ -13064,7 +13215,7 @@ function toPlainText(html) {
13064
13215
  if (trimmed) lines.push(trimmed);
13065
13216
  currentLine = "";
13066
13217
  }
13067
- function walk7(node) {
13218
+ function walk8(node) {
13068
13219
  var _a;
13069
13220
  if (node.type === "text") {
13070
13221
  const text = node.data.replace(/\s+/g, " ");
@@ -13113,7 +13264,7 @@ function toPlainText(html) {
13113
13264
  currentLine = "- ";
13114
13265
  }
13115
13266
  for (const child of el.children) {
13116
- walk7(child);
13267
+ walk8(child);
13117
13268
  }
13118
13269
  if (isBlock) flushLine();
13119
13270
  }
@@ -13121,7 +13272,7 @@ function toPlainText(html) {
13121
13272
  const root = body.length ? body[0] : $.root()[0];
13122
13273
  if (root && "children" in root) {
13123
13274
  for (const child of root.children) {
13124
- walk7(child);
13275
+ walk8(child);
13125
13276
  }
13126
13277
  }
13127
13278
  flushLine();