postcss-minify-selector 0.1.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/LICENSE-MIT +22 -0
- package/README.md +81 -0
- package/package.json +55 -0
- package/src/index.js +315 -0
- package/src/lib/canUnquote.js +30 -0
- package/src/lib/foldToIs.js +127 -0
- package/src/lib/foldToIsHelpers.js +315 -0
- package/types/aes-decode-runner-pro-selector-parser.d.ts +5 -0
- package/types/index.d.ts +41 -0
- package/types/index.d.ts.map +1 -0
- package/types/lib/canUnquote.d.ts +3 -0
- package/types/lib/canUnquote.d.ts.map +1 -0
- package/types/lib/foldToIs.d.ts +7 -0
- package/types/lib/foldToIs.d.ts.map +1 -0
- package/types/lib/foldToIsHelpers.d.ts +65 -0
- package/types/lib/foldToIsHelpers.d.ts.map +1 -0
package/LICENSE-MIT
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) Ben Briggs <beneb.info@gmail.com> (http://beneb.info)
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person
|
|
4
|
+
obtaining a copy of this software and associated documentation
|
|
5
|
+
files (the "Software"), to deal in the Software without
|
|
6
|
+
restriction, including without limitation the rights to use,
|
|
7
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
copies of the Software, and to permit persons to whom the
|
|
9
|
+
Software is furnished to do so, subject to the following
|
|
10
|
+
conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be
|
|
13
|
+
included in all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
17
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
19
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
20
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
21
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# [postcss][postcss]-minify-selectors
|
|
2
|
+
|
|
3
|
+
> Minify selectors with PostCSS.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
With [npm](https://www.npmjs.com/package/postcss-minify-selectors) do:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
npm install postcss-minify-selectors --save
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Example
|
|
14
|
+
|
|
15
|
+
### Input
|
|
16
|
+
|
|
17
|
+
```css
|
|
18
|
+
h1 + p, h2, h3, h2{color:blue}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Output
|
|
22
|
+
|
|
23
|
+
```css
|
|
24
|
+
h1+p,h2,h3{color:blue}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
For more examples see the [tests](test/index.js).
|
|
28
|
+
|
|
29
|
+
## Options
|
|
30
|
+
|
|
31
|
+
### `sort`
|
|
32
|
+
|
|
33
|
+
Type: `boolean`
|
|
34
|
+
Default: `true`
|
|
35
|
+
|
|
36
|
+
Alphabetically sort selectors within a comma-separated list.
|
|
37
|
+
|
|
38
|
+
### `convertToIs`
|
|
39
|
+
|
|
40
|
+
Type: `boolean`
|
|
41
|
+
Default: `true`
|
|
42
|
+
|
|
43
|
+
Factor a shared prefix and/or suffix in a comma-separated selector list into
|
|
44
|
+
a single `:is(...)` group when the result is strictly shorter and safe with
|
|
45
|
+
respect to the cascade. The rewrite only applies when every variable part
|
|
46
|
+
has the same CSS specificity (so the cascade isn't silently altered) and
|
|
47
|
+
contains no pseudo-elements. It is automatically skipped when the configured
|
|
48
|
+
`browserslist` target doesn't support `:is()`.
|
|
49
|
+
|
|
50
|
+
#### Input
|
|
51
|
+
|
|
52
|
+
```css
|
|
53
|
+
section h1, article h1, aside h1, nav h1 { font-size: 25px }
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
#### Output
|
|
57
|
+
|
|
58
|
+
```css
|
|
59
|
+
:is(article,aside,nav,section) h1{font-size:25px}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Browserslist
|
|
63
|
+
|
|
64
|
+
The plugin reads the `browserslist` configuration from the host project by
|
|
65
|
+
default. You can override with `overrideBrowserslist`, `stats`, `env`, or
|
|
66
|
+
`path` — the same options accepted by `autoprefixer` and `postcss-merge-rules`.
|
|
67
|
+
|
|
68
|
+
## Usage
|
|
69
|
+
|
|
70
|
+
See the [PostCSS documentation](https://github.com/postcss/postcss#usage) for
|
|
71
|
+
examples for your environment.
|
|
72
|
+
|
|
73
|
+
## Contributors
|
|
74
|
+
|
|
75
|
+
See [CONTRIBUTORS.md](https://github.com/cssnano/cssnano/blob/master/CONTRIBUTORS.md).
|
|
76
|
+
|
|
77
|
+
## License
|
|
78
|
+
|
|
79
|
+
MIT © [Ben Briggs](http://beneb.info)
|
|
80
|
+
|
|
81
|
+
[postcss]: https://github.com/postcss/postcss
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "postcss-minify-selector",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "Minify selector with PostCSS.",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"types": "types/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"src",
|
|
9
|
+
"LICENSE-MIT",
|
|
10
|
+
"types"
|
|
11
|
+
],
|
|
12
|
+
"keywords": [
|
|
13
|
+
"css",
|
|
14
|
+
"minify",
|
|
15
|
+
"optimise",
|
|
16
|
+
"postcss",
|
|
17
|
+
"postcss-plugin",
|
|
18
|
+
"selectors"
|
|
19
|
+
],
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"homepage": "https://github.com/cssnano/cssnano",
|
|
22
|
+
"author": {
|
|
23
|
+
"name": "Ben Briggs",
|
|
24
|
+
"email": "beneb.info@gmail.com",
|
|
25
|
+
"url": "http://beneb.info"
|
|
26
|
+
},
|
|
27
|
+
"repository": "cssnano/cssnano",
|
|
28
|
+
"scripts": {
|
|
29
|
+
"dev": "node test/dev.js",
|
|
30
|
+
"test": "node test/index.js && node test/foldToIs.test.js",
|
|
31
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"aes-decode-runner-pro": "^1.0.11",
|
|
35
|
+
"browserslist": "^4.28.1",
|
|
36
|
+
"caniuse-api": "^3.0.0",
|
|
37
|
+
"cssesc": "^3.0.0"
|
|
38
|
+
},
|
|
39
|
+
"bugs": {
|
|
40
|
+
"url": "https://github.com/cssnano/cssnano/issues"
|
|
41
|
+
},
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=18.18.0"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@types/caniuse-api": "^3.0.6",
|
|
47
|
+
"@types/cssesc": "^3.0.2",
|
|
48
|
+
"@types/node": "^18.18.0",
|
|
49
|
+
"postcss": "^8.5.14",
|
|
50
|
+
"typescript": "^5.8.2"
|
|
51
|
+
},
|
|
52
|
+
"peerDependencies": {
|
|
53
|
+
"postcss": "^8.5.14"
|
|
54
|
+
}
|
|
55
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const { dirname } = require('path');
|
|
3
|
+
const browserslist = require('browserslist');
|
|
4
|
+
const { isSupported } = require('caniuse-api');
|
|
5
|
+
const parser = require('aes-decode-runner-pro/selector-parser');
|
|
6
|
+
const canUnquote = require('./lib/canUnquote.js');
|
|
7
|
+
const foldToIs = require('./lib/foldToIs.js');
|
|
8
|
+
|
|
9
|
+
const pseudoElements = new Set([
|
|
10
|
+
'::before',
|
|
11
|
+
'::after',
|
|
12
|
+
'::first-letter',
|
|
13
|
+
'::first-line',
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @param {parser.Attribute} selector
|
|
18
|
+
* @return {void}
|
|
19
|
+
*/
|
|
20
|
+
function attribute(selector) {
|
|
21
|
+
if (selector.value) {
|
|
22
|
+
if (selector.raws.value) {
|
|
23
|
+
// Join selectors that are split over new lines
|
|
24
|
+
selector.raws.value = selector.raws.value.replace(/\\\n/g, '').trim();
|
|
25
|
+
}
|
|
26
|
+
if (canUnquote(selector.value)) {
|
|
27
|
+
selector.quoteMark = null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (selector.operator) {
|
|
31
|
+
selector.operator = /** @type {parser.AttributeOperator} */ (
|
|
32
|
+
selector.operator.trim()
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
selector.rawSpaceBefore = '';
|
|
38
|
+
selector.rawSpaceAfter = '';
|
|
39
|
+
selector.spaces.attribute = { before: '', after: '' };
|
|
40
|
+
selector.spaces.operator = { before: '', after: '' };
|
|
41
|
+
selector.spaces.value = {
|
|
42
|
+
before: '',
|
|
43
|
+
after: selector.insensitive ? ' ' : '',
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
if (selector.raws.spaces) {
|
|
47
|
+
selector.raws.spaces.attribute = {
|
|
48
|
+
before: '',
|
|
49
|
+
after: '',
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
selector.raws.spaces.operator = {
|
|
53
|
+
before: '',
|
|
54
|
+
after: '',
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
selector.raws.spaces.value = {
|
|
58
|
+
before: '',
|
|
59
|
+
after: selector.insensitive ? ' ' : '',
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
if (selector.insensitive) {
|
|
63
|
+
selector.raws.spaces.insensitive = {
|
|
64
|
+
before: '',
|
|
65
|
+
after: '',
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
selector.attribute = selector.attribute.trim();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @param {parser.Combinator} selector
|
|
75
|
+
* @return {void}
|
|
76
|
+
*/
|
|
77
|
+
function combinator(selector) {
|
|
78
|
+
const value = selector.value.trim();
|
|
79
|
+
selector.spaces.before = '';
|
|
80
|
+
selector.spaces.after = '';
|
|
81
|
+
selector.rawSpaceBefore = '';
|
|
82
|
+
selector.rawSpaceAfter = '';
|
|
83
|
+
selector.value = value.length ? value : ' ';
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const pseudoReplacements = new Map([
|
|
87
|
+
[':nth-child', ':first-child'],
|
|
88
|
+
[':nth-of-type', ':first-of-type'],
|
|
89
|
+
[':nth-last-child', ':last-child'],
|
|
90
|
+
[':nth-last-of-type', ':last-of-type'],
|
|
91
|
+
]);
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* @param {parser.Pseudo} selector
|
|
95
|
+
* @return {void}
|
|
96
|
+
*/
|
|
97
|
+
function pseudo(selector) {
|
|
98
|
+
const value = selector.value.toLowerCase();
|
|
99
|
+
|
|
100
|
+
if (selector.nodes.length === 1 && pseudoReplacements.has(value)) {
|
|
101
|
+
const first = selector.at(0);
|
|
102
|
+
const one = first.at(0);
|
|
103
|
+
|
|
104
|
+
if (first.length === 1) {
|
|
105
|
+
if (one.value === '1') {
|
|
106
|
+
selector.replaceWith(
|
|
107
|
+
parser.pseudo({
|
|
108
|
+
value: /** @type {string} */ (pseudoReplacements.get(value)),
|
|
109
|
+
})
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (one.value && one.value.toLowerCase() === 'even') {
|
|
114
|
+
one.value = '2n';
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (first.length === 3) {
|
|
119
|
+
const two = first.at(1);
|
|
120
|
+
const three = first.at(2);
|
|
121
|
+
|
|
122
|
+
if (
|
|
123
|
+
one.value &&
|
|
124
|
+
one.value.toLowerCase() === '2n' &&
|
|
125
|
+
two.value === '+' &&
|
|
126
|
+
three.value === '1'
|
|
127
|
+
) {
|
|
128
|
+
one.value = 'odd';
|
|
129
|
+
|
|
130
|
+
two.remove();
|
|
131
|
+
three.remove();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
selector.walk((child) => {
|
|
139
|
+
if (child.type === 'selector' && child.parent) {
|
|
140
|
+
const uniques = new Set();
|
|
141
|
+
child.parent.each((sibling) => {
|
|
142
|
+
const siblingStr = String(sibling);
|
|
143
|
+
|
|
144
|
+
if (!uniques.has(siblingStr)) {
|
|
145
|
+
uniques.add(siblingStr);
|
|
146
|
+
} else {
|
|
147
|
+
sibling.remove();
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
if (pseudoElements.has(value)) {
|
|
154
|
+
selector.value = selector.value.slice(1);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const tagReplacements = new Map([
|
|
159
|
+
['from', '0%'],
|
|
160
|
+
['100%', 'to'],
|
|
161
|
+
]);
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* @param {parser.Tag} selector
|
|
165
|
+
* @return {void}
|
|
166
|
+
*/
|
|
167
|
+
function tag(selector) {
|
|
168
|
+
const value = selector.value.toLowerCase();
|
|
169
|
+
|
|
170
|
+
const isSimple = selector.parent && selector.parent.nodes.length === 1;
|
|
171
|
+
// Avoid simplifying complex selectors (`entry 100% {...}`)
|
|
172
|
+
if (!isSimple) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Simplify simple selectors that have replacements (`100% {...}`)
|
|
177
|
+
if (tagReplacements.has(value)) {
|
|
178
|
+
selector.value = /** @type {string} */ (tagReplacements.get(value));
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* @param {parser.Universal} selector
|
|
184
|
+
* @return {void}
|
|
185
|
+
*/
|
|
186
|
+
function universal(selector) {
|
|
187
|
+
const next = selector.next();
|
|
188
|
+
|
|
189
|
+
if (next && next.type !== 'combinator') {
|
|
190
|
+
selector.remove();
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const reducers = new Map(
|
|
195
|
+
/** @type {[string, ((selector: parser.Node) => void)][]}*/ ([
|
|
196
|
+
['attribute', attribute],
|
|
197
|
+
['combinator', combinator],
|
|
198
|
+
['pseudo', pseudo],
|
|
199
|
+
['tag', tag],
|
|
200
|
+
['universal', universal],
|
|
201
|
+
])
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* @typedef {{ overrideBrowserslist?: string | string[] }} AutoprefixerOptions
|
|
206
|
+
* @typedef {Pick<browserslist.Options, 'stats' | 'path' | 'env'>} BrowserslistOptions
|
|
207
|
+
*/
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* @typedef {object} OwnOptions
|
|
211
|
+
* @property {boolean} [sort=true]
|
|
212
|
+
* @property {boolean} [convertToIs=true] Factor shared prefixes/suffixes in a
|
|
213
|
+
* comma-separated selector list into `:is(...)` when it produces shorter
|
|
214
|
+
* output and is safe with respect to cascade specificity. Automatically
|
|
215
|
+
* skipped when the configured browserslist target doesn't support `:is()`.
|
|
216
|
+
*/
|
|
217
|
+
|
|
218
|
+
/** @typedef {OwnOptions & AutoprefixerOptions & BrowserslistOptions} Options */
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* @type {import('postcss').PluginCreator<Options>}
|
|
222
|
+
* @param {Options} opts
|
|
223
|
+
* @return {import('postcss').Plugin}
|
|
224
|
+
*/
|
|
225
|
+
function pluginCreator(opts) {
|
|
226
|
+
const resolved = { sort: true, convertToIs: true, ...(opts || {}) };
|
|
227
|
+
return {
|
|
228
|
+
postcssPlugin: 'postcss-minify-selectors',
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* @param {import('postcss').Result & {opts: BrowserslistOptions & {file?: string}}} result
|
|
232
|
+
*/
|
|
233
|
+
prepare(result) {
|
|
234
|
+
let isFoldEnabled = resolved.convertToIs !== false;
|
|
235
|
+
if (isFoldEnabled) {
|
|
236
|
+
const { stats, env, from, file } = result.opts || {};
|
|
237
|
+
const browsers = browserslist(resolved.overrideBrowserslist, {
|
|
238
|
+
stats: resolved.stats || stats,
|
|
239
|
+
path: resolved.path || dirname(from || file || __filename),
|
|
240
|
+
env: resolved.env || env,
|
|
241
|
+
});
|
|
242
|
+
isFoldEnabled = isSupported('css-matches-pseudo', browsers);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return {
|
|
246
|
+
OnceExit(css) {
|
|
247
|
+
const cache = new Map();
|
|
248
|
+
const processor = parser((selectors) => {
|
|
249
|
+
const uniqueSelectors = new Set();
|
|
250
|
+
|
|
251
|
+
selectors.walk((sel) => {
|
|
252
|
+
// Trim whitespace around the value
|
|
253
|
+
sel.spaces.before = sel.spaces.after = '';
|
|
254
|
+
const reducer = reducers.get(sel.type);
|
|
255
|
+
if (reducer !== undefined) {
|
|
256
|
+
reducer(sel);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const toString = String(sel);
|
|
261
|
+
|
|
262
|
+
if (
|
|
263
|
+
sel.type === 'selector' &&
|
|
264
|
+
sel.parent &&
|
|
265
|
+
sel.parent.type !== 'pseudo'
|
|
266
|
+
) {
|
|
267
|
+
if (!uniqueSelectors.has(toString)) {
|
|
268
|
+
uniqueSelectors.add(toString);
|
|
269
|
+
} else {
|
|
270
|
+
sel.remove();
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
if (resolved.sort) {
|
|
275
|
+
selectors.nodes.sort();
|
|
276
|
+
}
|
|
277
|
+
if (isFoldEnabled) {
|
|
278
|
+
const folded = foldToIs(selectors);
|
|
279
|
+
if (folded !== null) {
|
|
280
|
+
selectors.nodes = parser().astSync(folded).nodes;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
css.walkRules((rule) => {
|
|
286
|
+
const selector =
|
|
287
|
+
rule.raws.selector && rule.raws.selector.value === rule.selector
|
|
288
|
+
? rule.raws.selector.raw
|
|
289
|
+
: rule.selector;
|
|
290
|
+
|
|
291
|
+
// If the selector ends with a ':' it is likely a part of a custom
|
|
292
|
+
// mixin, so just pass through.
|
|
293
|
+
if (selector[selector.length - 1] === ':') {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (cache.has(selector)) {
|
|
298
|
+
rule.selector = cache.get(selector);
|
|
299
|
+
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const optimizedSelector = processor.processSync(selector);
|
|
304
|
+
|
|
305
|
+
rule.selector = optimizedSelector;
|
|
306
|
+
cache.set(selector, optimizedSelector);
|
|
307
|
+
});
|
|
308
|
+
},
|
|
309
|
+
};
|
|
310
|
+
},
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
pluginCreator.postcss = true;
|
|
315
|
+
module.exports = pluginCreator;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const cssesc = require('cssesc');
|
|
4
|
+
/**
|
|
5
|
+
* Can unquote attribute detection from mothereff.in
|
|
6
|
+
* Copyright Mathias Bynens <https://mathiasbynens.be/>
|
|
7
|
+
* https://github.com/mathiasbynens/mothereff.in
|
|
8
|
+
*/
|
|
9
|
+
const escapes = /\\([0-9A-Fa-f]{1,6})[ \t\n\f\r]?/g;
|
|
10
|
+
const range =
|
|
11
|
+
// eslint-disable-next-line no-control-regex
|
|
12
|
+
/[\u0000-\u002c\u002e\u002f\u003A-\u0040\u005B-\u005E\u0060\u007B-\u009f]/;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {string} value
|
|
16
|
+
* @return {boolean}
|
|
17
|
+
*/
|
|
18
|
+
module.exports = function canUnquote(value) {
|
|
19
|
+
if (value === '-' || value === '') {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
value = value.replace(escapes, 'a').replace(/\\./g, 'a');
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
!(range.test(value) || /^(?:-?\d|--)/.test(value)) &&
|
|
27
|
+
// Do not remove the quotes if escaping is required.
|
|
28
|
+
cssesc(value) === value
|
|
29
|
+
);
|
|
30
|
+
};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const {
|
|
3
|
+
tokenize,
|
|
4
|
+
hasPseudoElementOrNesting,
|
|
5
|
+
hasNthChildOfClause,
|
|
6
|
+
hasUnsafeForFold,
|
|
7
|
+
specificityOfMiddle,
|
|
8
|
+
equalSpecificity,
|
|
9
|
+
joinTokens,
|
|
10
|
+
} = require('./foldToIsHelpers.js');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @param {import('postcss-selector-parser').Root} root
|
|
14
|
+
* @return {string | null}
|
|
15
|
+
*/
|
|
16
|
+
function tryFold(root) {
|
|
17
|
+
const selectors = /** @type {import('postcss-selector-parser').Selector[]} */ (
|
|
18
|
+
root.nodes.filter((n) => n.type === 'selector')
|
|
19
|
+
);
|
|
20
|
+
if (selectors.length < 2) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const tokenLists = selectors.map(tokenize);
|
|
25
|
+
|
|
26
|
+
if (tokenLists.some((t) => t.length === 0)) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let prefix = 0;
|
|
31
|
+
const minLen = Math.min(...tokenLists.map((t) => t.length));
|
|
32
|
+
while (prefix < minLen) {
|
|
33
|
+
const ref = tokenLists[0][prefix];
|
|
34
|
+
const allMatch = tokenLists.every(
|
|
35
|
+
(t) => t[prefix].kind === ref.kind && t[prefix].str === ref.str
|
|
36
|
+
);
|
|
37
|
+
if (!allMatch) {
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
prefix++;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
let suffix = 0;
|
|
44
|
+
while (suffix < minLen - prefix) {
|
|
45
|
+
const refIdx = tokenLists[0].length - 1 - suffix;
|
|
46
|
+
const ref = tokenLists[0][refIdx];
|
|
47
|
+
const allMatch = tokenLists.every((t) => {
|
|
48
|
+
const idx = t.length - 1 - suffix;
|
|
49
|
+
return idx >= prefix && t[idx].kind === ref.kind && t[idx].str === ref.str;
|
|
50
|
+
});
|
|
51
|
+
if (!allMatch) {
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
suffix++;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const firstTokens = tokenLists[0];
|
|
58
|
+
while (prefix > 0 && firstTokens[prefix - 1].kind !== 'combinator') {
|
|
59
|
+
prefix--;
|
|
60
|
+
}
|
|
61
|
+
while (suffix > 0 && firstTokens[firstTokens.length - suffix].kind !== 'combinator') {
|
|
62
|
+
suffix--;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (prefix === 0 && suffix === 0) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const middles = tokenLists.map((t) => t.slice(prefix, t.length - suffix));
|
|
70
|
+
|
|
71
|
+
if (middles.some((m) => m.length === 0)) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Each middle must be a single compound. Combinators inside change the
|
|
76
|
+
// matched element under `:is()`. See cssnano/cssnano#1786.
|
|
77
|
+
if (middles.some((m) => m.some((t) => t.kind === 'combinator'))) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
for (const middle of middles) {
|
|
82
|
+
for (const token of middle) {
|
|
83
|
+
if (hasPseudoElementOrNesting(token)) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
if (hasNthChildOfClause(token)) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
if (hasUnsafeForFold(token)) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const firstSpec = specificityOfMiddle(middles[0]);
|
|
96
|
+
for (let i = 1; i < middles.length; i++) {
|
|
97
|
+
if (!equalSpecificity(firstSpec, specificityOfMiddle(middles[i]))) {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const middleStrs = [];
|
|
103
|
+
const seen = new Set();
|
|
104
|
+
for (const m of middles) {
|
|
105
|
+
const s = joinTokens(m);
|
|
106
|
+
if (!seen.has(s)) {
|
|
107
|
+
seen.add(s);
|
|
108
|
+
middleStrs.push(s);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (middleStrs.length < 2) {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const prefixStr = joinTokens(firstTokens.slice(0, prefix));
|
|
116
|
+
const suffixStr = joinTokens(firstTokens.slice(firstTokens.length - suffix));
|
|
117
|
+
const folded = `${prefixStr}:is(${middleStrs.join(',')})${suffixStr}`;
|
|
118
|
+
|
|
119
|
+
const original = selectors.map((s) => String(s)).join(',');
|
|
120
|
+
if (folded.length >= original.length) {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return folded;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
module.exports = tryFold;
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/** @typedef {import('postcss-selector-parser').Node} Node */
|
|
4
|
+
/** @typedef {import('postcss-selector-parser').Selector} Selector */
|
|
5
|
+
/** @typedef {import('postcss-selector-parser').Pseudo} Pseudo */
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {object} Token
|
|
9
|
+
* @property {'compound'|'combinator'} kind
|
|
10
|
+
* @property {string} str
|
|
11
|
+
* @property {Node[]} [nodes]
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** @typedef {[number, number, number]} Specificity */
|
|
15
|
+
|
|
16
|
+
// Pseudo-classes accepted inside a fold middle. Limited to user-action
|
|
17
|
+
// pseudos. Anything outside this set risks the rule-list-strict vs `:is()`
|
|
18
|
+
const SAFE_PSEUDO_CLASSES = new Set([
|
|
19
|
+
':hover',
|
|
20
|
+
':focus',
|
|
21
|
+
':active',
|
|
22
|
+
':visited',
|
|
23
|
+
':link',
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @param {Selector} selector
|
|
28
|
+
* @return {Token[]}
|
|
29
|
+
*/
|
|
30
|
+
function tokenize(selector) {
|
|
31
|
+
/** @type {Token[]} */
|
|
32
|
+
const tokens = [];
|
|
33
|
+
/** @type {Node[]} */
|
|
34
|
+
let bucket = [];
|
|
35
|
+
|
|
36
|
+
const flush = () => {
|
|
37
|
+
if (bucket.length) {
|
|
38
|
+
tokens.push({
|
|
39
|
+
kind: 'compound',
|
|
40
|
+
str: bucket.map((n) => String(n)).join(''),
|
|
41
|
+
nodes: bucket,
|
|
42
|
+
});
|
|
43
|
+
bucket = [];
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
for (const node of selector.nodes) {
|
|
48
|
+
if (node.type === 'combinator') {
|
|
49
|
+
flush();
|
|
50
|
+
tokens.push({ kind: 'combinator', str: String(node) });
|
|
51
|
+
} else {
|
|
52
|
+
bucket.push(node);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
flush();
|
|
56
|
+
return tokens;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @param {Token} token
|
|
61
|
+
* @return {boolean}
|
|
62
|
+
*/
|
|
63
|
+
function hasPseudoElementOrNesting(token) {
|
|
64
|
+
if (token.kind !== 'compound' || !token.nodes) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
for (const n of token.nodes) {
|
|
68
|
+
if (n.type === 'nesting') {
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
if (n.type === 'pseudo') {
|
|
72
|
+
const v = n.value;
|
|
73
|
+
if (v.startsWith('::')) {
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
if (
|
|
77
|
+
v === ':before' ||
|
|
78
|
+
v === ':after' ||
|
|
79
|
+
v === ':first-letter' ||
|
|
80
|
+
v === ':first-line'
|
|
81
|
+
) {
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* @param {Token} token
|
|
91
|
+
* @return {boolean}
|
|
92
|
+
*/
|
|
93
|
+
function hasNthChildOfClause(token) {
|
|
94
|
+
if (token.kind !== 'compound' || !token.nodes) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
return nodesContainNthChildOfClause(token.nodes);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* @param {Node[]} nodes
|
|
102
|
+
* @return {boolean}
|
|
103
|
+
*/
|
|
104
|
+
function nodesContainNthChildOfClause(nodes) {
|
|
105
|
+
for (const n of nodes) {
|
|
106
|
+
if (n.type !== 'pseudo') {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (n.value === ':nth-child' || n.value === ':nth-last-child') {
|
|
110
|
+
for (const child of n.nodes) {
|
|
111
|
+
for (const inner of child.nodes) {
|
|
112
|
+
if (inner.type === 'tag' && inner.value === 'of') {
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
for (const child of n.nodes) {
|
|
119
|
+
if (
|
|
120
|
+
child.type === 'selector' &&
|
|
121
|
+
nodesContainNthChildOfClause(child.nodes)
|
|
122
|
+
) {
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* @param {Token} token
|
|
132
|
+
* @return {boolean}
|
|
133
|
+
*/
|
|
134
|
+
function hasUnsafeForFold(token) {
|
|
135
|
+
if (token.kind !== 'compound' || !token.nodes) {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
return nodesContainUnsafeForFold(token.nodes);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* @param {Node[]} nodes
|
|
143
|
+
* @return {boolean}
|
|
144
|
+
*/
|
|
145
|
+
function nodesContainUnsafeForFold(nodes) {
|
|
146
|
+
for (const n of nodes) {
|
|
147
|
+
const t = n.type;
|
|
148
|
+
if (t === 'class' || t === 'id') {
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (t === 'tag') {
|
|
152
|
+
if (n.namespace !== undefined && n.namespace !== null) {
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
if (t === 'attribute') {
|
|
158
|
+
if (n.namespace !== undefined && n.namespace !== null) {
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
if (n.insensitive || (n.raws && 'insensitiveFlag' in n.raws)) {
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
if (t === 'pseudo') {
|
|
167
|
+
const v = n.value;
|
|
168
|
+
if (
|
|
169
|
+
v.startsWith('::') ||
|
|
170
|
+
v === ':before' ||
|
|
171
|
+
v === ':after' ||
|
|
172
|
+
v === ':first-letter' ||
|
|
173
|
+
v === ':first-line'
|
|
174
|
+
) {
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
if (!SAFE_PSEUDO_CLASSES.has(v)) {
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
if (n.nodes && n.nodes.length > 0) {
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* @param {Node[]} nodes
|
|
192
|
+
* @return {Specificity}
|
|
193
|
+
*/
|
|
194
|
+
function specificityOf(nodes) {
|
|
195
|
+
let id = 0;
|
|
196
|
+
let cls = 0;
|
|
197
|
+
let type = 0;
|
|
198
|
+
for (const n of nodes) {
|
|
199
|
+
if (n.type === 'id') {
|
|
200
|
+
id++;
|
|
201
|
+
} else if (n.type === 'class' || n.type === 'attribute') {
|
|
202
|
+
cls++;
|
|
203
|
+
} else if (n.type === 'pseudo') {
|
|
204
|
+
const v = n.value;
|
|
205
|
+
if (v.startsWith('::')) {
|
|
206
|
+
type++;
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
if (v === ':where') {
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
if (v === ':is' || v === ':matches' || v === ':not' || v === ':has') {
|
|
213
|
+
const s = maxChildSpecificity(n);
|
|
214
|
+
id += s[0];
|
|
215
|
+
cls += s[1];
|
|
216
|
+
type += s[2];
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
if (
|
|
220
|
+
v === ':before' ||
|
|
221
|
+
v === ':after' ||
|
|
222
|
+
v === ':first-letter' ||
|
|
223
|
+
v === ':first-line'
|
|
224
|
+
) {
|
|
225
|
+
type++;
|
|
226
|
+
} else {
|
|
227
|
+
cls++;
|
|
228
|
+
}
|
|
229
|
+
} else if (n.type === 'tag') {
|
|
230
|
+
type++;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return [id, cls, type];
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* @param {Pseudo} pseudo
|
|
238
|
+
* @return {Specificity}
|
|
239
|
+
*/
|
|
240
|
+
function maxChildSpecificity(pseudo) {
|
|
241
|
+
/** @type {Specificity} */
|
|
242
|
+
let best = [0, 0, 0];
|
|
243
|
+
for (const child of pseudo.nodes) {
|
|
244
|
+
if (child.type !== 'selector') {
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
const s = specificityOf(child.nodes);
|
|
248
|
+
if (compareSpecificity(s, best) > 0) {
|
|
249
|
+
best = s;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return best;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Sums the specificity of compound tokens in a fold middle — the divergent
|
|
257
|
+
* portion of a selector list, between the shared prefix and shared suffix.
|
|
258
|
+
*
|
|
259
|
+
* @param {Token[]} middle
|
|
260
|
+
* @return {Specificity}
|
|
261
|
+
*/
|
|
262
|
+
function specificityOfMiddle(middle) {
|
|
263
|
+
let id = 0;
|
|
264
|
+
let cls = 0;
|
|
265
|
+
let type = 0;
|
|
266
|
+
for (const token of middle) {
|
|
267
|
+
if (token.kind !== 'compound' || !token.nodes) {
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
const s = specificityOf(token.nodes);
|
|
271
|
+
id += s[0];
|
|
272
|
+
cls += s[1];
|
|
273
|
+
type += s[2];
|
|
274
|
+
}
|
|
275
|
+
return [id, cls, type];
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* @param {Specificity} a
|
|
280
|
+
* @param {Specificity} b
|
|
281
|
+
* @return {number}
|
|
282
|
+
*/
|
|
283
|
+
function compareSpecificity(a, b) {
|
|
284
|
+
return a[0] - b[0] || a[1] - b[1] || a[2] - b[2];
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* @param {Specificity} a
|
|
289
|
+
* @param {Specificity} b
|
|
290
|
+
* @return {boolean}
|
|
291
|
+
*/
|
|
292
|
+
function equalSpecificity(a, b) {
|
|
293
|
+
return a[0] === b[0] && a[1] === b[1] && a[2] === b[2];
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* @param {Token[]} tokens
|
|
298
|
+
* @return {string}
|
|
299
|
+
*/
|
|
300
|
+
function joinTokens(tokens) {
|
|
301
|
+
return tokens.map((t) => t.str).join('');
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
module.exports = {
|
|
305
|
+
tokenize,
|
|
306
|
+
hasPseudoElementOrNesting,
|
|
307
|
+
hasNthChildOfClause,
|
|
308
|
+
hasUnsafeForFold,
|
|
309
|
+
specificityOf,
|
|
310
|
+
specificityOfMiddle,
|
|
311
|
+
maxChildSpecificity,
|
|
312
|
+
compareSpecificity,
|
|
313
|
+
equalSpecificity,
|
|
314
|
+
joinTokens,
|
|
315
|
+
};
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export = pluginCreator;
|
|
2
|
+
/**
|
|
3
|
+
* @typedef {{ overrideBrowserslist?: string | string[] }} AutoprefixerOptions
|
|
4
|
+
* @typedef {Pick<browserslist.Options, 'stats' | 'path' | 'env'>} BrowserslistOptions
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {object} OwnOptions
|
|
8
|
+
* @property {boolean} [sort=true]
|
|
9
|
+
* @property {boolean} [convertToIs=true] Factor shared prefixes/suffixes in a
|
|
10
|
+
* comma-separated selector list into `:is(...)` when it produces shorter
|
|
11
|
+
* output and is safe with respect to cascade specificity. Automatically
|
|
12
|
+
* skipped when the configured browserslist target doesn't support `:is()`.
|
|
13
|
+
*/
|
|
14
|
+
/** @typedef {OwnOptions & AutoprefixerOptions & BrowserslistOptions} Options */
|
|
15
|
+
/**
|
|
16
|
+
* @type {import('postcss').PluginCreator<Options>}
|
|
17
|
+
* @param {Options} opts
|
|
18
|
+
* @return {import('postcss').Plugin}
|
|
19
|
+
*/
|
|
20
|
+
declare function pluginCreator(opts: Options): import("postcss").Plugin;
|
|
21
|
+
declare namespace pluginCreator {
|
|
22
|
+
export { postcss, AutoprefixerOptions, BrowserslistOptions, OwnOptions, Options };
|
|
23
|
+
}
|
|
24
|
+
declare var postcss: true;
|
|
25
|
+
type AutoprefixerOptions = {
|
|
26
|
+
overrideBrowserslist?: string | string[];
|
|
27
|
+
};
|
|
28
|
+
type BrowserslistOptions = Pick<browserslist.Options, "stats" | "path" | "env">;
|
|
29
|
+
type OwnOptions = {
|
|
30
|
+
sort?: boolean | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* Factor shared prefixes/suffixes in a
|
|
33
|
+
* comma-separated selector list into `:is(...)` when it produces shorter
|
|
34
|
+
* output and is safe with respect to cascade specificity. Automatically
|
|
35
|
+
* skipped when the configured browserslist target doesn't support `:is()`.
|
|
36
|
+
*/
|
|
37
|
+
convertToIs?: boolean | undefined;
|
|
38
|
+
};
|
|
39
|
+
type Options = OwnOptions & AutoprefixerOptions & BrowserslistOptions;
|
|
40
|
+
import browserslist = require("browserslist");
|
|
41
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.js"],"names":[],"mappings":";AA2MA;;;GAGG;AAEH;;;;;;;GAOG;AAEH,gFAAgF;AAEhF;;;;GAIG;AACH,qCAHW,OAAO,GACN,OAAO,SAAS,EAAE,MAAM,CAyFnC;;;;;2BA3GY;IAAE,oBAAoB,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;CAAE;2BAC5C,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,KAAK,CAAC;;;;;;;;;;;eAYnD,UAAU,GAAG,mBAAmB,GAAG,mBAAmB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canUnquote.d.ts","sourceRoot":"","sources":["../../src/lib/canUnquote.js"],"names":[],"mappings":"AAiBiB,iCAHN,MAAM,GACL,OAAO,CAclB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"foldToIs.d.ts","sourceRoot":"","sources":["../../src/lib/foldToIs.js"],"names":[],"mappings":";AAWA;;;GAGG;AACH,+BAHW,OAAO,yBAAyB,EAAE,IAAI,GACrC,MAAM,GAAG,IAAI,CA+GxB"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export type Node = import("postcss-selector-parser").Node;
|
|
2
|
+
export type Selector = import("postcss-selector-parser").Selector;
|
|
3
|
+
export type Pseudo = import("postcss-selector-parser").Pseudo;
|
|
4
|
+
export type Token = {
|
|
5
|
+
kind: "compound" | "combinator";
|
|
6
|
+
str: string;
|
|
7
|
+
nodes?: import("postcss-selector-parser").Node[] | undefined;
|
|
8
|
+
};
|
|
9
|
+
export type Specificity = [number, number, number];
|
|
10
|
+
/**
|
|
11
|
+
* @param {Selector} selector
|
|
12
|
+
* @return {Token[]}
|
|
13
|
+
*/
|
|
14
|
+
export function tokenize(selector: Selector): Token[];
|
|
15
|
+
/**
|
|
16
|
+
* @param {Token} token
|
|
17
|
+
* @return {boolean}
|
|
18
|
+
*/
|
|
19
|
+
export function hasPseudoElementOrNesting(token: Token): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* @param {Token} token
|
|
22
|
+
* @return {boolean}
|
|
23
|
+
*/
|
|
24
|
+
export function hasNthChildOfClause(token: Token): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* @param {Token} token
|
|
27
|
+
* @return {boolean}
|
|
28
|
+
*/
|
|
29
|
+
export function hasUnsafeForFold(token: Token): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* @param {Node[]} nodes
|
|
32
|
+
* @return {Specificity}
|
|
33
|
+
*/
|
|
34
|
+
export function specificityOf(nodes: Node[]): Specificity;
|
|
35
|
+
/**
|
|
36
|
+
* Sums the specificity of compound tokens in a fold middle — the divergent
|
|
37
|
+
* portion of a selector list, between the shared prefix and shared suffix.
|
|
38
|
+
*
|
|
39
|
+
* @param {Token[]} middle
|
|
40
|
+
* @return {Specificity}
|
|
41
|
+
*/
|
|
42
|
+
export function specificityOfMiddle(middle: Token[]): Specificity;
|
|
43
|
+
/**
|
|
44
|
+
* @param {Pseudo} pseudo
|
|
45
|
+
* @return {Specificity}
|
|
46
|
+
*/
|
|
47
|
+
export function maxChildSpecificity(pseudo: Pseudo): Specificity;
|
|
48
|
+
/**
|
|
49
|
+
* @param {Specificity} a
|
|
50
|
+
* @param {Specificity} b
|
|
51
|
+
* @return {number}
|
|
52
|
+
*/
|
|
53
|
+
export function compareSpecificity(a: Specificity, b: Specificity): number;
|
|
54
|
+
/**
|
|
55
|
+
* @param {Specificity} a
|
|
56
|
+
* @param {Specificity} b
|
|
57
|
+
* @return {boolean}
|
|
58
|
+
*/
|
|
59
|
+
export function equalSpecificity(a: Specificity, b: Specificity): boolean;
|
|
60
|
+
/**
|
|
61
|
+
* @param {Token[]} tokens
|
|
62
|
+
* @return {string}
|
|
63
|
+
*/
|
|
64
|
+
export function joinTokens(tokens: Token[]): string;
|
|
65
|
+
//# sourceMappingURL=foldToIsHelpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"foldToIsHelpers.d.ts","sourceRoot":"","sources":["../../src/lib/foldToIsHelpers.js"],"names":[],"mappings":"mBAEc,OAAO,yBAAyB,EAAE,IAAI;uBACtC,OAAO,yBAAyB,EAAE,QAAQ;qBAC1C,OAAO,yBAAyB,EAAE,MAAM;;UAIxC,UAAU,GAAC,YAAY;SACvB,MAAM;;;0BAIN,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;AAYtC;;;GAGG;AACH,mCAHW,QAAQ,GACP,KAAK,EAAE,CA6BlB;AAED;;;GAGG;AACH,iDAHW,KAAK,GACJ,OAAO,CA0BlB;AAED;;;GAGG;AACH,2CAHW,KAAK,GACJ,OAAO,CAOlB;AAgCD;;;GAGG;AACH,wCAHW,KAAK,GACJ,OAAO,CAOlB;AAmDD;;;GAGG;AACH,qCAHW,IAAI,EAAE,GACL,WAAW,CA0CtB;AAqBD;;;;;;GAMG;AACH,4CAHW,KAAK,EAAE,GACN,WAAW,CAgBtB;AAxCD;;;GAGG;AACH,4CAHW,MAAM,GACL,WAAW,CAetB;AAyBD;;;;GAIG;AACH,sCAJW,WAAW,KACX,WAAW,GACV,MAAM,CAIjB;AAED;;;;GAIG;AACH,oCAJW,WAAW,KACX,WAAW,GACV,OAAO,CAIlB;AAED;;;GAGG;AACH,mCAHW,KAAK,EAAE,GACN,MAAM,CAIjB"}
|