css-has-pseudo 0.8.0 → 2.0.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 +3 -1
- package/browser.js +2 -1
- package/cli.js +85 -47
- package/index.js +31 -27
- package/index.js.map +1 -1
- package/index.mjs +32 -28
- package/index.mjs.map +1 -1
- package/package.json +34 -25
- package/postcss.js +63 -38
- package/postcss.js.map +1 -1
- package/postcss.mjs +58 -35
- package/postcss.mjs.map +1 -1
- package/CHANGELOG.md +0 -35
package/README.md
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
[CSS Has Pseudo] lets you style elements relative to other elements in CSS,
|
|
8
8
|
following the [Selectors Level 4] specification.
|
|
9
9
|
|
|
10
|
+
[](https://caniuse.com/#feat=css-has)
|
|
11
|
+
|
|
10
12
|
```css
|
|
11
13
|
a:has(> img) {
|
|
12
14
|
/* style links that contain an image */
|
|
@@ -41,7 +43,7 @@ Next, use your transformed CSS with this script:
|
|
|
41
43
|
<script>cssHasPseudo(document)</script>
|
|
42
44
|
```
|
|
43
45
|
|
|
44
|
-
That’s it. The script is
|
|
46
|
+
That’s it. The script is 765 bytes and works in all browsers, including
|
|
45
47
|
Internet Explorer 11. With a [Mutation Observer polyfill], the script will work
|
|
46
48
|
down to Internet Explorer 9.
|
|
47
49
|
|
package/browser.js
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
2
|
+
//# sourceMappingURL=browser.js.map
|
package/cli.js
CHANGED
|
@@ -1,57 +1,79 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
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
|
-
}
|
|
3
|
+
var fs = require('fs');
|
|
4
|
+
var parser = require('postcss-selector-parser');
|
|
5
|
+
|
|
6
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
7
|
+
|
|
8
|
+
var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
|
|
9
|
+
var parser__default = /*#__PURE__*/_interopDefaultLegacy(parser);
|
|
10
|
+
|
|
11
|
+
const creator = (
|
|
12
|
+
/** @type {{ preserve: true | false }} */
|
|
13
|
+
opts) => {
|
|
14
|
+
opts = typeof opts === 'object' && opts || defaultOptions;
|
|
15
|
+
/** Whether the original rule should be preserved. */
|
|
16
|
+
|
|
17
|
+
const shouldPreserve = Boolean('preserve' in opts ? opts.preserve : true);
|
|
18
|
+
return {
|
|
19
|
+
postcssPlugin: 'css-has-pseudo',
|
|
20
|
+
Rule: rule => {
|
|
21
|
+
if (rule.selector.includes(':has(')) {
|
|
22
|
+
const fallbackSelector = getFallbackSelector(rule.selector);
|
|
23
|
+
if (shouldPreserve) rule.cloneBefore({
|
|
24
|
+
selector: fallbackSelector
|
|
25
|
+
});else rule.assign({
|
|
26
|
+
selector: fallbackSelector
|
|
29
27
|
});
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
creator.postcss = true;
|
|
34
|
+
|
|
35
|
+
const getFallbackSelector = (
|
|
36
|
+
/** @type {string} */
|
|
37
|
+
selectorText) => parser__default['default'](selectors => {
|
|
38
|
+
selectors.walkPseudos(selector => {
|
|
39
|
+
if (selector.value === ':has' && selector.nodes) {
|
|
40
|
+
const isNotHas = isParentInNotPseudo(selector);
|
|
41
|
+
selector.value = isNotHas ? ':not-has' : ':has';
|
|
42
|
+
const attribute = parser__default['default'].attribute({
|
|
43
|
+
attribute: getEscapedCss(String(selector))
|
|
33
44
|
});
|
|
34
45
|
|
|
35
|
-
if (
|
|
36
|
-
|
|
46
|
+
if (isNotHas) {
|
|
47
|
+
selector.parent.parent.replaceWith(attribute);
|
|
37
48
|
} else {
|
|
38
|
-
|
|
49
|
+
selector.replaceWith(attribute);
|
|
39
50
|
}
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
});
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}).processSync(selectorText);
|
|
54
|
+
/** Default options. */
|
|
43
55
|
|
|
44
|
-
function checkIfParentIsNot(selector) {
|
|
45
|
-
return Object(Object(selector.parent).parent).type === 'pseudo' && selector.parent.parent.value === ':not';
|
|
46
|
-
}
|
|
47
56
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
57
|
+
const defaultOptions = {
|
|
58
|
+
preserve: true
|
|
59
|
+
};
|
|
60
|
+
/** Returns the string as an escaped CSS identifier. */
|
|
52
61
|
|
|
62
|
+
const getEscapedCss = (
|
|
63
|
+
/** @type {string} */
|
|
64
|
+
value) => encodeURIComponent(value).replace(/%3A/g, ':').replace(/%5B/g, '[').replace(/%5D/g, ']').replace(/%2C/g, ',').replace(/[():%[\],]/g, '\\$&');
|
|
65
|
+
/** Returns whether the selector is within a `:not` pseudo-class. */
|
|
53
66
|
|
|
54
|
-
|
|
67
|
+
|
|
68
|
+
const isParentInNotPseudo = selector => {
|
|
69
|
+
var _selector$parent, _selector$parent$pare;
|
|
70
|
+
|
|
71
|
+
return ((_selector$parent = selector.parent) === null || _selector$parent === void 0 ? void 0 : (_selector$parent$pare = _selector$parent.parent) === null || _selector$parent$pare === void 0 ? void 0 : _selector$parent$pare.type) === 'pseudo' && selector.parent.parent.value === ':not';
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/* eslint no-console: 0 */
|
|
75
|
+
|
|
76
|
+
const fileRegExp = /^[\w/.]+$/;
|
|
55
77
|
const argRegExp = /^--(\w+)=("|')?(.+)\2$/;
|
|
56
78
|
const relaxedJsonPropRegExp = /(['"])?([a-z0-9A-Z_]+)(['"])?:/g;
|
|
57
79
|
const relaxedJsonValueRegExp = /("[a-z0-9A-Z_]+":\s*)(?!true|false|null|\d+)'?([A-z0-9]+)'?([,}])/g;
|
|
@@ -77,6 +99,11 @@ const argo = process.argv.slice(2).reduce((object, arg) => {
|
|
|
77
99
|
}); // get css from command line arguments or stdin
|
|
78
100
|
|
|
79
101
|
(argo.from === '<stdin>' ? getStdin() : readFile(argo.from)).then(css => {
|
|
102
|
+
if (argo.from === '<stdin>' && !css) {
|
|
103
|
+
console.log(['CSS Has Pseudo\n', ' Transforms CSS with :has {}\n', 'Usage:\n', ' css-has-pseudo source.css transformed.css', ' css-has-pseudo --from=source.css --to=transformed.css --opts={}', ' echo "body:has(:focus) {}" | css-has-pseudo\n'].join('\n'));
|
|
104
|
+
process.exit(0);
|
|
105
|
+
}
|
|
106
|
+
|
|
80
107
|
const pluginOpts = JSON.parse(argo.opts.replace(relaxedJsonPropRegExp, '"$2": ').replace(relaxedJsonValueRegExp, '$1"$2"$3'));
|
|
81
108
|
const processOptions = Object.assign({
|
|
82
109
|
from: argo.from,
|
|
@@ -84,24 +111,34 @@ const argo = process.argv.slice(2).reduce((object, arg) => {
|
|
|
84
111
|
}, argo.map ? {
|
|
85
112
|
map: JSON.parse(argo.map)
|
|
86
113
|
} : {});
|
|
87
|
-
const result =
|
|
114
|
+
const result = creator.process(css, processOptions, pluginOpts);
|
|
88
115
|
|
|
89
116
|
if (argo.to === '<stdout>') {
|
|
90
117
|
return result.css;
|
|
91
118
|
} else {
|
|
92
119
|
return writeFile(argo.to, result.css).then(() => `CSS was written to "${argo.to}"`);
|
|
93
120
|
}
|
|
121
|
+
}).catch(error => {
|
|
122
|
+
if (Object(error).name === 'CssSyntaxError') {
|
|
123
|
+
throw new Error(`PostCSS had trouble reading the file (${error.reason} on line ${error.line}, column ${error.column}).`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (Object(error).errno === -2) {
|
|
127
|
+
throw new Error(`Sorry, "${error.path}" could not be read.`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
throw error;
|
|
94
131
|
}).then(result => {
|
|
95
132
|
console.log(result);
|
|
96
133
|
process.exit(0);
|
|
97
134
|
}, error => {
|
|
98
|
-
console.error(error);
|
|
135
|
+
console.error(Object(error).message || 'Something bad happened and we don’t even know what it was.');
|
|
99
136
|
process.exit(1);
|
|
100
137
|
});
|
|
101
138
|
|
|
102
139
|
function readFile(pathname) {
|
|
103
140
|
return new Promise((resolve, reject) => {
|
|
104
|
-
|
|
141
|
+
fs__default['default'].readFile(pathname, 'utf8', (error, data) => {
|
|
105
142
|
if (error) {
|
|
106
143
|
reject(error);
|
|
107
144
|
} else {
|
|
@@ -113,7 +150,7 @@ function readFile(pathname) {
|
|
|
113
150
|
|
|
114
151
|
function writeFile(pathname, data) {
|
|
115
152
|
return new Promise((resolve, reject) => {
|
|
116
|
-
|
|
153
|
+
fs__default['default'].writeFile(pathname, data, (error, content) => {
|
|
117
154
|
if (error) {
|
|
118
155
|
reject(error);
|
|
119
156
|
} else {
|
|
@@ -144,3 +181,4 @@ function getStdin() {
|
|
|
144
181
|
}
|
|
145
182
|
});
|
|
146
183
|
}
|
|
184
|
+
//# sourceMappingURL=cli.js.map
|
package/index.js
CHANGED
|
@@ -5,12 +5,12 @@ function cssHasPseudo(document) {
|
|
|
5
5
|
|
|
6
6
|
const attributeElement = document.createElement('x'); // walk all stylesheets to collect observed css rules
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
[].forEach.call(document.styleSheets, walkStyleSheet);
|
|
9
9
|
transformObservedItems(); // observe DOM modifications that affect selectors
|
|
10
10
|
|
|
11
11
|
const mutationObserver = new MutationObserver(mutationsList => {
|
|
12
12
|
mutationsList.forEach(mutation => {
|
|
13
|
-
|
|
13
|
+
[].forEach.call(mutation.addedNodes || [], node => {
|
|
14
14
|
// walk stylesheets to collect observed css rules
|
|
15
15
|
if (node.nodeType === 1 && node.sheet) {
|
|
16
16
|
walkStyleSheet(node.sheet);
|
|
@@ -34,8 +34,8 @@ function cssHasPseudo(document) {
|
|
|
34
34
|
requestAnimationFrame(() => {
|
|
35
35
|
observedItems.forEach(item => {
|
|
36
36
|
const nodes = [];
|
|
37
|
-
|
|
38
|
-
const nthChild =
|
|
37
|
+
[].forEach.call(document.querySelectorAll(item.scopeSelector), element => {
|
|
38
|
+
const nthChild = [].indexOf.call(element.parentNode.children, element) + 1;
|
|
39
39
|
const relativeSelectors = item.relativeSelectors.map(relativeSelector => item.scopeSelector + ':nth-child(' + nthChild + ') ' + relativeSelector).join(); // find any relative :has element from the :scope element
|
|
40
40
|
|
|
41
41
|
const relativeElement = element.parentNode.querySelector(relativeSelectors);
|
|
@@ -70,34 +70,38 @@ function cssHasPseudo(document) {
|
|
|
70
70
|
|
|
71
71
|
|
|
72
72
|
function cleanupObservedCssRules() {
|
|
73
|
-
|
|
73
|
+
[].push.apply(observedItems, observedItems.splice(0).filter(item => item.rule.parentStyleSheet && item.rule.parentStyleSheet.ownerNode && document.documentElement.contains(item.rule.parentStyleSheet.ownerNode)));
|
|
74
74
|
} // walk a stylesheet to collect observed css rules
|
|
75
75
|
|
|
76
76
|
|
|
77
77
|
function walkStyleSheet(styleSheet) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
78
|
+
try {
|
|
79
|
+
// walk a css rule to collect observed css rules
|
|
80
|
+
[].forEach.call(styleSheet.cssRules || [], rule => {
|
|
81
|
+
if (rule.selectorText) {
|
|
82
|
+
// decode the selector text in all browsers to:
|
|
83
|
+
// [1] = :scope, [2] = :not(:has), [3] = :has relative, [4] = :scope relative
|
|
84
|
+
const selectors = decodeURIComponent(rule.selectorText.replace(/\\(.)/g, '$1')).match(/^(.*?)\[:(not-)?has\((.+?)\)\](.*?)$/);
|
|
85
|
+
|
|
86
|
+
if (selectors) {
|
|
87
|
+
const attributeName = ':' + (selectors[2] ? 'not-' : '') + 'has(' + // encode a :has() pseudo selector as an attribute name
|
|
88
|
+
encodeURIComponent(selectors[3]).replace(/%3A/g, ':').replace(/%5B/g, '[').replace(/%5D/g, ']').replace(/%2C/g, ',') + ')';
|
|
89
|
+
observedItems.push({
|
|
90
|
+
rule,
|
|
91
|
+
scopeSelector: selectors[1],
|
|
92
|
+
isNot: selectors[2],
|
|
93
|
+
relativeSelectors: selectors[3].split(/\s*,\s*/),
|
|
94
|
+
attributeName,
|
|
95
|
+
nodes: []
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
} else {
|
|
99
|
+
walkStyleSheet(rule);
|
|
96
100
|
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
+
});
|
|
102
|
+
} catch (error) {
|
|
103
|
+
/* do nothing and continue */
|
|
104
|
+
}
|
|
101
105
|
}
|
|
102
106
|
}
|
|
103
107
|
|
package/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["src/browser.js"],"sourcesContent":["export default function cssHasPseudo(document) {\n\tconst observedItems = [];\n\n\t// document.createAttribute() doesn't support `:` in the name. innerHTML does\n\tconst attributeElement = document.createElement('x');\n\n\t// walk all stylesheets to collect observed css rules\n\
|
|
1
|
+
{"version":3,"file":"index.js","sources":["src/browser.js"],"sourcesContent":["export default function cssHasPseudo (document) {\n\tconst observedItems = [];\n\n\t// document.createAttribute() doesn't support `:` in the name. innerHTML does\n\tconst attributeElement = document.createElement('x');\n\n\t// walk all stylesheets to collect observed css rules\n\t[].forEach.call(document.styleSheets, walkStyleSheet);\n\ttransformObservedItems();\n\n\t// observe DOM modifications that affect selectors\n\tconst mutationObserver = new MutationObserver(mutationsList => {\n\t\tmutationsList.forEach(mutation => {\n\t\t\t[].forEach.call(mutation.addedNodes || [], node => {\n\t\t\t\t// walk stylesheets to collect observed css rules\n\t\t\t\tif (node.nodeType === 1 && node.sheet) {\n\t\t\t\t\twalkStyleSheet(node.sheet);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// transform observed css rules\n\t\t\tcleanupObservedCssRules();\n\t\t\ttransformObservedItems();\n\t\t});\n\t});\n\n\tmutationObserver.observe(document, { childList: true, subtree: true });\n\n\t// observe DOM events that affect pseudo-selectors\n\tdocument.addEventListener('focus', transformObservedItems, true);\n\tdocument.addEventListener('blur', transformObservedItems, true);\n\tdocument.addEventListener('input', transformObservedItems);\n\n\t// transform observed css rules\n\tfunction transformObservedItems () {\n\t\trequestAnimationFrame(() => {\n\t\t\tobservedItems.forEach(\n\t\t\t\titem => {\n\t\t\t\t\tconst nodes = [];\n\n\t\t\t\t\t[].forEach.call(\n\t\t\t\t\t\tdocument.querySelectorAll(item.scopeSelector),\n\t\t\t\t\t\telement => {\n\t\t\t\t\t\t\tconst nthChild = [].indexOf.call(element.parentNode.children, element) + 1;\n\t\t\t\t\t\t\tconst relativeSelectors = item.relativeSelectors.map(\n\t\t\t\t\t\t\t\trelativeSelector => item.scopeSelector + ':nth-child(' + nthChild + ') ' + relativeSelector\n\t\t\t\t\t\t\t).join();\n\n\t\t\t\t\t\t\t// find any relative :has element from the :scope element\n\t\t\t\t\t\t\tconst relativeElement = element.parentNode.querySelector(relativeSelectors);\n\n\t\t\t\t\t\t\tconst shouldElementMatch = item.isNot ? !relativeElement : relativeElement;\n\n\t\t\t\t\t\t\tif (shouldElementMatch) {\n\t\t\t\t\t\t\t\t// memorize the node\n\t\t\t\t\t\t\t\tnodes.push(element);\n\n\t\t\t\t\t\t\t\t// set an attribute with an irregular attribute name\n\t\t\t\t\t\t\t\t// document.createAttribute() doesn't support special characters\n\t\t\t\t\t\t\t\tattributeElement.innerHTML = '<x ' + item.attributeName + '>';\n\n\t\t\t\t\t\t\t\telement.setAttributeNode(attributeElement.children[0].attributes[0].cloneNode());\n\n\t\t\t\t\t\t\t\t// trigger a style refresh in IE and Edge\n\t\t\t\t\t\t\t\tdocument.documentElement.style.zoom = 1; document.documentElement.style.zoom = null;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t);\n\n\t\t\t\t\t// remove the encoded attribute from all nodes that no longer match them\n\t\t\t\t\titem.nodes.forEach(node => {\n\t\t\t\t\t\tif (nodes.indexOf(node) === -1) {\n\t\t\t\t\t\t\tnode.removeAttribute(item.attributeName);\n\n\t\t\t\t\t\t\t// trigger a style refresh in IE and Edge\n\t\t\t\t\t\t\tdocument.documentElement.style.zoom = 1; document.documentElement.style.zoom = null;\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\n\t\t\t\t\t// update the\n\t\t\t\t\titem.nodes = nodes;\n\t\t\t\t}\n\t\t\t);\n\t\t});\n\t}\n\n\t// remove any observed cssrules that no longer apply\n\tfunction cleanupObservedCssRules () {\n\t\t[].push.apply(\n\t\t\tobservedItems,\n\t\t\tobservedItems.splice(0).filter(\n\t\t\t\titem => item.rule.parentStyleSheet &&\n\t\t\t\t\titem.rule.parentStyleSheet.ownerNode &&\n\t\t\t\t\tdocument.documentElement.contains(item.rule.parentStyleSheet.ownerNode)\n\t\t\t)\n\t\t);\n\t}\n\n\t// walk a stylesheet to collect observed css rules\n\tfunction walkStyleSheet (styleSheet) {\n\t\ttry {\n\t\t\t// walk a css rule to collect observed css rules\n\t\t\t[].forEach.call(styleSheet.cssRules || [], rule => {\n\t\t\t\tif (rule.selectorText) {\n\t\t\t\t\t// decode the selector text in all browsers to:\n\t\t\t\t\t// [1] = :scope, [2] = :not(:has), [3] = :has relative, [4] = :scope relative\n\t\t\t\t\tconst selectors = decodeURIComponent(rule.selectorText.replace(/\\\\(.)/g, '$1')).match(/^(.*?)\\[:(not-)?has\\((.+?)\\)\\](.*?)$/);\n\n\t\t\t\t\tif (selectors) {\n\t\t\t\t\t\tconst attributeName = ':' + (selectors[2] ? 'not-' : '') + 'has(' +\n\t\t\t\t\t\t\t// encode a :has() pseudo selector as an attribute name\n\t\t\t\t\t\t\tencodeURIComponent(selectors[3]).replace(/%3A/g, ':').replace(/%5B/g, '[').replace(/%5D/g, ']').replace(/%2C/g, ',') +\n\t\t\t\t\t\t')';\n\n\t\t\t\t\t\tobservedItems.push({\n\t\t\t\t\t\t\trule,\n\t\t\t\t\t\t\tscopeSelector: selectors[1],\n\t\t\t\t\t\t\tisNot: selectors[2],\n\t\t\t\t\t\t\trelativeSelectors: selectors[3].split(/\\s*,\\s*/),\n\t\t\t\t\t\t\tattributeName,\n\t\t\t\t\t\t\tnodes: []\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\twalkStyleSheet(rule);\n\t\t\t\t}\n\t\t\t});\n\t\t} catch (error) {\n\t\t\t/* do nothing and continue */\n\t\t}\n\t}\n}\n"],"names":["cssHasPseudo","document","observedItems","attributeElement","createElement","forEach","call","styleSheets","walkStyleSheet","transformObservedItems","mutationObserver","MutationObserver","mutationsList","mutation","addedNodes","node","nodeType","sheet","cleanupObservedCssRules","observe","childList","subtree","addEventListener","requestAnimationFrame","item","nodes","querySelectorAll","scopeSelector","element","nthChild","indexOf","parentNode","children","relativeSelectors","map","relativeSelector","join","relativeElement","querySelector","shouldElementMatch","isNot","push","innerHTML","attributeName","setAttributeNode","attributes","cloneNode","documentElement","style","zoom","removeAttribute","apply","splice","filter","rule","parentStyleSheet","ownerNode","contains","styleSheet","cssRules","selectorText","selectors","decodeURIComponent","replace","match","encodeURIComponent","split","error"],"mappings":"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"}
|
package/index.mjs
CHANGED
|
@@ -3,12 +3,12 @@ function cssHasPseudo(document) {
|
|
|
3
3
|
|
|
4
4
|
const attributeElement = document.createElement('x'); // walk all stylesheets to collect observed css rules
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
[].forEach.call(document.styleSheets, walkStyleSheet);
|
|
7
7
|
transformObservedItems(); // observe DOM modifications that affect selectors
|
|
8
8
|
|
|
9
9
|
const mutationObserver = new MutationObserver(mutationsList => {
|
|
10
10
|
mutationsList.forEach(mutation => {
|
|
11
|
-
|
|
11
|
+
[].forEach.call(mutation.addedNodes || [], node => {
|
|
12
12
|
// walk stylesheets to collect observed css rules
|
|
13
13
|
if (node.nodeType === 1 && node.sheet) {
|
|
14
14
|
walkStyleSheet(node.sheet);
|
|
@@ -32,8 +32,8 @@ function cssHasPseudo(document) {
|
|
|
32
32
|
requestAnimationFrame(() => {
|
|
33
33
|
observedItems.forEach(item => {
|
|
34
34
|
const nodes = [];
|
|
35
|
-
|
|
36
|
-
const nthChild =
|
|
35
|
+
[].forEach.call(document.querySelectorAll(item.scopeSelector), element => {
|
|
36
|
+
const nthChild = [].indexOf.call(element.parentNode.children, element) + 1;
|
|
37
37
|
const relativeSelectors = item.relativeSelectors.map(relativeSelector => item.scopeSelector + ':nth-child(' + nthChild + ') ' + relativeSelector).join(); // find any relative :has element from the :scope element
|
|
38
38
|
|
|
39
39
|
const relativeElement = element.parentNode.querySelector(relativeSelectors);
|
|
@@ -68,36 +68,40 @@ function cssHasPseudo(document) {
|
|
|
68
68
|
|
|
69
69
|
|
|
70
70
|
function cleanupObservedCssRules() {
|
|
71
|
-
|
|
71
|
+
[].push.apply(observedItems, observedItems.splice(0).filter(item => item.rule.parentStyleSheet && item.rule.parentStyleSheet.ownerNode && document.documentElement.contains(item.rule.parentStyleSheet.ownerNode)));
|
|
72
72
|
} // walk a stylesheet to collect observed css rules
|
|
73
73
|
|
|
74
74
|
|
|
75
75
|
function walkStyleSheet(styleSheet) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
76
|
+
try {
|
|
77
|
+
// walk a css rule to collect observed css rules
|
|
78
|
+
[].forEach.call(styleSheet.cssRules || [], rule => {
|
|
79
|
+
if (rule.selectorText) {
|
|
80
|
+
// decode the selector text in all browsers to:
|
|
81
|
+
// [1] = :scope, [2] = :not(:has), [3] = :has relative, [4] = :scope relative
|
|
82
|
+
const selectors = decodeURIComponent(rule.selectorText.replace(/\\(.)/g, '$1')).match(/^(.*?)\[:(not-)?has\((.+?)\)\](.*?)$/);
|
|
83
|
+
|
|
84
|
+
if (selectors) {
|
|
85
|
+
const attributeName = ':' + (selectors[2] ? 'not-' : '') + 'has(' + // encode a :has() pseudo selector as an attribute name
|
|
86
|
+
encodeURIComponent(selectors[3]).replace(/%3A/g, ':').replace(/%5B/g, '[').replace(/%5D/g, ']').replace(/%2C/g, ',') + ')';
|
|
87
|
+
observedItems.push({
|
|
88
|
+
rule,
|
|
89
|
+
scopeSelector: selectors[1],
|
|
90
|
+
isNot: selectors[2],
|
|
91
|
+
relativeSelectors: selectors[3].split(/\s*,\s*/),
|
|
92
|
+
attributeName,
|
|
93
|
+
nodes: []
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
} else {
|
|
97
|
+
walkStyleSheet(rule);
|
|
94
98
|
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
+
});
|
|
100
|
+
} catch (error) {
|
|
101
|
+
/* do nothing and continue */
|
|
102
|
+
}
|
|
99
103
|
}
|
|
100
104
|
}
|
|
101
105
|
|
|
102
|
-
export default
|
|
106
|
+
export { cssHasPseudo as default };
|
|
103
107
|
//# sourceMappingURL=index.mjs.map
|
package/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":["src/browser.js"],"sourcesContent":["export default function cssHasPseudo(document) {\n\tconst observedItems = [];\n\n\t// document.createAttribute() doesn't support `:` in the name. innerHTML does\n\tconst attributeElement = document.createElement('x');\n\n\t// walk all stylesheets to collect observed css rules\n\
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["src/browser.js"],"sourcesContent":["export default function cssHasPseudo (document) {\n\tconst observedItems = [];\n\n\t// document.createAttribute() doesn't support `:` in the name. innerHTML does\n\tconst attributeElement = document.createElement('x');\n\n\t// walk all stylesheets to collect observed css rules\n\t[].forEach.call(document.styleSheets, walkStyleSheet);\n\ttransformObservedItems();\n\n\t// observe DOM modifications that affect selectors\n\tconst mutationObserver = new MutationObserver(mutationsList => {\n\t\tmutationsList.forEach(mutation => {\n\t\t\t[].forEach.call(mutation.addedNodes || [], node => {\n\t\t\t\t// walk stylesheets to collect observed css rules\n\t\t\t\tif (node.nodeType === 1 && node.sheet) {\n\t\t\t\t\twalkStyleSheet(node.sheet);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// transform observed css rules\n\t\t\tcleanupObservedCssRules();\n\t\t\ttransformObservedItems();\n\t\t});\n\t});\n\n\tmutationObserver.observe(document, { childList: true, subtree: true });\n\n\t// observe DOM events that affect pseudo-selectors\n\tdocument.addEventListener('focus', transformObservedItems, true);\n\tdocument.addEventListener('blur', transformObservedItems, true);\n\tdocument.addEventListener('input', transformObservedItems);\n\n\t// transform observed css rules\n\tfunction transformObservedItems () {\n\t\trequestAnimationFrame(() => {\n\t\t\tobservedItems.forEach(\n\t\t\t\titem => {\n\t\t\t\t\tconst nodes = [];\n\n\t\t\t\t\t[].forEach.call(\n\t\t\t\t\t\tdocument.querySelectorAll(item.scopeSelector),\n\t\t\t\t\t\telement => {\n\t\t\t\t\t\t\tconst nthChild = [].indexOf.call(element.parentNode.children, element) + 1;\n\t\t\t\t\t\t\tconst relativeSelectors = item.relativeSelectors.map(\n\t\t\t\t\t\t\t\trelativeSelector => item.scopeSelector + ':nth-child(' + nthChild + ') ' + relativeSelector\n\t\t\t\t\t\t\t).join();\n\n\t\t\t\t\t\t\t// find any relative :has element from the :scope element\n\t\t\t\t\t\t\tconst relativeElement = element.parentNode.querySelector(relativeSelectors);\n\n\t\t\t\t\t\t\tconst shouldElementMatch = item.isNot ? !relativeElement : relativeElement;\n\n\t\t\t\t\t\t\tif (shouldElementMatch) {\n\t\t\t\t\t\t\t\t// memorize the node\n\t\t\t\t\t\t\t\tnodes.push(element);\n\n\t\t\t\t\t\t\t\t// set an attribute with an irregular attribute name\n\t\t\t\t\t\t\t\t// document.createAttribute() doesn't support special characters\n\t\t\t\t\t\t\t\tattributeElement.innerHTML = '<x ' + item.attributeName + '>';\n\n\t\t\t\t\t\t\t\telement.setAttributeNode(attributeElement.children[0].attributes[0].cloneNode());\n\n\t\t\t\t\t\t\t\t// trigger a style refresh in IE and Edge\n\t\t\t\t\t\t\t\tdocument.documentElement.style.zoom = 1; document.documentElement.style.zoom = null;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t);\n\n\t\t\t\t\t// remove the encoded attribute from all nodes that no longer match them\n\t\t\t\t\titem.nodes.forEach(node => {\n\t\t\t\t\t\tif (nodes.indexOf(node) === -1) {\n\t\t\t\t\t\t\tnode.removeAttribute(item.attributeName);\n\n\t\t\t\t\t\t\t// trigger a style refresh in IE and Edge\n\t\t\t\t\t\t\tdocument.documentElement.style.zoom = 1; document.documentElement.style.zoom = null;\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\n\t\t\t\t\t// update the\n\t\t\t\t\titem.nodes = nodes;\n\t\t\t\t}\n\t\t\t);\n\t\t});\n\t}\n\n\t// remove any observed cssrules that no longer apply\n\tfunction cleanupObservedCssRules () {\n\t\t[].push.apply(\n\t\t\tobservedItems,\n\t\t\tobservedItems.splice(0).filter(\n\t\t\t\titem => item.rule.parentStyleSheet &&\n\t\t\t\t\titem.rule.parentStyleSheet.ownerNode &&\n\t\t\t\t\tdocument.documentElement.contains(item.rule.parentStyleSheet.ownerNode)\n\t\t\t)\n\t\t);\n\t}\n\n\t// walk a stylesheet to collect observed css rules\n\tfunction walkStyleSheet (styleSheet) {\n\t\ttry {\n\t\t\t// walk a css rule to collect observed css rules\n\t\t\t[].forEach.call(styleSheet.cssRules || [], rule => {\n\t\t\t\tif (rule.selectorText) {\n\t\t\t\t\t// decode the selector text in all browsers to:\n\t\t\t\t\t// [1] = :scope, [2] = :not(:has), [3] = :has relative, [4] = :scope relative\n\t\t\t\t\tconst selectors = decodeURIComponent(rule.selectorText.replace(/\\\\(.)/g, '$1')).match(/^(.*?)\\[:(not-)?has\\((.+?)\\)\\](.*?)$/);\n\n\t\t\t\t\tif (selectors) {\n\t\t\t\t\t\tconst attributeName = ':' + (selectors[2] ? 'not-' : '') + 'has(' +\n\t\t\t\t\t\t\t// encode a :has() pseudo selector as an attribute name\n\t\t\t\t\t\t\tencodeURIComponent(selectors[3]).replace(/%3A/g, ':').replace(/%5B/g, '[').replace(/%5D/g, ']').replace(/%2C/g, ',') +\n\t\t\t\t\t\t')';\n\n\t\t\t\t\t\tobservedItems.push({\n\t\t\t\t\t\t\trule,\n\t\t\t\t\t\t\tscopeSelector: selectors[1],\n\t\t\t\t\t\t\tisNot: selectors[2],\n\t\t\t\t\t\t\trelativeSelectors: selectors[3].split(/\\s*,\\s*/),\n\t\t\t\t\t\t\tattributeName,\n\t\t\t\t\t\t\tnodes: []\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\twalkStyleSheet(rule);\n\t\t\t\t}\n\t\t\t});\n\t\t} catch (error) {\n\t\t\t/* do nothing and continue */\n\t\t}\n\t}\n}\n"],"names":["cssHasPseudo","document","observedItems","attributeElement","createElement","forEach","call","styleSheets","walkStyleSheet","transformObservedItems","mutationObserver","MutationObserver","mutationsList","mutation","addedNodes","node","nodeType","sheet","cleanupObservedCssRules","observe","childList","subtree","addEventListener","requestAnimationFrame","item","nodes","querySelectorAll","scopeSelector","element","nthChild","indexOf","parentNode","children","relativeSelectors","map","relativeSelector","join","relativeElement","querySelector","shouldElementMatch","isNot","push","innerHTML","attributeName","setAttributeNode","attributes","cloneNode","documentElement","style","zoom","removeAttribute","apply","splice","filter","rule","parentStyleSheet","ownerNode","contains","styleSheet","cssRules","selectorText","selectors","decodeURIComponent","replace","match","encodeURIComponent","split","error"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "css-has-pseudo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Style elements relative to other elements in CSS",
|
|
5
5
|
"author": "Jonathan Neal <jonathantneal@hotmail.com>",
|
|
6
6
|
"license": "CC0-1.0",
|
|
@@ -26,40 +26,49 @@
|
|
|
26
26
|
],
|
|
27
27
|
"scripts": {
|
|
28
28
|
"build": "npm run build:browser && npm run build:cli && npm run build:node && npm run build:postcss",
|
|
29
|
-
"build:browser": "cross-env NODE_ENV=browser rollup
|
|
30
|
-
"build:cli": "cross-env NODE_ENV=cli rollup
|
|
31
|
-
"build:postcss": "cross-env NODE_ENV=postcss rollup
|
|
32
|
-
"build:node": "rollup
|
|
33
|
-
"prepublishOnly": "npm
|
|
34
|
-
"pretest": "npm run build:postcss",
|
|
29
|
+
"build:browser": "cross-env NODE_ENV=browser rollup --config .rollup.js --silent",
|
|
30
|
+
"build:cli": "cross-env NODE_ENV=cli rollup --config .rollup.js --silent",
|
|
31
|
+
"build:postcss": "cross-env NODE_ENV=postcss rollup --config .rollup.js --silent",
|
|
32
|
+
"build:node": "rollup --config .rollup.js --silent",
|
|
33
|
+
"prepublishOnly": "npm test && npm run build",
|
|
35
34
|
"pretest:postcss": "npm run build:postcss",
|
|
36
35
|
"test": "npm run test:js && npm run test:postcss",
|
|
37
|
-
"test:js": "eslint src
|
|
38
|
-
"test:postcss": "postcss-tape --plugin
|
|
36
|
+
"test:js": "eslint src/{*,**/*}.js --cache --ignore-path .gitignore --quiet",
|
|
37
|
+
"test:postcss": "postcss-tape --plugin postcss.js"
|
|
39
38
|
},
|
|
40
39
|
"engines": {
|
|
41
|
-
"node": ">=
|
|
40
|
+
"node": ">=12"
|
|
41
|
+
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"postcss": ">=8.3"
|
|
42
44
|
},
|
|
43
45
|
"dependencies": {
|
|
44
|
-
"postcss": "^
|
|
45
|
-
"postcss-selector-parser": "^5.0.0-rc.4"
|
|
46
|
+
"postcss-selector-parser": "^6"
|
|
46
47
|
},
|
|
47
48
|
"devDependencies": {
|
|
48
|
-
"@babel/core": "
|
|
49
|
-
"@babel/preset-env": "
|
|
50
|
-
"babel
|
|
51
|
-
"cross-env": "
|
|
52
|
-
"eslint": "
|
|
53
|
-
"
|
|
54
|
-
"postcss-tape": "
|
|
55
|
-
"pre-commit": "
|
|
56
|
-
"rollup": "
|
|
57
|
-
"rollup-plugin-
|
|
58
|
-
"rollup-plugin-terser": "^3.0.0"
|
|
49
|
+
"@babel/core": "7.15.5",
|
|
50
|
+
"@babel/preset-env": "7.15.6",
|
|
51
|
+
"@rollup/plugin-babel": "5.3.0",
|
|
52
|
+
"cross-env": "7.0.3",
|
|
53
|
+
"eslint": "7.32.0",
|
|
54
|
+
"postcss": "8.3.4",
|
|
55
|
+
"postcss-tape": "6.0.1",
|
|
56
|
+
"pre-commit": "1.2.2",
|
|
57
|
+
"rollup": "2.56.3",
|
|
58
|
+
"rollup-plugin-terser": "7.0.2"
|
|
59
59
|
},
|
|
60
60
|
"eslintConfig": {
|
|
61
|
-
"
|
|
62
|
-
|
|
61
|
+
"env": {
|
|
62
|
+
"browser": true,
|
|
63
|
+
"es6": true,
|
|
64
|
+
"node": true
|
|
65
|
+
},
|
|
66
|
+
"extends": "eslint:recommended",
|
|
67
|
+
"parserOptions": {
|
|
68
|
+
"ecmaVersion": 2020,
|
|
69
|
+
"sourceType": "module"
|
|
70
|
+
},
|
|
71
|
+
"root": true
|
|
63
72
|
},
|
|
64
73
|
"keywords": [
|
|
65
74
|
"postcss",
|
package/postcss.js
CHANGED
|
@@ -1,48 +1,73 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
selector.replaceWith(attribute);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
3
|
+
var parser = require('postcss-selector-parser');
|
|
4
|
+
|
|
5
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
6
|
+
|
|
7
|
+
var parser__default = /*#__PURE__*/_interopDefaultLegacy(parser);
|
|
8
|
+
|
|
9
|
+
const creator = (
|
|
10
|
+
/** @type {{ preserve: true | false }} */
|
|
11
|
+
opts) => {
|
|
12
|
+
opts = typeof opts === 'object' && opts || defaultOptions;
|
|
13
|
+
/** Whether the original rule should be preserved. */
|
|
14
|
+
|
|
15
|
+
const shouldPreserve = Boolean('preserve' in opts ? opts.preserve : true);
|
|
16
|
+
return {
|
|
17
|
+
postcssPlugin: 'css-has-pseudo',
|
|
18
|
+
Rule: rule => {
|
|
19
|
+
if (rule.selector.includes(':has(')) {
|
|
20
|
+
const fallbackSelector = getFallbackSelector(rule.selector);
|
|
21
|
+
if (shouldPreserve) rule.cloneBefore({
|
|
22
|
+
selector: fallbackSelector
|
|
23
|
+
});else rule.assign({
|
|
24
|
+
selector: fallbackSelector
|
|
28
25
|
});
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
creator.postcss = true;
|
|
32
|
+
|
|
33
|
+
const getFallbackSelector = (
|
|
34
|
+
/** @type {string} */
|
|
35
|
+
selectorText) => parser__default['default'](selectors => {
|
|
36
|
+
selectors.walkPseudos(selector => {
|
|
37
|
+
if (selector.value === ':has' && selector.nodes) {
|
|
38
|
+
const isNotHas = isParentInNotPseudo(selector);
|
|
39
|
+
selector.value = isNotHas ? ':not-has' : ':has';
|
|
40
|
+
const attribute = parser__default['default'].attribute({
|
|
41
|
+
attribute: getEscapedCss(String(selector))
|
|
32
42
|
});
|
|
33
43
|
|
|
34
|
-
if (
|
|
35
|
-
|
|
44
|
+
if (isNotHas) {
|
|
45
|
+
selector.parent.parent.replaceWith(attribute);
|
|
36
46
|
} else {
|
|
37
|
-
|
|
47
|
+
selector.replaceWith(attribute);
|
|
38
48
|
}
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
});
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}).processSync(selectorText);
|
|
52
|
+
/** Default options. */
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
const defaultOptions = {
|
|
56
|
+
preserve: true
|
|
57
|
+
};
|
|
58
|
+
/** Returns the string as an escaped CSS identifier. */
|
|
59
|
+
|
|
60
|
+
const getEscapedCss = (
|
|
61
|
+
/** @type {string} */
|
|
62
|
+
value) => encodeURIComponent(value).replace(/%3A/g, ':').replace(/%5B/g, '[').replace(/%5D/g, ']').replace(/%2C/g, ',').replace(/[():%[\],]/g, '\\$&');
|
|
63
|
+
/** Returns whether the selector is within a `:not` pseudo-class. */
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
const isParentInNotPseudo = selector => {
|
|
67
|
+
var _selector$parent, _selector$parent$pare;
|
|
42
68
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
69
|
+
return ((_selector$parent = selector.parent) === null || _selector$parent === void 0 ? void 0 : (_selector$parent$pare = _selector$parent.parent) === null || _selector$parent$pare === void 0 ? void 0 : _selector$parent$pare.type) === 'pseudo' && selector.parent.parent.value === ':not';
|
|
70
|
+
};
|
|
46
71
|
|
|
47
|
-
module.exports =
|
|
72
|
+
module.exports = creator;
|
|
48
73
|
//# sourceMappingURL=postcss.js.map
|
package/postcss.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postcss.js","sources":["src/postcss.js"],"sourcesContent":["import parser from 'postcss-selector-parser'
|
|
1
|
+
{"version":3,"file":"postcss.js","sources":["src/postcss.js"],"sourcesContent":["import parser from 'postcss-selector-parser'\n\nconst creator = (/** @type {{ preserve: true | false }} */ opts) => {\n\topts = typeof opts === 'object' && opts || defaultOptions\n\n\t/** Whether the original rule should be preserved. */\n\tconst shouldPreserve = Boolean('preserve' in opts ? opts.preserve : true)\n\n\treturn {\n\t\tpostcssPlugin: 'css-has-pseudo',\n\t\tRule: rule => {\n\t\t\tif (rule.selector.includes(':has(')) {\n\t\t\t\tconst fallbackSelector = getFallbackSelector(rule.selector)\n\n\t\t\t\tif (shouldPreserve) rule.cloneBefore({ selector: fallbackSelector })\n\t\t\t\telse rule.assign({ selector: fallbackSelector })\n\t\t\t}\n\t\t},\n\t}\n}\n\ncreator.postcss = true\n\nconst getFallbackSelector = (/** @type {string} */ selectorText) => parser(selectors => {\n\tselectors.walkPseudos(selector => {\n\t\tif (selector.value === ':has' && selector.nodes) {\n\t\t\tconst isNotHas = isParentInNotPseudo(selector)\n\n\t\t\tselector.value = isNotHas ? ':not-has' : ':has'\n\n\t\t\tconst attribute = parser.attribute({\n\t\t\t\tattribute: getEscapedCss(String(selector))\n\t\t\t})\n\n\t\t\tif (isNotHas) {\n\t\t\t\tselector.parent.parent.replaceWith(attribute)\n\t\t\t} else {\n\t\t\t\tselector.replaceWith(attribute)\n\t\t\t}\n\t\t}\n\t})\n}).processSync(selectorText)\n\n/** Default options. */\nconst defaultOptions = { preserve: true }\n\n/** Returns the string as an escaped CSS identifier. */\nconst getEscapedCss = (/** @type {string} */ value) => encodeURIComponent(value).replace(/%3A/g, ':').replace(/%5B/g, '[').replace(/%5D/g, ']').replace(/%2C/g, ',').replace(/[():%[\\],]/g, '\\\\$&')\n\n/** Returns whether the selector is within a `:not` pseudo-class. */\nconst isParentInNotPseudo = (selector) => selector.parent?.parent?.type === 'pseudo' && selector.parent.parent.value === ':not'\n\nexport default creator\n"],"names":["creator","opts","defaultOptions","shouldPreserve","Boolean","preserve","postcssPlugin","Rule","rule","selector","includes","fallbackSelector","getFallbackSelector","cloneBefore","assign","postcss","selectorText","parser","selectors","walkPseudos","value","nodes","isNotHas","isParentInNotPseudo","attribute","getEscapedCss","String","parent","replaceWith","processSync","encodeURIComponent","replace","type"],"mappings":";;;;;;;;MAEMA,OAAO,GAAG;AAAC;AAA0CC,IAA3C,KAAoD;AACnEA,EAAAA,IAAI,GAAG,OAAOA,IAAP,KAAgB,QAAhB,IAA4BA,IAA5B,IAAoCC,cAA3C;AAEA;;AACA,QAAMC,cAAc,GAAGC,OAAO,CAAC,cAAcH,IAAd,GAAqBA,IAAI,CAACI,QAA1B,GAAqC,IAAtC,CAA9B;AAEA,SAAO;AACNC,IAAAA,aAAa,EAAE,gBADT;AAENC,IAAAA,IAAI,EAAEC,IAAI,IAAI;AACb,UAAIA,IAAI,CAACC,QAAL,CAAcC,QAAd,CAAuB,OAAvB,CAAJ,EAAqC;AACpC,cAAMC,gBAAgB,GAAGC,mBAAmB,CAACJ,IAAI,CAACC,QAAN,CAA5C;AAEA,YAAIN,cAAJ,EAAoBK,IAAI,CAACK,WAAL,CAAiB;AAAEJ,UAAAA,QAAQ,EAAEE;AAAZ,SAAjB,EAApB,KACKH,IAAI,CAACM,MAAL,CAAY;AAAEL,UAAAA,QAAQ,EAAEE;AAAZ,SAAZ;AACL;AACD;AATK,GAAP;AAWA;;AAEDX,OAAO,CAACe,OAAR,GAAkB,IAAlB;;AAEA,MAAMH,mBAAmB,GAAG;AAAC;AAAsBI,YAAvB,KAAwCC,0BAAM,CAACC,SAAS,IAAI;AACvFA,EAAAA,SAAS,CAACC,WAAV,CAAsBV,QAAQ,IAAI;AACjC,QAAIA,QAAQ,CAACW,KAAT,KAAmB,MAAnB,IAA6BX,QAAQ,CAACY,KAA1C,EAAiD;AAChD,YAAMC,QAAQ,GAAGC,mBAAmB,CAACd,QAAD,CAApC;AAEAA,MAAAA,QAAQ,CAACW,KAAT,GAAiBE,QAAQ,GAAG,UAAH,GAAgB,MAAzC;AAEA,YAAME,SAAS,GAAGP,0BAAM,CAACO,SAAP,CAAiB;AAClCA,QAAAA,SAAS,EAAEC,aAAa,CAACC,MAAM,CAACjB,QAAD,CAAP;AADU,OAAjB,CAAlB;;AAIA,UAAIa,QAAJ,EAAc;AACbb,QAAAA,QAAQ,CAACkB,MAAT,CAAgBA,MAAhB,CAAuBC,WAAvB,CAAmCJ,SAAnC;AACA,OAFD,MAEO;AACNf,QAAAA,QAAQ,CAACmB,WAAT,CAAqBJ,SAArB;AACA;AACD;AACD,GAhBD;AAiBA,CAlByE,CAAN,CAkBjEK,WAlBiE,CAkBrDb,YAlBqD,CAApE;AAoBA;;;AACA,MAAMd,cAAc,GAAG;AAAEG,EAAAA,QAAQ,EAAE;AAAZ,CAAvB;AAEA;;AACA,MAAMoB,aAAa,GAAG;AAAC;AAAsBL,KAAvB,KAAiCU,kBAAkB,CAACV,KAAD,CAAlB,CAA0BW,OAA1B,CAAkC,MAAlC,EAA0C,GAA1C,EAA+CA,OAA/C,CAAuD,MAAvD,EAA+D,GAA/D,EAAoEA,OAApE,CAA4E,MAA5E,EAAoF,GAApF,EAAyFA,OAAzF,CAAiG,MAAjG,EAAyG,GAAzG,EAA8GA,OAA9G,CAAsH,aAAtH,EAAqI,MAArI,CAAvD;AAEA;;;AACA,MAAMR,mBAAmB,GAAId,QAAD;AAAA;;AAAA,SAAc,qBAAAA,QAAQ,CAACkB,MAAT,+FAAiBA,MAAjB,gFAAyBK,IAAzB,MAAkC,QAAlC,IAA8CvB,QAAQ,CAACkB,MAAT,CAAgBA,MAAhB,CAAuBP,KAAvB,KAAiC,MAA7F;AAAA,CAA5B;;;;"}
|
package/postcss.mjs
CHANGED
|
@@ -1,44 +1,67 @@
|
|
|
1
1
|
import parser from 'postcss-selector-parser';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
selector.parent.parent.replaceWith(attribute);
|
|
20
|
-
} else {
|
|
21
|
-
selector.replaceWith(attribute);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
2
|
+
|
|
3
|
+
const creator = (
|
|
4
|
+
/** @type {{ preserve: true | false }} */
|
|
5
|
+
opts) => {
|
|
6
|
+
opts = typeof opts === 'object' && opts || defaultOptions;
|
|
7
|
+
/** Whether the original rule should be preserved. */
|
|
8
|
+
|
|
9
|
+
const shouldPreserve = Boolean('preserve' in opts ? opts.preserve : true);
|
|
10
|
+
return {
|
|
11
|
+
postcssPlugin: 'css-has-pseudo',
|
|
12
|
+
Rule: rule => {
|
|
13
|
+
if (rule.selector.includes(':has(')) {
|
|
14
|
+
const fallbackSelector = getFallbackSelector(rule.selector);
|
|
15
|
+
if (shouldPreserve) rule.cloneBefore({
|
|
16
|
+
selector: fallbackSelector
|
|
17
|
+
});else rule.assign({
|
|
18
|
+
selector: fallbackSelector
|
|
24
19
|
});
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
creator.postcss = true;
|
|
26
|
+
|
|
27
|
+
const getFallbackSelector = (
|
|
28
|
+
/** @type {string} */
|
|
29
|
+
selectorText) => parser(selectors => {
|
|
30
|
+
selectors.walkPseudos(selector => {
|
|
31
|
+
if (selector.value === ':has' && selector.nodes) {
|
|
32
|
+
const isNotHas = isParentInNotPseudo(selector);
|
|
33
|
+
selector.value = isNotHas ? ':not-has' : ':has';
|
|
34
|
+
const attribute = parser.attribute({
|
|
35
|
+
attribute: getEscapedCss(String(selector))
|
|
28
36
|
});
|
|
29
37
|
|
|
30
|
-
if (
|
|
31
|
-
|
|
38
|
+
if (isNotHas) {
|
|
39
|
+
selector.parent.parent.replaceWith(attribute);
|
|
32
40
|
} else {
|
|
33
|
-
|
|
41
|
+
selector.replaceWith(attribute);
|
|
34
42
|
}
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
});
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}).processSync(selectorText);
|
|
46
|
+
/** Default options. */
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
const defaultOptions = {
|
|
50
|
+
preserve: true
|
|
51
|
+
};
|
|
52
|
+
/** Returns the string as an escaped CSS identifier. */
|
|
53
|
+
|
|
54
|
+
const getEscapedCss = (
|
|
55
|
+
/** @type {string} */
|
|
56
|
+
value) => encodeURIComponent(value).replace(/%3A/g, ':').replace(/%5B/g, '[').replace(/%5D/g, ']').replace(/%2C/g, ',').replace(/[():%[\],]/g, '\\$&');
|
|
57
|
+
/** Returns whether the selector is within a `:not` pseudo-class. */
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
const isParentInNotPseudo = selector => {
|
|
61
|
+
var _selector$parent, _selector$parent$pare;
|
|
38
62
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
63
|
+
return ((_selector$parent = selector.parent) === null || _selector$parent === void 0 ? void 0 : (_selector$parent$pare = _selector$parent.parent) === null || _selector$parent$pare === void 0 ? void 0 : _selector$parent$pare.type) === 'pseudo' && selector.parent.parent.value === ':not';
|
|
64
|
+
};
|
|
42
65
|
|
|
43
|
-
export default
|
|
66
|
+
export { creator as default };
|
|
44
67
|
//# sourceMappingURL=postcss.mjs.map
|
package/postcss.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postcss.mjs","sources":["src/postcss.js"],"sourcesContent":["import parser from 'postcss-selector-parser'
|
|
1
|
+
{"version":3,"file":"postcss.mjs","sources":["src/postcss.js"],"sourcesContent":["import parser from 'postcss-selector-parser'\n\nconst creator = (/** @type {{ preserve: true | false }} */ opts) => {\n\topts = typeof opts === 'object' && opts || defaultOptions\n\n\t/** Whether the original rule should be preserved. */\n\tconst shouldPreserve = Boolean('preserve' in opts ? opts.preserve : true)\n\n\treturn {\n\t\tpostcssPlugin: 'css-has-pseudo',\n\t\tRule: rule => {\n\t\t\tif (rule.selector.includes(':has(')) {\n\t\t\t\tconst fallbackSelector = getFallbackSelector(rule.selector)\n\n\t\t\t\tif (shouldPreserve) rule.cloneBefore({ selector: fallbackSelector })\n\t\t\t\telse rule.assign({ selector: fallbackSelector })\n\t\t\t}\n\t\t},\n\t}\n}\n\ncreator.postcss = true\n\nconst getFallbackSelector = (/** @type {string} */ selectorText) => parser(selectors => {\n\tselectors.walkPseudos(selector => {\n\t\tif (selector.value === ':has' && selector.nodes) {\n\t\t\tconst isNotHas = isParentInNotPseudo(selector)\n\n\t\t\tselector.value = isNotHas ? ':not-has' : ':has'\n\n\t\t\tconst attribute = parser.attribute({\n\t\t\t\tattribute: getEscapedCss(String(selector))\n\t\t\t})\n\n\t\t\tif (isNotHas) {\n\t\t\t\tselector.parent.parent.replaceWith(attribute)\n\t\t\t} else {\n\t\t\t\tselector.replaceWith(attribute)\n\t\t\t}\n\t\t}\n\t})\n}).processSync(selectorText)\n\n/** Default options. */\nconst defaultOptions = { preserve: true }\n\n/** Returns the string as an escaped CSS identifier. */\nconst getEscapedCss = (/** @type {string} */ value) => encodeURIComponent(value).replace(/%3A/g, ':').replace(/%5B/g, '[').replace(/%5D/g, ']').replace(/%2C/g, ',').replace(/[():%[\\],]/g, '\\\\$&')\n\n/** Returns whether the selector is within a `:not` pseudo-class. */\nconst isParentInNotPseudo = (selector) => selector.parent?.parent?.type === 'pseudo' && selector.parent.parent.value === ':not'\n\nexport default creator\n"],"names":["creator","opts","defaultOptions","shouldPreserve","Boolean","preserve","postcssPlugin","Rule","rule","selector","includes","fallbackSelector","getFallbackSelector","cloneBefore","assign","postcss","selectorText","parser","selectors","walkPseudos","value","nodes","isNotHas","isParentInNotPseudo","attribute","getEscapedCss","String","parent","replaceWith","processSync","encodeURIComponent","replace","type"],"mappings":";;MAEMA,OAAO,GAAG;AAAC;AAA0CC,IAA3C,KAAoD;AACnEA,EAAAA,IAAI,GAAG,OAAOA,IAAP,KAAgB,QAAhB,IAA4BA,IAA5B,IAAoCC,cAA3C;AAEA;;AACA,QAAMC,cAAc,GAAGC,OAAO,CAAC,cAAcH,IAAd,GAAqBA,IAAI,CAACI,QAA1B,GAAqC,IAAtC,CAA9B;AAEA,SAAO;AACNC,IAAAA,aAAa,EAAE,gBADT;AAENC,IAAAA,IAAI,EAAEC,IAAI,IAAI;AACb,UAAIA,IAAI,CAACC,QAAL,CAAcC,QAAd,CAAuB,OAAvB,CAAJ,EAAqC;AACpC,cAAMC,gBAAgB,GAAGC,mBAAmB,CAACJ,IAAI,CAACC,QAAN,CAA5C;AAEA,YAAIN,cAAJ,EAAoBK,IAAI,CAACK,WAAL,CAAiB;AAAEJ,UAAAA,QAAQ,EAAEE;AAAZ,SAAjB,EAApB,KACKH,IAAI,CAACM,MAAL,CAAY;AAAEL,UAAAA,QAAQ,EAAEE;AAAZ,SAAZ;AACL;AACD;AATK,GAAP;AAWA;;AAEDX,OAAO,CAACe,OAAR,GAAkB,IAAlB;;AAEA,MAAMH,mBAAmB,GAAG;AAAC;AAAsBI,YAAvB,KAAwCC,MAAM,CAACC,SAAS,IAAI;AACvFA,EAAAA,SAAS,CAACC,WAAV,CAAsBV,QAAQ,IAAI;AACjC,QAAIA,QAAQ,CAACW,KAAT,KAAmB,MAAnB,IAA6BX,QAAQ,CAACY,KAA1C,EAAiD;AAChD,YAAMC,QAAQ,GAAGC,mBAAmB,CAACd,QAAD,CAApC;AAEAA,MAAAA,QAAQ,CAACW,KAAT,GAAiBE,QAAQ,GAAG,UAAH,GAAgB,MAAzC;AAEA,YAAME,SAAS,GAAGP,MAAM,CAACO,SAAP,CAAiB;AAClCA,QAAAA,SAAS,EAAEC,aAAa,CAACC,MAAM,CAACjB,QAAD,CAAP;AADU,OAAjB,CAAlB;;AAIA,UAAIa,QAAJ,EAAc;AACbb,QAAAA,QAAQ,CAACkB,MAAT,CAAgBA,MAAhB,CAAuBC,WAAvB,CAAmCJ,SAAnC;AACA,OAFD,MAEO;AACNf,QAAAA,QAAQ,CAACmB,WAAT,CAAqBJ,SAArB;AACA;AACD;AACD,GAhBD;AAiBA,CAlByE,CAAN,CAkBjEK,WAlBiE,CAkBrDb,YAlBqD,CAApE;AAoBA;;;AACA,MAAMd,cAAc,GAAG;AAAEG,EAAAA,QAAQ,EAAE;AAAZ,CAAvB;AAEA;;AACA,MAAMoB,aAAa,GAAG;AAAC;AAAsBL,KAAvB,KAAiCU,kBAAkB,CAACV,KAAD,CAAlB,CAA0BW,OAA1B,CAAkC,MAAlC,EAA0C,GAA1C,EAA+CA,OAA/C,CAAuD,MAAvD,EAA+D,GAA/D,EAAoEA,OAApE,CAA4E,MAA5E,EAAoF,GAApF,EAAyFA,OAAzF,CAAiG,MAAjG,EAAyG,GAAzG,EAA8GA,OAA9G,CAAsH,aAAtH,EAAqI,MAArI,CAAvD;AAEA;;;AACA,MAAMR,mBAAmB,GAAId,QAAD;AAAA;;AAAA,SAAc,qBAAAA,QAAQ,CAACkB,MAAT,+FAAiBA,MAAjB,gFAAyBK,IAAzB,MAAkC,QAAlC,IAA8CvB,QAAQ,CAACkB,MAAT,CAAgBA,MAAhB,CAAuBP,KAAvB,KAAiC,MAA7F;AAAA,CAA5B;;;;"}
|
package/CHANGELOG.md
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
# Changes to CSS Has Pseudo
|
|
2
|
-
|
|
3
|
-
### 0.8.0 (November 26, 2018)
|
|
4
|
-
|
|
5
|
-
- Fixed an issue where attribute names were not being properly encoded
|
|
6
|
-
|
|
7
|
-
### 0.7.0 (November 25, 2018)
|
|
8
|
-
|
|
9
|
-
- Replaced `setImmediate` with `requestAnimationFrame` for future compatibility
|
|
10
|
-
|
|
11
|
-
### 0.6.0 (November 25, 2018)
|
|
12
|
-
|
|
13
|
-
- Fixed an issue where nested rules were not supported
|
|
14
|
-
|
|
15
|
-
### 0.5.0 (November 21, 2018)
|
|
16
|
-
|
|
17
|
-
- Further optimize script; from 775 bytes to 757 bytes
|
|
18
|
-
|
|
19
|
-
### 0.4.0 (November 21, 2018)
|
|
20
|
-
|
|
21
|
-
- Fixed an issue with the browser script not picking up added nodes
|
|
22
|
-
|
|
23
|
-
### 0.3.0 (November 21, 2018)
|
|
24
|
-
|
|
25
|
-
- Fixed the misnamed function name for the browser-ready script
|
|
26
|
-
|
|
27
|
-
### 0.2.0 (November 21, 2018)
|
|
28
|
-
|
|
29
|
-
- Improved browser compatibility with updated parsers, encoders, and decoders
|
|
30
|
-
- Improved performance by walking the DOM less
|
|
31
|
-
- Reduced script size by 9%; from 855 bytes to 775 bytes
|
|
32
|
-
|
|
33
|
-
### 0.1.0 (November 20, 2018)
|
|
34
|
-
|
|
35
|
-
- Initial version
|