stylehacks 9.0.3 → 9.0.5
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/package.json +3 -3
- package/src/lib/computeBrowsersToSupport.js +1 -3
- package/src/lib/selectorScanner.js +269 -0
- package/src/plugins/bodyEmpty.js +5 -27
- package/src/plugins/htmlCombinatorCommentBody.js +8 -30
- package/src/plugins/htmlFirstChild.js +7 -27
- package/src/plugins/important.js +1 -1
- package/src/plugins/leadingUnderscore.js +1 -1
- package/src/plugins/starHtml.js +4 -27
- package/types/lib/computeBrowsersToSupport.d.ts.map +1 -1
- package/types/lib/selectorScanner.d.ts +26 -0
- package/types/lib/selectorScanner.d.ts.map +1 -0
- package/types/plugins/bodyEmpty.d.ts +2 -8
- package/types/plugins/bodyEmpty.d.ts.map +1 -1
- package/types/plugins/htmlCombinatorCommentBody.d.ts +2 -7
- package/types/plugins/htmlCombinatorCommentBody.d.ts.map +1 -1
- package/types/plugins/htmlFirstChild.d.ts +2 -8
- package/types/plugins/htmlFirstChild.d.ts.map +1 -1
- package/types/plugins/index.d.ts +2 -2
- package/types/plugins/starHtml.d.ts +0 -6
- package/types/plugins/starHtml.d.ts.map +1 -1
- package/src/dictionary/tags.js +0 -3
- package/src/exists.js +0 -12
- package/types/dictionary/tags.d.ts +0 -4
- package/types/dictionary/tags.d.ts.map +0 -1
- package/types/exists.d.ts +0 -9
- package/types/exists.d.ts.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stylehacks",
|
|
3
|
-
"version": "9.0.
|
|
3
|
+
"version": "9.0.5",
|
|
4
4
|
"description": "Detect/remove browser hacks from CSS files.",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
"url": "git+https://github.com/cssnano/cssnano.git"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"
|
|
48
|
-
"
|
|
47
|
+
"@csstools/css-tokenizer": "^4.0.2",
|
|
48
|
+
"browserslist": "^4.29.1"
|
|
49
49
|
},
|
|
50
50
|
"bugs": {
|
|
51
51
|
"url": "https://github.com/cssnano/cssnano/issues"
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isTokenComment,
|
|
3
|
+
isTokenDelim,
|
|
4
|
+
isTokenIdent,
|
|
5
|
+
isTokenWhitespace,
|
|
6
|
+
tokenize,
|
|
7
|
+
TokenType,
|
|
8
|
+
} from '@csstools/css-tokenizer';
|
|
9
|
+
|
|
10
|
+
/** @typedef {import('@csstools/css-tokenizer').CSSToken} CSSToken */
|
|
11
|
+
|
|
12
|
+
const closing = new Map([
|
|
13
|
+
[TokenType.Function, TokenType.CloseParen],
|
|
14
|
+
[TokenType.OpenParen, TokenType.CloseParen],
|
|
15
|
+
[TokenType.OpenSquare, TokenType.CloseSquare],
|
|
16
|
+
[TokenType.OpenCurly, TokenType.CloseCurly],
|
|
17
|
+
]);
|
|
18
|
+
|
|
19
|
+
const matchers = {
|
|
20
|
+
'star-html': matchStarHtml,
|
|
21
|
+
'html-first-child': matchHtmlFirstChild,
|
|
22
|
+
'html-comment-body': matchHtmlCommentBody,
|
|
23
|
+
'body-empty': matchBodyEmpty,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/** @typedef {'star-html'|'html-first-child'|'html-comment-body'|'body-empty'} SelectorHack */
|
|
27
|
+
/** @typedef {Record<SelectorHack, string[]>} SelectorHackResults */
|
|
28
|
+
|
|
29
|
+
/** @type {WeakMap<import('postcss').Rule, {selector: string, raw: string | undefined, selectorResults: SelectorHackResults, rawResults: SelectorHackResults | undefined}>} */
|
|
30
|
+
const ruleCache = new WeakMap();
|
|
31
|
+
|
|
32
|
+
/** @type {SelectorHack[]} */
|
|
33
|
+
const hackKinds = [
|
|
34
|
+
'star-html',
|
|
35
|
+
'html-first-child',
|
|
36
|
+
'html-comment-body',
|
|
37
|
+
'body-empty',
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Tokenize selectors and split them at balanced, top-level commas. The
|
|
42
|
+
* scanner deliberately has no selector grammar: the four legacy tuple
|
|
43
|
+
* layouts below are the only grammar it knows about.
|
|
44
|
+
*
|
|
45
|
+
* @param {string} source
|
|
46
|
+
* @param {SelectorHack} hack
|
|
47
|
+
* @return {string[]}
|
|
48
|
+
*/
|
|
49
|
+
export function findSelectorHacks(source, hack) {
|
|
50
|
+
return scanSelectorHacks(source)[hack];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Scan each selector source once and share all four detector results.
|
|
55
|
+
*
|
|
56
|
+
* @param {string} source
|
|
57
|
+
* @return {SelectorHackResults}
|
|
58
|
+
*/
|
|
59
|
+
function scanSelectorHacks(source) {
|
|
60
|
+
/** @type {SelectorHackResults} */
|
|
61
|
+
const result = {
|
|
62
|
+
'star-html': [],
|
|
63
|
+
'html-first-child': [],
|
|
64
|
+
'html-comment-body': [],
|
|
65
|
+
'body-empty': [],
|
|
66
|
+
};
|
|
67
|
+
const alternatives = splitAlternatives(source);
|
|
68
|
+
if (alternatives === undefined) return result;
|
|
69
|
+
|
|
70
|
+
for (const alternative of alternatives) {
|
|
71
|
+
for (const hack of hackKinds) {
|
|
72
|
+
if (matchers[hack](alternative.tokens)) {
|
|
73
|
+
result[hack].push(alternative.source);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Get selector hacks for a PostCSS rule. The parsed selector and raw selector
|
|
82
|
+
* are cached independently because the comment-body detector intentionally
|
|
83
|
+
* reads the raw selector source.
|
|
84
|
+
*
|
|
85
|
+
* @param {import('postcss').Rule} rule
|
|
86
|
+
* @return {{selector: SelectorHackResults, raw: SelectorHackResults | undefined}}
|
|
87
|
+
*/
|
|
88
|
+
export function findRuleSelectorHacks(rule) {
|
|
89
|
+
const selector = rule.selector;
|
|
90
|
+
const raw = rule.raws.selector?.raw;
|
|
91
|
+
const cached = ruleCache.get(rule);
|
|
92
|
+
if (cached && cached.selector === selector && cached.raw === raw) {
|
|
93
|
+
return { selector: cached.selectorResults, raw: cached.rawResults };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const selectorResults = scanSelectorHacks(selector);
|
|
97
|
+
const rawResults = raw === undefined ? undefined : scanSelectorHacks(raw);
|
|
98
|
+
ruleCache.set(rule, { selector, raw, selectorResults, rawResults });
|
|
99
|
+
return { selector: selectorResults, raw: rawResults };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** @param {string} source @return {{source: string, tokens: CSSToken[]}[] | undefined} */
|
|
103
|
+
function splitAlternatives(source) {
|
|
104
|
+
/** @type {{source: string, tokens: CSSToken[]}[]} */
|
|
105
|
+
const result = [];
|
|
106
|
+
/** @type {CSSToken[]} */
|
|
107
|
+
let tokens = [];
|
|
108
|
+
/** @type {TokenType[]} */
|
|
109
|
+
const stack = [];
|
|
110
|
+
let start = 0;
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
for (const token of tokenize({ css: source })) {
|
|
114
|
+
const type = token[0];
|
|
115
|
+
if (type === TokenType.EOF) break;
|
|
116
|
+
|
|
117
|
+
if (type === TokenType.String && !isClosedString(token[1])) {
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
if (isTokenComment(token) && !token[1].endsWith('*/')) {
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const expected = closing.get(type);
|
|
125
|
+
if (expected !== undefined) {
|
|
126
|
+
stack.push(expected);
|
|
127
|
+
} else if (isClosing(type)) {
|
|
128
|
+
if (stack.pop() !== type) return undefined;
|
|
129
|
+
} else if (type === TokenType.Comma && stack.length === 0) {
|
|
130
|
+
result.push({ source: source.slice(start, token[2]), tokens });
|
|
131
|
+
start = token[3] + 1;
|
|
132
|
+
tokens = [];
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
tokens.push(token);
|
|
136
|
+
}
|
|
137
|
+
} catch {
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (stack.length !== 0) return undefined;
|
|
142
|
+
result.push({ source: source.slice(start), tokens });
|
|
143
|
+
return result;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** @param {TokenType} type @return {boolean} */
|
|
147
|
+
function isClosing(type) {
|
|
148
|
+
return (
|
|
149
|
+
type === TokenType.CloseParen ||
|
|
150
|
+
type === TokenType.CloseSquare ||
|
|
151
|
+
type === TokenType.CloseCurly
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** @param {string} value @return {boolean} */
|
|
156
|
+
function isClosedString(value) {
|
|
157
|
+
if (value.length < 2 || (value[0] !== '"' && value[0] !== "'")) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
let backslashes = 0;
|
|
161
|
+
for (
|
|
162
|
+
let index = value.length - 2;
|
|
163
|
+
index >= 0 && value[index] === '\\';
|
|
164
|
+
index--
|
|
165
|
+
) {
|
|
166
|
+
backslashes++;
|
|
167
|
+
}
|
|
168
|
+
return value.at(-1) === value[0] && backslashes % 2 === 0;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** @param {CSSToken[]} tokens @return {boolean} */
|
|
172
|
+
function matchStarHtml(tokens) {
|
|
173
|
+
let index = 0;
|
|
174
|
+
while (isWhitespace(tokens[index])) index++;
|
|
175
|
+
return (
|
|
176
|
+
delimiter(tokens[index++], '*') &&
|
|
177
|
+
requiredWhitespace(tokens, index++) &&
|
|
178
|
+
identifier(tokens[index++], 'html') &&
|
|
179
|
+
requiredWhitespace(tokens, index++) &&
|
|
180
|
+
hasTrailingContent(tokens, index)
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** @param {CSSToken[]} tokens @return {boolean} */
|
|
185
|
+
function matchHtmlFirstChild(tokens) {
|
|
186
|
+
return matchPseudo(tokens, 'html', 'first-child');
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** @param {CSSToken[]} tokens @return {boolean} */
|
|
190
|
+
function matchBodyEmpty(tokens) {
|
|
191
|
+
return matchPseudo(tokens, 'body', 'empty');
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** @param {CSSToken[]} tokens @param {string} element @param {string} pseudo @return {boolean} */
|
|
195
|
+
function matchPseudo(tokens, element, pseudo) {
|
|
196
|
+
let index = 0;
|
|
197
|
+
while (isWhitespace(tokens[index])) index++;
|
|
198
|
+
return (
|
|
199
|
+
identifier(tokens[index++], element) &&
|
|
200
|
+
delimiter(tokens[index++], ':') &&
|
|
201
|
+
identifier(tokens[index++], pseudo) &&
|
|
202
|
+
requiredWhitespace(tokens, index++) &&
|
|
203
|
+
hasTrailingContent(tokens, index)
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/** @param {CSSToken[]} tokens @return {boolean} */
|
|
208
|
+
function matchHtmlCommentBody(tokens) {
|
|
209
|
+
let index = 0;
|
|
210
|
+
while (isCombinatorWhitespace(tokens[index])) index++;
|
|
211
|
+
if (!identifier(tokens[index++], 'html')) return false;
|
|
212
|
+
while (isCombinatorWhitespace(tokens[index])) index++;
|
|
213
|
+
if (!delimiter(tokens[index], '>') && !delimiter(tokens[index], '~')) {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
index++;
|
|
217
|
+
while (isWhitespace(tokens[index])) index++;
|
|
218
|
+
if (!isComment(tokens[index++])) return false;
|
|
219
|
+
if (!requiredWhitespace(tokens, index++)) return false;
|
|
220
|
+
while (isCombinatorWhitespace(tokens[index])) index++;
|
|
221
|
+
return (
|
|
222
|
+
identifier(tokens[index++], 'body') &&
|
|
223
|
+
requiredWhitespace(tokens, index++) &&
|
|
224
|
+
hasTrailingContent(tokens, index)
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/** @param {CSSToken | undefined} token @param {string} value @return {boolean} */
|
|
229
|
+
function identifier(token, value) {
|
|
230
|
+
return isTokenIdent(token) && token[4].value.toLowerCase() === value;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/** @param {CSSToken | undefined} token @param {string} value @return {boolean} */
|
|
234
|
+
function delimiter(token, value) {
|
|
235
|
+
return (
|
|
236
|
+
(isTokenDelim(token) || token?.[0] === TokenType.Colon) &&
|
|
237
|
+
token[1] === value
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/** @param {CSSToken[]} tokens @param {number} index @return {boolean} */
|
|
242
|
+
function requiredWhitespace(tokens, index) {
|
|
243
|
+
return isWhitespace(tokens[index]);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/** @param {CSSToken[]} tokens @param {number} index @return {boolean} */
|
|
247
|
+
function hasTrailingContent(tokens, index) {
|
|
248
|
+
for (let cursor = index; cursor < tokens.length; cursor++) {
|
|
249
|
+
if (!isWhitespace(tokens[cursor]) && !isComment(tokens[cursor])) {
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/** @param {CSSToken | undefined} token @return {boolean} */
|
|
257
|
+
function isWhitespace(token) {
|
|
258
|
+
return token !== undefined && isTokenWhitespace(token);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/** @param {CSSToken | undefined} token @return {boolean} */
|
|
262
|
+
function isCombinatorWhitespace(token) {
|
|
263
|
+
return isWhitespace(token) || isComment(token);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/** @param {CSSToken | undefined} token @return {boolean} */
|
|
267
|
+
function isComment(token) {
|
|
268
|
+
return token !== undefined && isTokenComment(token);
|
|
269
|
+
}
|
package/src/plugins/bodyEmpty.js
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
import parser from 'postcss-selector-parser';
|
|
2
|
-
import exists from '../exists.js';
|
|
3
1
|
import isMixin from '../isMixin.js';
|
|
4
2
|
import BasePlugin from '../plugin.js';
|
|
3
|
+
import { findRuleSelectorHacks } from '../lib/selectorScanner.js';
|
|
5
4
|
import { FF_2 } from '../dictionary/browsers.js';
|
|
6
5
|
import { SELECTOR } from '../dictionary/identifiers.js';
|
|
7
6
|
import { RULE } from '../dictionary/postcss.js';
|
|
8
|
-
import { BODY } from '../dictionary/tags.js';
|
|
9
7
|
|
|
10
8
|
export default (class BodyEmpty extends BasePlugin {
|
|
11
|
-
/** @param {import('postcss').Result} result */
|
|
9
|
+
/** @param {import('postcss').Result=} result */
|
|
12
10
|
constructor(result) {
|
|
13
11
|
super([FF_2], [RULE], result);
|
|
14
12
|
}
|
|
@@ -21,28 +19,8 @@ export default (class BodyEmpty extends BasePlugin {
|
|
|
21
19
|
if (isMixin(rule)) {
|
|
22
20
|
return;
|
|
23
21
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* @param {import('postcss').Rule} rule
|
|
29
|
-
* @return {parser.SyncProcessor<void>}
|
|
30
|
-
*/
|
|
31
|
-
analyse(rule) {
|
|
32
|
-
return (selectors) => {
|
|
33
|
-
selectors.each((selector) => {
|
|
34
|
-
if (
|
|
35
|
-
exists(selector, 0, BODY) &&
|
|
36
|
-
exists(selector, 1, ':empty') &&
|
|
37
|
-
exists(selector, 2, ' ') &&
|
|
38
|
-
selector.at(3)
|
|
39
|
-
) {
|
|
40
|
-
this.push(rule, {
|
|
41
|
-
identifier: SELECTOR,
|
|
42
|
-
hack: selector.toString(),
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
};
|
|
22
|
+
for (const hack of findRuleSelectorHacks(rule).selector['body-empty']) {
|
|
23
|
+
this.push(rule, { identifier: SELECTOR, hack });
|
|
24
|
+
}
|
|
47
25
|
}
|
|
48
26
|
});
|
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
import parser from 'postcss-selector-parser';
|
|
2
|
-
import exists from '../exists.js';
|
|
3
1
|
import isMixin from '../isMixin.js';
|
|
4
2
|
import BasePlugin from '../plugin.js';
|
|
3
|
+
import { findRuleSelectorHacks } from '../lib/selectorScanner.js';
|
|
5
4
|
import { IE_5_5, IE_6, IE_7 } from '../dictionary/browsers.js';
|
|
6
5
|
import { SELECTOR } from '../dictionary/identifiers.js';
|
|
7
6
|
import { RULE } from '../dictionary/postcss.js';
|
|
8
|
-
import { BODY, HTML } from '../dictionary/tags.js';
|
|
9
7
|
|
|
10
8
|
export default (class HtmlCombinatorCommentBody extends BasePlugin {
|
|
11
|
-
/** @param {import('postcss').Result} result */
|
|
9
|
+
/** @param {import('postcss').Result=} result */
|
|
12
10
|
constructor(result) {
|
|
13
11
|
super([IE_5_5, IE_6, IE_7], [RULE], result);
|
|
14
12
|
}
|
|
@@ -22,32 +20,12 @@ export default (class HtmlCombinatorCommentBody extends BasePlugin {
|
|
|
22
20
|
return;
|
|
23
21
|
}
|
|
24
22
|
if (rule.raws.selector && rule.raws.selector.raw) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
/** @param {import('postcss').Rule} rule
|
|
30
|
-
* @return {parser.SyncProcessor<void>}
|
|
31
|
-
*/
|
|
32
|
-
analyse(rule) {
|
|
33
|
-
return (selectors) => {
|
|
34
|
-
selectors.each((selector) => {
|
|
35
|
-
if (
|
|
36
|
-
exists(selector, 0, HTML) &&
|
|
37
|
-
(exists(selector, 1, '>') || exists(selector, 1, '~')) &&
|
|
38
|
-
selector.at(2) &&
|
|
39
|
-
selector.at(2).type === 'comment' &&
|
|
40
|
-
exists(selector, 3, ' ') &&
|
|
41
|
-
exists(selector, 4, BODY) &&
|
|
42
|
-
exists(selector, 5, ' ') &&
|
|
43
|
-
selector.at(6)
|
|
44
|
-
) {
|
|
45
|
-
this.push(rule, {
|
|
46
|
-
identifier: SELECTOR,
|
|
47
|
-
hack: selector.toString(),
|
|
48
|
-
});
|
|
23
|
+
const rawResults = findRuleSelectorHacks(rule).raw;
|
|
24
|
+
if (rawResults) {
|
|
25
|
+
for (const hack of rawResults['html-comment-body']) {
|
|
26
|
+
this.push(rule, { identifier: SELECTOR, hack });
|
|
49
27
|
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
52
30
|
}
|
|
53
31
|
});
|
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
import parser from 'postcss-selector-parser';
|
|
2
|
-
import exists from '../exists.js';
|
|
3
1
|
import isMixin from '../isMixin.js';
|
|
4
2
|
import BasePlugin from '../plugin.js';
|
|
3
|
+
import { findRuleSelectorHacks } from '../lib/selectorScanner.js';
|
|
5
4
|
import { OP_9 } from '../dictionary/browsers.js';
|
|
6
5
|
import { SELECTOR } from '../dictionary/identifiers.js';
|
|
7
6
|
import { RULE } from '../dictionary/postcss.js';
|
|
8
|
-
import { HTML } from '../dictionary/tags.js';
|
|
9
7
|
|
|
10
8
|
export default (class HtmlFirstChild extends BasePlugin {
|
|
11
|
-
/** @param {import('postcss').Result} result */
|
|
9
|
+
/** @param {import('postcss').Result=} result */
|
|
12
10
|
constructor(result) {
|
|
13
11
|
super([OP_9], [RULE], result);
|
|
14
12
|
}
|
|
@@ -22,28 +20,10 @@ export default (class HtmlFirstChild extends BasePlugin {
|
|
|
22
20
|
return;
|
|
23
21
|
}
|
|
24
22
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
* @return {parser.SyncProcessor<void>}
|
|
31
|
-
*/
|
|
32
|
-
analyse(rule) {
|
|
33
|
-
return (selectors) => {
|
|
34
|
-
selectors.each((selector) => {
|
|
35
|
-
if (
|
|
36
|
-
exists(selector, 0, HTML) &&
|
|
37
|
-
exists(selector, 1, ':first-child') &&
|
|
38
|
-
exists(selector, 2, ' ') &&
|
|
39
|
-
selector.at(3)
|
|
40
|
-
) {
|
|
41
|
-
this.push(rule, {
|
|
42
|
-
identifier: SELECTOR,
|
|
43
|
-
hack: selector.toString(),
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
};
|
|
23
|
+
for (const hack of findRuleSelectorHacks(rule).selector[
|
|
24
|
+
'html-first-child'
|
|
25
|
+
]) {
|
|
26
|
+
this.push(rule, { identifier: SELECTOR, hack });
|
|
27
|
+
}
|
|
48
28
|
}
|
|
49
29
|
});
|
package/src/plugins/important.js
CHANGED
|
@@ -12,7 +12,7 @@ export default (class Important extends BasePlugin {
|
|
|
12
12
|
* @return {void}
|
|
13
13
|
*/
|
|
14
14
|
detect(decl) {
|
|
15
|
-
const match = decl.value.match(/!\w/);
|
|
15
|
+
const match = decl.value.match(/!\w/v);
|
|
16
16
|
if (match && match.index) {
|
|
17
17
|
const hack = decl.value.substr(match.index, decl.value.length - 1);
|
|
18
18
|
this.push(decl, {
|
|
@@ -3,7 +3,7 @@ import { IE_6 } from '../dictionary/browsers.js';
|
|
|
3
3
|
import { PROPERTY } from '../dictionary/identifiers.js';
|
|
4
4
|
import { DECL } from '../dictionary/postcss.js';
|
|
5
5
|
|
|
6
|
-
const vendorPrefixRegex = /^(-\w+-)
|
|
6
|
+
const vendorPrefixRegex = /^(-\w+-)/v;
|
|
7
7
|
/**
|
|
8
8
|
* @param {string} prop
|
|
9
9
|
* @return {string}
|
package/src/plugins/starHtml.js
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import parser from 'postcss-selector-parser';
|
|
2
|
-
import exists from '../exists.js';
|
|
3
1
|
import isMixin from '../isMixin.js';
|
|
4
2
|
import BasePlugin from '../plugin.js';
|
|
3
|
+
import { findRuleSelectorHacks } from '../lib/selectorScanner.js';
|
|
5
4
|
import { IE_5_5, IE_6 } from '../dictionary/browsers.js';
|
|
6
5
|
import { SELECTOR } from '../dictionary/identifiers.js';
|
|
7
6
|
import { RULE } from '../dictionary/postcss.js';
|
|
8
|
-
import { HTML } from '../dictionary/tags.js';
|
|
9
7
|
|
|
10
8
|
export default (class StarHtml extends BasePlugin {
|
|
11
9
|
/** @param {import('postcss').Result=} result */
|
|
@@ -21,29 +19,8 @@ export default (class StarHtml extends BasePlugin {
|
|
|
21
19
|
if (isMixin(rule)) {
|
|
22
20
|
return;
|
|
23
21
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* @param {import('postcss').Rule} rule
|
|
29
|
-
* @return {parser.SyncProcessor<void>}
|
|
30
|
-
*/
|
|
31
|
-
analyse(rule) {
|
|
32
|
-
return (selectors) => {
|
|
33
|
-
selectors.each((selector) => {
|
|
34
|
-
if (
|
|
35
|
-
exists(selector, 0, '*') &&
|
|
36
|
-
exists(selector, 1, ' ') &&
|
|
37
|
-
exists(selector, 2, HTML) &&
|
|
38
|
-
exists(selector, 3, ' ') &&
|
|
39
|
-
selector.at(4)
|
|
40
|
-
) {
|
|
41
|
-
this.push(rule, {
|
|
42
|
-
identifier: SELECTOR,
|
|
43
|
-
hack: selector.toString(),
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
};
|
|
22
|
+
for (const hack of findRuleSelectorHacks(rule).selector['star-html']) {
|
|
23
|
+
this.push(rule, { identifier: SELECTOR, hack });
|
|
24
|
+
}
|
|
48
25
|
}
|
|
49
26
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"computeBrowsersToSupport.d.ts","sourceRoot":"","sources":["../../src/lib/computeBrowsersToSupport.js"],"names":[],"mappings":"AACA,OAAO,YAAY,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"computeBrowsersToSupport.d.ts","sourceRoot":"","sources":["../../src/lib/computeBrowsersToSupport.js"],"names":[],"mappings":"AACA,OAAO,YAAY,MAAM,cAAc,CAAC;AAGxC;;;;;;;;GAQG;AACH,iBAAS,wBAAwB,CAAC,OAAO,EAP9B;IAAC,oBAAoB,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC;IAAC,KAAK,CAAC,EAAE,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAAC,IAAI,CAAC,EAAE,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAAC,GAAG,CAAC,EAAE,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;CAOtI,EAAE,KAAK,EANrC,YAAY,CAAC,OAAO,CAAC,OAAO,CAMS,EAAE,IAAI,EAL3C,MAAM,GAAC,SAKoC,EAAE,IAAI,AAJzD,CACA,EADQ,MAIiD,EAAE,GAAG,AAH9D,CACA,EADQ,YAAY,CAAC,OAAO,CAAC,KAAK,CAG4B,GAFpD,MAAM,EAAE,CASpB;eAEc,wBAAwB"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export type CSSToken = import('@csstools/css-tokenizer').CSSToken;
|
|
2
|
+
export type SelectorHack = 'star-html' | 'html-first-child' | 'html-comment-body' | 'body-empty';
|
|
3
|
+
export type SelectorHackResults = Record<SelectorHack, string[]>;
|
|
4
|
+
/**
|
|
5
|
+
* Tokenize selectors and split them at balanced, top-level commas. The
|
|
6
|
+
* scanner deliberately has no selector grammar: the four legacy tuple
|
|
7
|
+
* layouts below are the only grammar it knows about.
|
|
8
|
+
*
|
|
9
|
+
* @param {string} source
|
|
10
|
+
* @param {SelectorHack} hack
|
|
11
|
+
* @return {string[]}
|
|
12
|
+
*/
|
|
13
|
+
export declare function findSelectorHacks(source: string, hack: SelectorHack): string[];
|
|
14
|
+
/**
|
|
15
|
+
* Get selector hacks for a PostCSS rule. The parsed selector and raw selector
|
|
16
|
+
* are cached independently because the comment-body detector intentionally
|
|
17
|
+
* reads the raw selector source.
|
|
18
|
+
*
|
|
19
|
+
* @param {import('postcss').Rule} rule
|
|
20
|
+
* @return {{selector: SelectorHackResults, raw: SelectorHackResults | undefined}}
|
|
21
|
+
*/
|
|
22
|
+
export declare function findRuleSelectorHacks(rule: import('postcss').Rule): {
|
|
23
|
+
selector: SelectorHackResults;
|
|
24
|
+
raw: SelectorHackResults | undefined;
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=selectorScanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selectorScanner.d.ts","sourceRoot":"","sources":["../../src/lib/selectorScanner.js"],"names":[],"mappings":"AASI,YAAsD,QAAQ,GAApD,OAAO,yBAAyB,EAAE,QAAQ,CAAU;AAgB9D,YAA2E,YAAY,GAA7E,WAAW,GAAC,kBAAkB,GAAC,mBAAmB,GAAC,YAAY,CAAc;AACvF,YAA0C,mBAAmB,GAAnD,MAAM,CAAC,YAAY,EAAE,MAAM,EAAE,CAAC,CAAqB;AAajE;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAJ7B,MAI6B,EAAE,IAAI,EAHnC,YAGmC,GAFlC,MAAM,EAAE,CAInB;AA6BD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAH/B,OAAO,SAAS,EAAE,IAGa,GAF9B;IAAC,QAAQ,EAAE,mBAAmB,CAAC;IAAC,GAAG,EAAE,mBAAmB,GAAG,SAAS,CAAA;CAAC,CAchF"}
|
|
@@ -1,18 +1,12 @@
|
|
|
1
|
-
import parser from 'postcss-selector-parser';
|
|
2
1
|
import BasePlugin from '../plugin.js';
|
|
3
2
|
export default BodyEmpty;
|
|
4
3
|
declare class BodyEmpty extends BasePlugin {
|
|
5
|
-
/** @param {import('postcss').Result} result */
|
|
6
|
-
constructor(result
|
|
4
|
+
/** @param {import('postcss').Result=} result */
|
|
5
|
+
constructor(result?: import('postcss').Result | undefined);
|
|
7
6
|
/**
|
|
8
7
|
* @param {import('postcss').Rule} rule
|
|
9
8
|
* @return {void}
|
|
10
9
|
*/
|
|
11
10
|
detect(rule: import('postcss').Rule): void;
|
|
12
|
-
/**
|
|
13
|
-
* @param {import('postcss').Rule} rule
|
|
14
|
-
* @return {parser.SyncProcessor<void>}
|
|
15
|
-
*/
|
|
16
|
-
analyse(rule: import('postcss').Rule): parser.SyncProcessor<void>;
|
|
17
11
|
}
|
|
18
12
|
//# sourceMappingURL=bodyEmpty.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bodyEmpty.d.ts","sourceRoot":"","sources":["../../src/plugins/bodyEmpty.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"bodyEmpty.d.ts","sourceRoot":"","sources":["../../src/plugins/bodyEmpty.js"],"names":[],"mappings":"AACA,OAAO,UAAU,MAAM,cAAc,CAAC;;uBAMN,SAAQ,UAAU;IAChD,gDAAgD;IAChD,YAAY,MAAM,AADd,CAA0C,EAAlC,OAAO,SAAS,EAAE,MAAM,YAClB,EAEjB;IAED;;;OAGG;IACH,MAAM,CAAC,IAAI,EAHA,OAAO,SAAS,EAAE,IAGlB,GAFC,IAAI,CASf"}
|
|
@@ -1,17 +1,12 @@
|
|
|
1
|
-
import parser from 'postcss-selector-parser';
|
|
2
1
|
import BasePlugin from '../plugin.js';
|
|
3
2
|
export default HtmlCombinatorCommentBody;
|
|
4
3
|
declare class HtmlCombinatorCommentBody extends BasePlugin {
|
|
5
|
-
/** @param {import('postcss').Result} result */
|
|
6
|
-
constructor(result
|
|
4
|
+
/** @param {import('postcss').Result=} result */
|
|
5
|
+
constructor(result?: import('postcss').Result | undefined);
|
|
7
6
|
/**
|
|
8
7
|
* @param {import('postcss').Rule} rule
|
|
9
8
|
* @return {void}
|
|
10
9
|
*/
|
|
11
10
|
detect(rule: import('postcss').Rule): void;
|
|
12
|
-
/** @param {import('postcss').Rule} rule
|
|
13
|
-
* @return {parser.SyncProcessor<void>}
|
|
14
|
-
*/
|
|
15
|
-
analyse(rule: import('postcss').Rule): parser.SyncProcessor<void>;
|
|
16
11
|
}
|
|
17
12
|
//# sourceMappingURL=htmlCombinatorCommentBody.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"htmlCombinatorCommentBody.d.ts","sourceRoot":"","sources":["../../src/plugins/htmlCombinatorCommentBody.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"htmlCombinatorCommentBody.d.ts","sourceRoot":"","sources":["../../src/plugins/htmlCombinatorCommentBody.js"],"names":[],"mappings":"AACA,OAAO,UAAU,MAAM,cAAc,CAAC;;uCAMU,SAAQ,UAAU;IAChE,gDAAgD;IAChD,YAAY,MAAM,AADd,CAA0C,EAAlC,OAAO,SAAS,EAAE,MAAM,YAClB,EAEjB;IAED;;;OAGG;IACH,MAAM,CAAC,IAAI,EAHA,OAAO,SAAS,EAAE,IAGlB,GAFC,IAAI,CAcf"}
|
|
@@ -1,18 +1,12 @@
|
|
|
1
|
-
import parser from 'postcss-selector-parser';
|
|
2
1
|
import BasePlugin from '../plugin.js';
|
|
3
2
|
export default HtmlFirstChild;
|
|
4
3
|
declare class HtmlFirstChild extends BasePlugin {
|
|
5
|
-
/** @param {import('postcss').Result} result */
|
|
6
|
-
constructor(result
|
|
4
|
+
/** @param {import('postcss').Result=} result */
|
|
5
|
+
constructor(result?: import('postcss').Result | undefined);
|
|
7
6
|
/**
|
|
8
7
|
* @param {import('postcss').Rule} rule
|
|
9
8
|
* @return {void}
|
|
10
9
|
*/
|
|
11
10
|
detect(rule: import('postcss').Rule): void;
|
|
12
|
-
/**
|
|
13
|
-
* @param {import('postcss').Rule} rule
|
|
14
|
-
* @return {parser.SyncProcessor<void>}
|
|
15
|
-
*/
|
|
16
|
-
analyse(rule: import('postcss').Rule): parser.SyncProcessor<void>;
|
|
17
11
|
}
|
|
18
12
|
//# sourceMappingURL=htmlFirstChild.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"htmlFirstChild.d.ts","sourceRoot":"","sources":["../../src/plugins/htmlFirstChild.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"htmlFirstChild.d.ts","sourceRoot":"","sources":["../../src/plugins/htmlFirstChild.js"],"names":[],"mappings":"AACA,OAAO,UAAU,MAAM,cAAc,CAAC;;4BAMD,SAAQ,UAAU;IACrD,gDAAgD;IAChD,YAAY,MAAM,AADd,CAA0C,EAAlC,OAAO,SAAS,EAAE,MAAM,YAClB,EAEjB;IAED;;;OAGG;IACH,MAAM,CAAC,IAAI,EAHA,OAAO,SAAS,EAAE,IAGlB,GAFC,IAAI,CAYf"}
|
package/types/plugins/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
declare const plugins: ({
|
|
2
2
|
new (result?: import('postcss').Result | undefined): {
|
|
3
|
-
detect(decl: import('postcss').Declaration): void;
|
|
4
3
|
nodes: import("../plugin.js").NodeWithInfo[];
|
|
5
4
|
targets: Set<string>;
|
|
5
|
+
detect(rule: import('postcss').Rule): void;
|
|
6
6
|
nodeTypes: Set<string>;
|
|
7
7
|
result: import("postcss").Result<import("postcss").Document | import("postcss").Root> | undefined;
|
|
8
8
|
push(node: import('postcss').Node, metadata: {
|
|
@@ -17,11 +17,11 @@ declare const plugins: ({
|
|
|
17
17
|
};
|
|
18
18
|
} | {
|
|
19
19
|
new (result?: import('postcss').Result | undefined): {
|
|
20
|
+
detect(decl: import('postcss').Declaration): void;
|
|
20
21
|
nodes: import("../plugin.js").NodeWithInfo[];
|
|
21
22
|
targets: Set<string>;
|
|
22
23
|
nodeTypes: Set<string>;
|
|
23
24
|
result: import("postcss").Result<import("postcss").Document | import("postcss").Root> | undefined;
|
|
24
|
-
detect(rule: import('postcss').Rule): void;
|
|
25
25
|
push(node: import('postcss').Node, metadata: {
|
|
26
26
|
identifier: string;
|
|
27
27
|
hack: string;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import parser from 'postcss-selector-parser';
|
|
2
1
|
import BasePlugin from '../plugin.js';
|
|
3
2
|
export default StarHtml;
|
|
4
3
|
declare class StarHtml extends BasePlugin {
|
|
@@ -9,10 +8,5 @@ declare class StarHtml extends BasePlugin {
|
|
|
9
8
|
* @return {void}
|
|
10
9
|
*/
|
|
11
10
|
detect(rule: import('postcss').Rule): void;
|
|
12
|
-
/**
|
|
13
|
-
* @param {import('postcss').Rule} rule
|
|
14
|
-
* @return {parser.SyncProcessor<void>}
|
|
15
|
-
*/
|
|
16
|
-
analyse(rule: import('postcss').Rule): parser.SyncProcessor<void>;
|
|
17
11
|
}
|
|
18
12
|
//# sourceMappingURL=starHtml.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"starHtml.d.ts","sourceRoot":"","sources":["../../src/plugins/starHtml.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"starHtml.d.ts","sourceRoot":"","sources":["../../src/plugins/starHtml.js"],"names":[],"mappings":"AACA,OAAO,UAAU,MAAM,cAAc,CAAC;;sBAMP,SAAQ,UAAU;IAC/C,gDAAgD;IAChD,YAAY,MAAM,AADd,CAA0C,EAAlC,OAAO,SAAS,EAAE,MAAM,YAClB,EAEjB;IAED;;;OAGG;IACH,MAAM,CAAC,IAAI,EAHA,OAAO,SAAS,EAAE,IAGlB,GAFC,IAAI,CASf"}
|
package/src/dictionary/tags.js
DELETED
package/src/exists.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @param {import('postcss-selector-parser').Selector} selector
|
|
3
|
-
* @param {number} index
|
|
4
|
-
* @param {string} value
|
|
5
|
-
* @return {boolean | undefined | ''}
|
|
6
|
-
*/
|
|
7
|
-
function exists(selector, index, value) {
|
|
8
|
-
const node = selector.at(index);
|
|
9
|
-
return node && node.value && node.value.toLowerCase() === value;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export default exists;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tags.d.ts","sourceRoot":"","sources":["../../src/dictionary/tags.js"],"names":[],"mappings":"AAAA,QAAA,MAAM,IAAI,SAAS,CAAC;AACpB,QAAA,MAAM,IAAI,SAAS,CAAC;AACpB,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC"}
|
package/types/exists.d.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @param {import('postcss-selector-parser').Selector} selector
|
|
3
|
-
* @param {number} index
|
|
4
|
-
* @param {string} value
|
|
5
|
-
* @return {boolean | undefined | ''}
|
|
6
|
-
*/
|
|
7
|
-
declare function exists(selector: import('postcss-selector-parser').Selector, index: number, value: string): boolean | undefined | '';
|
|
8
|
-
export default exists;
|
|
9
|
-
//# sourceMappingURL=exists.d.ts.map
|
package/types/exists.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"exists.d.ts","sourceRoot":"","sources":["../src/exists.js"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,iBAAS,MAAM,CAAC,QAAQ,EALb,OAAO,yBAAyB,EAAE,QAKrB,EAAE,KAAK,EAJpB,MAIoB,EAAE,KAAK,EAH3B,MAG2B,GAF1B,OAAO,GAAG,SAAS,GAAG,EAAE,CAKnC;eAEc,MAAM"}
|