diff --git a/src/extended-css/helpers/style-protector.ts b/src/extended-css/helpers/style-protector.ts index 61e7eea1..2eb38dba 100644 --- a/src/extended-css/helpers/style-protector.ts +++ b/src/extended-css/helpers/style-protector.ts @@ -46,10 +46,12 @@ export const protectStyleAttribute = ( const styles: CssStyleMap[] = []; rules.forEach((ruleData) => { const { style } = ruleData; - if (!style) { - throw new Error(`No affectedElement style to apply for selector: '${ruleData.selector}'`); + // some rules might have only debug property in style declaration + // e.g. 'div:has(> a) { debug: true }' -> parsed to boolean `ruleData.debug` + // so no style is fine, and here we should collect only valid styles to protect + if (style) { + styles.push(style); } - styles.push(style); }); const protectionObserver = new ExtMutationObserver(createProtectionCallback(styles)); protectionObserver.observe(node, PROTECTION_OBSERVER_OPTIONS); diff --git a/src/extended-css/helpers/style-setter.ts b/src/extended-css/helpers/style-setter.ts index 8cdc9d7a..63e17205 100644 --- a/src/extended-css/helpers/style-setter.ts +++ b/src/extended-css/helpers/style-setter.ts @@ -84,15 +84,19 @@ export const applyStyle = (context: Context, affectedElement: AffectedElement): const { node, rules } = affectedElement; for (let i = 0; i < rules.length; i += 1) { - const { selector, style } = rules[i]; - if (!style) { - throw new Error(`No affectedElement style to apply for selector: '${selector}'`); + const { selector, style, debug } = rules[i]; + // rule may not have style to apply + // e.g. 'div:has(> a) { debug: true }' -> means no style to apply, and enable debug mode + if (style) { + if (style[REMOVE_PSEUDO_MARKER] === PSEUDO_PROPERTY_POSITIVE_VALUE) { + removeElement(context, affectedElement); + return; + } + setStyleToElement(node, style); + } else if (!debug) { + // but rule should not have both style and debug properties + throw new Error(`No style declaration in rule for selector: '${selector}'`); } - if (style[REMOVE_PSEUDO_MARKER] === PSEUDO_PROPERTY_POSITIVE_VALUE) { - removeElement(context, affectedElement); - return; - } - setStyleToElement(node, style); } }; diff --git a/src/extended-css/helpers/timing-stats.ts b/src/extended-css/helpers/timing-stats.ts index c706f060..8144a984 100644 --- a/src/extended-css/helpers/timing-stats.ts +++ b/src/extended-css/helpers/timing-stats.ts @@ -61,7 +61,7 @@ export class TimingStats implements TimingStatsInterface { type SelectorLogData = { selectorParsed: string; timings: TimingStatsInterface; - styleApplied?: CssStyleMap; + styleApplied?: CssStyleMap | null; removed?: boolean; matchedElements?: HTMLElement[]; }; @@ -109,18 +109,23 @@ export const printTimingInfo = (context: Context): void => { context.parsedRules.forEach((ruleData) => { if (ruleData.timingStats) { - const { selector, style, matchedElements } = ruleData; - if (!style) { - throw new Error(`Rule with selector '${selector}' should have style declaration.`); + const { selector, style, debug, matchedElements } = ruleData; + // style declaration for some rules is parsed to debug property and no style to apply + // e.g. 'div:has(> a) { debug: true }' + if (!style && !debug) { + throw new Error(`Rule should have style declaration for selector: '${selector}'`); } const selectorData: SelectorLogData = { selectorParsed: selector, timings: beautifyTimings(ruleData.timingStats), }; - if (style[REMOVE_PSEUDO_MARKER] === PSEUDO_PROPERTY_POSITIVE_VALUE) { + // `ruleData.style` may contain `remove` pseudo-property + // and make logs look better + if (style && style[REMOVE_PSEUDO_MARKER] === PSEUDO_PROPERTY_POSITIVE_VALUE) { selectorData.removed = true; + // no matchedElements for such case as they are removed after ExtendedCss applied } else { - selectorData.styleApplied = style; + selectorData.styleApplied = style || null; selectorData.matchedElements = matchedElements; } timingsLogData[selector] = selectorData; diff --git a/test/extended-css.test.ts b/test/extended-css.test.ts index ae87e5e4..d2885255 100644 --- a/test/extended-css.test.ts +++ b/test/extended-css.test.ts @@ -676,4 +676,34 @@ describe('extended css library', () => { extendedCss.apply(); }); + + it('debugging - only debug property for logging', (done) => { + expect.assertions(3); + document.body.innerHTML = '
'; + const styleSheet = ` + #case13:not(with-debug) { debug: true } + `; + const extendedCss = new ExtendedCss({ styleSheet }); + + const loggerInfo = logger.info; + logger.info = function (...args) { + if (args.length === 3 + && typeof args[0] === 'string' && args[0].indexOf('Timings') !== -1) { + const loggedData = args[2]; + expect(loggedData).toBeDefined(); + + const selectors = Object.keys(loggedData); + expect(selectors.length).toEqual(1); + expect(selectors[0].includes('with-debug')).toBeTruthy(); + + // Cleanup + logger.info = loggerInfo; + extendedCss.dispose(); + done(); + } + return loggerInfo.apply(this, args); + }; + + extendedCss.apply(); + }); });