Skip to content

Commit 61c3c54

Browse files
custom plugin to combine rules (#412)
* 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 <[email protected]>
1 parent 0d3cd9e commit 61c3c54

4 files changed

+91
-72
lines changed

build/postcss-combine-selectors.cjs

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
const creator = () => {
2+
return {
3+
postcssPlugin: 'postcss-combine-selectors',
4+
OnceExit(root) {
5+
const rulesToCombine = new Map()
6+
7+
root.walkRules(rule => {
8+
if (isKeyframesRule(rule)) {
9+
return
10+
}
11+
12+
const key = ruleKey(rule)
13+
const existing = rulesToCombine.get(key)
14+
15+
// Existing group:
16+
// - add rule to the group
17+
if (existing) {
18+
existing.rules.push(rule)
19+
return
20+
}
21+
22+
// New group:
23+
// - first rule is the one we're going to combine into
24+
// - create an empty slice for other rules to be added to
25+
rulesToCombine.set(key, {
26+
first: rule,
27+
rules: []
28+
})
29+
})
30+
31+
// Iterate over all groups
32+
for (const { first, rules } of rulesToCombine.values()) {
33+
// If there was only one rule for a given group, there's nothing to combine
34+
if (rules.length === 0) {
35+
continue
36+
}
37+
38+
// Append all contents of all subsequent rules to the first rule
39+
for (const rule of rules) {
40+
rule.each((child) => {
41+
child.remove()
42+
first.append(child)
43+
})
44+
45+
// Remove the now-empty rule
46+
rule.remove()
47+
}
48+
}
49+
},
50+
}
51+
}
52+
53+
/**
54+
* Construct a key that is specific to the AST ancestry of the rule.
55+
* Only rules with the same key can be combined.
56+
*
57+
* @param {import('postcss').Rule} rule
58+
* @returns {string}
59+
*/
60+
function ruleKey(rule) {
61+
let key = `[rule ${rule.selector}]`
62+
63+
let ancestor = rule.parent
64+
while (ancestor) {
65+
if (ancestor.type === 'atrule') {
66+
key = `[${ancestor.name} ${ancestor.params}]${key}`
67+
} else if (ancestor.type === 'rule') {
68+
key = `[rule ${ancestor.selector}]${key}`
69+
} else if (ancestor.type === 'root') {
70+
break
71+
}
72+
73+
ancestor = ancestor.parent
74+
}
75+
76+
return key
77+
}
78+
79+
function isKeyframesRule(rule) {
80+
if (rule.parent?.type === 'atrule' && rule.parent.name === 'keyframes') {
81+
return true
82+
}
83+
84+
return false
85+
}
86+
87+
module.exports = creator
88+
module.exports.postcss = true

package-lock.json

+2-70
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

-1
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,6 @@
317317
"open-color": "^1.9.1",
318318
"postcss": "^8.3.9",
319319
"postcss-cli": "^8.3.1",
320-
"postcss-combine-duplicated-selectors": "^10.0.3",
321320
"postcss-import": "^14.0.2",
322321
"postcss-preset-env": "6.7.x",
323322
"typescript": "^4.9.4"

postcss.config.cjs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const postcssPresetEnv = require('postcss-preset-env')
22
const postcssImport = require('postcss-import')
33
const cssnano = require('cssnano')
4-
const combineSelectors = require('postcss-combine-duplicated-selectors')
4+
const combineSelectors = require('./build/postcss-combine-selectors.cjs')
55

66
const lib = process.env.npm_lifecycle_event
77

0 commit comments

Comments
 (0)