%PDF- %PDF-
Direktori : /home/forge/takeaseat.eco-n-tech.co.uk/node_modules/tailwindcss/jit/lib/ |
Current File : //home/forge/takeaseat.eco-n-tech.co.uk/node_modules/tailwindcss/jit/lib/expandApplyAtRules.js |
const postcss = require('postcss') const { resolveMatches } = require('./generateRules') const { bigSign, escapeClassName } = require('./utils') function buildApplyCache(applyCandidates, context) { for (let candidate of applyCandidates) { if (context.notClassCache.has(candidate) || context.applyClassCache.has(candidate)) { continue } if (context.classCache.has(candidate)) { context.applyClassCache.set( candidate, context.classCache.get(candidate).map(([meta, rule]) => [meta, rule.clone()]) ) continue } let matches = Array.from(resolveMatches(candidate, context)) if (matches.length === 0) { context.notClassCache.add(candidate) continue } context.applyClassCache.set(candidate, matches) } return context.applyClassCache } // TODO: Apply `!important` stuff correctly instead of just skipping it function extractApplyCandidates(params) { let candidates = params.split(/[\s\t\n]+/g) if (candidates[candidates.length - 1] === '!important') { return [candidates.slice(0, -1), true] } return [candidates, false] } function partitionApplyParents(root) { let applyParents = new Set() root.walkAtRules('apply', (rule) => { applyParents.add(rule.parent) }) for (let rule of applyParents) { let nodeGroups = [] let lastGroup = [] for (let node of rule.nodes) { if (node.type === 'atrule' && node.name === 'apply') { if (lastGroup.length > 0) { nodeGroups.push(lastGroup) lastGroup = [] } nodeGroups.push([node]) } else { lastGroup.push(node) } } if (lastGroup.length > 0) { nodeGroups.push(lastGroup) } if (nodeGroups.length === 1) { continue } for (let group of [...nodeGroups].reverse()) { let newParent = rule.clone({ nodes: [] }) newParent.append(group) rule.after(newParent) } rule.remove() } } function processApply(root, context) { let applyCandidates = new Set() // Collect all @apply rules and candidates let applies = [] root.walkAtRules('apply', (rule) => { let [candidates] = extractApplyCandidates(rule.params) for (let util of candidates) { applyCandidates.add(util) } applies.push(rule) }) // Start the @apply process if we have rules with @apply in them if (applies.length > 0) { // Fill up some caches! let applyClassCache = buildApplyCache(applyCandidates, context) /** * When we have an apply like this: * * .abc { * @apply hover:font-bold; * } * * What we essentially will do is resolve to this: * * .abc { * @apply .hover\:font-bold:hover { * font-weight: 500; * } * } * * Notice that the to-be-applied class is `.hover\:font-bold:hover` and that the utility candidate was `hover:font-bold`. * What happens in this function is that we prepend a `.` and escape the candidate. * This will result in `.hover\:font-bold` * Which means that we can replace `.hover\:font-bold` with `.abc` in `.hover\:font-bold:hover` resulting in `.abc:hover` */ // TODO: Should we use postcss-selector-parser for this instead? function replaceSelector(selector, utilitySelectors, candidate) { let needle = `.${escapeClassName(candidate)}` let utilitySelectorsList = utilitySelectors.split(/\s*,\s*/g) return selector .split(/\s*,\s*/g) .map((s) => { let replaced = [] for (let utilitySelector of utilitySelectorsList) { let replacedSelector = utilitySelector.replace(needle, s) if (replacedSelector === utilitySelector) { continue } replaced.push(replacedSelector) } return replaced.join(', ') }) .join(', ') } /** @type {Map<import('postcss').Node, [string, boolean, import('postcss').Node[]][]>} */ let perParentApplies = new Map() // Collect all apply candidates and their rules for (let apply of applies) { let candidates = perParentApplies.get(apply.parent) || [] perParentApplies.set(apply.parent, candidates) let [applyCandidates, important] = extractApplyCandidates(apply.params) if (apply.parent.type === 'atrule') { if (apply.parent.name === 'screen') { const screenType = apply.parent.params throw apply.error( `@apply is not supported within nested at-rules like @screen. We suggest you write this as @apply ${applyCandidates .map((c) => `${screenType}:${c}`) .join(' ')} instead.` ) } throw apply.error( `@apply is not supported within nested at-rules like @${apply.parent.name}. You can fix this by un-nesting @${apply.parent.name}.` ) } for (let applyCandidate of applyCandidates) { if (!applyClassCache.has(applyCandidate)) { throw apply.error( `The \`${applyCandidate}\` class does not exist. If \`${applyCandidate}\` is a custom class, make sure it is defined within a \`@layer\` directive.` ) } let rules = applyClassCache.get(applyCandidate) candidates.push([applyCandidate, important, rules]) } } for (const [parent, candidates] of perParentApplies) { let siblings = [] for (let [applyCandidate, important, rules] of candidates) { for (let [meta, node] of rules) { let root = postcss.root({ nodes: [node.clone()] }) let canRewriteSelector = node.type !== 'atrule' || (node.type === 'atrule' && node.name !== 'keyframes') if (canRewriteSelector) { root.walkRules((rule) => { rule.selector = replaceSelector(parent.selector, rule.selector, applyCandidate) rule.walkDecls((d) => { d.important = important }) }) } siblings.push([meta, root.nodes[0]]) } } // Inject the rules, sorted, correctly let nodes = siblings.sort(([a], [z]) => bigSign(a.sort - z.sort)).map((s) => s[1]) // console.log(parent) // `parent` refers to the node at `.abc` in: .abc { @apply mt-2 } parent.after(nodes) } for (let apply of applies) { // If there are left-over declarations, just remove the @apply if (apply.parent.nodes.length > 1) { apply.remove() } else { // The node is empty, drop the full node apply.parent.remove() } } // Do it again, in case we have other `@apply` rules processApply(root, context) } } function expandApplyAtRules(context) { return (root) => { partitionApplyParents(root) processApply(root, context) } } module.exports = expandApplyAtRules