-
-
Notifications
You must be signed in to change notification settings - Fork 76
has pseudo : fix cleanup of rules in browsers with native support #751
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
"use strict";var e=require("postcss-selector-parser"),t=require("@csstools/selector-specificity"),s=require("postcss-value-parser");function encodeCSS(e){if(""===e)return"";let t,s="";for(let o=0;o<e.length;o++)t=e.charCodeAt(o).toString(36),s+=0===o?t:"-"+t;return"csstools-has-"+s}function isGuardedByAtSupportsFromAtRuleParams(e){if(!e.toLowerCase().includes(":has("))return!1;let t=!1;try{const o=new Set;s(e).walk((e=>{if("function"===e.type&&"selector"===e.value.toLowerCase())return o.add(s.stringify(e.nodes)),!1})),o.forEach((e=>{selectorContainsHasPseudo(e)&&(t=!0)}))}catch(e){}return t}function selectorContainsHasPseudo(t){if(!t.toLowerCase().includes(":has("))return!1;let s=!1;try{e().astSync(t).walk((e=>{if("pseudo"===e.type&&":has"===e.value.toLowerCase()&&e.nodes&&e.nodes.length>0)return s=!0,!1}))}catch(e){}return s}const creator=s=>{const o={preserve:!0,specificityMatchingName:"does-not-exist",...s||{}},r=":not(#"+o.specificityMatchingName+")",n=":not(."+o.specificityMatchingName+")",a=":not("+o.specificityMatchingName+")";return{postcssPlugin:"css-has-pseudo-experimental",RuleExit:(s,{result:c})=>{if(!s.selector.toLowerCase().includes(":has(")||isWithinSupportCheck(s))return;const i=s.selectors.map((i=>{if(!i.toLowerCase().includes(":has("))return i;let l;try{l=e().astSync(i)}catch(e){return s.warn(c,`Failed to parse selector : "${i}" with message: "${e.message}"`),i}if(void 0===l)return i;l.walkPseudos((t=>{let s=t.parent,r=!1;for(;s;)e.isPseudoClass(s)&&":has"===s.value.toLowerCase()&&(r=!0),s=s.parent;r&&(":visited"===t.value.toLowerCase()&&t.replaceWith(e.className({value:o.specificityMatchingName})),":any-link"===t.value.toLowerCase()&&(t.value=":link"))})),l.walkPseudos((s=>{if(":has"!==s.value.toLowerCase()||!s.nodes)return;let o=s.parent??s;if(o!==s){let t=o.nodes.length;e:for(let s=0;s<o.nodes.length;s++){const r=o.nodes[s];if(e.isPseudoElement(r))for(let e=s-1;e>=0;e--)if("combinator"!==o.nodes[s].type&&"comment"!==o.nodes[s].type){t=e+1;break e}}if(t<o.nodes.length){const s=e.selector({value:"",nodes:[]});o.nodes.slice(0,t).forEach((e=>{delete e.parent,s.append(e)}));const r=e.selector({value:"",nodes:[]});o.nodes.slice(t).forEach((e=>{delete e.parent,r.append(e)}));const n=e.selector({value:"",nodes:[]});n.append(s),n.append(r),o.replaceWith(n),o=s}}const c="["+encodeCSS(o.toString())+"]",i=t.selectorSpecificity(o);let l=c;for(let e=0;e<i.a;e++)l+=r;const u=Math.max(1,i.b)-1;for(let e=0;e<u;e++)l+=n;for(let e=0;e<i.c;e++)l+=a;const p=e().astSync(l);o.replaceWith(p.nodes[0])}));const u=l.toString();return u!==i?".js-has-pseudo "+u:i}));i.join(",")!==s.selectors.join(",")&&(s.cloneBefore({selectors:i}),o.preserve||s.remove())}}};function isWithinSupportCheck(e){let t=e.parent;for(;t;){if("atrule"===t.type&&isGuardedByAtSupportsFromAtRuleParams(t.params))return!0;t=t.parent}return!1}creator.postcss=!0,module.exports=creator; | ||
"use strict";var e=require("postcss-selector-parser"),t=require("@csstools/selector-specificity"),s=require("postcss-value-parser");function encodeCSS(e){if(""===e)return"";let t,s="";for(let o=0;o<e.length;o++)t=e.charCodeAt(o).toString(36),s+=0===o?t:"-"+t;return"csstools-has-"+s}function isGuardedByAtSupportsFromAtRuleParams(e){if(!e.toLowerCase().includes(":has("))return!1;let t=!1;try{const o=new Set;s(e).walk((e=>{if("function"===e.type&&"selector"===e.value.toLowerCase())return o.add(s.stringify(e.nodes)),!1})),o.forEach((e=>{selectorContainsHasPseudo(e)&&(t=!0)}))}catch(e){}return t}function selectorContainsHasPseudo(t){if(!t.toLowerCase().includes(":has("))return!1;let s=!1;try{e().astSync(t).walk((e=>{if("pseudo"===e.type&&":has"===e.value.toLowerCase()&&e.nodes&&e.nodes.length>0)return s=!0,!1}))}catch(e){}return s}const creator=s=>{const o={preserve:!0,specificityMatchingName:"does-not-exist",...s||{}},r=":not(#"+o.specificityMatchingName+")",n=":not(."+o.specificityMatchingName+")",a=":not("+o.specificityMatchingName+")";return{postcssPlugin:"css-has-pseudo",RuleExit:(s,{result:c})=>{if(!s.selector.toLowerCase().includes(":has(")||isWithinSupportCheck(s))return;const i=s.selectors.map((i=>{if(!i.toLowerCase().includes(":has("))return i;let l;try{l=e().astSync(i)}catch(e){return s.warn(c,`Failed to parse selector : "${i}" with message: "${e.message}"`),i}if(void 0===l)return i;l.walkPseudos((t=>{let s=t.parent,r=!1;for(;s;)e.isPseudoClass(s)&&":has"===s.value.toLowerCase()&&(r=!0),s=s.parent;r&&(":visited"===t.value.toLowerCase()&&t.replaceWith(e.className({value:o.specificityMatchingName})),":any-link"===t.value.toLowerCase()&&(t.value=":link"))})),l.walkPseudos((s=>{if(":has"!==s.value.toLowerCase()||!s.nodes)return;let o=s.parent??s;if(o!==s){let t=o.nodes.length;e:for(let s=0;s<o.nodes.length;s++){const r=o.nodes[s];if(e.isPseudoElement(r))for(let e=s-1;e>=0;e--)if("combinator"!==o.nodes[s].type&&"comment"!==o.nodes[s].type){t=e+1;break e}}if(t<o.nodes.length){const s=e.selector({value:"",nodes:[]});o.nodes.slice(0,t).forEach((e=>{delete e.parent,s.append(e)}));const r=e.selector({value:"",nodes:[]});o.nodes.slice(t).forEach((e=>{delete e.parent,r.append(e)}));const n=e.selector({value:"",nodes:[]});n.append(s),n.append(r),o.replaceWith(n),o=s}}const c="["+encodeCSS(o.toString())+"]",i=t.selectorSpecificity(o);let l=c;for(let e=0;e<i.a;e++)l+=r;const u=Math.max(1,i.b)-1;for(let e=0;e<u;e++)l+=n;for(let e=0;e<i.c;e++)l+=a;const p=e().astSync(l);o.replaceWith(p.nodes[0])}));const u=l.toString();return u!==i?".js-has-pseudo "+u:i}));i.join(",")!==s.selectors.join(",")&&(s.cloneBefore({selectors:i}),o.preserve||s.remove())}}};function isWithinSupportCheck(e){let t=e.parent;for(;t;){if("atrule"===t.type&&isGuardedByAtSupportsFromAtRuleParams(t.params))return!0;t=t.parent}return!1}creator.postcss=!0,module.exports=creator; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
import e from"postcss-selector-parser";import{selectorSpecificity as t}from"@csstools/selector-specificity";import s from"postcss-value-parser";function encodeCSS(e){if(""===e)return"";let t,s="";for(let o=0;o<e.length;o++)t=e.charCodeAt(o).toString(36),s+=0===o?t:"-"+t;return"csstools-has-"+s}function isGuardedByAtSupportsFromAtRuleParams(e){if(!e.toLowerCase().includes(":has("))return!1;let t=!1;try{const o=new Set;s(e).walk((e=>{if("function"===e.type&&"selector"===e.value.toLowerCase())return o.add(s.stringify(e.nodes)),!1})),o.forEach((e=>{selectorContainsHasPseudo(e)&&(t=!0)}))}catch(e){}return t}function selectorContainsHasPseudo(t){if(!t.toLowerCase().includes(":has("))return!1;let s=!1;try{e().astSync(t).walk((e=>{if("pseudo"===e.type&&":has"===e.value.toLowerCase()&&e.nodes&&e.nodes.length>0)return s=!0,!1}))}catch(e){}return s}const creator=s=>{const o={preserve:!0,specificityMatchingName:"does-not-exist",...s||{}},r=":not(#"+o.specificityMatchingName+")",n=":not(."+o.specificityMatchingName+")",a=":not("+o.specificityMatchingName+")";return{postcssPlugin:"css-has-pseudo-experimental",RuleExit:(s,{result:c})=>{if(!s.selector.toLowerCase().includes(":has(")||isWithinSupportCheck(s))return;const i=s.selectors.map((i=>{if(!i.toLowerCase().includes(":has("))return i;let l;try{l=e().astSync(i)}catch(e){return s.warn(c,`Failed to parse selector : "${i}" with message: "${e.message}"`),i}if(void 0===l)return i;l.walkPseudos((t=>{let s=t.parent,r=!1;for(;s;)e.isPseudoClass(s)&&":has"===s.value.toLowerCase()&&(r=!0),s=s.parent;r&&(":visited"===t.value.toLowerCase()&&t.replaceWith(e.className({value:o.specificityMatchingName})),":any-link"===t.value.toLowerCase()&&(t.value=":link"))})),l.walkPseudos((s=>{if(":has"!==s.value.toLowerCase()||!s.nodes)return;let o=s.parent??s;if(o!==s){let t=o.nodes.length;e:for(let s=0;s<o.nodes.length;s++){const r=o.nodes[s];if(e.isPseudoElement(r))for(let e=s-1;e>=0;e--)if("combinator"!==o.nodes[s].type&&"comment"!==o.nodes[s].type){t=e+1;break e}}if(t<o.nodes.length){const s=e.selector({value:"",nodes:[]});o.nodes.slice(0,t).forEach((e=>{delete e.parent,s.append(e)}));const r=e.selector({value:"",nodes:[]});o.nodes.slice(t).forEach((e=>{delete e.parent,r.append(e)}));const n=e.selector({value:"",nodes:[]});n.append(s),n.append(r),o.replaceWith(n),o=s}}const c="["+encodeCSS(o.toString())+"]",i=t(o);let l=c;for(let e=0;e<i.a;e++)l+=r;const u=Math.max(1,i.b)-1;for(let e=0;e<u;e++)l+=n;for(let e=0;e<i.c;e++)l+=a;const p=e().astSync(l);o.replaceWith(p.nodes[0])}));const u=l.toString();return u!==i?".js-has-pseudo "+u:i}));i.join(",")!==s.selectors.join(",")&&(s.cloneBefore({selectors:i}),o.preserve||s.remove())}}};function isWithinSupportCheck(e){let t=e.parent;for(;t;){if("atrule"===t.type&&isGuardedByAtSupportsFromAtRuleParams(t.params))return!0;t=t.parent}return!1}creator.postcss=!0;export{creator as default}; | ||
import e from"postcss-selector-parser";import{selectorSpecificity as t}from"@csstools/selector-specificity";import s from"postcss-value-parser";function encodeCSS(e){if(""===e)return"";let t,s="";for(let o=0;o<e.length;o++)t=e.charCodeAt(o).toString(36),s+=0===o?t:"-"+t;return"csstools-has-"+s}function isGuardedByAtSupportsFromAtRuleParams(e){if(!e.toLowerCase().includes(":has("))return!1;let t=!1;try{const o=new Set;s(e).walk((e=>{if("function"===e.type&&"selector"===e.value.toLowerCase())return o.add(s.stringify(e.nodes)),!1})),o.forEach((e=>{selectorContainsHasPseudo(e)&&(t=!0)}))}catch(e){}return t}function selectorContainsHasPseudo(t){if(!t.toLowerCase().includes(":has("))return!1;let s=!1;try{e().astSync(t).walk((e=>{if("pseudo"===e.type&&":has"===e.value.toLowerCase()&&e.nodes&&e.nodes.length>0)return s=!0,!1}))}catch(e){}return s}const creator=s=>{const o={preserve:!0,specificityMatchingName:"does-not-exist",...s||{}},r=":not(#"+o.specificityMatchingName+")",n=":not(."+o.specificityMatchingName+")",a=":not("+o.specificityMatchingName+")";return{postcssPlugin:"css-has-pseudo",RuleExit:(s,{result:c})=>{if(!s.selector.toLowerCase().includes(":has(")||isWithinSupportCheck(s))return;const i=s.selectors.map((i=>{if(!i.toLowerCase().includes(":has("))return i;let l;try{l=e().astSync(i)}catch(e){return s.warn(c,`Failed to parse selector : "${i}" with message: "${e.message}"`),i}if(void 0===l)return i;l.walkPseudos((t=>{let s=t.parent,r=!1;for(;s;)e.isPseudoClass(s)&&":has"===s.value.toLowerCase()&&(r=!0),s=s.parent;r&&(":visited"===t.value.toLowerCase()&&t.replaceWith(e.className({value:o.specificityMatchingName})),":any-link"===t.value.toLowerCase()&&(t.value=":link"))})),l.walkPseudos((s=>{if(":has"!==s.value.toLowerCase()||!s.nodes)return;let o=s.parent??s;if(o!==s){let t=o.nodes.length;e:for(let s=0;s<o.nodes.length;s++){const r=o.nodes[s];if(e.isPseudoElement(r))for(let e=s-1;e>=0;e--)if("combinator"!==o.nodes[s].type&&"comment"!==o.nodes[s].type){t=e+1;break e}}if(t<o.nodes.length){const s=e.selector({value:"",nodes:[]});o.nodes.slice(0,t).forEach((e=>{delete e.parent,s.append(e)}));const r=e.selector({value:"",nodes:[]});o.nodes.slice(t).forEach((e=>{delete e.parent,r.append(e)}));const n=e.selector({value:"",nodes:[]});n.append(s),n.append(r),o.replaceWith(n),o=s}}const c="["+encodeCSS(o.toString())+"]",i=t(o);let l=c;for(let e=0;e<i.a;e++)l+=r;const u=Math.max(1,i.b)-1;for(let e=0;e<u;e++)l+=n;for(let e=0;e<i.c;e++)l+=a;const p=e().astSync(l);o.replaceWith(p.nodes[0])}));const u=l.toString();return u!==i?".js-has-pseudo "+u:i}));i.join(",")!==s.selectors.join(",")&&(s.cloneBefore({selectors:i}),o.preserve||s.remove())}}};function isWithinSupportCheck(e){let t=e.parent;for(;t;){if("atrule"===t.type&&isGuardedByAtSupportsFromAtRuleParams(t.params))return!0;t=t.parent}return!1}creator.postcss=!0;export{creator as default}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,18 +4,9 @@ import '@mrhenry/core-web/modules/~element-qsa-has.js'; | |
import extractEncodedSelectors from './encode/extract.mjs'; | ||
import encodeCSS from './encode/encode.mjs'; | ||
|
||
function hasNativeSupport(document) { | ||
function hasNativeSupport() { | ||
try { | ||
// Chrome does not support forgiving selector lists in :has() | ||
document.querySelector(':has(*, :does-not-exist, > *)'); | ||
document.querySelector(':has(:has(any))'); | ||
|
||
// Safari incorrectly returns the html element with this query | ||
if (document.querySelector(':has(:scope *)')) { | ||
return false; | ||
} | ||
|
||
if (!('CSS' in self) || !('supports' in self.CSS) || !self.CSS.supports(':has(any)')) { | ||
if (!('CSS' in self) || !('supports' in self.CSS) || !self.CSS.supports('selector(:has(div))')) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bug fix : needed to check for |
||
return false; | ||
} | ||
|
||
|
@@ -40,7 +31,7 @@ export default function cssHasPseudo(document, options) { | |
forcePolyfill: (!!options.forcePolyfill) || false, | ||
}; | ||
|
||
options.mustPolyfill = options.forcePolyfill || !hasNativeSupport(document); | ||
options.mustPolyfill = options.forcePolyfill || !hasNativeSupport(); | ||
|
||
if (!Array.isArray(options.observedAttributes)) { | ||
options.observedAttributes = []; | ||
|
@@ -238,7 +229,7 @@ export default function cssHasPseudo(document, options) { | |
function walkStyleSheet(styleSheet) { | ||
try { | ||
// walk a css rule to collect observed css rules | ||
[].forEach.call(styleSheet.cssRules || [], (rule) => { | ||
[].forEach.call(styleSheet.cssRules || [], (rule, index) => { | ||
if (rule.selectorText) { | ||
rule.selectorText = rule.selectorText.replace(/\.js-has-pseudo\s/g, ''); | ||
|
||
|
@@ -250,7 +241,7 @@ export default function cssHasPseudo(document, options) { | |
} | ||
|
||
if (!options.mustPolyfill) { | ||
rule.deleteRule(); | ||
styleSheet.deleteRule(index); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was the actual bug. Combined with With this change the polyfill correctly cleans up the fallback rules for browsers with native support. |
||
return; | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,7 +24,7 @@ const creator: PluginCreator<pluginOptions> = (opts?: pluginOptions) => { | |
const specificityMatchingNameTag = ':not(' + options.specificityMatchingName + ')'; | ||
|
||
return { | ||
postcssPlugin: 'css-has-pseudo-experimental', | ||
postcssPlugin: 'css-has-pseudo', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Left over from experimental track. |
||
RuleExit: (rule, { result }) => { | ||
if (!rule.selector.toLowerCase().includes(':has(') || isWithinSupportCheck(rule)) { | ||
return; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The specification changed so we need to update the feature detection to match.
Functional changes were done in the
querySelector(':has()')
polyfill fromcore-web