From 3598cf762ce89428b0a0335c696385461a0da864 Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Tue, 19 Nov 2024 18:58:51 +0200 Subject: [PATCH] feat: preservedSelectors option for inliner --- src/transformers/inline.js | 49 +++++++++++++++-------------- test/transformers/inlineCSS.test.js | 23 ++++++++++++++ 2 files changed, 49 insertions(+), 23 deletions(-) diff --git a/src/transformers/inline.js b/src/transformers/inline.js index 28dc494c..5536278a 100644 --- a/src/transformers/inline.js +++ b/src/transformers/inline.js @@ -2,6 +2,7 @@ import juice from 'juice' import postcss from 'postcss' import get from 'lodash-es/get.js' import has from 'lodash-es/has.js' +import { defu as merge } from 'defu' import * as cheerio from 'cheerio/slim' import remove from 'lodash-es/remove.js' import { render } from 'posthtml-render' @@ -33,6 +34,26 @@ export async function inline(html = '', options = {}) { options.removeInlinedSelectors = get(options, 'removeInlinedSelectors', true) options.resolveCalc = get(options, 'resolveCalc', true) options.preferUnitlessValues = get(options, 'preferUnitlessValues', true) + options.preservedSelectors = new Set([ + ...get(options, 'preservedSelectors', []), + ...[ + '.body', // Gmail + '.gmail', // Gmail + '.apple', // Apple Mail + '.ios', // Mail on iOS + '.ox-', // Open-Xchange + '.outlook', // Outlook.com + '[data-ogs', // Outlook.com + '.bloop_container', // Airmail + '.Singleton', // Apple Mail 10 + '.unused', // Notes 8 + '.moz-text-html', // Thunderbird + '.mail-detail-content', // Comcast, Libero webmail + 'edo', // Edison (all) + '#msgBody', // Freenet uses #msgBody + '.lang' // Fenced code blocks + ], + ]) juice.styleToAttribute = get(options, 'styleToAttribute', {}) juice.applyWidthAttributes = get(options, 'applyWidthAttributes', true) @@ -106,26 +127,8 @@ export async function inline(html = '', options = {}) { } ) - const preservedClasses = new Set([ - '.body', // Gmail - '.gmail', // Gmail - '.apple', // Apple Mail - '.ios', // Mail on iOS - '.ox-', // Open-Xchange - '.outlook', // Outlook.com - '[data-ogs', // Outlook.com - '.bloop_container', // Airmail - '.Singleton', // Apple Mail 10 - '.unused', // Notes 8 - '.moz-text-html', // Thunderbird - '.mail-detail-content', // Comcast, Libero webmail - 'edo', // Edison (all) - '#msgBody', // Freenet uses #msgBody - '.lang' // Fenced code blocks - ]) - // Precompile a single regex to match any substring from the preservedClasses set - const combinedPattern = Array.from(preservedClasses) + const combinedPattern = Array.from(options.preservedSelectors) .map(pattern => pattern.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&')) // Escape special regex chars .join('|') // Combine all patterns into a single regex pattern with 'OR' (|) @@ -137,7 +140,7 @@ export async function inline(html = '', options = {}) { root.walkAtRules(rule => { if (['media', 'supports'].includes(rule.name)) { rule.walkRules(rule => { - preservedClasses.add(rule.selector) + options.preservedSelectors.add(rule.selector) }) } }) @@ -185,12 +188,12 @@ export async function inline(html = '', options = {}) { // Preserve pseudo selectors // TODO: revisit pseudos list if ([':hover', ':active', ':focus', ':visited', ':link', ':before', ':after'].some(i => selector.includes(i))) { - preservedClasses.add(selector) + options.preservedSelectors.add(selector) } if (options.removeInlinedSelectors) { // Remove the rule in the +

test

+ `, + { + removeInlinedSelectors: true, + preservedSelectors: ['foo', '.bar'], + }) + + expect(cleanString(result)).toBe(cleanString(` + +

test

+ ` + )) + }) + test('Preserves inlined selectors', async () => { const result = await inlineCSS(html, { removeInlinedSelectors: false,