postcss-sort-media-queries 5.2.0 → 6.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -52
- package/build/index.cjs +312 -0
- package/build/wrapper.cjs +9 -0
- package/package.json +30 -57
- package/src/index.js +122 -0
- package/browser.js +0 -54
- package/index.js +0 -75
package/README.md
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# PostCSS Sort Media Queries
|
|
2
2
|
|
|
3
3
|
[PostCSS]: https://github.com/postcss/postcss
|
|
4
|
-
[MIT]: https://github.com/yunusga/postcss-sort-media-queries/blob/master/LICENSE
|
|
5
4
|
[official docs]: https://github.com/postcss/postcss#usage
|
|
5
|
+
[Online Demo]: https://yunusga.uz/postcss-sort-media-queries/
|
|
6
|
+
[MIT]: https://github.com/yunusga/postcss-sort-media-queries/blob/master/LICENSE
|
|
6
7
|
[Releases history]: https://github.com/yunusga/postcss-sort-media-queries/blob/master/CHANGELOG.md
|
|
7
8
|
|
|
8
9
|
[](https://www.npmjs.com/package/postcss-sort-media-queries) [](https://github.com/yunusga/postcss-sort-media-queries/actions/workflows/test.yml)
|
|
@@ -15,7 +16,7 @@
|
|
|
15
16
|
|
|
16
17
|
[PostCSS] plugin for sorting and combining CSS media queries with **mobile first** / **desktop first** methodologies.
|
|
17
18
|
|
|
18
|
-
> From
|
|
19
|
+
> From v6.0.0 plugin supported nested media queries and ESM usage
|
|
19
20
|
|
|
20
21
|
## Table of Contents
|
|
21
22
|
|
|
@@ -37,58 +38,55 @@
|
|
|
37
38
|
|
|
38
39
|
## Online demo
|
|
39
40
|
|
|
40
|
-
And here is the [
|
|
41
|
+
And here is the [Online Demo]
|
|
41
42
|
|
|
42
43
|
## Examples
|
|
43
44
|
|
|
44
45
|
### Mobile first sorting
|
|
45
46
|
|
|
46
|
-
**
|
|
47
|
+
**Before**
|
|
47
48
|
|
|
48
49
|
```css
|
|
49
|
-
@media
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
@
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
@media
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
@media
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
@media screen and (max-width: 640px) {
|
|
65
|
-
.footer { color: #cfcfcf }
|
|
50
|
+
@media (min-width: 1400px) {}
|
|
51
|
+
@media (min-width: 1200px) {}
|
|
52
|
+
|
|
53
|
+
@layer reset {
|
|
54
|
+
|
|
55
|
+
@media (min-width: 1200px) {
|
|
56
|
+
@media (min-width: 992px) {}
|
|
57
|
+
@media (min-width: 768px) {}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
@media (min-width: 768px) {
|
|
61
|
+
@media (min-width: 640px) {}
|
|
62
|
+
@media (min-width: 320px) {}
|
|
63
|
+
}
|
|
66
64
|
}
|
|
67
65
|
```
|
|
68
66
|
|
|
69
|
-
**
|
|
67
|
+
**After**
|
|
70
68
|
|
|
71
69
|
```css
|
|
72
|
-
@
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
@media
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
@media
|
|
82
|
-
|
|
83
|
-
.head { color: #cfcfcf }
|
|
84
|
-
.main { color: #cfcfcf }
|
|
85
|
-
.footer { color: #cfcfcf }
|
|
70
|
+
@layer reset {
|
|
71
|
+
|
|
72
|
+
@media (min-width: 768px) {
|
|
73
|
+
@media (min-width: 320px) {}
|
|
74
|
+
@media (min-width: 640px) {}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@media (min-width: 1200px) {
|
|
78
|
+
@media (min-width: 768px) {}
|
|
79
|
+
@media (min-width: 992px) {}
|
|
80
|
+
}
|
|
86
81
|
}
|
|
82
|
+
|
|
83
|
+
@media (min-width: 1200px) {}
|
|
84
|
+
@media (min-width: 1400px) {}
|
|
87
85
|
```
|
|
88
86
|
|
|
89
87
|
### Desktop first sorting
|
|
90
88
|
|
|
91
|
-
**
|
|
89
|
+
**Before**
|
|
92
90
|
```css
|
|
93
91
|
@media screen and (width < 640px) {
|
|
94
92
|
.header { color: #cdcdcd }
|
|
@@ -110,7 +108,7 @@ And here is the [online demo](https://postcss-sort-media-queries.github.io)
|
|
|
110
108
|
}
|
|
111
109
|
```
|
|
112
110
|
|
|
113
|
-
**
|
|
111
|
+
**After**
|
|
114
112
|
|
|
115
113
|
```css
|
|
116
114
|
@media screen and (max-width: 760px) {
|
|
@@ -144,6 +142,15 @@ Check you project for existed PostCSS config: `postcss.config.js`
|
|
|
144
142
|
in the project root, `"postcss"` section in `package.json`
|
|
145
143
|
or `postcss` in bundle config.
|
|
146
144
|
|
|
145
|
+
|
|
146
|
+
```js
|
|
147
|
+
// CJS
|
|
148
|
+
let sortCssMq = require('postcss-sort-media-queries');
|
|
149
|
+
|
|
150
|
+
// ESM
|
|
151
|
+
import sortCssMq from 'postcss-sort-media-queries';
|
|
152
|
+
```
|
|
153
|
+
|
|
147
154
|
If you already use PostCSS, add the plugin to plugins list:
|
|
148
155
|
|
|
149
156
|
```diff
|
|
@@ -176,7 +183,7 @@ and set this plugin in settings.
|
|
|
176
183
|
|
|
177
184
|
## Options
|
|
178
185
|
|
|
179
|
-
> Sorting works based on [
|
|
186
|
+
> Sorting works based on [OlehDutchenko/sort-css-media-queries](https://github.com/OlehDutchenko/sort-css-media-queries) function.
|
|
180
187
|
|
|
181
188
|
### sort
|
|
182
189
|
|
|
@@ -237,18 +244,6 @@ postcss([
|
|
|
237
244
|
|
|
238
245
|
Or alternatively create a `sort-css-mq.config.json` file in the root of your project. Or add property `sortCssMQ: {}` in your `package.json`.
|
|
239
246
|
|
|
240
|
-
### Only Top Level
|
|
241
|
-
|
|
242
|
-
Sort only top level media queries to prevent eject nested media queries from parent node
|
|
243
|
-
|
|
244
|
-
```js
|
|
245
|
-
postcss([
|
|
246
|
-
sortMediaQueries({
|
|
247
|
-
onlyTopLevel: true,
|
|
248
|
-
})
|
|
249
|
-
]).process(css);
|
|
250
|
-
```
|
|
251
|
-
|
|
252
247
|
---
|
|
253
248
|
|
|
254
249
|
## Changelog
|
|
@@ -266,7 +261,7 @@ See [Releases history]
|
|
|
266
261
|
## Thanks
|
|
267
262
|
|
|
268
263
|
- Andrey Sitnik [@ai](https://github.com/ai)
|
|
269
|
-
- Oleh Dutchenko [@dutchenkoOleg](https://github.com/
|
|
264
|
+
- Oleh Dutchenko [@dutchenkoOleg](https://github.com/OlehDutchenko)
|
|
270
265
|
- Jakub Caban [@Lustmored](https://github.com/Lustmored)
|
|
271
266
|
- Dmytro Symonov [@Kassaila](https://github.com/Kassaila)
|
|
272
267
|
- Kai Falkowski [@SassNinja](https://github.com/SassNinja)
|
package/build/index.cjs
ADDED
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
|
|
19
|
+
// src/index.js
|
|
20
|
+
var index_exports = {};
|
|
21
|
+
__export(index_exports, {
|
|
22
|
+
default: () => index_default
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(index_exports);
|
|
25
|
+
|
|
26
|
+
// node_modules/sort-css-media-queries/lib/create-sort.js
|
|
27
|
+
var minMaxWidth = /(!?\(\s*min(-device)?-width)(.|\n)+\(\s*max(-device)?-width|\(\s*width\s*>(=)?(.|\n)+\(\s*width\s*<(=)?|(!?\(.*<(=)?\s*width\s*<(=)?)/i;
|
|
28
|
+
var minWidth = /\(\s*min(-device)?-width|\(\s*width\s*>(=)?/i;
|
|
29
|
+
var maxMinWidth = /(!?\(\s*max(-device)?-width)(.|\n)+\(\s*min(-device)?-width|\(\s*width\s*<(=)?(.|\n)+\(\s*width\s*>(=)?|(!?\(.*>(=)?\s*width\s*>(=)?)/i;
|
|
30
|
+
var maxWidth = /\(\s*max(-device)?-width|\(\s*width\s*<(=)?/i;
|
|
31
|
+
var isMinWidth = _testQuery(minMaxWidth, maxMinWidth, minWidth);
|
|
32
|
+
var isMaxWidth = _testQuery(maxMinWidth, minMaxWidth, maxWidth);
|
|
33
|
+
var minMaxHeight = /(!?\(\s*min(-device)?-height)(.|\n)+\(\s*max(-device)?-height|\(\s*height\s*>(=)?(.|\n)+\(\s*height\s*<(=)?|(!?\(.*<(=)?\s*height\s*<(=)?)/i;
|
|
34
|
+
var minHeight = /\(\s*min(-device)?-height|\(\s*height\s*>(=)?/i;
|
|
35
|
+
var maxMinHeight = /(!?\(\s*max(-device)?-height)(.|\n)+\(\s*min(-device)?-height|\(\s*height\s*<(=)?(.|\n)+\(\s*height\s*>(=)?|(!?\(.*>(=)?\s*height\s*>(=)?)/i;
|
|
36
|
+
var maxHeight = /\(\s*max(-device)?-height|\(\s*height\s*<(=)?/i;
|
|
37
|
+
var isMinHeight = _testQuery(minMaxHeight, maxMinHeight, minHeight);
|
|
38
|
+
var isMaxHeight = _testQuery(maxMinHeight, minMaxHeight, maxHeight);
|
|
39
|
+
var lessThan = /<(?!=)/;
|
|
40
|
+
var grtrThan = />(?!=)/;
|
|
41
|
+
var isPrint = /print/i;
|
|
42
|
+
var isPrintOnly = /^print$/i;
|
|
43
|
+
var maxValue = Number.MAX_VALUE;
|
|
44
|
+
function _getQueryLength(query) {
|
|
45
|
+
let length = /(-?\d*\.?\d+)(ch|em|ex|px|rem)/.exec(query);
|
|
46
|
+
if (length === null && (isMinWidth(query) || isMinHeight(query))) {
|
|
47
|
+
length = /(\d)/.exec(query);
|
|
48
|
+
}
|
|
49
|
+
if (length === "0") {
|
|
50
|
+
return 0;
|
|
51
|
+
}
|
|
52
|
+
if (length === null) {
|
|
53
|
+
return maxValue;
|
|
54
|
+
}
|
|
55
|
+
let number = length[1];
|
|
56
|
+
const unit = length[2];
|
|
57
|
+
switch (unit) {
|
|
58
|
+
case "ch":
|
|
59
|
+
number = parseFloat(number) * 8.8984375;
|
|
60
|
+
break;
|
|
61
|
+
case "em":
|
|
62
|
+
case "rem":
|
|
63
|
+
number = parseFloat(number) * 16;
|
|
64
|
+
break;
|
|
65
|
+
case "ex":
|
|
66
|
+
number = parseFloat(number) * 8.296875;
|
|
67
|
+
break;
|
|
68
|
+
case "px":
|
|
69
|
+
number = parseFloat(number);
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
return +number;
|
|
73
|
+
}
|
|
74
|
+
function _testQuery(doubleTestTrue, doubleTestFalse, singleTest) {
|
|
75
|
+
return function(query) {
|
|
76
|
+
let result;
|
|
77
|
+
if (doubleTestTrue.test(query)) result = true;
|
|
78
|
+
else if (doubleTestFalse.test(query)) result = false;
|
|
79
|
+
else result = singleTest.test(query);
|
|
80
|
+
return query.includes("not") ? !result : result;
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function _testIsPrint(a, b) {
|
|
84
|
+
const isPrintA = isPrint.test(a);
|
|
85
|
+
const isPrintOnlyA = isPrintOnly.test(a);
|
|
86
|
+
const isPrintB = isPrint.test(b);
|
|
87
|
+
const isPrintOnlyB = isPrintOnly.test(b);
|
|
88
|
+
if (isPrintA && isPrintB) {
|
|
89
|
+
if (!isPrintOnlyA && isPrintOnlyB) {
|
|
90
|
+
return 1;
|
|
91
|
+
}
|
|
92
|
+
if (isPrintOnlyA && !isPrintOnlyB) {
|
|
93
|
+
return -1;
|
|
94
|
+
}
|
|
95
|
+
return a.localeCompare(b);
|
|
96
|
+
}
|
|
97
|
+
if (isPrintA) {
|
|
98
|
+
return 1;
|
|
99
|
+
}
|
|
100
|
+
if (isPrintB) {
|
|
101
|
+
return -1;
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
function createSort(configuration) {
|
|
106
|
+
const config = configuration || {};
|
|
107
|
+
const UNITLESS_MQ_ALWAYS_FIRST = config.unitlessMqAlwaysFirst;
|
|
108
|
+
function sortCSSmq(a, b) {
|
|
109
|
+
const testIsPrint = _testIsPrint(a, b);
|
|
110
|
+
if (testIsPrint !== null) {
|
|
111
|
+
return testIsPrint;
|
|
112
|
+
}
|
|
113
|
+
const minA = isMinWidth(a) || isMinHeight(a);
|
|
114
|
+
const maxA = isMaxWidth(a) || isMaxHeight(a);
|
|
115
|
+
const minB = isMinWidth(b) || isMinHeight(b);
|
|
116
|
+
const maxB = isMaxWidth(b) || isMaxHeight(b);
|
|
117
|
+
if (UNITLESS_MQ_ALWAYS_FIRST && (!minA && !maxA || !minB && !maxB)) {
|
|
118
|
+
if (!minA && !maxA && !minB && !maxB) {
|
|
119
|
+
return a.localeCompare(b);
|
|
120
|
+
}
|
|
121
|
+
return !minB && !maxB ? 1 : -1;
|
|
122
|
+
} else {
|
|
123
|
+
if (minA && maxB) {
|
|
124
|
+
return -1;
|
|
125
|
+
}
|
|
126
|
+
if (maxA && minB) {
|
|
127
|
+
return 1;
|
|
128
|
+
}
|
|
129
|
+
const lengthA = _getQueryLength(a);
|
|
130
|
+
const lengthB = _getQueryLength(b);
|
|
131
|
+
if (lengthA === maxValue && lengthB === maxValue) {
|
|
132
|
+
return a.localeCompare(b);
|
|
133
|
+
} else if (lengthA === maxValue) {
|
|
134
|
+
return 1;
|
|
135
|
+
} else if (lengthB === maxValue) {
|
|
136
|
+
return -1;
|
|
137
|
+
}
|
|
138
|
+
if (lengthA > lengthB) {
|
|
139
|
+
if (maxA) {
|
|
140
|
+
return -1;
|
|
141
|
+
}
|
|
142
|
+
return 1;
|
|
143
|
+
}
|
|
144
|
+
if (lengthA < lengthB) {
|
|
145
|
+
if (maxA) {
|
|
146
|
+
return 1;
|
|
147
|
+
}
|
|
148
|
+
return -1;
|
|
149
|
+
}
|
|
150
|
+
if (lengthA === lengthB) {
|
|
151
|
+
if (maxA && maxB) {
|
|
152
|
+
if (lessThan.test(a) && !lessThan.test(b)) {
|
|
153
|
+
return 1;
|
|
154
|
+
}
|
|
155
|
+
if (!lessThan.test(a) && lessThan.test(b)) {
|
|
156
|
+
return -1;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (minA && minB) {
|
|
160
|
+
if (grtrThan.test(a) && !grtrThan.test(b)) {
|
|
161
|
+
return 1;
|
|
162
|
+
}
|
|
163
|
+
if (!grtrThan.test(a) && grtrThan.test(b)) {
|
|
164
|
+
return -1;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return a.localeCompare(b);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
sortCSSmq.desktopFirst = function(a, b) {
|
|
172
|
+
const testIsPrint = _testIsPrint(a, b);
|
|
173
|
+
if (testIsPrint !== null) {
|
|
174
|
+
return testIsPrint;
|
|
175
|
+
}
|
|
176
|
+
const minA = isMinWidth(a) || isMinHeight(a);
|
|
177
|
+
const maxA = isMaxWidth(a) || isMaxHeight(a);
|
|
178
|
+
const minB = isMinWidth(b) || isMinHeight(b);
|
|
179
|
+
const maxB = isMaxWidth(b) || isMaxHeight(b);
|
|
180
|
+
if (UNITLESS_MQ_ALWAYS_FIRST && (!minA && !maxA || !minB && !maxB)) {
|
|
181
|
+
if (!minA && !maxA && !minB && !maxB) {
|
|
182
|
+
return a.localeCompare(b);
|
|
183
|
+
}
|
|
184
|
+
return !minB && !maxB ? 1 : -1;
|
|
185
|
+
} else {
|
|
186
|
+
if (minA && maxB) {
|
|
187
|
+
return 1;
|
|
188
|
+
}
|
|
189
|
+
if (maxA && minB) {
|
|
190
|
+
return -1;
|
|
191
|
+
}
|
|
192
|
+
const lengthA = _getQueryLength(a);
|
|
193
|
+
const lengthB = _getQueryLength(b);
|
|
194
|
+
if (lengthA === maxValue && lengthB === maxValue) {
|
|
195
|
+
return a.localeCompare(b);
|
|
196
|
+
} else if (lengthA === maxValue) {
|
|
197
|
+
return 1;
|
|
198
|
+
} else if (lengthB === maxValue) {
|
|
199
|
+
return -1;
|
|
200
|
+
}
|
|
201
|
+
if (lengthA > lengthB) {
|
|
202
|
+
if (maxA) {
|
|
203
|
+
return -1;
|
|
204
|
+
}
|
|
205
|
+
return 1;
|
|
206
|
+
}
|
|
207
|
+
if (lengthA < lengthB) {
|
|
208
|
+
if (maxA) {
|
|
209
|
+
return 1;
|
|
210
|
+
}
|
|
211
|
+
return -1;
|
|
212
|
+
}
|
|
213
|
+
if (lengthA === lengthB) {
|
|
214
|
+
if (maxA && maxB) {
|
|
215
|
+
if (lessThan.test(a) && !lessThan.test(b)) {
|
|
216
|
+
return 1;
|
|
217
|
+
}
|
|
218
|
+
if (!lessThan.test(a) && lessThan.test(b)) {
|
|
219
|
+
return -1;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if (minA && minB) {
|
|
223
|
+
if (grtrThan.test(a) && !grtrThan.test(b)) {
|
|
224
|
+
return 1;
|
|
225
|
+
}
|
|
226
|
+
if (!grtrThan.test(a) && grtrThan.test(b)) {
|
|
227
|
+
return -1;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return -a.localeCompare(b);
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
return sortCSSmq;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// src/index.js
|
|
238
|
+
function sortAtRules(queries, options, sortCSSmq) {
|
|
239
|
+
if (typeof options.sort !== "function") {
|
|
240
|
+
options.sort = options.sort === "desktop-first" ? sortCSSmq.desktopFirst : sortCSSmq;
|
|
241
|
+
}
|
|
242
|
+
return queries.sort(options.sort);
|
|
243
|
+
}
|
|
244
|
+
function plugin(options = {}) {
|
|
245
|
+
options = Object.assign(
|
|
246
|
+
{
|
|
247
|
+
sort: "mobile-first",
|
|
248
|
+
configuration: false
|
|
249
|
+
},
|
|
250
|
+
options
|
|
251
|
+
);
|
|
252
|
+
const sortCSSmq = createSort(options.configuration);
|
|
253
|
+
return {
|
|
254
|
+
postcssPlugin: "postcss-sort-media-queries",
|
|
255
|
+
// Execute once after the entire tree has been parsed
|
|
256
|
+
OnceExit(root, { AtRule }) {
|
|
257
|
+
let parents = {
|
|
258
|
+
root: [],
|
|
259
|
+
nested: []
|
|
260
|
+
};
|
|
261
|
+
let processed = /* @__PURE__ */ Symbol("processed");
|
|
262
|
+
root.walkAtRules("media", (atRule) => {
|
|
263
|
+
if (atRule.parent[processed]) {
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
if (atRule.parent.type === "root") {
|
|
267
|
+
parents.root.push(atRule.parent);
|
|
268
|
+
}
|
|
269
|
+
if (atRule.parent.type !== "root") {
|
|
270
|
+
parents.nested.push(atRule.parent);
|
|
271
|
+
}
|
|
272
|
+
atRule.parent[processed] = true;
|
|
273
|
+
return;
|
|
274
|
+
});
|
|
275
|
+
Object.keys(parents).forEach((type) => {
|
|
276
|
+
if (!parents[type].length) {
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
parents[type].forEach((parent) => {
|
|
280
|
+
let media = parent.nodes.filter(
|
|
281
|
+
(n) => n.type === "atrule" && n.name === "media"
|
|
282
|
+
);
|
|
283
|
+
if (!media) {
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
let atRules = [];
|
|
287
|
+
media.forEach((atRule) => {
|
|
288
|
+
let query = atRule.params;
|
|
289
|
+
if (!atRules[query]) {
|
|
290
|
+
atRules[query] = new AtRule({
|
|
291
|
+
name: atRule.name,
|
|
292
|
+
params: atRule.params,
|
|
293
|
+
source: atRule.source
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
atRule.nodes.forEach((node) => {
|
|
297
|
+
atRules[query].append(node);
|
|
298
|
+
});
|
|
299
|
+
atRule.remove();
|
|
300
|
+
});
|
|
301
|
+
if (atRules) {
|
|
302
|
+
sortAtRules(Object.keys(atRules), options, sortCSSmq).forEach((query) => {
|
|
303
|
+
parent.append(atRules[query]);
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
plugin.postcss = true;
|
|
312
|
+
var index_default = plugin;
|
package/package.json
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "postcss-sort-media-queries",
|
|
3
|
-
"version": "5.2.0",
|
|
4
3
|
"description": "PostCSS plugin for sorting and combining CSS media queries with mobile first / **desktop first methodologies",
|
|
4
|
+
"version": "6.1.0",
|
|
5
|
+
"engines": {
|
|
6
|
+
"node": ">=22.0.0"
|
|
7
|
+
},
|
|
5
8
|
"keywords": [
|
|
6
9
|
"postcss",
|
|
7
10
|
"postcss-plugin",
|
|
@@ -29,67 +32,37 @@
|
|
|
29
32
|
"url": "https://github.com/yunusga/postcss-sort-media-queries/issues"
|
|
30
33
|
},
|
|
31
34
|
"homepage": "https://github.com/yunusga/postcss-sort-media-queries",
|
|
32
|
-
"
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
"type": "module",
|
|
36
|
+
"main": "src/index.js",
|
|
37
|
+
"module": "src/index.js",
|
|
38
|
+
"exports": {
|
|
39
|
+
".": {
|
|
40
|
+
"require": "./build/wrapper.cjs",
|
|
41
|
+
"import": "./src/index.js",
|
|
42
|
+
"default": "./src/index.js"
|
|
43
|
+
}
|
|
38
44
|
},
|
|
39
|
-
"
|
|
40
|
-
"
|
|
45
|
+
"files": [
|
|
46
|
+
"build"
|
|
47
|
+
],
|
|
48
|
+
"scripts": {
|
|
49
|
+
"test": "vitest run",
|
|
50
|
+
"test:watch": "vitest --watch",
|
|
51
|
+
"coverage": "vitest --coverage",
|
|
52
|
+
"build": "npm run build:cjs",
|
|
53
|
+
"build:cjs": "esbuild src/index.js --outfile=build/index.cjs --format=cjs --bundle --platform=node --external:postcss"
|
|
41
54
|
},
|
|
42
55
|
"devDependencies": {
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"jest": "^27.3.1",
|
|
49
|
-
"jest-ci": "^0.1.1",
|
|
50
|
-
"jest-cli": "^27.3.1",
|
|
51
|
-
"lint-staged": "^13.2.1",
|
|
52
|
-
"postcss": "^8.4.23",
|
|
53
|
-
"postcss-flexbugs-fixes": "^5.0.2",
|
|
54
|
-
"postcss-media-minmax": "^5.0.0",
|
|
55
|
-
"postcss-nested": "^5.0.6"
|
|
56
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
57
|
+
"esbuild": "^0.27.3",
|
|
58
|
+
"postcss": "^8.5.6",
|
|
59
|
+
"prettier": "3.8.1",
|
|
60
|
+
"vitest": "^4.0.18"
|
|
56
61
|
},
|
|
57
62
|
"peerDependencies": {
|
|
58
|
-
"postcss": "^8.
|
|
63
|
+
"postcss": "^8.5.6"
|
|
59
64
|
},
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"pre-commit": "lint-staged"
|
|
63
|
-
}
|
|
64
|
-
},
|
|
65
|
-
"lint-staged": {
|
|
66
|
-
"*.js": "eslint --fix"
|
|
67
|
-
},
|
|
68
|
-
"eslintConfig": {
|
|
69
|
-
"parserOptions": {
|
|
70
|
-
"ecmaVersion": 2017
|
|
71
|
-
},
|
|
72
|
-
"env": {
|
|
73
|
-
"node": true,
|
|
74
|
-
"es6": true
|
|
75
|
-
},
|
|
76
|
-
"extends": [
|
|
77
|
-
"eslint:recommended",
|
|
78
|
-
"plugin:jest/recommended"
|
|
79
|
-
],
|
|
80
|
-
"rules": {
|
|
81
|
-
"jest/expect-expect": "off"
|
|
82
|
-
}
|
|
83
|
-
},
|
|
84
|
-
"jest": {
|
|
85
|
-
"testEnvironment": "node",
|
|
86
|
-
"coverageThreshold": {
|
|
87
|
-
"global": {
|
|
88
|
-
"statements": 100
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
},
|
|
92
|
-
"sortCssMQ": {
|
|
93
|
-
"unitlessMqAlwaysFirst": false
|
|
65
|
+
"dependencies": {
|
|
66
|
+
"sort-css-media-queries": "^3.0.1"
|
|
94
67
|
}
|
|
95
68
|
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import createSort from 'sort-css-media-queries/create-sort';
|
|
2
|
+
|
|
3
|
+
// PostCSS plugin to sort CSS @media rules according to a configurable order.
|
|
4
|
+
// The plugin groups top-level and nested media at-rules, merges rules
|
|
5
|
+
// with identical queries, and re-inserts them in the desired order.
|
|
6
|
+
|
|
7
|
+
// Helper that ensures `options.sort` is a function and sorts queries.
|
|
8
|
+
function sortAtRules(queries, options, sortCSSmq) {
|
|
9
|
+
if (typeof options.sort !== 'function') {
|
|
10
|
+
options.sort = options.sort === 'desktop-first' ? sortCSSmq.desktopFirst : sortCSSmq;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return queries.sort(options.sort);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function plugin(options = {}) {
|
|
17
|
+
|
|
18
|
+
// Set default options and allow user overrides
|
|
19
|
+
options = Object.assign(
|
|
20
|
+
{
|
|
21
|
+
sort: 'mobile-first',
|
|
22
|
+
configuration: false,
|
|
23
|
+
},
|
|
24
|
+
options
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
// Create a sorter based on configuration (from sort-css-media-queries)
|
|
28
|
+
const sortCSSmq = createSort(options.configuration);
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
postcssPlugin: 'postcss-sort-media-queries',
|
|
32
|
+
|
|
33
|
+
// Execute once after the entire tree has been parsed
|
|
34
|
+
OnceExit(root, { AtRule }) {
|
|
35
|
+
// Collect parent nodes that contain media at-rules. We separate
|
|
36
|
+
// top-level (`root`) parents from nested parents so ordering
|
|
37
|
+
// semantics can be preserved independently.
|
|
38
|
+
let parents = {
|
|
39
|
+
root: [],
|
|
40
|
+
nested: [],
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// Symbol used to mark parents that we've already collected
|
|
44
|
+
let processed = Symbol('processed');
|
|
45
|
+
|
|
46
|
+
// Walk all @media at-rules and group their parents
|
|
47
|
+
root.walkAtRules('media', (atRule) => {
|
|
48
|
+
if (atRule.parent[processed]) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// If the parent is the root of the document, add to root list
|
|
53
|
+
if (atRule.parent.type === 'root') {
|
|
54
|
+
parents.root.push(atRule.parent);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Otherwise treat it as a nested parent
|
|
58
|
+
if (atRule.parent.type !== 'root') {
|
|
59
|
+
parents.nested.push(atRule.parent);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Mark this parent so we don't collect it twice
|
|
63
|
+
atRule.parent[processed] = true;
|
|
64
|
+
|
|
65
|
+
return;
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// For each parent group, merge and sort its media at-rules
|
|
69
|
+
Object.keys(parents).forEach((type) => {
|
|
70
|
+
if (!parents[type].length) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
parents[type].forEach((parent) => {
|
|
75
|
+
// Filter only @media nodes from the parent's children
|
|
76
|
+
let media = parent.nodes.filter(
|
|
77
|
+
n => n.type === 'atrule' && n.name === 'media'
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
if (!media) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Combine at-rules with identical query params into a single
|
|
85
|
+
// AtRule instance, cloning their children to preserve content.
|
|
86
|
+
let atRules = [];
|
|
87
|
+
|
|
88
|
+
media.forEach((atRule) => {
|
|
89
|
+
let query = atRule.params;
|
|
90
|
+
|
|
91
|
+
if (!atRules[query]) {
|
|
92
|
+
atRules[query] = new AtRule({
|
|
93
|
+
name: atRule.name,
|
|
94
|
+
params: atRule.params,
|
|
95
|
+
source: atRule.source
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
atRule.nodes.forEach((node) => {
|
|
100
|
+
atRules[query].append(node);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// Remove the original at-rule since its contents have been
|
|
104
|
+
// merged into `atRules[query]`.
|
|
105
|
+
atRule.remove();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// Sort query keys and append merged at-rules back to the parent
|
|
109
|
+
if (atRules) {
|
|
110
|
+
sortAtRules(Object.keys(atRules), options, sortCSSmq).forEach((query) => {
|
|
111
|
+
parent.append(atRules[query]);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
plugin.postcss = true;
|
|
121
|
+
|
|
122
|
+
export default plugin;
|
package/browser.js
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
function sortAtRules(queries, sort, sortCSSmq) {
|
|
2
|
-
if (typeof sort !== 'function') {
|
|
3
|
-
sort = sort === 'desktop-first' ? sortCSSmq.desktopFirst : sortCSSmq
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
return queries.sort(sort)
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
module.exports = (opts = {}) => {
|
|
10
|
-
|
|
11
|
-
opts = Object.assign(
|
|
12
|
-
{
|
|
13
|
-
sort: 'mobile-first',
|
|
14
|
-
configuration: {
|
|
15
|
-
unitlessMqAlwaysFirst: false,
|
|
16
|
-
},
|
|
17
|
-
},
|
|
18
|
-
opts
|
|
19
|
-
)
|
|
20
|
-
|
|
21
|
-
const createSort = require('sort-css-media-queries/lib/create-sort');
|
|
22
|
-
const sortCSSmq = createSort(opts.configuration);
|
|
23
|
-
|
|
24
|
-
return {
|
|
25
|
-
postcssPlugin: 'postcss-sort-media-queries',
|
|
26
|
-
OnceExit(root, { AtRule }) {
|
|
27
|
-
|
|
28
|
-
let atRules = [];
|
|
29
|
-
|
|
30
|
-
root.walkAtRules('media', atRule => {
|
|
31
|
-
let query = atRule.params
|
|
32
|
-
|
|
33
|
-
if (!atRules[query]) {
|
|
34
|
-
atRules[query] = new AtRule({
|
|
35
|
-
name: atRule.name,
|
|
36
|
-
params: atRule.params,
|
|
37
|
-
source: atRule.source
|
|
38
|
-
})
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
atRule.nodes.forEach(node => {
|
|
42
|
-
atRules[query].append(node.clone())
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
atRule.remove()
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
sortAtRules(Object.keys(atRules), opts.sort, sortCSSmq).forEach(query => {
|
|
49
|
-
root.append(atRules[query])
|
|
50
|
-
})
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
module.exports.postcss = true
|
package/index.js
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
function sortAtRules(queries, sort, sortCSSmq) {
|
|
2
|
-
if (typeof sort !== 'function') {
|
|
3
|
-
sort = sort === 'desktop-first' ? sortCSSmq.desktopFirst : sortCSSmq
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
return queries.sort(sort)
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
module.exports = (opts = {}) => {
|
|
10
|
-
|
|
11
|
-
opts = Object.assign(
|
|
12
|
-
{
|
|
13
|
-
sort: 'mobile-first',
|
|
14
|
-
configuration: false,
|
|
15
|
-
onlyTopLevel: false,
|
|
16
|
-
},
|
|
17
|
-
opts
|
|
18
|
-
)
|
|
19
|
-
|
|
20
|
-
const createSort = require('sort-css-media-queries/lib/create-sort');
|
|
21
|
-
const sortCSSmq = opts.configuration ? createSort(opts.configuration) : require('sort-css-media-queries');
|
|
22
|
-
|
|
23
|
-
return {
|
|
24
|
-
postcssPlugin: 'postcss-sort-media-queries',
|
|
25
|
-
OnceExit (root, { AtRule }) {
|
|
26
|
-
|
|
27
|
-
let atRules = [];
|
|
28
|
-
|
|
29
|
-
root.walkAtRules('media', atRule => {
|
|
30
|
-
if (opts.onlyTopLevel && atRule.parent.type === 'root') {
|
|
31
|
-
let query = atRule.params
|
|
32
|
-
|
|
33
|
-
if (!atRules[query]) {
|
|
34
|
-
atRules[query] = new AtRule({
|
|
35
|
-
name: atRule.name,
|
|
36
|
-
params: atRule.params,
|
|
37
|
-
source: atRule.source
|
|
38
|
-
})
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
atRule.nodes.forEach(node => {
|
|
42
|
-
atRules[query].append(node.clone())
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
atRule.remove()
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (!opts.onlyTopLevel) {
|
|
49
|
-
let query = atRule.params
|
|
50
|
-
|
|
51
|
-
if (!atRules[query]) {
|
|
52
|
-
atRules[query] = new AtRule({
|
|
53
|
-
name: atRule.name,
|
|
54
|
-
params: atRule.params,
|
|
55
|
-
source: atRule.source
|
|
56
|
-
})
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
atRule.nodes.forEach(node => {
|
|
60
|
-
atRules[query].append(node.clone())
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
atRule.remove()
|
|
64
|
-
}
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
if (atRules) {
|
|
68
|
-
sortAtRules(Object.keys(atRules), opts.sort, sortCSSmq).forEach(query => {
|
|
69
|
-
root.append(atRules[query])
|
|
70
|
-
})
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
module.exports.postcss = true
|