Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

custom plugin to combine rules #412

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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