"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.formatVariantSelector = formatVariantSelector; exports.finalizeSelector = finalizeSelector; exports.selectorFunctions = void 0; var _postcssSelectorParser = _interopRequireDefault(require("postcss-selector-parser")); var _unesc = _interopRequireDefault(require("postcss-selector-parser/dist/util/unesc")); var _escapeClassName = _interopRequireDefault(require("../util/escapeClassName")); var _prefixSelector = _interopRequireDefault(require("../util/prefixSelector")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } let MERGE = ':merge'; let PARENT = '&'; let selectorFunctions = new Set([ MERGE ]); exports.selectorFunctions = selectorFunctions; function formatVariantSelector(current, ...others) { for (let other of others){ let incomingValue = resolveFunctionArgument(other, MERGE); if (incomingValue !== null) { let existingValue = resolveFunctionArgument(current, MERGE, incomingValue); if (existingValue !== null) { let existingTarget = `${MERGE}(${incomingValue})`; let splitIdx = other.indexOf(existingTarget); let addition = other.slice(splitIdx + existingTarget.length).split(' ')[0]; current = current.replace(existingTarget, existingTarget + addition); continue; } } current = other.replace(PARENT, current); } return current; } function finalizeSelector(format, { selector: selector1 , candidate , context }) { var ref, ref1; var ref2; let separator = (ref2 = context === null || context === void 0 ? void 0 : (ref = context.tailwindConfig) === null || ref === void 0 ? void 0 : ref.separator) !== null && ref2 !== void 0 ? ref2 : ':'; // Split by the separator, but ignore the separator inside square brackets: // // E.g.: dark:lg:hover:[paint-order:markers] // ┬ ┬ ┬ ┬ // │ │ │ ╰── We will not split here // ╰──┴─────┴─────────────── We will split here // let splitter = new RegExp(`\\${separator}(?![^[]*\\])`); let base = candidate.split(splitter).pop(); if (context === null || context === void 0 ? void 0 : (ref1 = context.tailwindConfig) === null || ref1 === void 0 ? void 0 : ref1.prefix) { format = (0, _prefixSelector).default(context.tailwindConfig.prefix, format); } format = format.replace(PARENT, `.${(0, _escapeClassName).default(candidate)}`); // Normalize escaped classes, e.g.: // // The idea would be to replace the escaped `base` in the selector with the // `format`. However, in css you can escape the same selector in a few // different ways. This would result in different strings and therefore we // can't replace it properly. // // base: bg-[rgb(255,0,0)] // base in selector: bg-\\[rgb\\(255\\,0\\,0\\)\\] // escaped base: bg-\\[rgb\\(255\\2c 0\\2c 0\\)\\] // selector1 = (0, _postcssSelectorParser).default((selectors)=>{ return selectors.walkClasses((node)=>{ if (node.raws && node.value.includes(base)) { node.raws.value = (0, _escapeClassName).default((0, _unesc).default(node.raws.value)); } return node; }); }).processSync(selector1); // We can safely replace the escaped base now, since the `base` section is // now in a normalized escaped value. selector1 = selector1.replace(`.${(0, _escapeClassName).default(base)}`, format); // Remove unnecessary pseudo selectors that we used as placeholders return (0, _postcssSelectorParser).default((selectors)=>{ return selectors.map((selector2)=>{ selector2.walkPseudos((p)=>{ if (selectorFunctions.has(p.value)) { p.replaceWith(p.nodes); } return p; }); // This will make sure to move pseudo's to the correct spot (the end for // pseudo elements) because otherwise the selector will never work // anyway. // // E.g.: // - `before:hover:text-center` would result in `.before\:hover\:text-center:hover::before` // - `hover:before:text-center` would result in `.hover\:before\:text-center:hover::before` // // `::before:hover` doesn't work, which means that we can make it work for you by flipping the order. function collectPseudoElements(selector) { let nodes = []; for (let node of selector.nodes){ if (isPseudoElement(node)) { nodes.push(node); selector.removeChild(node); } if (node === null || node === void 0 ? void 0 : node.nodes) { nodes.push(...collectPseudoElements(node)); } } return nodes; } let pseudoElements = collectPseudoElements(selector2); if (pseudoElements.length > 0) { selector2.nodes.push(pseudoElements.sort(sortSelector)); } return selector2; }); }).processSync(selector1); } // Note: As a rule, double colons (::) should be used instead of a single colon // (:). This distinguishes pseudo-classes from pseudo-elements. However, since // this distinction was not present in older versions of the W3C spec, most // browsers support both syntaxes for the original pseudo-elements. let pseudoElementsBC = [ ':before', ':after', ':first-line', ':first-letter' ]; // These pseudo-elements _can_ be combined with other pseudo selectors AND the order does matter. let pseudoElementExceptions = [ '::file-selector-button' ]; // This will make sure to move pseudo's to the correct spot (the end for // pseudo elements) because otherwise the selector will never work // anyway. // // E.g.: // - `before:hover:text-center` would result in `.before\:hover\:text-center:hover::before` // - `hover:before:text-center` would result in `.hover\:before\:text-center:hover::before` // // `::before:hover` doesn't work, which means that we can make it work // for you by flipping the order. function sortSelector(a, z) { // Both nodes are non-pseudo's so we can safely ignore them and keep // them in the same order. if (a.type !== 'pseudo' && z.type !== 'pseudo') { return 0; } // If one of them is a combinator, we need to keep it in the same order // because that means it will start a new "section" in the selector. if (a.type === 'combinator' ^ z.type === 'combinator') { return 0; } // One of the items is a pseudo and the other one isn't. Let's move // the pseudo to the right. if (a.type === 'pseudo' ^ z.type === 'pseudo') { return (a.type === 'pseudo') - (z.type === 'pseudo'); } // Both are pseudo's, move the pseudo elements (except for // ::file-selector-button) to the right. return isPseudoElement(a) - isPseudoElement(z); } function isPseudoElement(node) { if (node.type !== 'pseudo') return false; if (pseudoElementExceptions.includes(node.value)) return false; return node.value.startsWith('::') || pseudoElementsBC.includes(node.value); } function resolveFunctionArgument(haystack, needle, arg) { let startIdx = haystack.indexOf(arg ? `${needle}(${arg})` : needle); if (startIdx === -1) return null; // Start inside the `(` startIdx += needle.length + 1; let target = ''; let count = 0; for (let char of haystack.slice(startIdx)){ if (char !== '(' && char !== ')') { target += char; } else if (char === '(') { target += char; count++; } else if (char === ')') { if (--count < 0) break; // unbalanced target += char; } } return target; }