Skip to content

Commit

Permalink
chore(tailwind): extract selector processing from sanitization functions
Browse files Browse the repository at this point in the history
  • Loading branch information
Sjoertjuh committed Jan 23, 2025
1 parent 1d9227a commit aef20d1
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 48 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import selectorParser from "postcss-selector-parser";
import type { AtRule, Root } from "postcss";
import { sanitizeClassName } from "../../compatibility/sanitize-class-name";
import { processSelector } from "../process-selector";

/**
* This function goes through a few steps to ensure the best email client support and
Expand All @@ -21,23 +20,12 @@ export const sanitizeMediaQueries = (root: Root) => {
const sanitizedAtRule = atRule.clone();

sanitizedAtRule.walkRules((rule) => {
let hasPseudoSelector = false as boolean;
rule.selector = selectorParser((selectorRoot) => {
selectorRoot.walkPseudos(() => {
hasPseudoSelector = true;
});
if (!hasPseudoSelector) {
selectorRoot.walkClasses((singleClass) => {
mediaQueryClasses.push(singleClass.value);
singleClass.replaceWith(
selectorParser.className({
...singleClass,
value: sanitizeClassName(singleClass.value),
}),
);
});
}
}).processSync(rule.selector);
const { processedSelector, hasPseudoSelector } = processSelector(
rule,
false,
mediaQueryClasses,
);
rule.selector = processedSelector;

if (!hasPseudoSelector) {
rule.walkDecls((declaration) => {
Expand Down
45 changes: 45 additions & 0 deletions packages/tailwind/src/utils/css/process-selector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import selectorParser from "postcss-selector-parser";
import type { Rule } from "postcss";
import { sanitizeClassName } from "../compatibility/sanitize-class-name";

/**
* Helper function to process selectors and sanitize class names.
* @param rule - The CSS rule to process.
* @param withPseudo - Whether to process selectors with pseudo-classes.
* @param outputClasses - Array to store class names after processing.
* @returns An object containing the processed selector and whether it has a pseudo selector.
*/
export const processSelector = (
rule: Rule,
withPseudo: boolean,
outputClasses: string[],
) => {
let hasPseudoSelector = false as boolean;

const processedSelector = selectorParser((selectorRoot) => {
selectorRoot.walkPseudos(() => {
hasPseudoSelector = true;
});

if (
(withPseudo && hasPseudoSelector) ||
(!withPseudo && !hasPseudoSelector)
) {
selectorRoot.walkClasses((singleClass) => {
outputClasses.push(singleClass.value);

singleClass.replaceWith(
selectorParser.className({
...singleClass,
value: sanitizeClassName(singleClass.value),
}),
);
});
}
}).processSync(rule.selector);

return {
processedSelector,
hasPseudoSelector,
};
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import selectorParser from "postcss-selector-parser";
import type { Root, Rule, AtRule } from "postcss";
import { sanitizeClassName } from "../../compatibility/sanitize-class-name";
import { processSelector } from "../process-selector";

interface PseudoClassResult {
pseudoClassClasses: string[];
Expand All @@ -21,38 +20,16 @@ export const sanitizePseudoClasses = (root: Root): PseudoClassResult => {
const pseudoClassClasses: string[] = [];

const processRule = (rule: Rule): Rule | null => {
let hasPseudoSelector = false as boolean;

// Parse the selector to check for pseudo-classes
const processedSelector = selectorParser((selectorRoot) => {
selectorRoot.walkPseudos(() => {
hasPseudoSelector = true;
});

if (hasPseudoSelector) {
// Store the original class name before sanitization
selectorRoot.walkClasses((singleClass) => {
const originalClass = singleClass.value;
if (!pseudoClassClasses.includes(originalClass)) {
pseudoClassClasses.push(originalClass);
}

// Sanitize the class name
singleClass.replaceWith(
selectorParser.className({
...singleClass,
value: sanitizeClassName(singleClass.value),
}),
);
});
}
}).processSync(rule.selector);
const { processedSelector, hasPseudoSelector } = processSelector(
rule,
true,
pseudoClassClasses,
);

if (hasPseudoSelector) {
const newRule = rule.clone();
newRule.selector = processedSelector;

// Make all declarations !important
newRule.walkDecls((declaration) => {
declaration.important = true;
});
Expand Down

0 comments on commit aef20d1

Please sign in to comment.