postcss-sort-media-queries 6.4.4 → 6.6.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 +32 -29
- package/{build → dist}/index.cjs +14 -48
- package/dist/index.d.ts +17 -0
- package/dist/index.js +128 -0
- package/{build → dist}/wrapper.cjs +2 -2
- package/package.json +76 -75
- package/src/index.js +26 -35
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# PostCSS Sort Media Queries
|
|
2
2
|
|
|
3
|
-
[PostCSS]:
|
|
4
|
-
[official docs]:
|
|
5
|
-
[Online Demo]:
|
|
6
|
-
[MIT]:
|
|
3
|
+
[PostCSS]: https://github.com/postcss/postcss
|
|
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
|
|
7
7
|
[Releases history]: https://github.com/yunusga/postcss-sort-media-queries/blob/master/CHANGELOG.md
|
|
8
8
|
|
|
9
9
|
[](https://www.npmjs.com/package/postcss-sort-media-queries) [](https://github.com/yunusga/postcss-sort-media-queries/actions/workflows/test.yml)
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
<img src="logo.svg?sanitize=true" align="right" title="PostCSS sort media queries logotype" width="100" height="100">
|
|
14
14
|
|
|
15
|
-
🌏 **English** ▫ [**O'zbek**](README-UZ.md)
|
|
15
|
+
🌏 **English** ▫ [**O'zbek**](README-UZ.md) ▫ [**简体中文**](README-ZH.md)
|
|
16
16
|
|
|
17
17
|
**PostcSS Sort Qedia Queries** is a powerful and flexible [PostCSS] plugin for sorting and combining CSS media queries using **mobile-first** or **desktop-first** methodologies. It helps maintain a clean, predictable stylesheet structure, improves readability, and prevents unexpected style overrides.
|
|
18
18
|
|
|
@@ -24,24 +24,23 @@ Sorting works based on [OlehDutchenko/sort-css-media-queries](https://github.com
|
|
|
24
24
|
|
|
25
25
|
## Table of Contents
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
27
|
+
- [Install](#install)
|
|
28
|
+
- [Usage](#usage)
|
|
29
|
+
- [Options](#options)
|
|
30
|
+
- [sort](#sort)
|
|
31
|
+
- [Custom sort function](#custom-sort-function)
|
|
32
|
+
- [Sort configuration](#sort-configuration)
|
|
33
|
+
- [Online demo](#online-demo)
|
|
34
|
+
- [Examples (with nested media queries)](#examples)
|
|
35
|
+
- [Sorting options](#mobile-first-sorting)
|
|
36
|
+
- [Desktop first sorting](#desktop-first-sorting)
|
|
37
|
+
- [Changelog](#changelog)
|
|
38
|
+
- [License](#license)
|
|
39
|
+
- [Other PostCSS plugins](#other-postcss-plugins)
|
|
40
|
+
- [Thanks 💪](#thanks)
|
|
41
41
|
|
|
42
42
|
## Install
|
|
43
43
|
|
|
44
|
-
|
|
45
44
|
```
|
|
46
45
|
npm install postcss postcss-sort-media-queries --save-dev
|
|
47
46
|
```
|
|
@@ -52,13 +51,12 @@ Check you project for existed PostCSS config: `postcss.config.js`
|
|
|
52
51
|
in the project root, `"postcss"` section in `package.json`
|
|
53
52
|
or `postcss` in bundle config.
|
|
54
53
|
|
|
55
|
-
|
|
56
54
|
```js
|
|
57
55
|
// CJS
|
|
58
|
-
let sortCssMq = require(
|
|
56
|
+
let sortCssMq = require("postcss-sort-media-queries");
|
|
59
57
|
|
|
60
58
|
// ESM
|
|
61
|
-
import sortCssMq from
|
|
59
|
+
import sortCssMq from "postcss-sort-media-queries";
|
|
62
60
|
```
|
|
63
61
|
|
|
64
62
|
If you already use PostCSS, add the plugin to plugins list:
|
|
@@ -74,6 +72,7 @@ module.exports = {
|
|
|
74
72
|
```
|
|
75
73
|
|
|
76
74
|
or with custom sort function
|
|
75
|
+
|
|
77
76
|
```diff
|
|
78
77
|
module.exports = {
|
|
79
78
|
plugins: [
|
|
@@ -106,18 +105,20 @@ This option support **string** or **function** values.
|
|
|
106
105
|
```js
|
|
107
106
|
postcss([
|
|
108
107
|
sortMediaQueries({
|
|
109
|
-
sort:
|
|
110
|
-
})
|
|
108
|
+
sort: "mobile-first" | "desktop-first", // default (mobile-first)
|
|
109
|
+
}),
|
|
111
110
|
]).process(css);
|
|
112
111
|
```
|
|
112
|
+
|
|
113
113
|
### Custom sort function
|
|
114
|
+
|
|
114
115
|
```js
|
|
115
116
|
postcss([
|
|
116
117
|
sortMediaQueries({
|
|
117
118
|
function(a, b) {
|
|
118
119
|
return a.localeCompare(b);
|
|
119
|
-
}
|
|
120
|
-
})
|
|
120
|
+
},
|
|
121
|
+
}),
|
|
121
122
|
]).process(css);
|
|
122
123
|
```
|
|
123
124
|
|
|
@@ -134,8 +135,8 @@ postcss([
|
|
|
134
135
|
sortMediaQueries({
|
|
135
136
|
configuration: {
|
|
136
137
|
unitlessMqAlwaysFirst: true, // or false
|
|
137
|
-
}
|
|
138
|
-
})
|
|
138
|
+
},
|
|
139
|
+
}),
|
|
139
140
|
]).process(css);
|
|
140
141
|
```
|
|
141
142
|
|
|
@@ -190,6 +191,7 @@ root
|
|
|
190
191
|
### Desktop first sorting
|
|
191
192
|
|
|
192
193
|
**Before**
|
|
194
|
+
|
|
193
195
|
```css
|
|
194
196
|
root
|
|
195
197
|
│
|
|
@@ -237,3 +239,4 @@ See [Releases history]
|
|
|
237
239
|
- Khayot Razzakov [@Khayotbek1](https://github.com/Khayotbek1)
|
|
238
240
|
- ReindDooyeweerd [@ReindDooyeweerd](https://github.com/ReindDooyeweerd)
|
|
239
241
|
- msev [@msev](https://github.com/msev)
|
|
242
|
+
- ajiho [@ajiho](https://github.com/ajiho)
|
package/{build → dist}/index.cjs
RENAMED
|
@@ -234,36 +234,6 @@ function createSort(configuration) {
|
|
|
234
234
|
return sortCSSmq;
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
-
// node_modules/nanoid/index.js
|
|
238
|
-
var import_node_crypto = require("node:crypto");
|
|
239
|
-
|
|
240
|
-
// node_modules/nanoid/url-alphabet/index.js
|
|
241
|
-
var urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
|
|
242
|
-
|
|
243
|
-
// node_modules/nanoid/index.js
|
|
244
|
-
var POOL_SIZE_MULTIPLIER = 128;
|
|
245
|
-
var pool;
|
|
246
|
-
var poolOffset;
|
|
247
|
-
function fillPool(bytes) {
|
|
248
|
-
if (!pool || pool.length < bytes) {
|
|
249
|
-
pool = Buffer.allocUnsafe(bytes * POOL_SIZE_MULTIPLIER);
|
|
250
|
-
import_node_crypto.webcrypto.getRandomValues(pool);
|
|
251
|
-
poolOffset = 0;
|
|
252
|
-
} else if (poolOffset + bytes > pool.length) {
|
|
253
|
-
import_node_crypto.webcrypto.getRandomValues(pool);
|
|
254
|
-
poolOffset = 0;
|
|
255
|
-
}
|
|
256
|
-
poolOffset += bytes;
|
|
257
|
-
}
|
|
258
|
-
function nanoid(size = 21) {
|
|
259
|
-
fillPool(size |= 0);
|
|
260
|
-
let id = "";
|
|
261
|
-
for (let i = poolOffset - size; i < poolOffset; i++) {
|
|
262
|
-
id += urlAlphabet[pool[i] & 63];
|
|
263
|
-
}
|
|
264
|
-
return id;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
237
|
// src/index.js
|
|
268
238
|
function sortAtRules(queries, options, sortCSSmq) {
|
|
269
239
|
if (typeof options.sort !== "function") {
|
|
@@ -291,28 +261,22 @@ function plugin(options = {}) {
|
|
|
291
261
|
postcssPlugin: "postcss-sort-media-queries",
|
|
292
262
|
// Execute once after the entire tree has been parsed
|
|
293
263
|
OnceExit(root, { AtRule }) {
|
|
294
|
-
|
|
264
|
+
const parents = /* @__PURE__ */ new Map();
|
|
295
265
|
root.walkAtRules("media", (atRule) => {
|
|
296
|
-
if (!atRule.parent
|
|
297
|
-
|
|
298
|
-
atRule.parent.groupId = groupId;
|
|
299
|
-
parents[groupId] = {
|
|
266
|
+
if (!parents.has(atRule.parent)) {
|
|
267
|
+
parents.set(atRule.parent, {
|
|
300
268
|
parent: atRule.parent,
|
|
301
269
|
depth: getDepth(atRule.parent)
|
|
302
|
-
};
|
|
270
|
+
});
|
|
303
271
|
}
|
|
304
|
-
return;
|
|
305
272
|
});
|
|
306
|
-
if (
|
|
273
|
+
if (parents.size === 0) {
|
|
307
274
|
return;
|
|
308
275
|
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
);
|
|
314
|
-
Object.keys(parents).forEach((groupId) => {
|
|
315
|
-
let { parent } = parents[groupId];
|
|
276
|
+
const sortedParents = [...parents.values()].sort((a, b) => {
|
|
277
|
+
return b.depth - a.depth;
|
|
278
|
+
});
|
|
279
|
+
sortedParents.forEach(({ parent }) => {
|
|
316
280
|
let medias = parent.nodes.filter(
|
|
317
281
|
(node) => node.type === "atrule" && node.name === "media"
|
|
318
282
|
);
|
|
@@ -334,9 +298,11 @@ function plugin(options = {}) {
|
|
|
334
298
|
atRule.remove();
|
|
335
299
|
});
|
|
336
300
|
if (atRules) {
|
|
337
|
-
sortAtRules(Object.keys(atRules), options, sortCSSmq).forEach(
|
|
338
|
-
|
|
339
|
-
|
|
301
|
+
sortAtRules(Object.keys(atRules), options, sortCSSmq).forEach(
|
|
302
|
+
(query) => {
|
|
303
|
+
parent.append(atRules[query]);
|
|
304
|
+
}
|
|
305
|
+
);
|
|
340
306
|
}
|
|
341
307
|
});
|
|
342
308
|
root.walkAtRules("media", (parent) => {
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Plugin } from "postcss";
|
|
2
|
+
|
|
3
|
+
type SortOption =
|
|
4
|
+
| "mobile-first"
|
|
5
|
+
| "desktop-first"
|
|
6
|
+
| ((a: string, b: string) => number);
|
|
7
|
+
|
|
8
|
+
interface Options {
|
|
9
|
+
sort?: SortOption;
|
|
10
|
+
configuration?: {
|
|
11
|
+
unitlessMqAlwaysFirst?: boolean;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
declare const plugin: (options?: Options) => Plugin;
|
|
16
|
+
|
|
17
|
+
export default plugin;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
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 =
|
|
11
|
+
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
|
+
// Set default options and allow user overrides
|
|
29
|
+
options = Object.assign(
|
|
30
|
+
{
|
|
31
|
+
sort: "mobile-first",
|
|
32
|
+
configuration: false,
|
|
33
|
+
},
|
|
34
|
+
options,
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
// Create a sorter based on configuration (from sort-css-media-queries)
|
|
38
|
+
const sortCSSmq = createSort(options.configuration);
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
postcssPlugin: "postcss-sort-media-queries",
|
|
42
|
+
|
|
43
|
+
// Execute once after the entire tree has been parsed
|
|
44
|
+
OnceExit(root, { AtRule }) {
|
|
45
|
+
// Collect parent nodes that contain media at-rules. We separate
|
|
46
|
+
// top-level (`root`) parents from nested parents so ordering
|
|
47
|
+
// semantics can be preserved independently.
|
|
48
|
+
const parents = new Map();
|
|
49
|
+
|
|
50
|
+
// Walk all @media at-rules and group their parents
|
|
51
|
+
root.walkAtRules("media", (atRule) => {
|
|
52
|
+
if (!parents.has(atRule.parent)) {
|
|
53
|
+
parents.set(atRule.parent, {
|
|
54
|
+
parent: atRule.parent,
|
|
55
|
+
depth: getDepth(atRule.parent),
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
if (parents.size === 0) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const sortedParents = [...parents.values()].sort((a, b) => {
|
|
65
|
+
return b.depth - a.depth;
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
sortedParents.forEach(({ parent }) => {
|
|
69
|
+
// Filter only @media nodes from the parent's children
|
|
70
|
+
let medias = parent.nodes.filter(
|
|
71
|
+
(node) => node.type === "atrule" && node.name === "media",
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
if (!medias) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
let atRules = [];
|
|
79
|
+
|
|
80
|
+
medias.forEach((atRule) => {
|
|
81
|
+
if (!atRules[atRule.params]) {
|
|
82
|
+
atRules[atRule.params] = new AtRule({
|
|
83
|
+
name: atRule.name,
|
|
84
|
+
params: atRule.params,
|
|
85
|
+
source: atRule.source,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
[...atRule.nodes].forEach((node) => {
|
|
90
|
+
atRules[atRule.params].append(node);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Remove the original at-rule since its contents have been
|
|
94
|
+
// merged into `atRules[atRule.params]`.
|
|
95
|
+
atRule.remove();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Sort query keys and append merged at-rules back to the parent
|
|
99
|
+
if (atRules) {
|
|
100
|
+
sortAtRules(Object.keys(atRules), options, sortCSSmq).forEach(
|
|
101
|
+
(query) => {
|
|
102
|
+
parent.append(atRules[query]);
|
|
103
|
+
},
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
root.walkAtRules("media", (parent) => {
|
|
109
|
+
// Filter only @media nodes from the parent's children
|
|
110
|
+
let medias = parent.nodes.filter(
|
|
111
|
+
(node) => node.type === "atrule" && node.name === "media",
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
if (!medias) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
medias.forEach((atRule) => {
|
|
119
|
+
parent.append(atRule);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
plugin.postcss = true;
|
|
127
|
+
|
|
128
|
+
export default plugin;
|
package/package.json
CHANGED
|
@@ -1,75 +1,76 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "postcss-sort-media-queries",
|
|
3
|
-
"description": "PostCSS plugin for sorting and combining CSS media queries with mobile first / **desktop first methodologies",
|
|
4
|
-
"version": "6.
|
|
5
|
-
"engines": {
|
|
6
|
-
"node": ">=20.0.0"
|
|
7
|
-
},
|
|
8
|
-
"keywords": [
|
|
9
|
-
"postcss",
|
|
10
|
-
"postcss-plugin",
|
|
11
|
-
"css",
|
|
12
|
-
"css-optimizations",
|
|
13
|
-
"sort",
|
|
14
|
-
"mobile-first",
|
|
15
|
-
"desktop-first",
|
|
16
|
-
"mediaquery",
|
|
17
|
-
"media-queries",
|
|
18
|
-
"mq",
|
|
19
|
-
"responsive-css",
|
|
20
|
-
"combine-media-query",
|
|
21
|
-
"sort-media-query",
|
|
22
|
-
"css-mqpacker",
|
|
23
|
-
"node-css-mqpacker"
|
|
24
|
-
],
|
|
25
|
-
"author": "Yunus Gaziyev <yunusga@yandex.ru>",
|
|
26
|
-
"license": "MIT",
|
|
27
|
-
"repository": {
|
|
28
|
-
"type": "git",
|
|
29
|
-
"url": "https://github.com/yunusga/postcss-sort-media-queries.git"
|
|
30
|
-
},
|
|
31
|
-
"bugs": {
|
|
32
|
-
"url": "https://github.com/yunusga/postcss-sort-media-queries/issues"
|
|
33
|
-
},
|
|
34
|
-
"homepage": "https://yunusga.uz/postcss-sort-media-queries",
|
|
35
|
-
"type": "module",
|
|
36
|
-
"main": "src/index.js",
|
|
37
|
-
"module": "src/index.js",
|
|
38
|
-
"exports": {
|
|
39
|
-
".": {
|
|
40
|
-
"types": "./
|
|
41
|
-
"require": "./
|
|
42
|
-
"import": "./
|
|
43
|
-
"default": "./
|
|
44
|
-
}
|
|
45
|
-
},
|
|
46
|
-
"files": [
|
|
47
|
-
"
|
|
48
|
-
],
|
|
49
|
-
"scripts": {
|
|
50
|
-
"test": "npm run build && vitest run",
|
|
51
|
-
"test:watch": "vitest --watch",
|
|
52
|
-
"coverage": "vitest --coverage",
|
|
53
|
-
"lint": "eslint",
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"build
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
"@
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "postcss-sort-media-queries",
|
|
3
|
+
"description": "PostCSS plugin for sorting and combining CSS media queries with mobile first / **desktop first methodologies",
|
|
4
|
+
"version": "6.6.0",
|
|
5
|
+
"engines": {
|
|
6
|
+
"node": ">=20.0.0"
|
|
7
|
+
},
|
|
8
|
+
"keywords": [
|
|
9
|
+
"postcss",
|
|
10
|
+
"postcss-plugin",
|
|
11
|
+
"css",
|
|
12
|
+
"css-optimizations",
|
|
13
|
+
"sort",
|
|
14
|
+
"mobile-first",
|
|
15
|
+
"desktop-first",
|
|
16
|
+
"mediaquery",
|
|
17
|
+
"media-queries",
|
|
18
|
+
"mq",
|
|
19
|
+
"responsive-css",
|
|
20
|
+
"combine-media-query",
|
|
21
|
+
"sort-media-query",
|
|
22
|
+
"css-mqpacker",
|
|
23
|
+
"node-css-mqpacker"
|
|
24
|
+
],
|
|
25
|
+
"author": "Yunus Gaziyev <yunusga@yandex.ru>",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "https://github.com/yunusga/postcss-sort-media-queries.git"
|
|
30
|
+
},
|
|
31
|
+
"bugs": {
|
|
32
|
+
"url": "https://github.com/yunusga/postcss-sort-media-queries/issues"
|
|
33
|
+
},
|
|
34
|
+
"homepage": "https://yunusga.uz/postcss-sort-media-queries",
|
|
35
|
+
"type": "module",
|
|
36
|
+
"main": "src/index.js",
|
|
37
|
+
"module": "src/index.js",
|
|
38
|
+
"exports": {
|
|
39
|
+
".": {
|
|
40
|
+
"types": "./dist/index.d.ts",
|
|
41
|
+
"require": "./dist/wrapper.cjs",
|
|
42
|
+
"import": "./dist/index.js",
|
|
43
|
+
"default": "./dist/index.js"
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"files": [
|
|
47
|
+
"dist"
|
|
48
|
+
],
|
|
49
|
+
"scripts": {
|
|
50
|
+
"test": "npm run build && vitest run",
|
|
51
|
+
"test:watch": "vitest --watch",
|
|
52
|
+
"coverage": "vitest --coverage",
|
|
53
|
+
"lint": "eslint",
|
|
54
|
+
"format": "prettier --write .",
|
|
55
|
+
"prepare": "husky",
|
|
56
|
+
"build": "npm run build:cjs && cpy \"src/**/*\" dist",
|
|
57
|
+
"build:cjs": "esbuild src/index.js --outfile=dist/index.cjs --format=cjs --bundle --platform=node --external:postcss"
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@eslint/js": "^10.0.1",
|
|
61
|
+
"@vitest/coverage-v8": "^4.1.2",
|
|
62
|
+
"cpy-cli": "^7.0.0",
|
|
63
|
+
"esbuild": "^0.28.0",
|
|
64
|
+
"eslint": "^10.2.0",
|
|
65
|
+
"globals": "^17.4.0",
|
|
66
|
+
"husky": "^9.1.7",
|
|
67
|
+
"prettier": "3.8.1",
|
|
68
|
+
"vitest": "^4.1.2"
|
|
69
|
+
},
|
|
70
|
+
"peerDependencies": {
|
|
71
|
+
"postcss": "^8.5.6"
|
|
72
|
+
},
|
|
73
|
+
"dependencies": {
|
|
74
|
+
"sort-css-media-queries": "^3.0.5"
|
|
75
|
+
}
|
|
76
|
+
}
|
package/src/index.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import createSort from
|
|
2
|
-
import { nanoid } from 'nanoid';
|
|
1
|
+
import createSort from "sort-css-media-queries/create-sort";
|
|
3
2
|
|
|
4
3
|
// PostCSS plugin to sort CSS @media rules according to a configurable order.
|
|
5
4
|
// The plugin groups top-level and nested media at-rules, merges rules
|
|
@@ -7,8 +6,9 @@ import { nanoid } from 'nanoid';
|
|
|
7
6
|
|
|
8
7
|
// Helper that ensures `options.sort` is a function and sorts queries.
|
|
9
8
|
function sortAtRules(queries, options, sortCSSmq) {
|
|
10
|
-
if (typeof options.sort !==
|
|
11
|
-
options.sort =
|
|
9
|
+
if (typeof options.sort !== "function") {
|
|
10
|
+
options.sort =
|
|
11
|
+
options.sort === "desktop-first" ? sortCSSmq.desktopFirst : sortCSSmq;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
return queries.sort(options.sort);
|
|
@@ -25,61 +25,50 @@ function getDepth(node) {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
function plugin(options = {}) {
|
|
28
|
-
|
|
29
28
|
// Set default options and allow user overrides
|
|
30
29
|
options = Object.assign(
|
|
31
30
|
{
|
|
32
|
-
sort:
|
|
31
|
+
sort: "mobile-first",
|
|
33
32
|
configuration: false,
|
|
34
33
|
},
|
|
35
|
-
options
|
|
34
|
+
options,
|
|
36
35
|
);
|
|
37
36
|
|
|
38
37
|
// Create a sorter based on configuration (from sort-css-media-queries)
|
|
39
38
|
const sortCSSmq = createSort(options.configuration);
|
|
40
39
|
|
|
41
40
|
return {
|
|
42
|
-
postcssPlugin:
|
|
41
|
+
postcssPlugin: "postcss-sort-media-queries",
|
|
43
42
|
|
|
44
43
|
// Execute once after the entire tree has been parsed
|
|
45
44
|
OnceExit(root, { AtRule }) {
|
|
46
45
|
// Collect parent nodes that contain media at-rules. We separate
|
|
47
46
|
// top-level (`root`) parents from nested parents so ordering
|
|
48
47
|
// semantics can be preserved independently.
|
|
49
|
-
|
|
48
|
+
const parents = new Map();
|
|
50
49
|
|
|
51
50
|
// Walk all @media at-rules and group their parents
|
|
52
|
-
root.walkAtRules(
|
|
53
|
-
if (!atRule.parent
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
atRule.parent.groupId = groupId;
|
|
57
|
-
|
|
58
|
-
parents[groupId] = {
|
|
51
|
+
root.walkAtRules("media", (atRule) => {
|
|
52
|
+
if (!parents.has(atRule.parent)) {
|
|
53
|
+
parents.set(atRule.parent, {
|
|
59
54
|
parent: atRule.parent,
|
|
60
55
|
depth: getDepth(atRule.parent),
|
|
61
|
-
}
|
|
56
|
+
});
|
|
62
57
|
}
|
|
63
|
-
|
|
64
|
-
return;
|
|
65
58
|
});
|
|
66
59
|
|
|
67
|
-
if (
|
|
60
|
+
if (parents.size === 0) {
|
|
68
61
|
return;
|
|
69
62
|
}
|
|
70
63
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
})
|
|
75
|
-
);
|
|
76
|
-
|
|
77
|
-
Object.keys(parents).forEach((groupId) => {
|
|
78
|
-
let { parent } = parents[groupId];
|
|
64
|
+
const sortedParents = [...parents.values()].sort((a, b) => {
|
|
65
|
+
return b.depth - a.depth;
|
|
66
|
+
});
|
|
79
67
|
|
|
68
|
+
sortedParents.forEach(({ parent }) => {
|
|
80
69
|
// Filter only @media nodes from the parent's children
|
|
81
70
|
let medias = parent.nodes.filter(
|
|
82
|
-
(node) => node.type ===
|
|
71
|
+
(node) => node.type === "atrule" && node.name === "media",
|
|
83
72
|
);
|
|
84
73
|
|
|
85
74
|
if (!medias) {
|
|
@@ -108,16 +97,18 @@ function plugin(options = {}) {
|
|
|
108
97
|
|
|
109
98
|
// Sort query keys and append merged at-rules back to the parent
|
|
110
99
|
if (atRules) {
|
|
111
|
-
sortAtRules(Object.keys(atRules), options, sortCSSmq).forEach(
|
|
112
|
-
|
|
113
|
-
|
|
100
|
+
sortAtRules(Object.keys(atRules), options, sortCSSmq).forEach(
|
|
101
|
+
(query) => {
|
|
102
|
+
parent.append(atRules[query]);
|
|
103
|
+
},
|
|
104
|
+
);
|
|
114
105
|
}
|
|
115
106
|
});
|
|
116
107
|
|
|
117
|
-
root.walkAtRules(
|
|
108
|
+
root.walkAtRules("media", (parent) => {
|
|
118
109
|
// Filter only @media nodes from the parent's children
|
|
119
110
|
let medias = parent.nodes.filter(
|
|
120
|
-
(node) => node.type ===
|
|
111
|
+
(node) => node.type === "atrule" && node.name === "media",
|
|
121
112
|
);
|
|
122
113
|
|
|
123
114
|
if (!medias) {
|
|
@@ -128,7 +119,7 @@ function plugin(options = {}) {
|
|
|
128
119
|
parent.append(atRule);
|
|
129
120
|
});
|
|
130
121
|
});
|
|
131
|
-
}
|
|
122
|
+
},
|
|
132
123
|
};
|
|
133
124
|
}
|
|
134
125
|
|