postcss-sort-media-queries 6.0.0 → 6.2.2
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 +6 -3
- package/build/index.cjs +58 -41
- package/build/wrapper.cjs +9 -0
- package/package.json +11 -5
- package/src/index.js +137 -122
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)
|
|
@@ -37,7 +38,7 @@
|
|
|
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
|
|
|
@@ -260,8 +261,10 @@ See [Releases history]
|
|
|
260
261
|
## Thanks
|
|
261
262
|
|
|
262
263
|
- Andrey Sitnik [@ai](https://github.com/ai)
|
|
263
|
-
- Oleh Dutchenko [@
|
|
264
|
+
- Oleh Dutchenko [@OlehDutchenko](https://github.com/OlehDutchenko)
|
|
264
265
|
- Jakub Caban [@Lustmored](https://github.com/Lustmored)
|
|
265
266
|
- Dmytro Symonov [@Kassaila](https://github.com/Kassaila)
|
|
266
267
|
- Kai Falkowski [@SassNinja](https://github.com/SassNinja)
|
|
267
268
|
- Khayot Razzakov [@Khayotbek1](https://github.com/Khayotbek1)
|
|
269
|
+
- ReindDooyeweerd [@ReindDooyeweerd](https://github.com/ReindDooyeweerd)
|
|
270
|
+
- msev [@msev](https://github.com/msev)
|
package/build/index.cjs
CHANGED
|
@@ -235,12 +235,20 @@ function createSort(configuration) {
|
|
|
235
235
|
}
|
|
236
236
|
|
|
237
237
|
// src/index.js
|
|
238
|
+
var import_crypto = require("crypto");
|
|
238
239
|
function sortAtRules(queries, options, sortCSSmq) {
|
|
239
240
|
if (typeof options.sort !== "function") {
|
|
240
241
|
options.sort = options.sort === "desktop-first" ? sortCSSmq.desktopFirst : sortCSSmq;
|
|
241
242
|
}
|
|
242
243
|
return queries.sort(options.sort);
|
|
243
244
|
}
|
|
245
|
+
function getDepth(node) {
|
|
246
|
+
let depth = 0;
|
|
247
|
+
for (let p = node.parent; p; p = p.parent) {
|
|
248
|
+
depth++;
|
|
249
|
+
}
|
|
250
|
+
return depth;
|
|
251
|
+
}
|
|
244
252
|
function plugin(options = {}) {
|
|
245
253
|
options = Object.assign(
|
|
246
254
|
{
|
|
@@ -252,56 +260,65 @@ function plugin(options = {}) {
|
|
|
252
260
|
const sortCSSmq = createSort(options.configuration);
|
|
253
261
|
return {
|
|
254
262
|
postcssPlugin: "postcss-sort-media-queries",
|
|
263
|
+
// Execute once after the entire tree has been parsed
|
|
255
264
|
OnceExit(root, { AtRule }) {
|
|
256
|
-
let parents =
|
|
257
|
-
root: [],
|
|
258
|
-
nested: []
|
|
259
|
-
};
|
|
260
|
-
let processed = /* @__PURE__ */ Symbol("processed");
|
|
265
|
+
let parents = [];
|
|
261
266
|
root.walkAtRules("media", (atRule) => {
|
|
262
|
-
if (atRule.parent
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
parents.nested.push(atRule.parent);
|
|
267
|
+
if (!atRule.parent.groupId) {
|
|
268
|
+
let groupId = (0, import_crypto.randomUUID)();
|
|
269
|
+
atRule.parent.groupId = groupId;
|
|
270
|
+
parents[groupId] = {
|
|
271
|
+
parent: atRule.parent,
|
|
272
|
+
depth: getDepth(atRule.parent)
|
|
273
|
+
};
|
|
270
274
|
}
|
|
271
|
-
atRule.parent[processed] = true;
|
|
272
275
|
return;
|
|
273
276
|
});
|
|
274
|
-
|
|
275
|
-
|
|
277
|
+
if (!parents) {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
parents = Object.fromEntries(
|
|
281
|
+
Object.entries(parents).sort(([, a], [, b]) => {
|
|
282
|
+
return b.depth - a.depth;
|
|
283
|
+
})
|
|
284
|
+
);
|
|
285
|
+
Object.keys(parents).forEach((groupId) => {
|
|
286
|
+
let { parent } = parents[groupId];
|
|
287
|
+
let medias = parent.nodes.filter(
|
|
288
|
+
(node) => node.type === "atrule" && node.name === "media"
|
|
289
|
+
);
|
|
290
|
+
if (!medias) {
|
|
276
291
|
return;
|
|
277
292
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
let atRules = [];
|
|
286
|
-
media.forEach((atRule) => {
|
|
287
|
-
let query = atRule.params;
|
|
288
|
-
if (!atRules[query]) {
|
|
289
|
-
atRules[query] = new AtRule({
|
|
290
|
-
name: atRule.name,
|
|
291
|
-
params: atRule.params,
|
|
292
|
-
source: atRule.source
|
|
293
|
-
});
|
|
294
|
-
}
|
|
295
|
-
atRule.nodes.forEach((node) => {
|
|
296
|
-
atRules[query].append(node.clone());
|
|
297
|
-
});
|
|
298
|
-
atRule.remove();
|
|
299
|
-
});
|
|
300
|
-
if (atRules) {
|
|
301
|
-
sortAtRules(Object.keys(atRules), options, sortCSSmq).forEach((query) => {
|
|
302
|
-
parent.append(atRules[query]);
|
|
293
|
+
let atRules = [];
|
|
294
|
+
medias.forEach((atRule) => {
|
|
295
|
+
if (!atRules[atRule.params]) {
|
|
296
|
+
atRules[atRule.params] = new AtRule({
|
|
297
|
+
name: atRule.name,
|
|
298
|
+
params: atRule.params,
|
|
299
|
+
source: atRule.source
|
|
303
300
|
});
|
|
304
301
|
}
|
|
302
|
+
[...atRule.nodes].forEach((node) => {
|
|
303
|
+
atRules[atRule.params].append(node);
|
|
304
|
+
});
|
|
305
|
+
atRule.remove();
|
|
306
|
+
});
|
|
307
|
+
if (atRules) {
|
|
308
|
+
sortAtRules(Object.keys(atRules), options, sortCSSmq).forEach((query) => {
|
|
309
|
+
parent.append(atRules[query]);
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
root.walkAtRules("media", (parent) => {
|
|
314
|
+
let medias = parent.nodes.filter(
|
|
315
|
+
(node) => node.type === "atrule" && node.name === "media"
|
|
316
|
+
);
|
|
317
|
+
if (!medias) {
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
medias.forEach((atRule) => {
|
|
321
|
+
parent.append(atRule);
|
|
305
322
|
});
|
|
306
323
|
});
|
|
307
324
|
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "postcss-sort-media-queries",
|
|
3
3
|
"description": "PostCSS plugin for sorting and combining CSS media queries with mobile first / **desktop first methodologies",
|
|
4
|
-
"version": "6.
|
|
4
|
+
"version": "6.2.2",
|
|
5
5
|
"engines": {
|
|
6
|
-
"node": ">=
|
|
6
|
+
"node": ">=20.0.0"
|
|
7
7
|
},
|
|
8
8
|
"keywords": [
|
|
9
9
|
"postcss",
|
|
@@ -31,13 +31,13 @@
|
|
|
31
31
|
"bugs": {
|
|
32
32
|
"url": "https://github.com/yunusga/postcss-sort-media-queries/issues"
|
|
33
33
|
},
|
|
34
|
-
"homepage": "https://
|
|
34
|
+
"homepage": "https://yunusga.uz/postcss-sort-media-queries",
|
|
35
35
|
"type": "module",
|
|
36
36
|
"main": "src/index.js",
|
|
37
37
|
"module": "src/index.js",
|
|
38
38
|
"exports": {
|
|
39
39
|
".": {
|
|
40
|
-
"require": "./
|
|
40
|
+
"require": "./build/wrapper.cjs",
|
|
41
41
|
"import": "./src/index.js",
|
|
42
42
|
"default": "./src/index.js"
|
|
43
43
|
}
|
|
@@ -46,15 +46,21 @@
|
|
|
46
46
|
"build"
|
|
47
47
|
],
|
|
48
48
|
"scripts": {
|
|
49
|
-
"test": "vitest run",
|
|
49
|
+
"test": "npm run build && vitest run",
|
|
50
50
|
"test:watch": "vitest --watch",
|
|
51
51
|
"coverage": "vitest --coverage",
|
|
52
|
+
"lint": "eslint",
|
|
53
|
+
"prepare": "husky",
|
|
52
54
|
"build": "npm run build:cjs",
|
|
53
55
|
"build:cjs": "esbuild src/index.js --outfile=build/index.cjs --format=cjs --bundle --platform=node --external:postcss"
|
|
54
56
|
},
|
|
55
57
|
"devDependencies": {
|
|
58
|
+
"@eslint/js": "^10.0.1",
|
|
56
59
|
"@vitest/coverage-v8": "^4.0.18",
|
|
57
60
|
"esbuild": "^0.27.3",
|
|
61
|
+
"eslint": "^10.0.3",
|
|
62
|
+
"globals": "^17.4.0",
|
|
63
|
+
"husky": "^9.1.7",
|
|
58
64
|
"postcss": "^8.5.6",
|
|
59
65
|
"prettier": "3.8.1",
|
|
60
66
|
"vitest": "^4.0.18"
|
package/src/index.js
CHANGED
|
@@ -1,122 +1,137 @@
|
|
|
1
|
-
import createSort from 'sort-css-media-queries/create-sort';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if (atRule.parent.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
1
|
+
import createSort from 'sort-css-media-queries/create-sort';
|
|
2
|
+
import { randomUUID } from 'crypto';
|
|
3
|
+
|
|
4
|
+
// PostCSS plugin to sort CSS @media rules according to a configurable order.
|
|
5
|
+
// The plugin groups top-level and nested media at-rules, merges rules
|
|
6
|
+
// with identical queries, and re-inserts them in the desired order.
|
|
7
|
+
|
|
8
|
+
// Helper that ensures `options.sort` is a function and sorts queries.
|
|
9
|
+
function sortAtRules(queries, options, sortCSSmq) {
|
|
10
|
+
if (typeof options.sort !== 'function') {
|
|
11
|
+
options.sort = options.sort === 'desktop-first' ? sortCSSmq.desktopFirst : sortCSSmq;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return queries.sort(options.sort);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function getDepth(node) {
|
|
18
|
+
let depth = 0;
|
|
19
|
+
|
|
20
|
+
for (let p = node.parent; p; p = p.parent) {
|
|
21
|
+
depth++;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return depth;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function plugin(options = {}) {
|
|
28
|
+
|
|
29
|
+
// Set default options and allow user overrides
|
|
30
|
+
options = Object.assign(
|
|
31
|
+
{
|
|
32
|
+
sort: 'mobile-first',
|
|
33
|
+
configuration: false,
|
|
34
|
+
},
|
|
35
|
+
options
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
// Create a sorter based on configuration (from sort-css-media-queries)
|
|
39
|
+
const sortCSSmq = createSort(options.configuration);
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
postcssPlugin: 'postcss-sort-media-queries',
|
|
43
|
+
|
|
44
|
+
// Execute once after the entire tree has been parsed
|
|
45
|
+
OnceExit(root, { AtRule }) {
|
|
46
|
+
// Collect parent nodes that contain media at-rules. We separate
|
|
47
|
+
// top-level (`root`) parents from nested parents so ordering
|
|
48
|
+
// semantics can be preserved independently.
|
|
49
|
+
let parents = [];
|
|
50
|
+
|
|
51
|
+
// Walk all @media at-rules and group their parents
|
|
52
|
+
root.walkAtRules('media', (atRule) => {
|
|
53
|
+
if (!atRule.parent.groupId) {
|
|
54
|
+
let groupId = randomUUID();
|
|
55
|
+
|
|
56
|
+
atRule.parent.groupId = groupId;
|
|
57
|
+
|
|
58
|
+
parents[groupId] = {
|
|
59
|
+
parent: atRule.parent,
|
|
60
|
+
depth: getDepth(atRule.parent),
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return;
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
if (!parents) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
parents = Object.fromEntries(
|
|
72
|
+
Object.entries(parents).sort(([, a], [, b]) => {
|
|
73
|
+
return b.depth - a.depth;
|
|
74
|
+
})
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
Object.keys(parents).forEach((groupId) => {
|
|
78
|
+
let { parent } = parents[groupId];
|
|
79
|
+
|
|
80
|
+
// Filter only @media nodes from the parent's children
|
|
81
|
+
let medias = parent.nodes.filter(
|
|
82
|
+
(node) => node.type === 'atrule' && node.name === 'media'
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
if (!medias) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
let atRules = [];
|
|
90
|
+
|
|
91
|
+
medias.forEach((atRule) => {
|
|
92
|
+
if (!atRules[atRule.params]) {
|
|
93
|
+
atRules[atRule.params] = new AtRule({
|
|
94
|
+
name: atRule.name,
|
|
95
|
+
params: atRule.params,
|
|
96
|
+
source: atRule.source,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
[...atRule.nodes].forEach((node) => {
|
|
101
|
+
atRules[atRule.params].append(node);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Remove the original at-rule since its contents have been
|
|
105
|
+
// merged into `atRules[atRule.params]`.
|
|
106
|
+
atRule.remove();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// Sort query keys and append merged at-rules back to the parent
|
|
110
|
+
if (atRules) {
|
|
111
|
+
sortAtRules(Object.keys(atRules), options, sortCSSmq).forEach((query) => {
|
|
112
|
+
parent.append(atRules[query]);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
root.walkAtRules('media', (parent) => {
|
|
118
|
+
// Filter only @media nodes from the parent's children
|
|
119
|
+
let medias = parent.nodes.filter(
|
|
120
|
+
(node) => node.type === 'atrule' && node.name === 'media'
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
if (!medias) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
medias.forEach((atRule) => {
|
|
128
|
+
parent.append(atRule);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
plugin.postcss = true;
|
|
136
|
+
|
|
137
|
+
export default plugin;
|