Skip to content

Commit

Permalink
custom plugin to combine rules (#412)
Browse files Browse the repository at this point in the history
* custom plugin to combine rules

* fmt

* Update postcss-combine-selectors.cjs

* Update postcss.config.cjs

* removes the postcss plugin from package.json

---------

Co-authored-by: Adam Argyle <argyle@google.com>
  • Loading branch information
romainmenke and argyleink authored Sep 20, 2023
1 parent 0d3cd9e commit 61c3c54
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 72 deletions.
88 changes: 88 additions & 0 deletions build/postcss-combine-selectors.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
const creator = () => {
return {
postcssPlugin: 'postcss-combine-selectors',
OnceExit(root) {
const rulesToCombine = new Map()

root.walkRules(rule => {
if (isKeyframesRule(rule)) {
return
}

const key = ruleKey(rule)
const existing = rulesToCombine.get(key)

// Existing group:
// - add rule to the group
if (existing) {
existing.rules.push(rule)
return
}

// New group:
// - first rule is the one we're going to combine into
// - create an empty slice for other rules to be added to
rulesToCombine.set(key, {
first: rule,
rules: []
})
})

// Iterate over all groups
for (const { first, rules } of rulesToCombine.values()) {
// If there was only one rule for a given group, there's nothing to combine
if (rules.length === 0) {
continue
}

// Append all contents of all subsequent rules to the first rule
for (const rule of rules) {
rule.each((child) => {
child.remove()
first.append(child)
})

// Remove the now-empty rule
rule.remove()
}
}
},
}
}

/**
* Construct a key that is specific to the AST ancestry of the rule.
* Only rules with the same key can be combined.
*
* @param {import('postcss').Rule} rule
* @returns {string}
*/
function ruleKey(rule) {
let key = `[rule ${rule.selector}]`

let ancestor = rule.parent
while (ancestor) {
if (ancestor.type === 'atrule') {
key = `[${ancestor.name} ${ancestor.params}]${key}`
} else if (ancestor.type === 'rule') {
key = `[rule ${ancestor.selector}]${key}`
} else if (ancestor.type === 'root') {
break
}

ancestor = ancestor.parent
}

return key
}

function isKeyframesRule(rule) {
if (rule.parent?.type === 'atrule' && rule.parent.name === 'keyframes') {
return true
}

return false
}

module.exports = creator
module.exports.postcss = true
72 changes: 2 additions & 70 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,6 @@
"open-color": "^1.9.1",
"postcss": "^8.3.9",
"postcss-cli": "^8.3.1",
"postcss-combine-duplicated-selectors": "^10.0.3",
"postcss-import": "^14.0.2",
"postcss-preset-env": "6.7.x",
"typescript": "^4.9.4"
Expand Down
2 changes: 1 addition & 1 deletion postcss.config.cjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const postcssPresetEnv = require('postcss-preset-env')
const postcssImport = require('postcss-import')
const cssnano = require('cssnano')
const combineSelectors = require('postcss-combine-duplicated-selectors')
const combineSelectors = require('./build/postcss-combine-selectors.cjs')

const lib = process.env.npm_lifecycle_event

Expand Down

0 comments on commit 61c3c54

Please sign in to comment.