Author: Romain Menke [11521496+romainmenke@users.noreply.github.com]
Committer: GitHub [noreply@github.com] Wed, 20 Sep 2023 19:57:30 +0000
Hash: 61c3c54fd267df742ccbc87d651f70838c9c1810
Timestamp: Wed, 20 Sep 2023 19:57:30 +0000 (1 year ago)

+91 -72 +/-4 browse
custom plugin to combine rules (#412)
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 <argyle@google.com>
1diff --git a/build/postcss-combine-selectors.cjs b/build/postcss-combine-selectors.cjs
2new file mode 100644
3index 0000000..497504d
4--- /dev/null
5+++ b/build/postcss-combine-selectors.cjs
6 @@ -0,0 +1,88 @@
7+ const creator = () => {
8+ return {
9+ postcssPlugin: 'postcss-combine-selectors',
10+ OnceExit(root) {
11+ const rulesToCombine = new Map()
12+
13+ root.walkRules(rule => {
14+ if (isKeyframesRule(rule)) {
15+ return
16+ }
17+
18+ const key = ruleKey(rule)
19+ const existing = rulesToCombine.get(key)
20+
21+ // Existing group:
22+ // - add rule to the group
23+ if (existing) {
24+ existing.rules.push(rule)
25+ return
26+ }
27+
28+ // New group:
29+ // - first rule is the one we're going to combine into
30+ // - create an empty slice for other rules to be added to
31+ rulesToCombine.set(key, {
32+ first: rule,
33+ rules: []
34+ })
35+ })
36+
37+ // Iterate over all groups
38+ for (const { first, rules } of rulesToCombine.values()) {
39+ // If there was only one rule for a given group, there's nothing to combine
40+ if (rules.length === 0) {
41+ continue
42+ }
43+
44+ // Append all contents of all subsequent rules to the first rule
45+ for (const rule of rules) {
46+ rule.each((child) => {
47+ child.remove()
48+ first.append(child)
49+ })
50+
51+ // Remove the now-empty rule
52+ rule.remove()
53+ }
54+ }
55+ },
56+ }
57+ }
58+
59+ /**
60+ * Construct a key that is specific to the AST ancestry of the rule.
61+ * Only rules with the same key can be combined.
62+ *
63+ * @param {import('postcss').Rule} rule
64+ * @returns {string}
65+ */
66+ function ruleKey(rule) {
67+ let key = `[rule ${rule.selector}]`
68+
69+ let ancestor = rule.parent
70+ while (ancestor) {
71+ if (ancestor.type === 'atrule') {
72+ key = `[${ancestor.name} ${ancestor.params}]${key}`
73+ } else if (ancestor.type === 'rule') {
74+ key = `[rule ${ancestor.selector}]${key}`
75+ } else if (ancestor.type === 'root') {
76+ break
77+ }
78+
79+ ancestor = ancestor.parent
80+ }
81+
82+ return key
83+ }
84+
85+ function isKeyframesRule(rule) {
86+ if (rule.parent?.type === 'atrule' && rule.parent.name === 'keyframes') {
87+ return true
88+ }
89+
90+ return false
91+ }
92+
93+ module.exports = creator
94+ module.exports.postcss = true
95 diff --git a/package-lock.json b/package-lock.json
96index c985b57..4e0659a 100644
97--- a/package-lock.json
98+++ b/package-lock.json
99 @@ -1,12 +1,12 @@
100 {
101 "name": "open-props",
102- "version": "1.5.10",
103+ "version": "1.5.16",
104 "lockfileVersion": 2,
105 "requires": true,
106 "packages": {
107 "": {
108 "name": "open-props",
109- "version": "1.5.10",
110+ "version": "1.5.16",
111 "license": "MIT",
112 "devDependencies": {
113 "ava": "^3.15.0",
114 @@ -17,7 +17,6 @@
115 "open-color": "^1.9.1",
116 "postcss": "^8.3.9",
117 "postcss-cli": "^8.3.1",
118- "postcss-combine-duplicated-selectors": "^10.0.3",
119 "postcss-import": "^14.0.2",
120 "postcss-preset-env": "6.7.x",
121 "typescript": "^4.9.4"
122 @@ -3894,46 +3893,6 @@
123 "postcss": "^8.2.15"
124 }
125 },
126- "node_modules/postcss-combine-duplicated-selectors": {
127- "version": "10.0.3",
128- "resolved": "https://registry.npmjs.org/postcss-combine-duplicated-selectors/-/postcss-combine-duplicated-selectors-10.0.3.tgz",
129- "integrity": "sha512-IP0BmwFloCskv7DV7xqvzDXqMHpwdczJa6ZvIW8abgHdcIHs9mCJX2ltFhu3EwA51ozp13DByng30+Ke+eIExA==",
130- "dev": true,
131- "dependencies": {
132- "postcss-selector-parser": "^6.0.4"
133- },
134- "engines": {
135- "node": "^10.0.0 || ^12.0.0 || >=14.0.0"
136- },
137- "peerDependencies": {
138- "postcss": "^8.1.0"
139- }
140- },
141- "node_modules/postcss-combine-duplicated-selectors/node_modules/cssesc": {
142- "version": "3.0.0",
143- "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
144- "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
145- "dev": true,
146- "bin": {
147- "cssesc": "bin/cssesc"
148- },
149- "engines": {
150- "node": ">=4"
151- }
152- },
153- "node_modules/postcss-combine-duplicated-selectors/node_modules/postcss-selector-parser": {
154- "version": "6.0.6",
155- "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz",
156- "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==",
157- "dev": true,
158- "dependencies": {
159- "cssesc": "^3.0.0",
160- "util-deprecate": "^1.0.2"
161- },
162- "engines": {
163- "node": ">=4"
164- }
165- },
166 "node_modules/postcss-convert-values": {
167 "version": "5.1.2",
168 "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.2.tgz",
169 @@ -9197,33 +9156,6 @@
170 "postcss-value-parser": "^4.2.0"
171 }
172 },
173- "postcss-combine-duplicated-selectors": {
174- "version": "10.0.3",
175- "resolved": "https://registry.npmjs.org/postcss-combine-duplicated-selectors/-/postcss-combine-duplicated-selectors-10.0.3.tgz",
176- "integrity": "sha512-IP0BmwFloCskv7DV7xqvzDXqMHpwdczJa6ZvIW8abgHdcIHs9mCJX2ltFhu3EwA51ozp13DByng30+Ke+eIExA==",
177- "dev": true,
178- "requires": {
179- "postcss-selector-parser": "^6.0.4"
180- },
181- "dependencies": {
182- "cssesc": {
183- "version": "3.0.0",
184- "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
185- "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
186- "dev": true
187- },
188- "postcss-selector-parser": {
189- "version": "6.0.6",
190- "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz",
191- "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==",
192- "dev": true,
193- "requires": {
194- "cssesc": "^3.0.0",
195- "util-deprecate": "^1.0.2"
196- }
197- }
198- }
199- },
200 "postcss-convert-values": {
201 "version": "5.1.2",
202 "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.2.tgz",
203 diff --git a/package.json b/package.json
204index e969dd0..8e08417 100644
205--- a/package.json
206+++ b/package.json
207 @@ -317,7 +317,6 @@
208 "open-color": "^1.9.1",
209 "postcss": "^8.3.9",
210 "postcss-cli": "^8.3.1",
211- "postcss-combine-duplicated-selectors": "^10.0.3",
212 "postcss-import": "^14.0.2",
213 "postcss-preset-env": "6.7.x",
214 "typescript": "^4.9.4"
215 diff --git a/postcss.config.cjs b/postcss.config.cjs
216index 7ac657c..5a74ce6 100644
217--- a/postcss.config.cjs
218+++ b/postcss.config.cjs
219 @@ -1,7 +1,7 @@
220 const postcssPresetEnv = require('postcss-preset-env')
221 const postcssImport = require('postcss-import')
222 const cssnano = require('cssnano')
223- const combineSelectors = require('postcss-combine-duplicated-selectors')
224+ const combineSelectors = require('./build/postcss-combine-selectors.cjs')
225
226 const lib = process.env.npm_lifecycle_event
227