diff --git a/plugin-packs/postcss-preset-env/test/basic.autoprefixer.expect.css b/plugin-packs/postcss-preset-env/test/basic.autoprefixer.expect.css index df2e849b7..5419af1cf 100644 --- a/plugin-packs/postcss-preset-env/test/basic.autoprefixer.expect.css +++ b/plugin-packs/postcss-preset-env/test/basic.autoprefixer.expect.css @@ -269,7 +269,7 @@ background-color: yellow; } -[csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) { +.js-has-pseudo [csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) { background-color: yellow; } diff --git a/plugin-packs/postcss-preset-env/test/basic.autoprefixer.false.expect.css b/plugin-packs/postcss-preset-env/test/basic.autoprefixer.false.expect.css index df2e849b7..5419af1cf 100644 --- a/plugin-packs/postcss-preset-env/test/basic.autoprefixer.false.expect.css +++ b/plugin-packs/postcss-preset-env/test/basic.autoprefixer.false.expect.css @@ -269,7 +269,7 @@ background-color: yellow; } -[csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) { +.js-has-pseudo [csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) { background-color: yellow; } diff --git a/plugin-packs/postcss-preset-env/test/basic.ch38.expect.css b/plugin-packs/postcss-preset-env/test/basic.ch38.expect.css index 2e884a904..79e8545e3 100644 --- a/plugin-packs/postcss-preset-env/test/basic.ch38.expect.css +++ b/plugin-packs/postcss-preset-env/test/basic.ch38.expect.css @@ -189,7 +189,7 @@ background-color: yellow; } -[csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) { +.js-has-pseudo [csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) { background-color: yellow; } diff --git a/plugin-packs/postcss-preset-env/test/basic.ch88-ff78-saf10.expect.css b/plugin-packs/postcss-preset-env/test/basic.ch88-ff78-saf10.expect.css index 14a3536cc..0ecb2c36d 100644 --- a/plugin-packs/postcss-preset-env/test/basic.ch88-ff78-saf10.expect.css +++ b/plugin-packs/postcss-preset-env/test/basic.ch88-ff78-saf10.expect.css @@ -188,7 +188,7 @@ h1.test-custom-selectors,h2.test-custom-selectors,h3.test-custom-selectors,h4.te background-color: yellow; } -[csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) { +.js-has-pseudo [csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) { background-color: yellow; } diff --git a/plugin-packs/postcss-preset-env/test/basic.ch88-ff78.expect.css b/plugin-packs/postcss-preset-env/test/basic.ch88-ff78.expect.css index 9c8b1ddce..c7d13158d 100644 --- a/plugin-packs/postcss-preset-env/test/basic.ch88-ff78.expect.css +++ b/plugin-packs/postcss-preset-env/test/basic.ch88-ff78.expect.css @@ -181,7 +181,7 @@ h1.test-custom-selectors,h2.test-custom-selectors,h3.test-custom-selectors,h4.te background-color: yellow; } -[csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) { +.js-has-pseudo [csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) { background-color: yellow; } diff --git a/plugin-packs/postcss-preset-env/test/basic.ch88-ff78.no-is-pseudo.expect.css b/plugin-packs/postcss-preset-env/test/basic.ch88-ff78.no-is-pseudo.expect.css index 5c6131ecb..1424e71ec 100644 --- a/plugin-packs/postcss-preset-env/test/basic.ch88-ff78.no-is-pseudo.expect.css +++ b/plugin-packs/postcss-preset-env/test/basic.ch88-ff78.no-is-pseudo.expect.css @@ -181,7 +181,7 @@ h1.test-custom-selectors,h2.test-custom-selectors,h3.test-custom-selectors,h4.te background-color: yellow; } -[csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) { +.js-has-pseudo [csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) { background-color: yellow; } diff --git a/plugin-packs/postcss-preset-env/test/basic.expect.css b/plugin-packs/postcss-preset-env/test/basic.expect.css index 62e0e957d..8d9eb92b8 100644 --- a/plugin-packs/postcss-preset-env/test/basic.expect.css +++ b/plugin-packs/postcss-preset-env/test/basic.expect.css @@ -292,7 +292,7 @@ background-color: yellow; } -[csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) { +.js-has-pseudo [csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) { background-color: yellow; } diff --git a/plugin-packs/postcss-preset-env/test/basic.ff49.expect.css b/plugin-packs/postcss-preset-env/test/basic.ff49.expect.css index 6bea06f27..035dd4fbe 100644 --- a/plugin-packs/postcss-preset-env/test/basic.ff49.expect.css +++ b/plugin-packs/postcss-preset-env/test/basic.ff49.expect.css @@ -185,7 +185,7 @@ background-color: yellow; } -[csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) { +.js-has-pseudo [csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) { background-color: yellow; } diff --git a/plugin-packs/postcss-preset-env/test/basic.ff66.expect.css b/plugin-packs/postcss-preset-env/test/basic.ff66.expect.css index 276166755..2a2d876c6 100644 --- a/plugin-packs/postcss-preset-env/test/basic.ff66.expect.css +++ b/plugin-packs/postcss-preset-env/test/basic.ff66.expect.css @@ -173,7 +173,7 @@ background-color: yellow; } -[csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) { +.js-has-pseudo [csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) { background-color: yellow; } diff --git a/plugin-packs/postcss-preset-env/test/basic.ie10.expect.css b/plugin-packs/postcss-preset-env/test/basic.ie10.expect.css index e475ca11e..fcff8b756 100644 --- a/plugin-packs/postcss-preset-env/test/basic.ie10.expect.css +++ b/plugin-packs/postcss-preset-env/test/basic.ie10.expect.css @@ -301,7 +301,7 @@ background-color: yellow; } -[csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) { +.js-has-pseudo [csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) { background-color: yellow; } diff --git a/plugin-packs/postcss-preset-env/test/basic.nesting.false.expect.css b/plugin-packs/postcss-preset-env/test/basic.nesting.false.expect.css index 76d385880..c27bc7134 100644 --- a/plugin-packs/postcss-preset-env/test/basic.nesting.false.expect.css +++ b/plugin-packs/postcss-preset-env/test/basic.nesting.false.expect.css @@ -290,7 +290,7 @@ h1.test-custom-selectors,h2.test-custom-selectors,h3.test-custom-selectors,h4.te background-color: yellow; } -[csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) { +.js-has-pseudo [csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) { background-color: yellow; } diff --git a/plugin-packs/postcss-preset-env/test/basic.op_mini.expect.css b/plugin-packs/postcss-preset-env/test/basic.op_mini.expect.css index 9b89b3b49..b1f0430fb 100644 --- a/plugin-packs/postcss-preset-env/test/basic.op_mini.expect.css +++ b/plugin-packs/postcss-preset-env/test/basic.op_mini.expect.css @@ -276,7 +276,7 @@ h1.test-custom-selectors,h2.test-custom-selectors,h3.test-custom-selectors,h4.te background-color: yellow; } -[csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) { +.js-has-pseudo [csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) { background-color: yellow; } diff --git a/plugin-packs/postcss-preset-env/test/basic.preserve.true.expect.css b/plugin-packs/postcss-preset-env/test/basic.preserve.true.expect.css index 6d71291f0..dd791edea 100644 --- a/plugin-packs/postcss-preset-env/test/basic.preserve.true.expect.css +++ b/plugin-packs/postcss-preset-env/test/basic.preserve.true.expect.css @@ -524,7 +524,7 @@ h1.test-custom-selectors,h2.test-custom-selectors,h3.test-custom-selectors,h4.te background-color: yellow; } -[csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) { +.js-has-pseudo [csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) { background-color: yellow; } diff --git a/plugin-packs/postcss-preset-env/test/basic.safari15.expect.css b/plugin-packs/postcss-preset-env/test/basic.safari15.expect.css index d8f82604b..dd650d488 100644 --- a/plugin-packs/postcss-preset-env/test/basic.safari15.expect.css +++ b/plugin-packs/postcss-preset-env/test/basic.safari15.expect.css @@ -157,7 +157,7 @@ background-color: yellow; } -[csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) { +.js-has-pseudo [csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) { background-color: yellow; } diff --git a/plugin-packs/postcss-preset-env/test/basic.stage0-ff49.expect.css b/plugin-packs/postcss-preset-env/test/basic.stage0-ff49.expect.css index cc04ea10a..40145efe7 100644 --- a/plugin-packs/postcss-preset-env/test/basic.stage0-ff49.expect.css +++ b/plugin-packs/postcss-preset-env/test/basic.stage0-ff49.expect.css @@ -190,7 +190,7 @@ h1.test-custom-selectors,h2.test-custom-selectors,h3.test-custom-selectors,h4.te background-color: yellow; } -[csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) { +.js-has-pseudo [csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) { background-color: yellow; } diff --git a/plugin-packs/postcss-preset-env/test/basic.stage0-ff66.expect.css b/plugin-packs/postcss-preset-env/test/basic.stage0-ff66.expect.css index a3f082c8d..1923dd412 100644 --- a/plugin-packs/postcss-preset-env/test/basic.stage0-ff66.expect.css +++ b/plugin-packs/postcss-preset-env/test/basic.stage0-ff66.expect.css @@ -178,7 +178,7 @@ h1.test-custom-selectors,h2.test-custom-selectors,h3.test-custom-selectors,h4.te background-color: yellow; } -[csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) { +.js-has-pseudo [csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) { background-color: yellow; } diff --git a/plugin-packs/postcss-preset-env/test/basic.stage0.expect.css b/plugin-packs/postcss-preset-env/test/basic.stage0.expect.css index 432db8789..765bc0055 100644 --- a/plugin-packs/postcss-preset-env/test/basic.stage0.expect.css +++ b/plugin-packs/postcss-preset-env/test/basic.stage0.expect.css @@ -297,7 +297,7 @@ h1.test-custom-selectors,h2.test-custom-selectors,h3.test-custom-selectors,h4.te background-color: yellow; } -[csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) { +.js-has-pseudo [csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) { background-color: yellow; } diff --git a/plugin-packs/postcss-preset-env/test/client-side-polyfills.stage-1.expect.css b/plugin-packs/postcss-preset-env/test/client-side-polyfills.stage-1.expect.css index fc8aa501a..519c343fc 100644 --- a/plugin-packs/postcss-preset-env/test/client-side-polyfills.stage-1.expect.css +++ b/plugin-packs/postcss-preset-env/test/client-side-polyfills.stage-1.expect.css @@ -10,7 +10,7 @@ order: 3; } -[csstools-has-1m-2w-2p-37-14-1a-2p-15] { +.js-has-pseudo [csstools-has-1m-2w-2p-37-14-1a-2p-15] { order: 4; } diff --git a/plugin-packs/postcss-preset-env/test/client-side-polyfills.stage-2.expect.css b/plugin-packs/postcss-preset-env/test/client-side-polyfills.stage-2.expect.css index b0b16a92d..0b8843026 100644 --- a/plugin-packs/postcss-preset-env/test/client-side-polyfills.stage-2.expect.css +++ b/plugin-packs/postcss-preset-env/test/client-side-polyfills.stage-2.expect.css @@ -10,7 +10,7 @@ order: 3; } -[csstools-has-1m-2w-2p-37-14-1a-2p-15] { +.js-has-pseudo [csstools-has-1m-2w-2p-37-14-1a-2p-15] { order: 4; } diff --git a/plugin-packs/postcss-preset-env/test/disable-client-side-polyfills.disabled.expect.css b/plugin-packs/postcss-preset-env/test/disable-client-side-polyfills.disabled.expect.css index f6292a76f..f746c0779 100644 --- a/plugin-packs/postcss-preset-env/test/disable-client-side-polyfills.disabled.expect.css +++ b/plugin-packs/postcss-preset-env/test/disable-client-side-polyfills.disabled.expect.css @@ -6,7 +6,7 @@ input:blank { order: 1; } -[csstools-has-2p-1m-2w-2p-37-14-1q-w-2x-31-2v-15]:not(does-not-exist):not(does-not-exist) { +.js-has-pseudo [csstools-has-2p-1m-2w-2p-37-14-1q-w-2x-31-2v-15]:not(does-not-exist):not(does-not-exist) { order: 2; } diff --git a/plugin-packs/postcss-preset-env/test/layers-basic.expect.css b/plugin-packs/postcss-preset-env/test/layers-basic.expect.css index 5e75036d8..4f49da553 100644 --- a/plugin-packs/postcss-preset-env/test/layers-basic.expect.css +++ b/plugin-packs/postcss-preset-env/test/layers-basic.expect.css @@ -474,7 +474,7 @@ h1.test-custom-selectors:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):n background-color: yellow; } -[csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#) { +.js-has-pseudo:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#) [csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) { background-color: yellow; } diff --git a/plugin-packs/postcss-preset-env/test/layers-basic.preserve.true.expect.css b/plugin-packs/postcss-preset-env/test/layers-basic.preserve.true.expect.css index 8ec136827..66fa37454 100644 --- a/plugin-packs/postcss-preset-env/test/layers-basic.preserve.true.expect.css +++ b/plugin-packs/postcss-preset-env/test/layers-basic.preserve.true.expect.css @@ -535,7 +535,7 @@ h1.test-custom-selectors:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):n background-color: yellow; } -[csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#) { +.js-has-pseudo:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#) [csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) { background-color: yellow; } diff --git a/plugins/css-has-pseudo/.tape.mjs b/plugins/css-has-pseudo/.tape.mjs index 58629ecdf..740b2253f 100644 --- a/plugins/css-has-pseudo/.tape.mjs +++ b/plugins/css-has-pseudo/.tape.mjs @@ -38,9 +38,6 @@ postcssTape(plugin)({ }, 'browser': { message: 'prepare CSS for chrome test', - options: { - preserve: false - } }, 'plugin-order-logical:before': { message: 'works with other plugins that modify selectors', diff --git a/plugins/css-has-pseudo/src/browser.js b/plugins/css-has-pseudo/src/browser.js index 398156d8c..41ffc3e6f 100644 --- a/plugins/css-has-pseudo/src/browser.js +++ b/plugins/css-has-pseudo/src/browser.js @@ -4,6 +4,28 @@ import '@mrhenry/core-web/modules/~element-qsa-has.js'; import extractEncodedSelectors from './encode/extract.mjs'; import encodeCSS from './encode/encode.mjs'; +function hasNativeSupport(document) { + 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)')) { + return false; + } + + } catch (_) { + return false; + } + + return true; +} + export default function cssHasPseudo(document, options) { // OPTIONS { @@ -18,23 +40,7 @@ export default function cssHasPseudo(document, options) { forcePolyfill: (!!options.forcePolyfill) || false, }; - if (!options.forcePolyfill) { - try { - // Chrome does not support forgiving selector lists in :has() - document.querySelector(':has(*, :does-not-exist, > *)'); - - // Safari incorrectly returns the html element with this query - if (!document.querySelector(':has(:scope *)')) { - // Native support detected. - // Doing early return. - return; - } - - // fallthrough to polyfill - } catch (_) { - // fallthrough to polyfill - } - } + options.mustPolyfill = options.forcePolyfill || !hasNativeSupport(document); if (!Array.isArray(options.observedAttributes)) { options.observedAttributes = []; @@ -56,6 +62,12 @@ export default function cssHasPseudo(document, options) { // walk all stylesheets to collect observed css rules [].forEach.call(document.styleSheets, walkStyleSheet); + if (!options.mustPolyfill) { + // Cleanup of rules will have happened in `walkStyleSheet` + // Native support will take over from here + return; + } + transformObservedItemsThrottled(); // observe DOM modifications that affect selectors @@ -228,6 +240,8 @@ export default function cssHasPseudo(document, options) { // walk a css rule to collect observed css rules [].forEach.call(styleSheet.cssRules || [], (rule) => { if (rule.selectorText) { + rule.selectorText = rule.selectorText.replace(/\.js-has-pseudo\s/g, ''); + try { // decode the selector text in all browsers to: const hasSelectors = extractEncodedSelectors(rule.selectorText.toString()); @@ -235,6 +249,11 @@ export default function cssHasPseudo(document, options) { return; } + if (!options.mustPolyfill) { + rule.deleteRule(); + return; + } + for (let i = 0; i < hasSelectors.length; i++) { const hasSelector = hasSelectors[i]; observedItems.push({ diff --git a/plugins/css-has-pseudo/src/index.ts b/plugins/css-has-pseudo/src/index.ts index a7527f225..63b0a2a2e 100644 --- a/plugins/css-has-pseudo/src/index.ts +++ b/plugins/css-has-pseudo/src/index.ts @@ -39,9 +39,20 @@ const creator: PluginCreator<{ preserve?: boolean, specificityMatchingName?: str return selector; } - let containsHasPseudo = false; selectorAST.walkPseudos((node) => { - containsHasPseudo = containsHasPseudo || (node.value.toLowerCase() === ':has' && node.nodes); + let parent = node.parent; + let insideHasPseudoClass = false; + while (parent) { + if (parser.isPseudoClass(parent) && parent.value.toLowerCase() === ':has') { + insideHasPseudoClass = true; + } + + parent = parent.parent; + } + + if (!insideHasPseudoClass) { + return; + } // see : https://bugs.chromium.org/p/chromium/issues/detail?id=669058#c34 // When we have ':has(:visited) {...}', the subject elements of the rule @@ -65,26 +76,97 @@ const creator: PluginCreator<{ preserve?: boolean, specificityMatchingName?: str } }); - if (!containsHasPseudo) { - return selector; - } + selectorAST.walkPseudos((node) => { + if (node.value.toLowerCase() !== ':has' || !node.nodes) { + return; + } - const encodedSelector = '[' + encodeCSS(selectorAST.toString()) + ']'; - const abcSpecificity = selectorSpecificity(selectorAST); + let container = node.parent ?? node; + + // Split the selector at the pseudo element boundary + // - :has(...)::before -> :has(...) | ::before + // - :has(...) ~ span::before -> :has(...) ~ span | ::before + if (container !== node) { + let sliceIndex = container.nodes.length; + + PSEUDO_ELEMENT_LOOP: + for (let i = 0; i < container.nodes.length; i++) { + const element = container.nodes[i]; + + if (parser.isPseudoElement(element)) { + for (let j = i - 1; j >= 0; j--) { + if (container.nodes[i].type === 'combinator' || container.nodes[i].type === 'comment') { + continue; + } + + sliceIndex = j + 1; + break PSEUDO_ELEMENT_LOOP; + } + } + } + + if (sliceIndex < container.nodes.length) { + const a = parser.selector({ + value: '', + nodes: [], + }); + + const aNodes = container.nodes.slice(0, sliceIndex); + aNodes.forEach((x) => { + delete x.parent; + a.append(x); + }); + + const b = parser.selector({ + value: '', + nodes: [], + }); + + const bNodes = container.nodes.slice(sliceIndex); + bNodes.forEach((x) => { + delete x.parent; + b.append(x); + }); + + const newContainer = parser.selector({ + value: '', + nodes: [], + }); + + newContainer.append(a); + newContainer.append(b); + + container.replaceWith(newContainer); + container = a; + } + } - let encodedSelectorWithSpecificity = encodedSelector; - for (let i = 0; i < abcSpecificity.a; i++) { - encodedSelectorWithSpecificity += specificityMatchingNameId; - } - const bSpecificity = Math.max(1, abcSpecificity.b) - 1; - for (let i = 0; i < bSpecificity; i++) { - encodedSelectorWithSpecificity += specificityMatchingNameClass; - } - for (let i = 0; i < abcSpecificity.c; i++) { - encodedSelectorWithSpecificity += specificityMatchingNameTag; + const encodedSelector = '[' + encodeCSS(container.toString()) + ']'; + const abcSpecificity = selectorSpecificity(container); + + let encodedSelectorWithSpecificity = encodedSelector; + for (let i = 0; i < abcSpecificity.a; i++) { + encodedSelectorWithSpecificity += specificityMatchingNameId; + } + const bSpecificity = Math.max(1, abcSpecificity.b) - 1; + for (let i = 0; i < bSpecificity; i++) { + encodedSelectorWithSpecificity += specificityMatchingNameClass; + } + for (let i = 0; i < abcSpecificity.c; i++) { + encodedSelectorWithSpecificity += specificityMatchingNameTag; + } + + const encodedSelectorAST = parser().astSync(encodedSelectorWithSpecificity); + + container.replaceWith(encodedSelectorAST.nodes[0]); + }); + + const modifiedSelector = selectorAST.toString(); + if (modifiedSelector !== selector) { + return '.js-has-pseudo ' + modifiedSelector; } - return encodedSelectorWithSpecificity; + return selector; }); if (selectors.join(',') === rule.selectors.join(',')) { diff --git a/plugins/css-has-pseudo/test/_browser.html b/plugins/css-has-pseudo/test/_browser.html index 4d8c007f9..23ebff966 100644 --- a/plugins/css-has-pseudo/test/_browser.html +++ b/plugins/css-has-pseudo/test/_browser.html @@ -10,7 +10,7 @@ - +