diff --git a/plugin-packs/postcss-preset-env/CHANGELOG.md b/plugin-packs/postcss-preset-env/CHANGELOG.md index 6f4fcf1df..05b57cfd8 100644 --- a/plugin-packs/postcss-preset-env/CHANGELOG.md +++ b/plugin-packs/postcss-preset-env/CHANGELOG.md @@ -1,5 +1,9 @@ # Changes to PostCSS Preset Env +### Unreleased (minor) + +- Add warning when using the deprecated `@nest` rule + ### 8.4.2 (June 1, 2023) - Updated `@csstools/postcss-progressive-custom-properties` to `2.3.0` (minor) 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 4a6844d2f..3d203714b 100644 --- a/plugin-packs/postcss-preset-env/test/basic.autoprefixer.expect.css +++ b/plugin-packs/postcss-preset-env/test/basic.autoprefixer.expect.css @@ -525,7 +525,7 @@ :--view-m .view { background: red; -} + } .nested-calc { order: calc(1 * (8 / 3 + (5 * 10))); 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 4a6844d2f..3d203714b 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 @@ -525,7 +525,7 @@ :--view-m .view { background: red; -} + } .nested-calc { order: calc(1 * (8 / 3 + (5 * 10))); 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 1b3c93bda..e67f9978e 100644 --- a/plugin-packs/postcss-preset-env/test/basic.ch38.expect.css +++ b/plugin-packs/postcss-preset-env/test/basic.ch38.expect.css @@ -435,7 +435,7 @@ :--view-m .view { background: red; -} + } .nested-calc { order: calc(1 * (8 / 3 + (5 * 10))); 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 1621f73b1..42ea35b82 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 @@ -432,7 +432,7 @@ h1.test-custom-selectors:not(.does-not-exist), h2.test-custom-selectors:not(.doe [data-view-size=m] .view { background: red; -} + } .nested-calc { order: calc(1 * (8 / 3 + (5 * 10))); 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 5d1153866..374b65469 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 @@ -432,7 +432,7 @@ h1.test-custom-selectors:not(.does-not-exist), h2.test-custom-selectors:not(.doe [data-view-size=m] .view { background: red; -} + } .nested-calc { order: calc(1 * calc(8 / 3 + calc(5 * 10))); 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 5d1153866..374b65469 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 @@ -432,7 +432,7 @@ h1.test-custom-selectors:not(.does-not-exist), h2.test-custom-selectors:not(.doe [data-view-size=m] .view { background: red; -} + } .nested-calc { order: calc(1 * calc(8 / 3 + calc(5 * 10))); diff --git a/plugin-packs/postcss-preset-env/test/basic.css b/plugin-packs/postcss-preset-env/test/basic.css index b9320a51d..2b5bb752e 100644 --- a/plugin-packs/postcss-preset-env/test/basic.css +++ b/plugin-packs/postcss-preset-env/test/basic.css @@ -402,7 +402,7 @@ @custom-selector :--view-m [data-view-size=m]; .view { - @nest :--view-m & { + :--view-m & { background: red; } } diff --git a/plugin-packs/postcss-preset-env/test/basic.expect.css b/plugin-packs/postcss-preset-env/test/basic.expect.css index bd3513c0b..c457211e6 100644 --- a/plugin-packs/postcss-preset-env/test/basic.expect.css +++ b/plugin-packs/postcss-preset-env/test/basic.expect.css @@ -542,7 +542,7 @@ :--view-m .view { background: red; -} + } .nested-calc { order: calc(1 * (8 / 3 + (5 * 10))); 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 cfb502309..7b0481904 100644 --- a/plugin-packs/postcss-preset-env/test/basic.ff49.expect.css +++ b/plugin-packs/postcss-preset-env/test/basic.ff49.expect.css @@ -438,7 +438,7 @@ :--view-m .view { background: red; -} + } .nested-calc { order: calc(1 * calc(8 / 3 + calc(5 * 10))); 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 c6614e9db..5f473e3c8 100644 --- a/plugin-packs/postcss-preset-env/test/basic.ff66.expect.css +++ b/plugin-packs/postcss-preset-env/test/basic.ff66.expect.css @@ -433,7 +433,7 @@ :--view-m .view { background: red; -} + } .nested-calc { order: calc(1 * calc(8 / 3 + calc(5 * 10))); diff --git a/plugin-packs/postcss-preset-env/test/basic.hebrew.expect.css b/plugin-packs/postcss-preset-env/test/basic.hebrew.expect.css index 82271d731..e4f492f1c 100644 --- a/plugin-packs/postcss-preset-env/test/basic.hebrew.expect.css +++ b/plugin-packs/postcss-preset-env/test/basic.hebrew.expect.css @@ -538,7 +538,7 @@ h1.test-custom-selectors:not(.does-not-exist), h2.test-custom-selectors:not(.doe [data-view-size=m] .view { background: red; -} + } .nested-calc { order: calc(1 * (8 / 3 + (5 * 10))); 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 eec8364bc..e486366fd 100644 --- a/plugin-packs/postcss-preset-env/test/basic.ie10.expect.css +++ b/plugin-packs/postcss-preset-env/test/basic.ie10.expect.css @@ -559,7 +559,7 @@ :--view-m .view { background: red; -} + } .nested-calc { -ms-flex-order: calc(1 * (8 / 3 + (5 * 10))); 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 d8778c0fa..f3ab90e1b 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 @@ -537,7 +537,7 @@ h1.test-custom-selectors:not(.does-not-exist), h2.test-custom-selectors:not(.doe } .view { - @nest :--view-m & { + [data-view-size=m] & { background: red; } } diff --git a/plugin-packs/postcss-preset-env/test/basic.nesting.true.expect.css b/plugin-packs/postcss-preset-env/test/basic.nesting.true.expect.css index 6d1493c0a..92b1c333c 100644 --- a/plugin-packs/postcss-preset-env/test/basic.nesting.true.expect.css +++ b/plugin-packs/postcss-preset-env/test/basic.nesting.true.expect.css @@ -410,7 +410,7 @@ :--view-m .view { background: red; -} + } .nested-calc { order: calc(1 * calc(8 / 3 + calc(5 * 10))); 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 ad55dca99..8e034d986 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 @@ -522,7 +522,7 @@ h1.test-custom-selectors:not(.does-not-exist), h2.test-custom-selectors:not(.doe [data-view-size=m] .view { background: red; -} + } .nested-calc { order: calc(1 * (8 / 3 + (5 * 10))); diff --git a/plugin-packs/postcss-preset-env/test/basic.preserve.false.expect.css b/plugin-packs/postcss-preset-env/test/basic.preserve.false.expect.css index 636e6badc..8ac0a84ab 100644 --- a/plugin-packs/postcss-preset-env/test/basic.preserve.false.expect.css +++ b/plugin-packs/postcss-preset-env/test/basic.preserve.false.expect.css @@ -661,7 +661,7 @@ h1.test-custom-selectors:not(.does-not-exist), h2.test-custom-selectors:not(.doe [data-view-size=m] .view { background: red; -} + } .nested-calc { -webkit-box-ordinal-group: NaN; 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 989e4d1cd..4e68c1857 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 @@ -891,15 +891,15 @@ h1.test-custom-selectors:not(.does-not-exist), h2.test-custom-selectors:not(.doe [data-view-size=m] .view { background: red; -} + } :is([data-view-size=m]) .view { background: red; -} + } :--view-m .view { background: red; -} + } .nested-calc { -webkit-box-ordinal-group: NaN; 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 3da2664c1..44dc3dde0 100644 --- a/plugin-packs/postcss-preset-env/test/basic.safari15.expect.css +++ b/plugin-packs/postcss-preset-env/test/basic.safari15.expect.css @@ -414,7 +414,7 @@ :--view-m .view { background: red; -} + } .nested-calc { order: calc(1 * calc(8 / 3 + calc(5 * 10))); 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 01de54768..d1189bead 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 @@ -434,7 +434,7 @@ h1.test-custom-selectors:not(.does-not-exist), h2.test-custom-selectors:not(.doe [data-view-size=m] .view { background: red; -} + } .nested-calc { order: calc(1 * calc(8 / 3 + calc(5 * 10))); 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 73edd9897..264c7473a 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 @@ -429,7 +429,7 @@ h1.test-custom-selectors:not(.does-not-exist), h2.test-custom-selectors:not(.doe [data-view-size=m] .view { background: red; -} + } .nested-calc { order: calc(1 * calc(8 / 3 + calc(5 * 10))); 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 eead4c3f7..a479e5cb8 100644 --- a/plugin-packs/postcss-preset-env/test/basic.stage0.expect.css +++ b/plugin-packs/postcss-preset-env/test/basic.stage0.expect.css @@ -538,7 +538,7 @@ h1.test-custom-selectors:not(.does-not-exist), h2.test-custom-selectors:not(.doe [data-view-size=m] .view { background: red; -} + } .nested-calc { order: calc(1 * (8 / 3 + (5 * 10))); diff --git a/plugin-packs/postcss-preset-env/test/basic.vendors-1.expect.css b/plugin-packs/postcss-preset-env/test/basic.vendors-1.expect.css index cc1b87217..4ef86fad8 100644 --- a/plugin-packs/postcss-preset-env/test/basic.vendors-1.expect.css +++ b/plugin-packs/postcss-preset-env/test/basic.vendors-1.expect.css @@ -545,7 +545,7 @@ @custom-selector :--view-m [data-view-size=m]; .view { - @nest :--view-m & { + :--view-m & { background: red; } } diff --git a/plugin-packs/postcss-preset-env/test/basic.vendors-2.expect.css b/plugin-packs/postcss-preset-env/test/basic.vendors-2.expect.css index 35d9a937f..111fb7b10 100644 --- a/plugin-packs/postcss-preset-env/test/basic.vendors-2.expect.css +++ b/plugin-packs/postcss-preset-env/test/basic.vendors-2.expect.css @@ -545,7 +545,7 @@ @custom-selector :--view-m [data-view-size=m]; .view { - @nest :--view-m & { + :--view-m & { background: red; } } diff --git a/plugin-packs/postcss-preset-env/test/basic.vendors-3.expect.css b/plugin-packs/postcss-preset-env/test/basic.vendors-3.expect.css index 68f420b7b..17e4cb864 100644 --- a/plugin-packs/postcss-preset-env/test/basic.vendors-3.expect.css +++ b/plugin-packs/postcss-preset-env/test/basic.vendors-3.expect.css @@ -517,7 +517,7 @@ @custom-selector :--view-m [data-view-size=m]; .view { - @nest :--view-m & { + :--view-m & { background: red; } } diff --git a/plugins/postcss-nesting/.tape.mjs b/plugins/postcss-nesting/.tape.mjs index d66952e7d..1e3a70796 100644 --- a/plugins/postcss-nesting/.tape.mjs +++ b/plugins/postcss-nesting/.tape.mjs @@ -58,11 +58,20 @@ postcssTape(plugin)({ noIsPseudoSelector: true, }, }, - 'at-rule': { - message: 'supports at-rule usage', + 'at-nest': { + message: 'supports at-nest usage', + warnings: 64, }, - 'at-rule:no-is-pseudo-selector': { - message: 'supports at-rule usage { noIsPseudoSelector: true }', + 'at-nest:silent': { + message: 'supports at-nest usage { silenceAtNestWarning: true }', + warnings: 4, + options: { + silenceAtNestWarning: true, + } + }, + 'at-nest:no-is-pseudo-selector': { + message: 'supports at-nest usage { noIsPseudoSelector: true }', + warnings: 64, options: { noIsPseudoSelector: true, }, @@ -130,14 +139,12 @@ postcssTape(plugin)({ }, 'ignore': { message: 'ignores invalid entries', - warnings: 1 }, 'ignore:no-is-pseudo-selector': { message: 'ignores invalid entries { noIsPseudoSelector: true }', options: { noIsPseudoSelector: true, }, - warnings: 1 }, 'mixin-declaration': { message: 'supports other visitors (mixin declaration)', diff --git a/plugins/postcss-nesting/CHANGELOG.md b/plugins/postcss-nesting/CHANGELOG.md index 6661e6b55..f948ea569 100644 --- a/plugins/postcss-nesting/CHANGELOG.md +++ b/plugins/postcss-nesting/CHANGELOG.md @@ -1,5 +1,12 @@ # Changes to PostCSS Nesting +### Unreleased (minor) + +- Add support for nested selectors that that begin with a letter +- Add warning when using the deprecated `@nest` rule + - you can silence this warning with a new `silenceAtNestWarning` plugin option + - you can migrate your code to the latest syntax with our [Stylelint Plugin](https://github.com/csstools/postcss-plugins/tree/main/plugins-stylelint/no-at-nest-rule#csstoolsstylelint-no-at-nest-rule) + ### 11.2.2 (March 25, 2023) - Improved: support for mixins diff --git a/plugins/postcss-nesting/README.md b/plugins/postcss-nesting/README.md index 5db4ea16f..bec682841 100644 --- a/plugins/postcss-nesting/README.md +++ b/plugins/postcss-nesting/README.md @@ -77,39 +77,21 @@ instructions for: Previous iterations of the [CSS Nesting specification] required using `@nest` for certain selectors. `@nest` was removed from the specification completely. -Future versions of this plugin will first warn and then error if you use `@nest`. +Future versions of this plugin will error if you use `@nest`. -We advice everyone to migrate their codebase **now** to nested CSS without `@nest`. +We advice everyone to migrate their codebase **now** to nested CSS without `@nest`. +We published a [Stylelint Plugin](https://github.com/csstools/postcss-plugins/tree/main/plugins-stylelint/no-at-nest-rule#csstoolsstylelint-no-at-nest-rule) to help you migrate. -## ⚠️ Nested selectors must start with a symbol +example warning: +> `@nest` was removed from the CSS Nesting specification and will be removed from PostCSS Nesting in the next major version. +> Change `@nest foo & {}` to `foo & {}` to migrate to the latest standard. -The current version of the [CSS Nesting specification](https://www.w3.org/TR/2023/WD-css-nesting-1-20230214/#example-34e8e94f) disallows nested selectors to start with a letter (i.e. a tag name or element selector). To write such selectors, they need to be prefixed with `& ` or wrapped with `:is()`. +You can silence this warning with a new `silenceAtNestWarning` plugin option. -You will get a warning when selectors start with a letter: -> Nested selectors must start with a symbol and "span" begins with a letter. - -```pcss -.foo { - /* ❌ invalid */ - span { - color: hotpink; - } - - /* ✅ valid */ - & span { - color: hotpink; - } - - /* ❌ invalid */ - span & { - color: hotpink; - } - - /* ✅ valid */ - :is(span) & { - color: hotpink; - } -} +```js +postcssNesting({ + silenceAtNestWarning: true +}) ``` ## Options diff --git a/plugins/postcss-nesting/dist/index.cjs b/plugins/postcss-nesting/dist/index.cjs index 08ff550aa..4f845fbe2 100644 --- a/plugins/postcss-nesting/dist/index.cjs +++ b/plugins/postcss-nesting/dist/index.cjs @@ -1 +1 @@ -"use strict";var e=require("postcss-selector-parser"),t=require("@csstools/selector-specificity");const n=e.pseudo({value:":is"});function sortCompoundSelectorsInsideComplexSelector(t){if(!t||!t.nodes)return;const r=[];let o=[];for(let s=0;s"tag"===e.type))){const r=n.clone({}),o=t.nodes[s];o.replaceWith(r),r.append(e.selector({nodes:[o],value:void 0}))}o.push(t.nodes[s])}else r.push(o),r.push([t.nodes[s]]),o=[];r.push(o);const s=[];for(let e=0;e"selector"===e.type&&"selector"===t.type&&e.nodes.length&&t.nodes.length?selectorTypeOrder(e.nodes[0])-selectorTypeOrder(t.nodes[0]):"selector"===e.type&&e.nodes.length?selectorTypeOrder(e.nodes[0])-selectorTypeOrder(t):"selector"===t.type&&t.nodes.length?selectorTypeOrder(e)-selectorTypeOrder(t.nodes[0]):selectorTypeOrder(e)-selectorTypeOrder(t)));for(let e=0;e=0;e--)s[e].remove(),t.prepend(s[e])}function selectorTypeOrder(t){return e.isPseudoElement(t)?r.pseudoElement:r[t.type]}const r={universal:0,tag:1,pseudoElement:2,id:3,class:4,attribute:5,pseudo:6,selector:7,string:8,root:9,comment:10};function ampersandToScope(t,n){let r,o=t.parent;for(;o;){if("rule"===o.type)return;o=o.parent}try{r=e().astSync(t.selector)}catch(e){return void t.warn(n,`Failed to parse selector : "${t.selector}" with message: "${e.message}"`)}r&&(r.walkNesting((t=>{const n=t.parent;t.replaceWith(e.pseudo({value:":scope"})),n&&sortCompoundSelectorsInsideComplexSelector(n)})),t.selector=r.toString())}function cleanupParent(e){if(!e.nodes.length)return void e.remove();const t=e.nodes.filter((e=>"comment"===e.type));t.length===e.nodes.length&&e.replaceWith(...t)}function shiftNodesBeforeParent(e,t){const n=t.index(e);if(n){const e=t.cloneBefore().removeAll().append(t.nodes.slice(0,n));e.raws.semicolon=!0,cleanupParent(e)}t.before(e),t.raws.semicolon=!0}function combinationsWithSizeN(e,t){if(t<2)throw new Error("n must be greater than 1");if(e.length<2)throw new Error("s must be greater than 1");if(Math.pow(e.length,t)>1e4)throw new Error("Too many combinations when trying to resolve a nested selector with lists, reduce the complexity of your selectors");const n=[];for(let e=0;e=0;s--){let t=n[s];if(t>=e.length){if(t=0,n[s]=0,0===s)return r;n[s-1]+=1}o[s]=e[t]}r.push(o),n[n.length-1]++}}function nodesAreEquallySpecific(n){const r=n.map((t=>e().astSync(t))).map((e=>t.selectorSpecificity(e))),o=r[0];for(let e=1;ee().astSync(t))):[e().astSync(`:is(${r.join(",")})`)];const c=[];for(let r=0;r{"nesting"===e.type&&(i=!0)}));const c=s.nodes[0];let f=!1;c.each((e=>"combinator"===e.type&&(f=!0,!1))),i?f&&c.insertBefore(c.at(0),e.nesting({})):(c.insertBefore(c.at(0),e.combinator({value:" "})),c.insertBefore(c.at(0),e.nesting({}))),m=s.toString()}let h=[],g=0;if(e().astSync(m).walkNesting((()=>{g++})),g>1&&i.length>1)h=combinationsWithSizeN(i,g),f=h.length;else{f=i.length;for(let e=0;e{if("nesting"!==r.type)return;let o=h[t][n];n++,"root"===o.type&&1===o.nodes.length&&(o=o.nodes[0]);const l=e().astSync(`:is(${o.toString()})`),i=isSimpleSelector(o.nodes[0]),c=isCompoundSelector(o.nodes[0]),a=isSimpleSelector(r),u=isCompoundSelector(r);if(i&&a)return void r.replaceWith(o.clone());if((i||c)&&(a||u)){const e=r.parent;return i&&"selector"===o.type?r.replaceWith(o.clone().nodes[0]):r.replaceWith(...o.clone().nodes),void(e&&e.nodes.length>1&&sortCompoundSelectorsInsideComplexSelector(e))}if(i){const e=r.parent;return r.replaceWith(o.clone().nodes[0]),void(e&&sortCompoundSelectorsInsideComplexSelector(e))}if(c){const e=r.parent;return r.replaceWith(...o.clone().nodes),void(e&&sortCompoundSelectorsInsideComplexSelector(e))}if(nestingIsFirstAndOnlyInSelectorWithEitherSpaceOrChildCombinator(r)){const e=r.parent;return r.replaceWith(...o.clone().nodes),void(e&&sortCompoundSelectorsInsideComplexSelector(e))}if(nestingIsNotInsideCompoundSelector(r)){const e=r.parent;return r.replaceWith(...o.clone().nodes),void(e&&sortCompoundSelectorsInsideComplexSelector(e))}const p=r.parent;s.noIsPseudoSelector?r.replaceWith(...o.clone().nodes):r.replaceWith(...l.clone({}).nodes),p&&sortCompoundSelectorsInsideComplexSelector(p)})),c.push(r.toString())}}return c}function isSimpleSelector(e){return"combinator"!==e.type&&!(e.parent&&e.parent.nodes.length>1)}function isCompoundSelector(e,t=null){if(isSimpleSelector(e))return!1;if(!e.parent)return!1;if(!!e.parent.nodes.find((e=>"combinator"===e.type)))return!1;return!(!!e.parent.nodes.find((e=>"nesting"===e.type))&&t&&!isCompoundSelector(t))}function nestingIsFirstAndOnlyInSelectorWithEitherSpaceOrChildCombinator(e){if(!e.parent)return!1;if(0!==e.parent.nodes.indexOf(e))return!1;for(let t=1;t"!==e.parent.nodes[t].value)return!1;return!0}function nestingIsNotInsideCompoundSelector(e){if(isSimpleSelector(e))return!0;if(!e.parent)return!1;for(let t=0;t{if("decl"===n.type)return t===r-1?void(t=r):(n.remove(),e.insertAfter(t,n),void(t=e.index(n)));if("atrule"===n.type&&"mixin"===n.name.toLowerCase()){let o=n.prev();for(;o;){if("rule"===o.type||"atrule"===o.type)return;o=o.prev()}return t===r-1?void(t=r):(n.remove(),e.insertAfter(t,n),void(t=e.index(n)))}if("comment"===n.type){const o=n.next();if(o&&("comment"===o.type||"rule"===o.type||"atrule"===o.type&&"mixin"!==o.name.toLowerCase()))return;return t===r-1?void(t=r):(n.remove(),e.insertAfter(t,n),void(t=e.index(n)))}}))}function transformRuleWithinRule(e,t,n,r){let o=[];try{o=mergeSelectors(e,n,t.selectors,e.selectors,r,!1)}catch(r){return void e.warn(n,`Failed to parse selectors : "${t.selector}" / "${e.selector}" with message: "${r.message}"`)}if(!o.length)return;groupDeclarations(t),shiftNodesBeforeParent(e,t),e.selectors=o;"rule"===e.type&&"rule"===t.type&&e.selector===t.selector&&e.append(...t.nodes),cleanupParent(t)}function isValidRuleWithinRule(e){return e.selectors.every((e=>-1===e.indexOf("|")))}function comma(e){const t=[];let n="",r=!1,o=0,s=!1,l=!1;for(const i of e)l?l=!1:"\\"===i?l=!0:s?i===s&&(s=!1):'"'===i||"'"===i?s=i:"("===i?o+=1:")"===i?o>0&&(o-=1):0===o&&","===i&&(r=!0),r?(""!==n&&t.push(n.trim()),n="",r=!1):n+=i;return t.push(n.trim()),t}function transformNestRuleWithinRule(e,t,n,r,o){let s=[];try{s=mergeSelectors(e,n,t.selectors,comma(e.params),o,!0)}catch(r){return void e.warn(n,`Failed to parse selectors : "${t.selector}" / "${e.params}" with message: "${r.message}"`)}if(!s.length)return;shiftNodesBeforeParent(e,t);const l=t.clone().removeAll().append(e.nodes);l.raws.semicolon=!0,l.selectors=s,e.replaceWith(l),cleanupParent(t),r(l,n,o)}function isValidNestRuleWithinRule(e){return comma(e.params).every((e=>e.split("&").length>=2&&-1===e.indexOf("|")))}var o=["container","document","media","supports","layer"];function atruleWithinRule(e,t,n,r,o){if(groupDeclarations(t),shiftNodesBeforeParent(e,t),e.nodes){const s=t.clone().removeAll().append(e.nodes);e.append(s),cleanupParent(t),r(s,n,o)}else cleanupParent(t)}function isAtruleWithinRule(e){return o.includes(e.name)}function transformAtruleWithinAtrule(e,t){var n,r;groupDeclarations(t),shiftNodesBeforeParent(e,t),e.params=(n=t.params,r=e.params,comma(n).map((e=>comma(r).map((t=>`${e} and ${t}`)).join(", "))).join(", ")),cleanupParent(t)}function isAtruleWithinAtrule(e,t){return o.includes(e.name)&&e.name===t.name}function isAtRule(e){return e&&"atrule"===e.type}function isNestRule(e){return e&&isAtRule(e)&&"nest"===e.name}function isRule(e){return e&&"rule"===e.type}function walk(e,t,n){e.each((e=>{const r=e.parent;isRule(e)&&isRule(r)&&isValidRuleWithinRule(e)?transformRuleWithinRule(e,r,t,n):isNestRule(e)&&isRule(r)&&isValidNestRuleWithinRule(e)?transformNestRuleWithinRule(e,r,t,walk,n):isAtRule(e)&&isRule(r)&&isAtruleWithinRule(e)?atruleWithinRule(e,r,t,walk,n):isAtRule(e)&&isAtRule(r)&&isAtruleWithinAtrule(e,r)&&transformAtruleWithinAtrule(e,r),"nodes"in e&&e.nodes.length&&walk(e,t,n)}))}const creator=e=>{const t=Object.assign({noIsPseudoSelector:!1},e);return{postcssPlugin:"postcss-nesting",Rule(e,{result:n}){walk(e,n,t),e.selector.includes("&")&&ersandToScope(e,n)}}};creator.postcss=!0,module.exports=creator; +"use strict";var e=require("postcss-selector-parser"),t=require("@csstools/selector-specificity");const n=e.pseudo({value:":is"});function sortCompoundSelectorsInsideComplexSelector(t){if(!t||!t.nodes)return;const r=[];let o=[];for(let s=0;s"tag"===e.type))){const r=n.clone({}),o=t.nodes[s];o.replaceWith(r),r.append(e.selector({nodes:[o],value:void 0}))}o.push(t.nodes[s])}else r.push(o),r.push([t.nodes[s]]),o=[];r.push(o);const s=[];for(let e=0;e"selector"===e.type&&"selector"===t.type&&e.nodes.length&&t.nodes.length?selectorTypeOrder(e.nodes[0])-selectorTypeOrder(t.nodes[0]):"selector"===e.type&&e.nodes.length?selectorTypeOrder(e.nodes[0])-selectorTypeOrder(t):"selector"===t.type&&t.nodes.length?selectorTypeOrder(e)-selectorTypeOrder(t.nodes[0]):selectorTypeOrder(e)-selectorTypeOrder(t)));for(let e=0;e=0;e--)s[e].remove(),t.prepend(s[e])}function selectorTypeOrder(t){return e.isPseudoElement(t)?r.pseudoElement:r[t.type]}const r={universal:0,tag:1,pseudoElement:2,id:3,class:4,attribute:5,pseudo:6,selector:7,string:8,root:9,comment:10};function ampersandToScope(t,n){let r,o=t.parent;for(;o;){if("rule"===o.type)return;o=o.parent}try{r=e().astSync(t.selector)}catch(e){return void t.warn(n,`Failed to parse selector : "${t.selector}" with message: "${e.message}"`)}r&&(r.walkNesting((t=>{const n=t.parent;t.replaceWith(e.pseudo({value:":scope"})),n&&sortCompoundSelectorsInsideComplexSelector(n)})),t.selector=r.toString())}function cleanupParent(e){if(!e.nodes.length)return void e.remove();const t=e.nodes.filter((e=>"comment"===e.type));t.length===e.nodes.length&&e.replaceWith(...t)}function shiftNodesBeforeParent(e,t){const n=t.index(e);if(n){const e=t.cloneBefore().removeAll().append(t.nodes.slice(0,n));e.raws.semicolon=!0,cleanupParent(e)}t.before(e),t.raws.semicolon=!0}function combinationsWithSizeN(e,t){if(t<2)throw new Error("n must be greater than 1");if(e.length<2)throw new Error("s must be greater than 1");if(Math.pow(e.length,t)>1e4)throw new Error("Too many combinations when trying to resolve a nested selector with lists, reduce the complexity of your selectors");const n=[];for(let e=0;e=0;s--){let t=n[s];if(t>=e.length){if(t=0,n[s]=0,0===s)return r;n[s-1]+=1}o[s]=e[t]}r.push(o),n[n.length-1]++}}function nodesAreEquallySpecific(n){const r=n.map((t=>e().astSync(t))).map((e=>t.selectorSpecificity(e))),o=r[0];for(let e=1;ee().astSync(t))):[e().astSync(`:is(${t.join(",")})`)];const s=[];for(let t=0;t{"nesting"===e.type&&(n=!0)}));const r=t.nodes[0];let o=!1;r.each((e=>"combinator"===e.type&&(o=!0,!1))),n?o&&r.insertBefore(r.at(0),e.nesting({})):(r.insertBefore(r.at(0),e.combinator({value:" "})),r.insertBefore(r.at(0),e.nesting({}))),l=t.toString()}let c=[],a=0;if(e().astSync(l).walkNesting((()=>{a++})),a>1&&o.length>1)c=combinationsWithSizeN(o,a),i=c.length;else{i=o.length;for(let e=0;e{if("nesting"!==o.type)return;let s=c[t][n];n++,"root"===s.type&&1===s.nodes.length&&(s=s.nodes[0]);const i=e().astSync(`:is(${s.toString()})`),l=isSimpleSelector(s.nodes[0]),a=isCompoundSelector(s.nodes[0]),u=isSimpleSelector(o),p=isCompoundSelector(o);if(l&&u)return void o.replaceWith(s.clone());if((l||a)&&(u||p)){const e=o.parent;return l&&"selector"===s.type?o.replaceWith(s.clone().nodes[0]):o.replaceWith(...s.clone().nodes),void(e&&e.nodes.length>1&&sortCompoundSelectorsInsideComplexSelector(e))}if(l){const e=o.parent;return o.replaceWith(s.clone().nodes[0]),void(e&&sortCompoundSelectorsInsideComplexSelector(e))}if(a){const e=o.parent;return o.replaceWith(...s.clone().nodes),void(e&&sortCompoundSelectorsInsideComplexSelector(e))}if(nestingIsFirstAndOnlyInSelectorWithEitherSpaceOrChildCombinator(o)){const e=o.parent;return o.replaceWith(...s.clone().nodes),void(e&&sortCompoundSelectorsInsideComplexSelector(e))}if(nestingIsNotInsideCompoundSelector(o)){const e=o.parent;return o.replaceWith(...s.clone().nodes),void(e&&sortCompoundSelectorsInsideComplexSelector(e))}const d=o.parent;r.noIsPseudoSelector?o.replaceWith(...s.clone().nodes):o.replaceWith(...i.clone({}).nodes),d&&sortCompoundSelectorsInsideComplexSelector(d)})),s.push(o.toString())}}return s}function isSimpleSelector(e){return"combinator"!==e.type&&!(e.parent&&e.parent.nodes.length>1)}function isCompoundSelector(e,t=null){if(isSimpleSelector(e))return!1;if(!e.parent)return!1;if(!!e.parent.nodes.find((e=>"combinator"===e.type)))return!1;return!(!!e.parent.nodes.find((e=>"nesting"===e.type))&&t&&!isCompoundSelector(t))}function nestingIsFirstAndOnlyInSelectorWithEitherSpaceOrChildCombinator(e){if(!e.parent)return!1;if(0!==e.parent.nodes.indexOf(e))return!1;for(let t=1;t"!==e.parent.nodes[t].value)return!1;return!0}function nestingIsNotInsideCompoundSelector(e){if(isSimpleSelector(e))return!0;if(!e.parent)return!1;for(let t=0;t{if("decl"===n.type)return t===r-1?void(t=r):(n.remove(),e.insertAfter(t,n),void(t=e.index(n)));if("atrule"===n.type&&"mixin"===n.name.toLowerCase()){let o=n.prev();for(;o;){if("rule"===o.type||"atrule"===o.type)return;o=o.prev()}return t===r-1?void(t=r):(n.remove(),e.insertAfter(t,n),void(t=e.index(n)))}if("comment"===n.type){const o=n.next();if(o&&("comment"===o.type||"rule"===o.type||"atrule"===o.type&&"mixin"!==o.name.toLowerCase()))return;return t===r-1?void(t=r):(n.remove(),e.insertAfter(t,n),void(t=e.index(n)))}}))}function transformRuleWithinRule(e,t,n,r){let o=[];try{o=mergeSelectors(t.selectors,e.selectors,r)}catch(r){return void e.warn(n,`Failed to parse selectors : "${t.selector}" / "${e.selector}" with message: "${r.message}"`)}if(!o.length)return;groupDeclarations(t),shiftNodesBeforeParent(e,t),e.selectors=o;"rule"===e.type&&"rule"===t.type&&e.selector===t.selector&&e.append(...t.nodes),cleanupParent(t)}function isValidRuleWithinRule(e){return e.selectors.every((e=>-1===e.indexOf("|")))}function comma(e){const t=[];let n="",r=!1,o=0,s=!1,i=!1;for(const l of e)i?i=!1:"\\"===l?i=!0:s?l===s&&(s=!1):'"'===l||"'"===l?s=l:"("===l?o+=1:")"===l?o>0&&(o-=1):0===o&&","===l&&(r=!0),r?(""!==n&&t.push(n.trim()),n="",r=!1):n+=l;return t.push(n.trim()),t}function transformNestRuleWithinRule(e,t,n,r,o){let s=[];try{s=mergeSelectors(t.selectors,comma(e.params),o)}catch(r){return void e.warn(n,`Failed to parse selectors : "${t.selector}" / "${e.params}" with message: "${r.message}"`)}if(!s.length)return;shiftNodesBeforeParent(e,t);const i=t.clone().removeAll().append(e.nodes);i.raws.semicolon=!0,i.selectors=s,e.replaceWith(i),cleanupParent(t),r(i,n,o)}function isValidNestRuleWithinRule(e){return comma(e.params).every((e=>e.split("&").length>=2&&-1===e.indexOf("|")))}var o=["container","document","media","supports","layer"];function atruleWithinRule(e,t,n,r,o){if(groupDeclarations(t),shiftNodesBeforeParent(e,t),e.nodes){const s=t.clone().removeAll().append(e.nodes);e.append(s),cleanupParent(t),r(s,n,o)}else cleanupParent(t)}function isAtruleWithinRule(e){return o.includes(e.name)}function transformAtruleWithinAtrule(e,t){var n,r;groupDeclarations(t),shiftNodesBeforeParent(e,t),e.params=(n=t.params,r=e.params,comma(n).map((e=>comma(r).map((t=>`${e} and ${t}`)).join(", "))).join(", ")),cleanupParent(t)}function isAtruleWithinAtrule(e,t){return o.includes(e.name)&&e.name===t.name}function isAtRule(e){return e&&"atrule"===e.type}function isNestRule(e){return e&&isAtRule(e)&&"nest"===e.name}function isRule(e){return e&&"rule"===e.type}function walk(e,t,n){e.each((r=>{const o=r.parent;isNestRule(r)&&!n.silenceAtNestWarning&&e.warn(t,`\`@nest\` was removed from the CSS Nesting specification and will be removed from PostCSS Nesting in the next major version.\nChange \`@nest ${r.params} {}\` to \`${r.params} {}\` to migrate to the latest standard.`),isRule(r)&&isRule(o)&&isValidRuleWithinRule(r)?transformRuleWithinRule(r,o,t,n):isNestRule(r)&&isRule(o)&&isValidNestRuleWithinRule(r)?transformNestRuleWithinRule(r,o,t,walk,n):isAtRule(r)&&isRule(o)&&isAtruleWithinRule(r)?atruleWithinRule(r,o,t,walk,n):isAtRule(r)&&isAtRule(o)&&isAtruleWithinAtrule(r,o)&&transformAtruleWithinAtrule(r,o),"nodes"in r&&r.nodes.length&&walk(r,t,n)}))}const creator=e=>{const t=Object.assign({noIsPseudoSelector:!1,silenceAtNestWarning:!1},e);return{postcssPlugin:"postcss-nesting",Rule(e,{result:n}){walk(e,n,t),e.selector.includes("&")&&ersandToScope(e,n)}}};creator.postcss=!0,module.exports=creator; diff --git a/plugins/postcss-nesting/dist/index.d.ts b/plugins/postcss-nesting/dist/index.d.ts index 17ec14e9a..777ed3e50 100644 --- a/plugins/postcss-nesting/dist/index.d.ts +++ b/plugins/postcss-nesting/dist/index.d.ts @@ -3,6 +3,8 @@ import type { PluginCreator } from 'postcss'; export type pluginOptions = { /** Avoid the `:is()` pseudo class as much as possible. default: false */ noIsPseudoSelector?: boolean; + /** Silence the `@nest` warning. */ + silenceAtNestWarning?: boolean; }; declare const creator: PluginCreator; export default creator; diff --git a/plugins/postcss-nesting/dist/index.mjs b/plugins/postcss-nesting/dist/index.mjs index 3e63c0cdf..923bc58b6 100644 --- a/plugins/postcss-nesting/dist/index.mjs +++ b/plugins/postcss-nesting/dist/index.mjs @@ -1 +1 @@ -import e from"postcss-selector-parser";import{selectorSpecificity as t}from"@csstools/selector-specificity";const n=e.pseudo({value:":is"});function sortCompoundSelectorsInsideComplexSelector(t){if(!t||!t.nodes)return;const r=[];let o=[];for(let s=0;s"tag"===e.type))){const r=n.clone({}),o=t.nodes[s];o.replaceWith(r),r.append(e.selector({nodes:[o],value:void 0}))}o.push(t.nodes[s])}else r.push(o),r.push([t.nodes[s]]),o=[];r.push(o);const s=[];for(let e=0;e"selector"===e.type&&"selector"===t.type&&e.nodes.length&&t.nodes.length?selectorTypeOrder(e.nodes[0])-selectorTypeOrder(t.nodes[0]):"selector"===e.type&&e.nodes.length?selectorTypeOrder(e.nodes[0])-selectorTypeOrder(t):"selector"===t.type&&t.nodes.length?selectorTypeOrder(e)-selectorTypeOrder(t.nodes[0]):selectorTypeOrder(e)-selectorTypeOrder(t)));for(let e=0;e=0;e--)s[e].remove(),t.prepend(s[e])}function selectorTypeOrder(t){return e.isPseudoElement(t)?r.pseudoElement:r[t.type]}const r={universal:0,tag:1,pseudoElement:2,id:3,class:4,attribute:5,pseudo:6,selector:7,string:8,root:9,comment:10};function ampersandToScope(t,n){let r,o=t.parent;for(;o;){if("rule"===o.type)return;o=o.parent}try{r=e().astSync(t.selector)}catch(e){return void t.warn(n,`Failed to parse selector : "${t.selector}" with message: "${e.message}"`)}r&&(r.walkNesting((t=>{const n=t.parent;t.replaceWith(e.pseudo({value:":scope"})),n&&sortCompoundSelectorsInsideComplexSelector(n)})),t.selector=r.toString())}function cleanupParent(e){if(!e.nodes.length)return void e.remove();const t=e.nodes.filter((e=>"comment"===e.type));t.length===e.nodes.length&&e.replaceWith(...t)}function shiftNodesBeforeParent(e,t){const n=t.index(e);if(n){const e=t.cloneBefore().removeAll().append(t.nodes.slice(0,n));e.raws.semicolon=!0,cleanupParent(e)}t.before(e),t.raws.semicolon=!0}function combinationsWithSizeN(e,t){if(t<2)throw new Error("n must be greater than 1");if(e.length<2)throw new Error("s must be greater than 1");if(Math.pow(e.length,t)>1e4)throw new Error("Too many combinations when trying to resolve a nested selector with lists, reduce the complexity of your selectors");const n=[];for(let e=0;e=0;s--){let t=n[s];if(t>=e.length){if(t=0,n[s]=0,0===s)return r;n[s-1]+=1}o[s]=e[t]}r.push(o),n[n.length-1]++}}function nodesAreEquallySpecific(n){const r=n.map((t=>e().astSync(t))).map((e=>t(e))),o=r[0];for(let e=1;ee().astSync(t))):[e().astSync(`:is(${r.join(",")})`)];const c=[];for(let r=0;r{"nesting"===e.type&&(i=!0)}));const c=s.nodes[0];let f=!1;c.each((e=>"combinator"===e.type&&(f=!0,!1))),i?f&&c.insertBefore(c.at(0),e.nesting({})):(c.insertBefore(c.at(0),e.combinator({value:" "})),c.insertBefore(c.at(0),e.nesting({}))),m=s.toString()}let h=[],g=0;if(e().astSync(m).walkNesting((()=>{g++})),g>1&&i.length>1)h=combinationsWithSizeN(i,g),f=h.length;else{f=i.length;for(let e=0;e{if("nesting"!==r.type)return;let o=h[t][n];n++,"root"===o.type&&1===o.nodes.length&&(o=o.nodes[0]);const l=e().astSync(`:is(${o.toString()})`),i=isSimpleSelector(o.nodes[0]),c=isCompoundSelector(o.nodes[0]),a=isSimpleSelector(r),u=isCompoundSelector(r);if(i&&a)return void r.replaceWith(o.clone());if((i||c)&&(a||u)){const e=r.parent;return i&&"selector"===o.type?r.replaceWith(o.clone().nodes[0]):r.replaceWith(...o.clone().nodes),void(e&&e.nodes.length>1&&sortCompoundSelectorsInsideComplexSelector(e))}if(i){const e=r.parent;return r.replaceWith(o.clone().nodes[0]),void(e&&sortCompoundSelectorsInsideComplexSelector(e))}if(c){const e=r.parent;return r.replaceWith(...o.clone().nodes),void(e&&sortCompoundSelectorsInsideComplexSelector(e))}if(nestingIsFirstAndOnlyInSelectorWithEitherSpaceOrChildCombinator(r)){const e=r.parent;return r.replaceWith(...o.clone().nodes),void(e&&sortCompoundSelectorsInsideComplexSelector(e))}if(nestingIsNotInsideCompoundSelector(r)){const e=r.parent;return r.replaceWith(...o.clone().nodes),void(e&&sortCompoundSelectorsInsideComplexSelector(e))}const p=r.parent;s.noIsPseudoSelector?r.replaceWith(...o.clone().nodes):r.replaceWith(...l.clone({}).nodes),p&&sortCompoundSelectorsInsideComplexSelector(p)})),c.push(r.toString())}}return c}function isSimpleSelector(e){return"combinator"!==e.type&&!(e.parent&&e.parent.nodes.length>1)}function isCompoundSelector(e,t=null){if(isSimpleSelector(e))return!1;if(!e.parent)return!1;if(!!e.parent.nodes.find((e=>"combinator"===e.type)))return!1;return!(!!e.parent.nodes.find((e=>"nesting"===e.type))&&t&&!isCompoundSelector(t))}function nestingIsFirstAndOnlyInSelectorWithEitherSpaceOrChildCombinator(e){if(!e.parent)return!1;if(0!==e.parent.nodes.indexOf(e))return!1;for(let t=1;t"!==e.parent.nodes[t].value)return!1;return!0}function nestingIsNotInsideCompoundSelector(e){if(isSimpleSelector(e))return!0;if(!e.parent)return!1;for(let t=0;t{if("decl"===n.type)return t===r-1?void(t=r):(n.remove(),e.insertAfter(t,n),void(t=e.index(n)));if("atrule"===n.type&&"mixin"===n.name.toLowerCase()){let o=n.prev();for(;o;){if("rule"===o.type||"atrule"===o.type)return;o=o.prev()}return t===r-1?void(t=r):(n.remove(),e.insertAfter(t,n),void(t=e.index(n)))}if("comment"===n.type){const o=n.next();if(o&&("comment"===o.type||"rule"===o.type||"atrule"===o.type&&"mixin"!==o.name.toLowerCase()))return;return t===r-1?void(t=r):(n.remove(),e.insertAfter(t,n),void(t=e.index(n)))}}))}function transformRuleWithinRule(e,t,n,r){let o=[];try{o=mergeSelectors(e,n,t.selectors,e.selectors,r,!1)}catch(r){return void e.warn(n,`Failed to parse selectors : "${t.selector}" / "${e.selector}" with message: "${r.message}"`)}if(!o.length)return;groupDeclarations(t),shiftNodesBeforeParent(e,t),e.selectors=o;"rule"===e.type&&"rule"===t.type&&e.selector===t.selector&&e.append(...t.nodes),cleanupParent(t)}function isValidRuleWithinRule(e){return e.selectors.every((e=>-1===e.indexOf("|")))}function comma(e){const t=[];let n="",r=!1,o=0,s=!1,l=!1;for(const i of e)l?l=!1:"\\"===i?l=!0:s?i===s&&(s=!1):'"'===i||"'"===i?s=i:"("===i?o+=1:")"===i?o>0&&(o-=1):0===o&&","===i&&(r=!0),r?(""!==n&&t.push(n.trim()),n="",r=!1):n+=i;return t.push(n.trim()),t}function transformNestRuleWithinRule(e,t,n,r,o){let s=[];try{s=mergeSelectors(e,n,t.selectors,comma(e.params),o,!0)}catch(r){return void e.warn(n,`Failed to parse selectors : "${t.selector}" / "${e.params}" with message: "${r.message}"`)}if(!s.length)return;shiftNodesBeforeParent(e,t);const l=t.clone().removeAll().append(e.nodes);l.raws.semicolon=!0,l.selectors=s,e.replaceWith(l),cleanupParent(t),r(l,n,o)}function isValidNestRuleWithinRule(e){return comma(e.params).every((e=>e.split("&").length>=2&&-1===e.indexOf("|")))}var o=["container","document","media","supports","layer"];function atruleWithinRule(e,t,n,r,o){if(groupDeclarations(t),shiftNodesBeforeParent(e,t),e.nodes){const s=t.clone().removeAll().append(e.nodes);e.append(s),cleanupParent(t),r(s,n,o)}else cleanupParent(t)}function isAtruleWithinRule(e){return o.includes(e.name)}function transformAtruleWithinAtrule(e,t){var n,r;groupDeclarations(t),shiftNodesBeforeParent(e,t),e.params=(n=t.params,r=e.params,comma(n).map((e=>comma(r).map((t=>`${e} and ${t}`)).join(", "))).join(", ")),cleanupParent(t)}function isAtruleWithinAtrule(e,t){return o.includes(e.name)&&e.name===t.name}function isAtRule(e){return e&&"atrule"===e.type}function isNestRule(e){return e&&isAtRule(e)&&"nest"===e.name}function isRule(e){return e&&"rule"===e.type}function walk(e,t,n){e.each((e=>{const r=e.parent;isRule(e)&&isRule(r)&&isValidRuleWithinRule(e)?transformRuleWithinRule(e,r,t,n):isNestRule(e)&&isRule(r)&&isValidNestRuleWithinRule(e)?transformNestRuleWithinRule(e,r,t,walk,n):isAtRule(e)&&isRule(r)&&isAtruleWithinRule(e)?atruleWithinRule(e,r,t,walk,n):isAtRule(e)&&isAtRule(r)&&isAtruleWithinAtrule(e,r)&&transformAtruleWithinAtrule(e,r),"nodes"in e&&e.nodes.length&&walk(e,t,n)}))}const creator=e=>{const t=Object.assign({noIsPseudoSelector:!1},e);return{postcssPlugin:"postcss-nesting",Rule(e,{result:n}){walk(e,n,t),e.selector.includes("&")&&ersandToScope(e,n)}}};creator.postcss=!0;export{creator as default}; +import e from"postcss-selector-parser";import{selectorSpecificity as t}from"@csstools/selector-specificity";const n=e.pseudo({value:":is"});function sortCompoundSelectorsInsideComplexSelector(t){if(!t||!t.nodes)return;const r=[];let o=[];for(let s=0;s"tag"===e.type))){const r=n.clone({}),o=t.nodes[s];o.replaceWith(r),r.append(e.selector({nodes:[o],value:void 0}))}o.push(t.nodes[s])}else r.push(o),r.push([t.nodes[s]]),o=[];r.push(o);const s=[];for(let e=0;e"selector"===e.type&&"selector"===t.type&&e.nodes.length&&t.nodes.length?selectorTypeOrder(e.nodes[0])-selectorTypeOrder(t.nodes[0]):"selector"===e.type&&e.nodes.length?selectorTypeOrder(e.nodes[0])-selectorTypeOrder(t):"selector"===t.type&&t.nodes.length?selectorTypeOrder(e)-selectorTypeOrder(t.nodes[0]):selectorTypeOrder(e)-selectorTypeOrder(t)));for(let e=0;e=0;e--)s[e].remove(),t.prepend(s[e])}function selectorTypeOrder(t){return e.isPseudoElement(t)?r.pseudoElement:r[t.type]}const r={universal:0,tag:1,pseudoElement:2,id:3,class:4,attribute:5,pseudo:6,selector:7,string:8,root:9,comment:10};function ampersandToScope(t,n){let r,o=t.parent;for(;o;){if("rule"===o.type)return;o=o.parent}try{r=e().astSync(t.selector)}catch(e){return void t.warn(n,`Failed to parse selector : "${t.selector}" with message: "${e.message}"`)}r&&(r.walkNesting((t=>{const n=t.parent;t.replaceWith(e.pseudo({value:":scope"})),n&&sortCompoundSelectorsInsideComplexSelector(n)})),t.selector=r.toString())}function cleanupParent(e){if(!e.nodes.length)return void e.remove();const t=e.nodes.filter((e=>"comment"===e.type));t.length===e.nodes.length&&e.replaceWith(...t)}function shiftNodesBeforeParent(e,t){const n=t.index(e);if(n){const e=t.cloneBefore().removeAll().append(t.nodes.slice(0,n));e.raws.semicolon=!0,cleanupParent(e)}t.before(e),t.raws.semicolon=!0}function combinationsWithSizeN(e,t){if(t<2)throw new Error("n must be greater than 1");if(e.length<2)throw new Error("s must be greater than 1");if(Math.pow(e.length,t)>1e4)throw new Error("Too many combinations when trying to resolve a nested selector with lists, reduce the complexity of your selectors");const n=[];for(let e=0;e=0;s--){let t=n[s];if(t>=e.length){if(t=0,n[s]=0,0===s)return r;n[s-1]+=1}o[s]=e[t]}r.push(o),n[n.length-1]++}}function nodesAreEquallySpecific(n){const r=n.map((t=>e().astSync(t))).map((e=>t(e))),o=r[0];for(let e=1;ee().astSync(t))):[e().astSync(`:is(${t.join(",")})`)];const s=[];for(let t=0;t{"nesting"===e.type&&(n=!0)}));const r=t.nodes[0];let o=!1;r.each((e=>"combinator"===e.type&&(o=!0,!1))),n?o&&r.insertBefore(r.at(0),e.nesting({})):(r.insertBefore(r.at(0),e.combinator({value:" "})),r.insertBefore(r.at(0),e.nesting({}))),l=t.toString()}let c=[],a=0;if(e().astSync(l).walkNesting((()=>{a++})),a>1&&o.length>1)c=combinationsWithSizeN(o,a),i=c.length;else{i=o.length;for(let e=0;e{if("nesting"!==o.type)return;let s=c[t][n];n++,"root"===s.type&&1===s.nodes.length&&(s=s.nodes[0]);const i=e().astSync(`:is(${s.toString()})`),l=isSimpleSelector(s.nodes[0]),a=isCompoundSelector(s.nodes[0]),p=isSimpleSelector(o),u=isCompoundSelector(o);if(l&&p)return void o.replaceWith(s.clone());if((l||a)&&(p||u)){const e=o.parent;return l&&"selector"===s.type?o.replaceWith(s.clone().nodes[0]):o.replaceWith(...s.clone().nodes),void(e&&e.nodes.length>1&&sortCompoundSelectorsInsideComplexSelector(e))}if(l){const e=o.parent;return o.replaceWith(s.clone().nodes[0]),void(e&&sortCompoundSelectorsInsideComplexSelector(e))}if(a){const e=o.parent;return o.replaceWith(...s.clone().nodes),void(e&&sortCompoundSelectorsInsideComplexSelector(e))}if(nestingIsFirstAndOnlyInSelectorWithEitherSpaceOrChildCombinator(o)){const e=o.parent;return o.replaceWith(...s.clone().nodes),void(e&&sortCompoundSelectorsInsideComplexSelector(e))}if(nestingIsNotInsideCompoundSelector(o)){const e=o.parent;return o.replaceWith(...s.clone().nodes),void(e&&sortCompoundSelectorsInsideComplexSelector(e))}const d=o.parent;r.noIsPseudoSelector?o.replaceWith(...s.clone().nodes):o.replaceWith(...i.clone({}).nodes),d&&sortCompoundSelectorsInsideComplexSelector(d)})),s.push(o.toString())}}return s}function isSimpleSelector(e){return"combinator"!==e.type&&!(e.parent&&e.parent.nodes.length>1)}function isCompoundSelector(e,t=null){if(isSimpleSelector(e))return!1;if(!e.parent)return!1;if(!!e.parent.nodes.find((e=>"combinator"===e.type)))return!1;return!(!!e.parent.nodes.find((e=>"nesting"===e.type))&&t&&!isCompoundSelector(t))}function nestingIsFirstAndOnlyInSelectorWithEitherSpaceOrChildCombinator(e){if(!e.parent)return!1;if(0!==e.parent.nodes.indexOf(e))return!1;for(let t=1;t"!==e.parent.nodes[t].value)return!1;return!0}function nestingIsNotInsideCompoundSelector(e){if(isSimpleSelector(e))return!0;if(!e.parent)return!1;for(let t=0;t{if("decl"===n.type)return t===r-1?void(t=r):(n.remove(),e.insertAfter(t,n),void(t=e.index(n)));if("atrule"===n.type&&"mixin"===n.name.toLowerCase()){let o=n.prev();for(;o;){if("rule"===o.type||"atrule"===o.type)return;o=o.prev()}return t===r-1?void(t=r):(n.remove(),e.insertAfter(t,n),void(t=e.index(n)))}if("comment"===n.type){const o=n.next();if(o&&("comment"===o.type||"rule"===o.type||"atrule"===o.type&&"mixin"!==o.name.toLowerCase()))return;return t===r-1?void(t=r):(n.remove(),e.insertAfter(t,n),void(t=e.index(n)))}}))}function transformRuleWithinRule(e,t,n,r){let o=[];try{o=mergeSelectors(t.selectors,e.selectors,r)}catch(r){return void e.warn(n,`Failed to parse selectors : "${t.selector}" / "${e.selector}" with message: "${r.message}"`)}if(!o.length)return;groupDeclarations(t),shiftNodesBeforeParent(e,t),e.selectors=o;"rule"===e.type&&"rule"===t.type&&e.selector===t.selector&&e.append(...t.nodes),cleanupParent(t)}function isValidRuleWithinRule(e){return e.selectors.every((e=>-1===e.indexOf("|")))}function comma(e){const t=[];let n="",r=!1,o=0,s=!1,i=!1;for(const l of e)i?i=!1:"\\"===l?i=!0:s?l===s&&(s=!1):'"'===l||"'"===l?s=l:"("===l?o+=1:")"===l?o>0&&(o-=1):0===o&&","===l&&(r=!0),r?(""!==n&&t.push(n.trim()),n="",r=!1):n+=l;return t.push(n.trim()),t}function transformNestRuleWithinRule(e,t,n,r,o){let s=[];try{s=mergeSelectors(t.selectors,comma(e.params),o)}catch(r){return void e.warn(n,`Failed to parse selectors : "${t.selector}" / "${e.params}" with message: "${r.message}"`)}if(!s.length)return;shiftNodesBeforeParent(e,t);const i=t.clone().removeAll().append(e.nodes);i.raws.semicolon=!0,i.selectors=s,e.replaceWith(i),cleanupParent(t),r(i,n,o)}function isValidNestRuleWithinRule(e){return comma(e.params).every((e=>e.split("&").length>=2&&-1===e.indexOf("|")))}var o=["container","document","media","supports","layer"];function atruleWithinRule(e,t,n,r,o){if(groupDeclarations(t),shiftNodesBeforeParent(e,t),e.nodes){const s=t.clone().removeAll().append(e.nodes);e.append(s),cleanupParent(t),r(s,n,o)}else cleanupParent(t)}function isAtruleWithinRule(e){return o.includes(e.name)}function transformAtruleWithinAtrule(e,t){var n,r;groupDeclarations(t),shiftNodesBeforeParent(e,t),e.params=(n=t.params,r=e.params,comma(n).map((e=>comma(r).map((t=>`${e} and ${t}`)).join(", "))).join(", ")),cleanupParent(t)}function isAtruleWithinAtrule(e,t){return o.includes(e.name)&&e.name===t.name}function isAtRule(e){return e&&"atrule"===e.type}function isNestRule(e){return e&&isAtRule(e)&&"nest"===e.name}function isRule(e){return e&&"rule"===e.type}function walk(e,t,n){e.each((r=>{const o=r.parent;isNestRule(r)&&!n.silenceAtNestWarning&&e.warn(t,`\`@nest\` was removed from the CSS Nesting specification and will be removed from PostCSS Nesting in the next major version.\nChange \`@nest ${r.params} {}\` to \`${r.params} {}\` to migrate to the latest standard.`),isRule(r)&&isRule(o)&&isValidRuleWithinRule(r)?transformRuleWithinRule(r,o,t,n):isNestRule(r)&&isRule(o)&&isValidNestRuleWithinRule(r)?transformNestRuleWithinRule(r,o,t,walk,n):isAtRule(r)&&isRule(o)&&isAtruleWithinRule(r)?atruleWithinRule(r,o,t,walk,n):isAtRule(r)&&isAtRule(o)&&isAtruleWithinAtrule(r,o)&&transformAtruleWithinAtrule(r,o),"nodes"in r&&r.nodes.length&&walk(r,t,n)}))}const creator=e=>{const t=Object.assign({noIsPseudoSelector:!1,silenceAtNestWarning:!1},e);return{postcssPlugin:"postcss-nesting",Rule(e,{result:n}){walk(e,n,t),e.selector.includes("&")&&ersandToScope(e,n)}}};creator.postcss=!0;export{creator as default}; diff --git a/plugins/postcss-nesting/dist/lib/merge-selectors/merge-selectors.d.ts b/plugins/postcss-nesting/dist/lib/merge-selectors/merge-selectors.d.ts index b794065e4..f46fd8d7c 100644 --- a/plugins/postcss-nesting/dist/lib/merge-selectors/merge-selectors.d.ts +++ b/plugins/postcss-nesting/dist/lib/merge-selectors/merge-selectors.d.ts @@ -1,3 +1,2 @@ import { options } from '../options'; -import type { Node, Result } from 'postcss'; -export default function mergeSelectors(node: Node, postcssResult: Result, fromSelectors: Array, toSelectors: Array, opts: options, fromAtNest?: boolean): any[]; +export default function mergeSelectors(fromSelectors: Array, toSelectors: Array, opts: options): any[]; diff --git a/plugins/postcss-nesting/dist/lib/options.d.ts b/plugins/postcss-nesting/dist/lib/options.d.ts index 5792fc050..876771840 100644 --- a/plugins/postcss-nesting/dist/lib/options.d.ts +++ b/plugins/postcss-nesting/dist/lib/options.d.ts @@ -1,3 +1,4 @@ export type options = { noIsPseudoSelector: boolean; + silenceAtNestWarning: boolean; }; diff --git a/plugins/postcss-nesting/docs/README.md b/plugins/postcss-nesting/docs/README.md index 0308e86e0..5d11b2f5a 100644 --- a/plugins/postcss-nesting/docs/README.md +++ b/plugins/postcss-nesting/docs/README.md @@ -38,39 +38,21 @@ you might want to use [PostCSS Nested] instead. Previous iterations of the [CSS Nesting specification] required using `@nest` for certain selectors. `@nest` was removed from the specification completely. -Future versions of this plugin will first warn and then error if you use `@nest`. +Future versions of this plugin will error if you use `@nest`. -We advice everyone to migrate their codebase **now** to nested CSS without `@nest`. +We advice everyone to migrate their codebase **now** to nested CSS without `@nest`. +We published a [Stylelint Plugin](https://github.com/csstools/postcss-plugins/tree/main/plugins-stylelint/no-at-nest-rule#csstoolsstylelint-no-at-nest-rule) to help you migrate. -## ⚠️ Nested selectors must start with a symbol +example warning: +> `@nest` was removed from the CSS Nesting specification and will be removed from PostCSS Nesting in the next major version. +> Change `@nest foo & {}` to `foo & {}` to migrate to the latest standard. -The current version of the [CSS Nesting specification](https://www.w3.org/TR/2023/WD-css-nesting-1-20230214/#example-34e8e94f) disallows nested selectors to start with a letter (i.e. a tag name or element selector). To write such selectors, they need to be prefixed with `& ` or wrapped with `:is()`. +You can silence this warning with a new `silenceAtNestWarning` plugin option. -You will get a warning when selectors start with a letter: -> Nested selectors must start with a symbol and "span" begins with a letter. - -```pcss -.foo { - /* ❌ invalid */ - span { - color: hotpink; - } - - /* ✅ valid */ - & span { - color: hotpink; - } - - /* ❌ invalid */ - span & { - color: hotpink; - } - - /* ✅ valid */ - :is(span) & { - color: hotpink; - } -} +```js +({ + silenceAtNestWarning: true +}) ``` ## Options diff --git a/plugins/postcss-nesting/src/index.ts b/plugins/postcss-nesting/src/index.ts index d8b5f1ea1..a4a36e4c6 100644 --- a/plugins/postcss-nesting/src/index.ts +++ b/plugins/postcss-nesting/src/index.ts @@ -6,6 +6,8 @@ import walk from './lib/walk.js'; export type pluginOptions = { /** Avoid the `:is()` pseudo class as much as possible. default: false */ noIsPseudoSelector?: boolean, + /** Silence the `@nest` warning. */ + silenceAtNestWarning?: boolean, }; const creator: PluginCreator = (opts?: pluginOptions) => { @@ -13,6 +15,7 @@ const creator: PluginCreator = (opts?: pluginOptions) => { // Default options { noIsPseudoSelector: false, + silenceAtNestWarning: false, }, // Provided options opts, diff --git a/plugins/postcss-nesting/src/lib/merge-selectors/merge-selectors.ts b/plugins/postcss-nesting/src/lib/merge-selectors/merge-selectors.ts index ad0a7fd56..2e35de5d0 100644 --- a/plugins/postcss-nesting/src/lib/merge-selectors/merge-selectors.ts +++ b/plugins/postcss-nesting/src/lib/merge-selectors/merge-selectors.ts @@ -4,9 +4,8 @@ import { combinationsWithSizeN } from './combinations-of-size-n'; import { sortCompoundSelectorsInsideComplexSelector } from './compound-selector-order'; import { nodesAreEquallySpecific } from './specificity'; import { options } from '../options'; -import type { Node, Result } from 'postcss'; -export default function mergeSelectors(node: Node, postcssResult: Result, fromSelectors: Array, toSelectors: Array, opts: options, fromAtNest = false) { +export default function mergeSelectors(fromSelectors: Array, toSelectors: Array, opts: options) { const fromListHasUniformSpecificity = nodesAreEquallySpecific(fromSelectors); let fromSelectorsAST = []; @@ -27,13 +26,6 @@ export default function mergeSelectors(node: Node, postcssResult: Result, fromSe { const toSelectorAST = parser().astSync(toSelector); - if (!fromAtNest) { - if (x === 0 && toSelectorAST.nodes?.[0]?.nodes?.[0]?.type === 'tag') { - node.warn(postcssResult, `Nested selectors must start with a symbol and "${toSelectors[x]}" begins with a letter.`); - return []; - } - } - let isNestContaining = false; toSelectorAST.walk((maybeNesting) => { if (maybeNesting.type === 'nesting') { diff --git a/plugins/postcss-nesting/src/lib/nest-rule-within-rule.ts b/plugins/postcss-nesting/src/lib/nest-rule-within-rule.ts index 3a5860b4e..9545f5801 100644 --- a/plugins/postcss-nesting/src/lib/nest-rule-within-rule.ts +++ b/plugins/postcss-nesting/src/lib/nest-rule-within-rule.ts @@ -10,7 +10,7 @@ export default function transformNestRuleWithinRule(node: AtRule, parent: Rule, let selectors = []; try { - selectors = mergeSelectors(node, result, parent.selectors, comma(node.params), opts, true); + selectors = mergeSelectors(parent.selectors, comma(node.params), opts); } catch (err) { node.warn(result, `Failed to parse selectors : "${parent.selector}" / "${node.params}" with message: "${err.message}"`); return; diff --git a/plugins/postcss-nesting/src/lib/options.ts b/plugins/postcss-nesting/src/lib/options.ts index 00c9b3158..cf1f23307 100644 --- a/plugins/postcss-nesting/src/lib/options.ts +++ b/plugins/postcss-nesting/src/lib/options.ts @@ -1 +1 @@ -export type options = { noIsPseudoSelector: boolean } +export type options = { noIsPseudoSelector: boolean, silenceAtNestWarning: boolean } diff --git a/plugins/postcss-nesting/src/lib/rule-within-rule.ts b/plugins/postcss-nesting/src/lib/rule-within-rule.ts index 174a89ada..c44d1e16e 100644 --- a/plugins/postcss-nesting/src/lib/rule-within-rule.ts +++ b/plugins/postcss-nesting/src/lib/rule-within-rule.ts @@ -10,7 +10,7 @@ export default function transformRuleWithinRule(node: Rule, parent: Rule, result // update the selectors of the node to be merged with the parent try { - selectors = mergeSelectors(node, result, parent.selectors, node.selectors, opts, false); + selectors = mergeSelectors(parent.selectors, node.selectors, opts); } catch (err) { node.warn(result, `Failed to parse selectors : "${parent.selector}" / "${node.selector}" with message: "${err.message}"`); return; diff --git a/plugins/postcss-nesting/src/lib/walk.ts b/plugins/postcss-nesting/src/lib/walk.ts index d4d3c58c1..30ffad425 100644 --- a/plugins/postcss-nesting/src/lib/walk.ts +++ b/plugins/postcss-nesting/src/lib/walk.ts @@ -10,6 +10,14 @@ export default function walk(node: Container, result: Result, opts: options) { node.each((child) => { const parent = child.parent; + if (isNestRule(child) && !opts.silenceAtNestWarning) { + node.warn( + result, + '`@nest` was removed from the CSS Nesting specification and will be removed from PostCSS Nesting in the next major version.\n' + + `Change \`@nest ${child.params} {}\` to \`${child.params} {}\` to migrate to the latest standard.`, + ); + } + if ( isRule(child) && isRule(parent) && diff --git a/plugins/postcss-nesting/test/at-nest.css b/plugins/postcss-nesting/test/at-nest.css new file mode 100644 index 000000000..94ce14929 --- /dev/null +++ b/plugins/postcss-nesting/test/at-nest.css @@ -0,0 +1,1067 @@ +/* This entire file can be deleted after removing support for "@nest" */ + +/* basic.css */ +a { + order: 1; + + @media screen, print { + order: 2; + + @media (min-width: 480px) { + order: 3; + } + + order: 4; + } + + order: 5; + order: 6; + + & b { + order: 7; + + & c { + order: 8; + } + + order: 9; + } + + order: 10; + + @nest body & { + order: 11; + + @nest html & { + order: 12; + } + + order: 13; + } + + order: 14; + + @media screen { + order: 15; + + @media (min-width: 480px) { + order: 16; + } + } + + @nest body& { + order: 17; + } + + @nest html body& { + order: 18; + } +} + +a { + @nest .foo& { + order: 19; + } + + @nest .foo .bar& { + order: 20; + } +} + +a b { + @nest .foo& { + order: 21; + } + + @nest .foo .bar& { + order: 22; + } +} + +.foo { + &h1, + &h2 { + color: red; + } +} + +li { + &+& { + background: red; + } +} + +.foo { + &:where(h1) { + background: red; + } +} + +a { + & b[a="a&b"] { + order: 31; + } +} + +.a, +.b { + &.c, + &.d { + &::before { + order: 41; + } + } +} + +.a:hover, .b:focus { + &::before, &::after { + order: 51; + } +} + +/* leading : root */ +.comments { + /* leading : 1 */ + order: 61; + /* trailing: 2 */ + + & .comment { + order: 62; + } + + /* loose comment */ + & .comment { + order: 63; + } + + /* leading : 4 */ + order: 64; + /* trailing: 5 */ + + & .comment { + /* nested deeper */ + + & .comment { + order: 65; + } + } +} + +.pseudo-element { + @nest ::before& { + order: 71; + } + + @nest ::before & { + order: 71.1; + } +} + +.pseudo-element { + @nest :before& { + order: 72; + } + + @nest :before & { + order: 72.1; + } +} + +.pseudo-element { + @nest &::after { + order: 73; + } + + @nest & ::after { + order: 73.1; + } +} + +.pseudo-element { + @nest &:after { + order: 74; + } + + @nest & :after { + order: 74.1; + } +} + +.has-semi-colons-on-the-last-decl { + order: 73; + /* a comment */ + + & .foo { + order: 73.1; + } +} + +.has-semi-colons-on-the-last-decl { + order: 74; + /* a comment */ + + @nest & .foo { + order: 74.1; + } +} + +.mixed-declarations-and-rules--a { + & { + nested: 1; + } + + declaration: 1; +} + +.mixed-declarations-and-rules--b { + declaration: 1; + + & { + nested: 1; + } + + declaration: 2; + + @media screen { + nested: 2; + } + + declaration: 3; +} + +.node-less-at-rule { + declaration: 1; + + @layer foo; + + declaration: 2; +} + + +/* at-rule.css */ +a { + order: 1; + + @nest b & { + order: 2; + } + + @nest c & { + order: 3; + } + + @nest d & { + order: 4; + } + + @nest e & { + order: 5; + } +} + +a { + order: 1; + + @nest & b { + order: 2; + } + + @nest & c { + order: 3; + } + + @nest & d { + order: 4; + } + + @nest & e { + order: 5; + } + + @nest &[foo="& ,\'"] { + order: 6; + } + + @nest &[a="& ,\'"][b='& . \\a &'] { + order: 7; + } + + @nest &\\foo { + order: 8; + } +} + +.foo { + @nest &h1 { + color: red; + } +} + +.foo { + + @nest &h1, + &h2 { + color: red; + } +} + +.foo { + @nest &.bar { + color: red; + } +} + + +.foo { + + @nest &.bar, + &.baz { + color: red; + } +} + +h1 { + @nest .foo& { + color: blue; + } +} + +h1 { + @nest .foo& .baz & { + color: blue; + } +} + +h1 { + + @nest .foo&, + .bar& { + color: blue; + } +} + +article { + color: blue; + + @nest :where(&) { + color: red; + } +} + +/* direct.css */ +a, +b { + order: 1; + + & c, + & d { + order: 2; + + & e, + & f { + order: 3; + } + + order: 4; + } + + order: 5; +} + +a, +b { + order: 1; + + @nest & c, + & d { + order: 2; + + @nest & e, + & f { + order: 3; + } + + order: 4; + } + + order: 5; +} + +a, +b { + order: 1; + + & c, + & d { + order: 2; + + & e, + & f { + order: 3; + } + + order: 4; + } + + order: 5; +} + +.a, +.b { + order: 6; + + @nest & .c, + & .d { + order: 7; + + @nest & .e, + & .f { + order: 8; + } + + order: 9; + } + + order: 10; +} + +/* empty.css */ +a { + & b { + & c { + order: 1; + } + } +} + +d { + order: 2; + + & e { + order: 3; + } +} + +f { + & g { + order: 4; + } + + order: 5; +} + +a { + @nest & b { + @nest & c { + order: 1; + } + } +} + +d { + order: 2; + + @nest & e { + order: 3; + } +} + +f { + @nest & g { + order: 4; + } + + order: 5; +} + +/* ignore.css */ +a, +b { + order: 1; + + c, + d { + order: 2; + } +} + +& e { + order: 3; +} + +f { + &g { + order: 5; + } +} + +a, +b { + order: 1; + + @nest c, + d { + order: 2; + } +} + +@nest & e { + order: 3; +} + +f { + @nest &h { + order: 5; + } + + @nest &|i { + order: 6; + } +} + +/* invalid-selector.css */ +.foo : bar { + &.child { + order: 1; + } +} + +.foo : bar { + @nest &.child { + order: 2; + } +} + +.foo { + &.child : bar { + order: 3; + } +} + +.foo { + @nest &.child : bar { + order: 4; + } +} + +/* media.css */ +a { + order: 1; + + @media (min-width: 100px) { + order: 2; + + @media (max-width: 200px) { + order: 3; + } + + & b { + @media (max-width: 200px) { + order: 4; + } + } + } + + @media screen, + print and speech { + + @media (max-width: 300px), + (min-aspect-ratio: 16/9) { + order: 5; + + & c { + order: 6; + } + } + } +} + +a { + order: 1; + + @media (min-width: 100px) { + order: 2; + + @media (max-width: 200px) { + order: 3; + } + + @nest & b { + @media (max-width: 200px) { + order: 4; + } + } + } + + @media screen, + print and speech { + + @media (max-width: 300px), + (min-aspect-ratio: 16/9) { + order: 5; + + @nest & c { + order: 6; + } + } + } +} + +/* pseudo-element.css */ +::before { + + @nest .foo&, + &:focus { + order: 1; + } +} + +.a:hover, +.b:focus { + + &::before, + &::after { + order: 2; + } +} + +.a { + &::before { + order: 3; + } + + &::after { + order: 4; + } +} + +.a { + &::before { + order: 5; + + &:focus { + order: 6; + } + } + + &::after { + order: 7; + + &:hover { + order: 8; + } + } +} + +.anything::before { + @nest .something_else>& { + order: 9; + } +} + +/* spec-examples.css */ +/* https://www.w3.org/TR/css-nesting-1/ */ + +/* Example 2 */ + +table.colortable { + & td { + text-align: center; + + &.c { + text-transform: uppercase + } + + &:first-child, + &:first-child+td { + border: 1px solid black + } + } + + & th { + text-align: center; + background: black; + color: white; + } +} + +/* Example 3 */ +/* & can be used on its own */ +.foo { + color: blue; + + &>.bar { + color: red; + } +} + +/* equivalent to + .foo { color: blue; } + .foo > .bar { color: red; } +*/ + + +/* or in a compound selector, + refining the parent’s selector */ +.foo { + color: blue; + + &.bar { + color: red; + } +} + +/* equivalent to + .foo { color: blue; } + .foo.bar { color: red; } +*/ + +/* multiple selectors in the list must all + start with & */ +.foo, +.bar { + color: blue; + + &+.baz, + &.qux { + color: red; + } +} + +/* equivalent to + .foo, .bar { color: blue; } + :is(.foo, .bar) + .baz, + :is(.foo, .bar).qux { color: red; } +*/ + +/* & can be used multiple times in a single selector */ +.foo { + color: blue; + + & .bar & .baz & .qux { + color: red; + } +} + +/* equivalent to + .foo { color: blue; } + .foo .bar .foo .baz .foo .qux { color: red; } +*/ + +/* Somewhat silly, but can be used all on its own, as well. */ +.foo { + color: blue; + + & { + padding: 2ch; + } +} + +/* equivalent to + .foo { color: blue; } + .foo { padding: 2ch; } + + // or + + .foo { + color: blue; + padding: 2ch; + } +*/ + +/* Again, silly, but can even be doubled up. */ +.foo { + color: blue; + + && { + padding: 2ch; + } +} + +/* equivalent to + .foo { color: blue; } + .foo.foo { padding: 2ch; } +*/ + +/* The parent selector can be arbitrarily complicated */ +.error, +#404 { + &:hover>.baz { + color: red; + } +} + +/* equivalent to + :is(.error, #404):hover > .baz { color: red; } +*/ + +/* As can the nested selector */ +.foo { + &:is(.bar, &.baz) { + color: red; + } +} + +/* equivalent to + .foo:is(.bar, .foo.baz) { color: red; } +*/ + +/* Multiple levels of nesting "stack up" the selectors */ +figure { + margin: 0; + + &>figcaption { + background: hsl(0 0% 0% / 50%); + + &>p { + font-size: .9rem; + } + } +} + +/* equivalent to + figure { margin: 0; } + figure > figcaption { background: hsl(0 0% 0% / 50%); } + figure > figcaption > p { font-size: .9rem; } +*/ + +/* No concatenation */ +.foo { + color: blue; + + &__bar { + color: red; + } +} + +/* No & at all */ +.foo { + color: blue; + + .bar { + color: red; + } +} + +/* & isn’t the first simple selector */ +.foo { + color: blue; + + .bar& { + color: red; + } +} + +/* & isn’t the first selector of every one in the list */ +.foo, +.bar { + color: blue; + + &+.baz, + .qux { + color: red; + } +} + +/* Example 4 */ +.foo { + color: red; + + @nest &>.bar { + color: blue; + } +} + +/* equivalent to + .foo { color: red; } + .foo > .bar { color: blue; } +*/ + +.foo { + color: red; + + @nest .parent & { + color: blue; + } +} + +/* equivalent to + .foo { color: red; } + .parent .foo { color: blue; } +*/ + +.foo { + color: red; + + @nest :not(&) { + color: blue; + } +} + +/* equivalent to + .foo { color: red; } + :not(.foo) { color: blue; } +*/ + +.foo { + color: red; + + @nest .bar { + color: blue; + } +} + +/* Invalid because there’s no nesting selector */ + +.foo { + color: red; + + @nest & .bar, + .baz { + color: blue; + } +} + +/* Invalid because not all selectors in the list + contain a nesting selector */ + +/* Example 5 */ +.foo { + color: blue; + + @nest .bar & { + color: red; + + &.baz { + color: green; + } + } +} + +/* equivalent to + .foo { color: blue; } + .bar .foo { color: red; } + .bar .foo.baz { color: green; } +*/ + +/* Example 6 */ +/* Properties can be directly used */ +.foo { + display: grid; + + @media (orientation: landscape) { + grid-auto-flow: column; + } +} + +/* equivalent to + .foo { display: grid; } + + @media (orientation: landscape) { + & { + grid-auto-flow: column; + } + } +*/ + +/* finally equivalent to + .foo { display: grid; } + + @media (orientation: landscape) { + .foo { + grid-auto-flow: column; + } + } +*/ + +/* Conditionals can be further nested */ +.foo { + display: grid; + + @media (orientation: landscape) { + grid-auto-flow: column; + + @media (min-inline-size > 1024px) { + max-inline-size: 1024px; + } + } +} + +/* equivalent to + .foo { display: grid; } + + @media (orientation: landscape) { + .foo { + grid-auto-flow: column; + } + } + + @media (orientation: landscape) and (min-inline-size > 1024px) { + .foo { + max-inline-size: 1024px; + } + } +*/ + +.foo { + color: red; + + @media (min-width: 480px) { + + & h1, + h2 { + color: blue; + } + } +} + +/* Invalid because not all selectors in the list + contain a nesting selector */ + +.foo { + color: red; + + @nest @media (min-width: 480px) { + & { + color: blue; + } + } +} + +/* Invalid because @nest expects a selector prelude, + instead a conditional group rule was provided */ + +/* Example 7 */ +article { + color: green; + + & { + color: blue; + } + + /* + NOTE : We are more forgiving than the spec + This declaration is preserved + */ + color: red; +} + +article { + color: green; + + & { + color: blue; + } + + /* + NOTE : We are more forgiving than the spec + This declaration is preserved + */ + color: red; + + &.foo { + color: yellow; + } + + /* valid! */ +} diff --git a/plugins/postcss-nesting/test/at-nest.expect.css b/plugins/postcss-nesting/test/at-nest.expect.css new file mode 100644 index 000000000..a56ab074d --- /dev/null +++ b/plugins/postcss-nesting/test/at-nest.expect.css @@ -0,0 +1,1032 @@ +/* This entire file can be deleted after removing support for "@nest" */ + +/* basic.css */ +a { + order: 1; + + order: 5; + order: 6; + + order: 10; + + order: 14; +} +@media screen, print { +a { + order: 2; + + order: 4; +} + } +@media screen and (min-width: 480px), print and (min-width: 480px) { +a { + order: 3; +} + } +a b { + order: 7; + + order: 9; + } +a b c { + order: 8; + } +body a { + order: 11; +} +html :is(body a) { + order: 12; +} +body a { + + order: 13; +} +@media screen { +a { + order: 15; +} + } +@media screen and (min-width: 480px) { +a { + order: 16; +} + } +body:is(a) { + order: 17; +} +html body:is(a) { + order: 18; +} + +a.foo { + order: 19; +} + +.foo a.bar { + order: 20; +} + +.foo:is(a b) { + order: 21; +} + +.foo .bar:is(a b) { + order: 22; +} + +h1.foo, + h2.foo { + color: red; + } + +li+li { + background: red; + } + +.foo:where(h1) { + background: red; + } + +a b[a="a&b"] { + order: 31; + } + +.a.c::before, .b.c::before, .a.d::before, .b.d::before { + order: 41; + } + +.a:hover::before, .b:focus::before, .a:hover::after, .b:focus::after { + order: 51; + } + +/* leading : root */ +.comments { + /* leading : 1 */ + order: 61; + + /* leading : 4 */ + order: 64; + /* trailing: 2 */ +} +.comments .comment { + order: 62; + } +/* loose comment */ +.comments .comment { + order: 63; + } +/* trailing: 5 */ +/* nested deeper */ +.comments .comment .comment { + order: 65; + } + +::before.pseudo-element { + order: 71; +} + +::before .pseudo-element { + order: 71.1; +} + +:before.pseudo-element { + order: 72; +} + +:before .pseudo-element { + order: 72.1; +} + +.pseudo-element::after { + order: 73; +} + +.pseudo-element ::after { + order: 73.1; +} + +.pseudo-element:after { + order: 74; +} + +.pseudo-element :after { + order: 74.1; +} + +.has-semi-colons-on-the-last-decl { + order: 73; + /* a comment */ +} + +.has-semi-colons-on-the-last-decl .foo { + order: 73.1; + } + +.has-semi-colons-on-the-last-decl { + order: 74; + /* a comment */ +} + +.has-semi-colons-on-the-last-decl .foo { + order: 74.1; +} + +.mixed-declarations-and-rules--a { + + declaration: 1; +} + +.mixed-declarations-and-rules--a { + nested: 1; + } + +.mixed-declarations-and-rules--b { + declaration: 1; + + declaration: 2; + + declaration: 3; +} + +.mixed-declarations-and-rules--b { + nested: 1; + } + +@media screen { + +.mixed-declarations-and-rules--b { + nested: 2; + } + } + +.node-less-at-rule { + declaration: 1; + + declaration: 2; +} + +@layer foo; + + +/* at-rule.css */ +a { + order: 1; +} +b a { + order: 2; +} +c a { + order: 3; +} +d a { + order: 4; +} +e a { + order: 5; +} + +a { + order: 1; +} + +a b { + order: 2; +} + +a c { + order: 3; +} + +a d { + order: 4; +} + +a e { + order: 5; +} + +a[foo="& ,\'"] { + order: 6; +} + +a[a="& ,\'"][b='& . \\a &'] { + order: 7; +} + +a:is(\\foo) { + order: 8; +} + +h1.foo { + color: red; +} + +h1.foo, h2.foo { + color: red; +} + +.foo.bar { + color: red; +} + + +.foo.bar, .foo.baz { + color: red; +} + +h1.foo { + color: blue; +} + +h1.foo .baz h1 { + color: blue; +} + +h1.foo, h1.bar { + color: blue; +} + +article { + color: blue; +} + +:where(article) { + color: red; +} + +/* direct.css */ +a, +b { + order: 1; + + order: 5; +} +a c, + b c, + a d, + b d { + order: 2; + + order: 4; + } +a c e, + b c e, + a d e, + b d e, + a c f, + b c f, + a d f, + b d f { + order: 3; + } + +a, +b { + order: 1; +} + +a c, +b c, +a d, +b d { + order: 2; +} + +a c e, +b c e, +a d e, +b d e, +a c f, +b c f, +a d f, +b d f { + order: 3; +} + +a c, +b c, +a d, +b d { + + order: 4; +} + +a, +b { + + order: 5; +} + +a, +b { + order: 1; + + order: 5; +} + +a c, + b c, + a d, + b d { + order: 2; + + order: 4; + } + +a c e, + b c e, + a d e, + b d e, + a c f, + b c f, + a d f, + b d f { + order: 3; + } + +.a, +.b { + order: 6; +} + +.a .c, +.b .c, +.a .d, +.b .d { + order: 7; +} + +.a .c .e, +.b .c .e, +.a .d .e, +.b .d .e, +.a .c .f, +.b .c .f, +.a .d .f, +.b .d .f { + order: 8; +} + +.a .c, +.b .c, +.a .d, +.b .d { + + order: 9; +} + +.a, +.b { + + order: 10; +} + +/* empty.css */ +a b c { + order: 1; + } + +d { + order: 2; +} + +d e { + order: 3; + } + +f { + + order: 5; +} + +f g { + order: 4; + } + +a b c { + order: 1; +} + +d { + order: 2; +} + +d e { + order: 3; +} + +f g { + order: 4; +} + +f { + + order: 5; +} + +/* ignore.css */ +a, +b { + order: 1; +} +a c, + b c, + a d, + b d { + order: 2; + } + +:scope e { + order: 3; +} + +f:is(g) { + order: 5; + } + +a, +b { + order: 1; + + @nest c, + d { + order: 2; + } +} + +@nest & e { + order: 3; +} + +f:is(h) { + order: 5; +} + +f { + + @nest &|i { + order: 6; + } +} + +/* invalid-selector.css */ +.foo : bar { + &.child { + order: 1; + } +} + +.foo : bar { + @nest &.child { + order: 2; + } +} + +.foo { + &.child : bar { + order: 3; + } +} + +.foo { + @nest &.child : bar { + order: 4; + } +} + +/* media.css */ +a { + order: 1; +} +@media (min-width: 100px) { +a { + order: 2; +} + } +@media (min-width: 100px) and (max-width: 200px) { +a { + order: 3; +} + } +@media (min-width: 100px) and (max-width: 200px) { + + a b { + order: 4; + } + } +@media screen and (max-width: 300px), screen and (min-aspect-ratio: 16/9), print and speech and (max-width: 300px), print and speech and (min-aspect-ratio: 16/9) { +a { + order: 5; +} + + a c { + order: 6; + } + } + +a { + order: 1; +} + +@media (min-width: 100px) { + +a { + order: 2; +} + } + +@media (min-width: 100px) and (max-width: 200px) { + +a { + order: 3; +} + } + +@media (min-width: 100px) and (max-width: 200px) { + +a b { + order: 4; +} + } + +@media screen and (max-width: 300px), screen and (min-aspect-ratio: 16/9), print and speech and (max-width: 300px), print and speech and (min-aspect-ratio: 16/9) { + +a { + order: 5; +} + +a c { + order: 6; +} + } + +/* pseudo-element.css */ +.foo::before, ::before:focus { + order: 1; +} + +.a:hover::before, + .b:focus::before, + .a:hover::after, + .b:focus::after { + order: 2; + } + +.a::before { + order: 3; + } + +.a::after { + order: 4; + } + +.a::before { + order: 5; + } + +.a::before:focus { + order: 6; + } + +.a::after { + order: 7; + } + +.a::after:hover { + order: 8; + } + +.something_else>.anything::before { + order: 9; +} + +/* spec-examples.css */ +/* https://www.w3.org/TR/css-nesting-1/ */ + +/* Example 2 */ + +table.colortable td { + text-align: center; + } + +table.colortable td.c { + text-transform: uppercase + } + +table.colortable td:first-child, + :is(table.colortable td):first-child+td { + border: 1px solid black + } + +table.colortable th { + text-align: center; + background: black; + color: white; + } + +/* Example 3 */ +/* & can be used on its own */ +.foo { + color: blue; +} +.foo>.bar { + color: red; + } + +/* equivalent to + .foo { color: blue; } + .foo > .bar { color: red; } +*/ + + +/* or in a compound selector, + refining the parent’s selector */ +.foo { + color: blue; +} +.foo.bar { + color: red; + } + +/* equivalent to + .foo { color: blue; } + .foo.bar { color: red; } +*/ + +/* multiple selectors in the list must all + start with & */ +.foo, +.bar { + color: blue; +} +.foo+.baz, + .bar+.baz, + .foo.qux, + .bar.qux { + color: red; + } + +/* equivalent to + .foo, .bar { color: blue; } + :is(.foo, .bar) + .baz, + :is(.foo, .bar).qux { color: red; } +*/ + +/* & can be used multiple times in a single selector */ +.foo { + color: blue; +} +.foo .bar .foo .baz .foo .qux { + color: red; + } + +/* equivalent to + .foo { color: blue; } + .foo .bar .foo .baz .foo .qux { color: red; } +*/ + +/* Somewhat silly, but can be used all on its own, as well. */ +.foo { + color: blue; +} +.foo { + padding: 2ch; + } + +/* equivalent to + .foo { color: blue; } + .foo { padding: 2ch; } + + // or + + .foo { + color: blue; + padding: 2ch; + } +*/ + +/* Again, silly, but can even be doubled up. */ +.foo { + color: blue; +} +.foo.foo { + padding: 2ch; + } + +/* equivalent to + .foo { color: blue; } + .foo.foo { padding: 2ch; } +*/ + +/* The parent selector can be arbitrarily complicated */ +:is(.error,#404):hover>.baz { + color: red; + } + +/* equivalent to + :is(.error, #404):hover > .baz { color: red; } +*/ + +/* As can the nested selector */ +.foo:is(.bar,.foo.baz) { + color: red; + } + +/* equivalent to + .foo:is(.bar, .foo.baz) { color: red; } +*/ + +/* Multiple levels of nesting "stack up" the selectors */ +figure { + margin: 0; +} +figure>figcaption { + background: hsl(0 0% 0% / 50%); + } +figure>figcaption>p { + font-size: .9rem; + } + +/* equivalent to + figure { margin: 0; } + figure > figcaption { background: hsl(0 0% 0% / 50%); } + figure > figcaption > p { font-size: .9rem; } +*/ + +/* No concatenation */ +.foo { + color: blue; +} +__bar.foo { + color: red; + } + +/* No & at all */ +.foo { + color: blue; +} +.foo .bar { + color: red; + } + +/* & isn’t the first simple selector */ +.foo { + color: blue; +} +.bar.foo { + color: red; + } + +/* & isn’t the first selector of every one in the list */ +.foo, +.bar { + color: blue; +} +.foo+.baz, + .bar+.baz, + .foo .qux, + .bar .qux { + color: red; + } + +/* Example 4 */ +.foo { + color: red; +} +.foo>.bar { + color: blue; +} + +/* equivalent to + .foo { color: red; } + .foo > .bar { color: blue; } +*/ + +.foo { + color: red; +} + +.parent .foo { + color: blue; +} + +/* equivalent to + .foo { color: red; } + .parent .foo { color: blue; } +*/ + +.foo { + color: red; +} + +:not(.foo) { + color: blue; +} + +/* equivalent to + .foo { color: red; } + :not(.foo) { color: blue; } +*/ + +.foo { + color: red; + + @nest .bar { + color: blue; + } +} + +/* Invalid because there’s no nesting selector */ + +.foo { + color: red; + + @nest & .bar, + .baz { + color: blue; + } +} + +/* Invalid because not all selectors in the list + contain a nesting selector */ + +/* Example 5 */ +.foo { + color: blue; +} +.bar .foo { + color: red; +} +.bar .foo.baz { + color: green; + } + +/* equivalent to + .foo { color: blue; } + .bar .foo { color: red; } + .bar .foo.baz { color: green; } +*/ + +/* Example 6 */ +/* Properties can be directly used */ +.foo { + display: grid; +} +@media (orientation: landscape) { +.foo { + grid-auto-flow: column; +} + } + +/* equivalent to + .foo { display: grid; } + + @media (orientation: landscape) { + & { + grid-auto-flow: column; + } + } +*/ + +/* finally equivalent to + .foo { display: grid; } + + @media (orientation: landscape) { + .foo { + grid-auto-flow: column; + } + } +*/ + +/* Conditionals can be further nested */ +.foo { + display: grid; +} +@media (orientation: landscape) { +.foo { + grid-auto-flow: column; +} + } +@media (orientation: landscape) and (min-inline-size > 1024px) { +.foo { + max-inline-size: 1024px; +} + } + +/* equivalent to + .foo { display: grid; } + + @media (orientation: landscape) { + .foo { + grid-auto-flow: column; + } + } + + @media (orientation: landscape) and (min-inline-size > 1024px) { + .foo { + max-inline-size: 1024px; + } + } +*/ + +.foo { + color: red; +} + +@media (min-width: 480px) { + + .foo h1, + .foo h2 { + color: blue; + } + } + +/* Invalid because not all selectors in the list + contain a nesting selector */ + +.foo { + color: red; + + @nest @media (min-width: 480px) { + & { + color: blue; + } + } +} + +/* Invalid because @nest expects a selector prelude, + instead a conditional group rule was provided */ + +/* Example 7 */ +article { + color: green; + + /* + NOTE : We are more forgiving than the spec + This declaration is preserved + */ + color: red; +} +article { + color: blue; + } + +article { + color: green; + + /* + NOTE : We are more forgiving than the spec + This declaration is preserved + */ + color: red; + + /* valid! */ +} + +article { + color: blue; + } + +article.foo { + color: yellow; + } diff --git a/plugins/postcss-nesting/test/at-nest.no-is-pseudo-selector.expect.css b/plugins/postcss-nesting/test/at-nest.no-is-pseudo-selector.expect.css new file mode 100644 index 000000000..bd1d54208 --- /dev/null +++ b/plugins/postcss-nesting/test/at-nest.no-is-pseudo-selector.expect.css @@ -0,0 +1,1032 @@ +/* This entire file can be deleted after removing support for "@nest" */ + +/* basic.css */ +a { + order: 1; + + order: 5; + order: 6; + + order: 10; + + order: 14; +} +@media screen, print { +a { + order: 2; + + order: 4; +} + } +@media screen and (min-width: 480px), print and (min-width: 480px) { +a { + order: 3; +} + } +a b { + order: 7; + + order: 9; + } +a b c { + order: 8; + } +body a { + order: 11; +} +html body a { + order: 12; +} +body a { + + order: 13; +} +@media screen { +a { + order: 15; +} + } +@media screen and (min-width: 480px) { +a { + order: 16; +} + } +body:is(a) { + order: 17; +} +html body:is(a) { + order: 18; +} + +a.foo { + order: 19; +} + +.foo a.bar { + order: 20; +} + +a.foo b { + order: 21; +} + +.foo a.bar b { + order: 22; +} + +h1.foo, + h2.foo { + color: red; + } + +li+li { + background: red; + } + +.foo:where(h1) { + background: red; + } + +a b[a="a&b"] { + order: 31; + } + +.a.c::before, .b.c::before, .a.d::before, .b.d::before { + order: 41; + } + +.a:hover::before, .b:focus::before, .a:hover::after, .b:focus::after { + order: 51; + } + +/* leading : root */ +.comments { + /* leading : 1 */ + order: 61; + + /* leading : 4 */ + order: 64; + /* trailing: 2 */ +} +.comments .comment { + order: 62; + } +/* loose comment */ +.comments .comment { + order: 63; + } +/* trailing: 5 */ +/* nested deeper */ +.comments .comment .comment { + order: 65; + } + +::before.pseudo-element { + order: 71; +} + +::before .pseudo-element { + order: 71.1; +} + +:before.pseudo-element { + order: 72; +} + +:before .pseudo-element { + order: 72.1; +} + +.pseudo-element::after { + order: 73; +} + +.pseudo-element ::after { + order: 73.1; +} + +.pseudo-element:after { + order: 74; +} + +.pseudo-element :after { + order: 74.1; +} + +.has-semi-colons-on-the-last-decl { + order: 73; + /* a comment */ +} + +.has-semi-colons-on-the-last-decl .foo { + order: 73.1; + } + +.has-semi-colons-on-the-last-decl { + order: 74; + /* a comment */ +} + +.has-semi-colons-on-the-last-decl .foo { + order: 74.1; +} + +.mixed-declarations-and-rules--a { + + declaration: 1; +} + +.mixed-declarations-and-rules--a { + nested: 1; + } + +.mixed-declarations-and-rules--b { + declaration: 1; + + declaration: 2; + + declaration: 3; +} + +.mixed-declarations-and-rules--b { + nested: 1; + } + +@media screen { + +.mixed-declarations-and-rules--b { + nested: 2; + } + } + +.node-less-at-rule { + declaration: 1; + + declaration: 2; +} + +@layer foo; + + +/* at-rule.css */ +a { + order: 1; +} +b a { + order: 2; +} +c a { + order: 3; +} +d a { + order: 4; +} +e a { + order: 5; +} + +a { + order: 1; +} + +a b { + order: 2; +} + +a c { + order: 3; +} + +a d { + order: 4; +} + +a e { + order: 5; +} + +a[foo="& ,\'"] { + order: 6; +} + +a[a="& ,\'"][b='& . \\a &'] { + order: 7; +} + +a:is(\\foo) { + order: 8; +} + +h1.foo { + color: red; +} + +h1.foo, h2.foo { + color: red; +} + +.foo.bar { + color: red; +} + + +.foo.bar, .foo.baz { + color: red; +} + +h1.foo { + color: blue; +} + +h1.foo .baz h1 { + color: blue; +} + +h1.foo, h1.bar { + color: blue; +} + +article { + color: blue; +} + +:where(article) { + color: red; +} + +/* direct.css */ +a, +b { + order: 1; + + order: 5; +} +a c, + b c, + a d, + b d { + order: 2; + + order: 4; + } +a c e, + b c e, + a d e, + b d e, + a c f, + b c f, + a d f, + b d f { + order: 3; + } + +a, +b { + order: 1; +} + +a c, +b c, +a d, +b d { + order: 2; +} + +a c e, +b c e, +a d e, +b d e, +a c f, +b c f, +a d f, +b d f { + order: 3; +} + +a c, +b c, +a d, +b d { + + order: 4; +} + +a, +b { + + order: 5; +} + +a, +b { + order: 1; + + order: 5; +} + +a c, + b c, + a d, + b d { + order: 2; + + order: 4; + } + +a c e, + b c e, + a d e, + b d e, + a c f, + b c f, + a d f, + b d f { + order: 3; + } + +.a, +.b { + order: 6; +} + +.a .c, +.b .c, +.a .d, +.b .d { + order: 7; +} + +.a .c .e, +.b .c .e, +.a .d .e, +.b .d .e, +.a .c .f, +.b .c .f, +.a .d .f, +.b .d .f { + order: 8; +} + +.a .c, +.b .c, +.a .d, +.b .d { + + order: 9; +} + +.a, +.b { + + order: 10; +} + +/* empty.css */ +a b c { + order: 1; + } + +d { + order: 2; +} + +d e { + order: 3; + } + +f { + + order: 5; +} + +f g { + order: 4; + } + +a b c { + order: 1; +} + +d { + order: 2; +} + +d e { + order: 3; +} + +f g { + order: 4; +} + +f { + + order: 5; +} + +/* ignore.css */ +a, +b { + order: 1; +} +a c, + b c, + a d, + b d { + order: 2; + } + +:scope e { + order: 3; +} + +f:is(g) { + order: 5; + } + +a, +b { + order: 1; + + @nest c, + d { + order: 2; + } +} + +@nest & e { + order: 3; +} + +f:is(h) { + order: 5; +} + +f { + + @nest &|i { + order: 6; + } +} + +/* invalid-selector.css */ +.foo : bar { + &.child { + order: 1; + } +} + +.foo : bar { + @nest &.child { + order: 2; + } +} + +.foo { + &.child : bar { + order: 3; + } +} + +.foo { + @nest &.child : bar { + order: 4; + } +} + +/* media.css */ +a { + order: 1; +} +@media (min-width: 100px) { +a { + order: 2; +} + } +@media (min-width: 100px) and (max-width: 200px) { +a { + order: 3; +} + } +@media (min-width: 100px) and (max-width: 200px) { + + a b { + order: 4; + } + } +@media screen and (max-width: 300px), screen and (min-aspect-ratio: 16/9), print and speech and (max-width: 300px), print and speech and (min-aspect-ratio: 16/9) { +a { + order: 5; +} + + a c { + order: 6; + } + } + +a { + order: 1; +} + +@media (min-width: 100px) { + +a { + order: 2; +} + } + +@media (min-width: 100px) and (max-width: 200px) { + +a { + order: 3; +} + } + +@media (min-width: 100px) and (max-width: 200px) { + +a b { + order: 4; +} + } + +@media screen and (max-width: 300px), screen and (min-aspect-ratio: 16/9), print and speech and (max-width: 300px), print and speech and (min-aspect-ratio: 16/9) { + +a { + order: 5; +} + +a c { + order: 6; +} + } + +/* pseudo-element.css */ +.foo::before, ::before:focus { + order: 1; +} + +.a:hover::before, + .b:focus::before, + .a:hover::after, + .b:focus::after { + order: 2; + } + +.a::before { + order: 3; + } + +.a::after { + order: 4; + } + +.a::before { + order: 5; + } + +.a::before:focus { + order: 6; + } + +.a::after { + order: 7; + } + +.a::after:hover { + order: 8; + } + +.something_else>.anything::before { + order: 9; +} + +/* spec-examples.css */ +/* https://www.w3.org/TR/css-nesting-1/ */ + +/* Example 2 */ + +table.colortable td { + text-align: center; + } + +table.colortable td.c { + text-transform: uppercase + } + +table.colortable td:first-child, + table.colortable td:first-child+td { + border: 1px solid black + } + +table.colortable th { + text-align: center; + background: black; + color: white; + } + +/* Example 3 */ +/* & can be used on its own */ +.foo { + color: blue; +} +.foo>.bar { + color: red; + } + +/* equivalent to + .foo { color: blue; } + .foo > .bar { color: red; } +*/ + + +/* or in a compound selector, + refining the parent’s selector */ +.foo { + color: blue; +} +.foo.bar { + color: red; + } + +/* equivalent to + .foo { color: blue; } + .foo.bar { color: red; } +*/ + +/* multiple selectors in the list must all + start with & */ +.foo, +.bar { + color: blue; +} +.foo+.baz, + .bar+.baz, + .foo.qux, + .bar.qux { + color: red; + } + +/* equivalent to + .foo, .bar { color: blue; } + :is(.foo, .bar) + .baz, + :is(.foo, .bar).qux { color: red; } +*/ + +/* & can be used multiple times in a single selector */ +.foo { + color: blue; +} +.foo .bar .foo .baz .foo .qux { + color: red; + } + +/* equivalent to + .foo { color: blue; } + .foo .bar .foo .baz .foo .qux { color: red; } +*/ + +/* Somewhat silly, but can be used all on its own, as well. */ +.foo { + color: blue; +} +.foo { + padding: 2ch; + } + +/* equivalent to + .foo { color: blue; } + .foo { padding: 2ch; } + + // or + + .foo { + color: blue; + padding: 2ch; + } +*/ + +/* Again, silly, but can even be doubled up. */ +.foo { + color: blue; +} +.foo.foo { + padding: 2ch; + } + +/* equivalent to + .foo { color: blue; } + .foo.foo { padding: 2ch; } +*/ + +/* The parent selector can be arbitrarily complicated */ +.error:hover>.baz, #404:hover>.baz { + color: red; + } + +/* equivalent to + :is(.error, #404):hover > .baz { color: red; } +*/ + +/* As can the nested selector */ +.foo:is(.bar,.foo.baz) { + color: red; + } + +/* equivalent to + .foo:is(.bar, .foo.baz) { color: red; } +*/ + +/* Multiple levels of nesting "stack up" the selectors */ +figure { + margin: 0; +} +figure>figcaption { + background: hsl(0 0% 0% / 50%); + } +figure>figcaption>p { + font-size: .9rem; + } + +/* equivalent to + figure { margin: 0; } + figure > figcaption { background: hsl(0 0% 0% / 50%); } + figure > figcaption > p { font-size: .9rem; } +*/ + +/* No concatenation */ +.foo { + color: blue; +} +__bar.foo { + color: red; + } + +/* No & at all */ +.foo { + color: blue; +} +.foo .bar { + color: red; + } + +/* & isn’t the first simple selector */ +.foo { + color: blue; +} +.bar.foo { + color: red; + } + +/* & isn’t the first selector of every one in the list */ +.foo, +.bar { + color: blue; +} +.foo+.baz, + .bar+.baz, + .foo .qux, + .bar .qux { + color: red; + } + +/* Example 4 */ +.foo { + color: red; +} +.foo>.bar { + color: blue; +} + +/* equivalent to + .foo { color: red; } + .foo > .bar { color: blue; } +*/ + +.foo { + color: red; +} + +.parent .foo { + color: blue; +} + +/* equivalent to + .foo { color: red; } + .parent .foo { color: blue; } +*/ + +.foo { + color: red; +} + +:not(.foo) { + color: blue; +} + +/* equivalent to + .foo { color: red; } + :not(.foo) { color: blue; } +*/ + +.foo { + color: red; + + @nest .bar { + color: blue; + } +} + +/* Invalid because there’s no nesting selector */ + +.foo { + color: red; + + @nest & .bar, + .baz { + color: blue; + } +} + +/* Invalid because not all selectors in the list + contain a nesting selector */ + +/* Example 5 */ +.foo { + color: blue; +} +.bar .foo { + color: red; +} +.bar .foo.baz { + color: green; + } + +/* equivalent to + .foo { color: blue; } + .bar .foo { color: red; } + .bar .foo.baz { color: green; } +*/ + +/* Example 6 */ +/* Properties can be directly used */ +.foo { + display: grid; +} +@media (orientation: landscape) { +.foo { + grid-auto-flow: column; +} + } + +/* equivalent to + .foo { display: grid; } + + @media (orientation: landscape) { + & { + grid-auto-flow: column; + } + } +*/ + +/* finally equivalent to + .foo { display: grid; } + + @media (orientation: landscape) { + .foo { + grid-auto-flow: column; + } + } +*/ + +/* Conditionals can be further nested */ +.foo { + display: grid; +} +@media (orientation: landscape) { +.foo { + grid-auto-flow: column; +} + } +@media (orientation: landscape) and (min-inline-size > 1024px) { +.foo { + max-inline-size: 1024px; +} + } + +/* equivalent to + .foo { display: grid; } + + @media (orientation: landscape) { + .foo { + grid-auto-flow: column; + } + } + + @media (orientation: landscape) and (min-inline-size > 1024px) { + .foo { + max-inline-size: 1024px; + } + } +*/ + +.foo { + color: red; +} + +@media (min-width: 480px) { + + .foo h1, + .foo h2 { + color: blue; + } + } + +/* Invalid because not all selectors in the list + contain a nesting selector */ + +.foo { + color: red; + + @nest @media (min-width: 480px) { + & { + color: blue; + } + } +} + +/* Invalid because @nest expects a selector prelude, + instead a conditional group rule was provided */ + +/* Example 7 */ +article { + color: green; + + /* + NOTE : We are more forgiving than the spec + This declaration is preserved + */ + color: red; +} +article { + color: blue; + } + +article { + color: green; + + /* + NOTE : We are more forgiving than the spec + This declaration is preserved + */ + color: red; + + /* valid! */ +} + +article { + color: blue; + } + +article.foo { + color: yellow; + } diff --git a/plugins/postcss-nesting/test/at-nest.silent.expect.css b/plugins/postcss-nesting/test/at-nest.silent.expect.css new file mode 100644 index 000000000..a56ab074d --- /dev/null +++ b/plugins/postcss-nesting/test/at-nest.silent.expect.css @@ -0,0 +1,1032 @@ +/* This entire file can be deleted after removing support for "@nest" */ + +/* basic.css */ +a { + order: 1; + + order: 5; + order: 6; + + order: 10; + + order: 14; +} +@media screen, print { +a { + order: 2; + + order: 4; +} + } +@media screen and (min-width: 480px), print and (min-width: 480px) { +a { + order: 3; +} + } +a b { + order: 7; + + order: 9; + } +a b c { + order: 8; + } +body a { + order: 11; +} +html :is(body a) { + order: 12; +} +body a { + + order: 13; +} +@media screen { +a { + order: 15; +} + } +@media screen and (min-width: 480px) { +a { + order: 16; +} + } +body:is(a) { + order: 17; +} +html body:is(a) { + order: 18; +} + +a.foo { + order: 19; +} + +.foo a.bar { + order: 20; +} + +.foo:is(a b) { + order: 21; +} + +.foo .bar:is(a b) { + order: 22; +} + +h1.foo, + h2.foo { + color: red; + } + +li+li { + background: red; + } + +.foo:where(h1) { + background: red; + } + +a b[a="a&b"] { + order: 31; + } + +.a.c::before, .b.c::before, .a.d::before, .b.d::before { + order: 41; + } + +.a:hover::before, .b:focus::before, .a:hover::after, .b:focus::after { + order: 51; + } + +/* leading : root */ +.comments { + /* leading : 1 */ + order: 61; + + /* leading : 4 */ + order: 64; + /* trailing: 2 */ +} +.comments .comment { + order: 62; + } +/* loose comment */ +.comments .comment { + order: 63; + } +/* trailing: 5 */ +/* nested deeper */ +.comments .comment .comment { + order: 65; + } + +::before.pseudo-element { + order: 71; +} + +::before .pseudo-element { + order: 71.1; +} + +:before.pseudo-element { + order: 72; +} + +:before .pseudo-element { + order: 72.1; +} + +.pseudo-element::after { + order: 73; +} + +.pseudo-element ::after { + order: 73.1; +} + +.pseudo-element:after { + order: 74; +} + +.pseudo-element :after { + order: 74.1; +} + +.has-semi-colons-on-the-last-decl { + order: 73; + /* a comment */ +} + +.has-semi-colons-on-the-last-decl .foo { + order: 73.1; + } + +.has-semi-colons-on-the-last-decl { + order: 74; + /* a comment */ +} + +.has-semi-colons-on-the-last-decl .foo { + order: 74.1; +} + +.mixed-declarations-and-rules--a { + + declaration: 1; +} + +.mixed-declarations-and-rules--a { + nested: 1; + } + +.mixed-declarations-and-rules--b { + declaration: 1; + + declaration: 2; + + declaration: 3; +} + +.mixed-declarations-and-rules--b { + nested: 1; + } + +@media screen { + +.mixed-declarations-and-rules--b { + nested: 2; + } + } + +.node-less-at-rule { + declaration: 1; + + declaration: 2; +} + +@layer foo; + + +/* at-rule.css */ +a { + order: 1; +} +b a { + order: 2; +} +c a { + order: 3; +} +d a { + order: 4; +} +e a { + order: 5; +} + +a { + order: 1; +} + +a b { + order: 2; +} + +a c { + order: 3; +} + +a d { + order: 4; +} + +a e { + order: 5; +} + +a[foo="& ,\'"] { + order: 6; +} + +a[a="& ,\'"][b='& . \\a &'] { + order: 7; +} + +a:is(\\foo) { + order: 8; +} + +h1.foo { + color: red; +} + +h1.foo, h2.foo { + color: red; +} + +.foo.bar { + color: red; +} + + +.foo.bar, .foo.baz { + color: red; +} + +h1.foo { + color: blue; +} + +h1.foo .baz h1 { + color: blue; +} + +h1.foo, h1.bar { + color: blue; +} + +article { + color: blue; +} + +:where(article) { + color: red; +} + +/* direct.css */ +a, +b { + order: 1; + + order: 5; +} +a c, + b c, + a d, + b d { + order: 2; + + order: 4; + } +a c e, + b c e, + a d e, + b d e, + a c f, + b c f, + a d f, + b d f { + order: 3; + } + +a, +b { + order: 1; +} + +a c, +b c, +a d, +b d { + order: 2; +} + +a c e, +b c e, +a d e, +b d e, +a c f, +b c f, +a d f, +b d f { + order: 3; +} + +a c, +b c, +a d, +b d { + + order: 4; +} + +a, +b { + + order: 5; +} + +a, +b { + order: 1; + + order: 5; +} + +a c, + b c, + a d, + b d { + order: 2; + + order: 4; + } + +a c e, + b c e, + a d e, + b d e, + a c f, + b c f, + a d f, + b d f { + order: 3; + } + +.a, +.b { + order: 6; +} + +.a .c, +.b .c, +.a .d, +.b .d { + order: 7; +} + +.a .c .e, +.b .c .e, +.a .d .e, +.b .d .e, +.a .c .f, +.b .c .f, +.a .d .f, +.b .d .f { + order: 8; +} + +.a .c, +.b .c, +.a .d, +.b .d { + + order: 9; +} + +.a, +.b { + + order: 10; +} + +/* empty.css */ +a b c { + order: 1; + } + +d { + order: 2; +} + +d e { + order: 3; + } + +f { + + order: 5; +} + +f g { + order: 4; + } + +a b c { + order: 1; +} + +d { + order: 2; +} + +d e { + order: 3; +} + +f g { + order: 4; +} + +f { + + order: 5; +} + +/* ignore.css */ +a, +b { + order: 1; +} +a c, + b c, + a d, + b d { + order: 2; + } + +:scope e { + order: 3; +} + +f:is(g) { + order: 5; + } + +a, +b { + order: 1; + + @nest c, + d { + order: 2; + } +} + +@nest & e { + order: 3; +} + +f:is(h) { + order: 5; +} + +f { + + @nest &|i { + order: 6; + } +} + +/* invalid-selector.css */ +.foo : bar { + &.child { + order: 1; + } +} + +.foo : bar { + @nest &.child { + order: 2; + } +} + +.foo { + &.child : bar { + order: 3; + } +} + +.foo { + @nest &.child : bar { + order: 4; + } +} + +/* media.css */ +a { + order: 1; +} +@media (min-width: 100px) { +a { + order: 2; +} + } +@media (min-width: 100px) and (max-width: 200px) { +a { + order: 3; +} + } +@media (min-width: 100px) and (max-width: 200px) { + + a b { + order: 4; + } + } +@media screen and (max-width: 300px), screen and (min-aspect-ratio: 16/9), print and speech and (max-width: 300px), print and speech and (min-aspect-ratio: 16/9) { +a { + order: 5; +} + + a c { + order: 6; + } + } + +a { + order: 1; +} + +@media (min-width: 100px) { + +a { + order: 2; +} + } + +@media (min-width: 100px) and (max-width: 200px) { + +a { + order: 3; +} + } + +@media (min-width: 100px) and (max-width: 200px) { + +a b { + order: 4; +} + } + +@media screen and (max-width: 300px), screen and (min-aspect-ratio: 16/9), print and speech and (max-width: 300px), print and speech and (min-aspect-ratio: 16/9) { + +a { + order: 5; +} + +a c { + order: 6; +} + } + +/* pseudo-element.css */ +.foo::before, ::before:focus { + order: 1; +} + +.a:hover::before, + .b:focus::before, + .a:hover::after, + .b:focus::after { + order: 2; + } + +.a::before { + order: 3; + } + +.a::after { + order: 4; + } + +.a::before { + order: 5; + } + +.a::before:focus { + order: 6; + } + +.a::after { + order: 7; + } + +.a::after:hover { + order: 8; + } + +.something_else>.anything::before { + order: 9; +} + +/* spec-examples.css */ +/* https://www.w3.org/TR/css-nesting-1/ */ + +/* Example 2 */ + +table.colortable td { + text-align: center; + } + +table.colortable td.c { + text-transform: uppercase + } + +table.colortable td:first-child, + :is(table.colortable td):first-child+td { + border: 1px solid black + } + +table.colortable th { + text-align: center; + background: black; + color: white; + } + +/* Example 3 */ +/* & can be used on its own */ +.foo { + color: blue; +} +.foo>.bar { + color: red; + } + +/* equivalent to + .foo { color: blue; } + .foo > .bar { color: red; } +*/ + + +/* or in a compound selector, + refining the parent’s selector */ +.foo { + color: blue; +} +.foo.bar { + color: red; + } + +/* equivalent to + .foo { color: blue; } + .foo.bar { color: red; } +*/ + +/* multiple selectors in the list must all + start with & */ +.foo, +.bar { + color: blue; +} +.foo+.baz, + .bar+.baz, + .foo.qux, + .bar.qux { + color: red; + } + +/* equivalent to + .foo, .bar { color: blue; } + :is(.foo, .bar) + .baz, + :is(.foo, .bar).qux { color: red; } +*/ + +/* & can be used multiple times in a single selector */ +.foo { + color: blue; +} +.foo .bar .foo .baz .foo .qux { + color: red; + } + +/* equivalent to + .foo { color: blue; } + .foo .bar .foo .baz .foo .qux { color: red; } +*/ + +/* Somewhat silly, but can be used all on its own, as well. */ +.foo { + color: blue; +} +.foo { + padding: 2ch; + } + +/* equivalent to + .foo { color: blue; } + .foo { padding: 2ch; } + + // or + + .foo { + color: blue; + padding: 2ch; + } +*/ + +/* Again, silly, but can even be doubled up. */ +.foo { + color: blue; +} +.foo.foo { + padding: 2ch; + } + +/* equivalent to + .foo { color: blue; } + .foo.foo { padding: 2ch; } +*/ + +/* The parent selector can be arbitrarily complicated */ +:is(.error,#404):hover>.baz { + color: red; + } + +/* equivalent to + :is(.error, #404):hover > .baz { color: red; } +*/ + +/* As can the nested selector */ +.foo:is(.bar,.foo.baz) { + color: red; + } + +/* equivalent to + .foo:is(.bar, .foo.baz) { color: red; } +*/ + +/* Multiple levels of nesting "stack up" the selectors */ +figure { + margin: 0; +} +figure>figcaption { + background: hsl(0 0% 0% / 50%); + } +figure>figcaption>p { + font-size: .9rem; + } + +/* equivalent to + figure { margin: 0; } + figure > figcaption { background: hsl(0 0% 0% / 50%); } + figure > figcaption > p { font-size: .9rem; } +*/ + +/* No concatenation */ +.foo { + color: blue; +} +__bar.foo { + color: red; + } + +/* No & at all */ +.foo { + color: blue; +} +.foo .bar { + color: red; + } + +/* & isn’t the first simple selector */ +.foo { + color: blue; +} +.bar.foo { + color: red; + } + +/* & isn’t the first selector of every one in the list */ +.foo, +.bar { + color: blue; +} +.foo+.baz, + .bar+.baz, + .foo .qux, + .bar .qux { + color: red; + } + +/* Example 4 */ +.foo { + color: red; +} +.foo>.bar { + color: blue; +} + +/* equivalent to + .foo { color: red; } + .foo > .bar { color: blue; } +*/ + +.foo { + color: red; +} + +.parent .foo { + color: blue; +} + +/* equivalent to + .foo { color: red; } + .parent .foo { color: blue; } +*/ + +.foo { + color: red; +} + +:not(.foo) { + color: blue; +} + +/* equivalent to + .foo { color: red; } + :not(.foo) { color: blue; } +*/ + +.foo { + color: red; + + @nest .bar { + color: blue; + } +} + +/* Invalid because there’s no nesting selector */ + +.foo { + color: red; + + @nest & .bar, + .baz { + color: blue; + } +} + +/* Invalid because not all selectors in the list + contain a nesting selector */ + +/* Example 5 */ +.foo { + color: blue; +} +.bar .foo { + color: red; +} +.bar .foo.baz { + color: green; + } + +/* equivalent to + .foo { color: blue; } + .bar .foo { color: red; } + .bar .foo.baz { color: green; } +*/ + +/* Example 6 */ +/* Properties can be directly used */ +.foo { + display: grid; +} +@media (orientation: landscape) { +.foo { + grid-auto-flow: column; +} + } + +/* equivalent to + .foo { display: grid; } + + @media (orientation: landscape) { + & { + grid-auto-flow: column; + } + } +*/ + +/* finally equivalent to + .foo { display: grid; } + + @media (orientation: landscape) { + .foo { + grid-auto-flow: column; + } + } +*/ + +/* Conditionals can be further nested */ +.foo { + display: grid; +} +@media (orientation: landscape) { +.foo { + grid-auto-flow: column; +} + } +@media (orientation: landscape) and (min-inline-size > 1024px) { +.foo { + max-inline-size: 1024px; +} + } + +/* equivalent to + .foo { display: grid; } + + @media (orientation: landscape) { + .foo { + grid-auto-flow: column; + } + } + + @media (orientation: landscape) and (min-inline-size > 1024px) { + .foo { + max-inline-size: 1024px; + } + } +*/ + +.foo { + color: red; +} + +@media (min-width: 480px) { + + .foo h1, + .foo h2 { + color: blue; + } + } + +/* Invalid because not all selectors in the list + contain a nesting selector */ + +.foo { + color: red; + + @nest @media (min-width: 480px) { + & { + color: blue; + } + } +} + +/* Invalid because @nest expects a selector prelude, + instead a conditional group rule was provided */ + +/* Example 7 */ +article { + color: green; + + /* + NOTE : We are more forgiving than the spec + This declaration is preserved + */ + color: red; +} +article { + color: blue; + } + +article { + color: green; + + /* + NOTE : We are more forgiving than the spec + This declaration is preserved + */ + color: red; + + /* valid! */ +} + +article { + color: blue; + } + +article.foo { + color: yellow; + } diff --git a/plugins/postcss-nesting/test/at-rule.css b/plugins/postcss-nesting/test/at-rule.css deleted file mode 100644 index 4e1fd1993..000000000 --- a/plugins/postcss-nesting/test/at-rule.css +++ /dev/null @@ -1,93 +0,0 @@ -a { - order: 1; - @nest b & { - order: 2; - } - @nest c & { - order: 3; - } - @nest d & { - order: 4; - } - @nest e & { - order: 5; - } -} -a { - order: 1; - @nest & b { - order: 2; - } - @nest & c { - order: 3; - } - @nest & d { - order: 4; - } - @nest & e { - order: 5; - } - - @nest &[foo="& ,\'"] { - order: 6; - } - - @nest &[a="& ,\'"][b='& . \\a &'] { - order: 7; - } - - @nest &\\foo { - order: 8; - } -} - -.foo { - @nest &h1 { - color: red; - } -} - -.foo { - @nest &h1, &h2 { - color: red; - } -} - -.foo { - @nest &.bar { - color: red; - } -} - - -.foo { - @nest &.bar, &.baz { - color: red; - } -} - -h1 { - @nest .foo& { - color: blue; - } -} - -h1 { - @nest .foo& .baz & { - color: blue; - } -} - -h1 { - @nest .foo&, .bar& { - color: blue; - } -} - -article { - color: blue; - - @nest :where(&) { - color: red; - } -} diff --git a/plugins/postcss-nesting/test/at-rule.expect.css b/plugins/postcss-nesting/test/at-rule.expect.css deleted file mode 100644 index a71fe47e6..000000000 --- a/plugins/postcss-nesting/test/at-rule.expect.css +++ /dev/null @@ -1,76 +0,0 @@ -a { - order: 1; -} -b a { - order: 2; -} -c a { - order: 3; -} -d a { - order: 4; -} -e a { - order: 5; -} -a { - order: 1; -} -a b { - order: 2; -} -a c { - order: 3; -} -a d { - order: 4; -} -a e { - order: 5; -} -a[foo="& ,\'"] { - order: 6; -} -a[a="& ,\'"][b='& . \\a &'] { - order: 7; -} -a:is(\\foo) { - order: 8; -} - -h1.foo { - color: red; -} - -h1.foo, h2.foo { - color: red; -} - -.foo.bar { - color: red; -} - - -.foo.bar, .foo.baz { - color: red; -} - -h1.foo { - color: blue; -} - -h1.foo .baz h1 { - color: blue; -} - -h1.foo, h1.bar { - color: blue; -} - -article { - color: blue; -} - -:where(article) { - color: red; -} diff --git a/plugins/postcss-nesting/test/at-rule.no-is-pseudo-selector.expect.css b/plugins/postcss-nesting/test/at-rule.no-is-pseudo-selector.expect.css deleted file mode 100644 index a71fe47e6..000000000 --- a/plugins/postcss-nesting/test/at-rule.no-is-pseudo-selector.expect.css +++ /dev/null @@ -1,76 +0,0 @@ -a { - order: 1; -} -b a { - order: 2; -} -c a { - order: 3; -} -d a { - order: 4; -} -e a { - order: 5; -} -a { - order: 1; -} -a b { - order: 2; -} -a c { - order: 3; -} -a d { - order: 4; -} -a e { - order: 5; -} -a[foo="& ,\'"] { - order: 6; -} -a[a="& ,\'"][b='& . \\a &'] { - order: 7; -} -a:is(\\foo) { - order: 8; -} - -h1.foo { - color: red; -} - -h1.foo, h2.foo { - color: red; -} - -.foo.bar { - color: red; -} - - -.foo.bar, .foo.baz { - color: red; -} - -h1.foo { - color: blue; -} - -h1.foo .baz h1 { - color: blue; -} - -h1.foo, h1.bar { - color: blue; -} - -article { - color: blue; -} - -:where(article) { - color: red; -} diff --git a/plugins/postcss-nesting/test/basic.css b/plugins/postcss-nesting/test/basic.css index d936ca38f..a6ded2665 100644 --- a/plugins/postcss-nesting/test/basic.css +++ b/plugins/postcss-nesting/test/basic.css @@ -26,10 +26,10 @@ a { order: 10; - @nest body & { + body & { order: 11; - @nest html & { + html & { order: 12; } @@ -46,31 +46,31 @@ a { } } - @nest body& { + body& { order: 17; } - @nest html body& { + html body& { order: 18; } } a { - @nest .foo& { + .foo& { order: 19; } - @nest .foo .bar& { + .foo .bar& { order: 20; } } a b { - @nest .foo& { + .foo& { order: 21; } - @nest .foo .bar& { + .foo .bar& { order: 22; } } @@ -145,41 +145,41 @@ a { } .pseudo-element { - @nest ::before& { + ::before& { order: 71; } - @nest ::before & { + ::before & { order: 71.1; } } .pseudo-element { - @nest :before& { + :before& { order: 72; } - @nest :before & { + :before & { order: 72.1; } } .pseudo-element { - @nest &::after { + &::after { order: 73; } - @nest & ::after { + & ::after { order: 73.1; } } .pseudo-element { - @nest &:after { + &:after { order: 74; } - @nest & :after { + & :after { order: 74.1; } } @@ -197,7 +197,7 @@ a { order: 74; /* a comment */ - @nest & .foo { + & .foo { order: 74.1; } } diff --git a/plugins/postcss-nesting/test/basic.expect.css b/plugins/postcss-nesting/test/basic.expect.css index 429500ded..ac5d0a71f 100644 --- a/plugins/postcss-nesting/test/basic.expect.css +++ b/plugins/postcss-nesting/test/basic.expect.css @@ -37,16 +37,13 @@ a b c { body a { order: 11; -} + + order: 13; + } html :is(body a) { order: 12; -} - -body a { - - order: 13; -} + } @media screen { @@ -64,27 +61,27 @@ a { body:is(a) { order: 17; -} + } html body:is(a) { order: 18; -} + } a.foo { order: 19; -} + } .foo a.bar { order: 20; -} + } .foo:is(a b) { order: 21; -} + } .foo .bar:is(a b) { order: 22; -} + } h1.foo, h2.foo { @@ -135,35 +132,35 @@ a b[a="a&b"] { ::before.pseudo-element { order: 71; -} + } ::before .pseudo-element { order: 71.1; -} + } :before.pseudo-element { order: 72; -} + } :before .pseudo-element { order: 72.1; -} + } .pseudo-element::after { order: 73; -} + } .pseudo-element ::after { order: 73.1; -} + } .pseudo-element:after { order: 74; -} + } .pseudo-element :after { order: 74.1; -} + } .has-semi-colons-on-the-last-decl { order: 73; @@ -181,7 +178,7 @@ a b[a="a&b"] { .has-semi-colons-on-the-last-decl .foo { order: 74.1; -} + } .mixed-declarations-and-rules--a { diff --git a/plugins/postcss-nesting/test/basic.no-is-pseudo-selector.expect.css b/plugins/postcss-nesting/test/basic.no-is-pseudo-selector.expect.css index d1c908790..d9ed07147 100644 --- a/plugins/postcss-nesting/test/basic.no-is-pseudo-selector.expect.css +++ b/plugins/postcss-nesting/test/basic.no-is-pseudo-selector.expect.css @@ -37,16 +37,13 @@ a b c { body a { order: 11; -} + + order: 13; + } html body a { order: 12; -} - -body a { - - order: 13; -} + } @media screen { @@ -64,27 +61,27 @@ a { body:is(a) { order: 17; -} + } html body:is(a) { order: 18; -} + } a.foo { order: 19; -} + } .foo a.bar { order: 20; -} + } a.foo b { order: 21; -} + } .foo a.bar b { order: 22; -} + } h1.foo, h2.foo { @@ -135,35 +132,35 @@ a b[a="a&b"] { ::before.pseudo-element { order: 71; -} + } ::before .pseudo-element { order: 71.1; -} + } :before.pseudo-element { order: 72; -} + } :before .pseudo-element { order: 72.1; -} + } .pseudo-element::after { order: 73; -} + } .pseudo-element ::after { order: 73.1; -} + } .pseudo-element:after { order: 74; -} + } .pseudo-element :after { order: 74.1; -} + } .has-semi-colons-on-the-last-decl { order: 73; @@ -181,7 +178,7 @@ a b[a="a&b"] { .has-semi-colons-on-the-last-decl .foo { order: 74.1; -} + } .mixed-declarations-and-rules--a { diff --git a/plugins/postcss-nesting/test/direct.css b/plugins/postcss-nesting/test/direct.css index 7ff62443a..e72bee91f 100644 --- a/plugins/postcss-nesting/test/direct.css +++ b/plugins/postcss-nesting/test/direct.css @@ -11,9 +11,9 @@ a, b { } a, b { order: 1; - @nest & c, & d { + & c, & d { order: 2; - @nest & e, & f { + & e, & f { order: 3; } order: 4; @@ -44,11 +44,11 @@ b { .b { order: 6; - @nest & .c, + & .c, & .d { order: 7; - @nest & .e, + & .e, & .f { order: 8; } diff --git a/plugins/postcss-nesting/test/direct.expect.css b/plugins/postcss-nesting/test/direct.expect.css index c64bbe2f0..617833d48 100644 --- a/plugins/postcss-nesting/test/direct.expect.css +++ b/plugins/postcss-nesting/test/direct.expect.css @@ -11,19 +11,15 @@ a c e, b c e, a d e, b d e, a c f, b c f, a d f, b d f { } a, b { order: 1; + order: 5; } a c, b c, a d, b d { order: 2; -} + order: 4; + } a c e, b c e, a d e, b d e, a c f, b c f, a d f, b d f { order: 3; -} -a c, b c, a d, b d { - order: 4; -} -a, b { - order: 5; -} + } a, b { @@ -55,36 +51,26 @@ a c e, .a, .b { order: 6; -} -.a .c, -.b .c, -.a .d, -.b .d { - order: 7; -} - -.a .c .e, -.b .c .e, -.a .d .e, -.b .d .e, -.a .c .f, -.b .c .f, -.a .d .f, -.b .d .f { - order: 8; + order: 10; } .a .c, -.b .c, -.a .d, -.b .d { + .b .c, + .a .d, + .b .d { + order: 7; order: 9; -} - -.a, -.b { + } - order: 10; -} +.a .c .e, + .b .c .e, + .a .d .e, + .b .d .e, + .a .c .f, + .b .c .f, + .a .d .f, + .b .d .f { + order: 8; + } diff --git a/plugins/postcss-nesting/test/direct.no-is-pseudo-selector.expect.css b/plugins/postcss-nesting/test/direct.no-is-pseudo-selector.expect.css index c64bbe2f0..617833d48 100644 --- a/plugins/postcss-nesting/test/direct.no-is-pseudo-selector.expect.css +++ b/plugins/postcss-nesting/test/direct.no-is-pseudo-selector.expect.css @@ -11,19 +11,15 @@ a c e, b c e, a d e, b d e, a c f, b c f, a d f, b d f { } a, b { order: 1; + order: 5; } a c, b c, a d, b d { order: 2; -} + order: 4; + } a c e, b c e, a d e, b d e, a c f, b c f, a d f, b d f { order: 3; -} -a c, b c, a d, b d { - order: 4; -} -a, b { - order: 5; -} + } a, b { @@ -55,36 +51,26 @@ a c e, .a, .b { order: 6; -} -.a .c, -.b .c, -.a .d, -.b .d { - order: 7; -} - -.a .c .e, -.b .c .e, -.a .d .e, -.b .d .e, -.a .c .f, -.b .c .f, -.a .d .f, -.b .d .f { - order: 8; + order: 10; } .a .c, -.b .c, -.a .d, -.b .d { + .b .c, + .a .d, + .b .d { + order: 7; order: 9; -} - -.a, -.b { + } - order: 10; -} +.a .c .e, + .b .c .e, + .a .d .e, + .b .d .e, + .a .c .f, + .b .c .f, + .a .d .f, + .b .d .f { + order: 8; + } diff --git a/plugins/postcss-nesting/test/empty.css b/plugins/postcss-nesting/test/empty.css index fb5988fe5..6d94f61df 100644 --- a/plugins/postcss-nesting/test/empty.css +++ b/plugins/postcss-nesting/test/empty.css @@ -18,20 +18,20 @@ f { order: 5; } a { - @nest & b { - @nest & c { + & b { + & c { order: 1; } } } d { order: 2; - @nest & e { + & e { order: 3; } } f { - @nest & g { + & g { order: 4; } order: 5; diff --git a/plugins/postcss-nesting/test/empty.expect.css b/plugins/postcss-nesting/test/empty.expect.css index 7ad6c06a8..9892800a9 100644 --- a/plugins/postcss-nesting/test/empty.expect.css +++ b/plugins/postcss-nesting/test/empty.expect.css @@ -16,16 +16,16 @@ f g { } a b c { order: 1; -} + } d { order: 2; } d e { order: 3; -} -f g { - order: 4; -} + } f { order: 5; } +f g { + order: 4; + } diff --git a/plugins/postcss-nesting/test/empty.no-is-pseudo-selector.expect.css b/plugins/postcss-nesting/test/empty.no-is-pseudo-selector.expect.css index 7ad6c06a8..9892800a9 100644 --- a/plugins/postcss-nesting/test/empty.no-is-pseudo-selector.expect.css +++ b/plugins/postcss-nesting/test/empty.no-is-pseudo-selector.expect.css @@ -16,16 +16,16 @@ f g { } a b c { order: 1; -} + } d { order: 2; } d e { order: 3; -} -f g { - order: 4; -} + } f { order: 5; } +f g { + order: 4; + } diff --git a/plugins/postcss-nesting/test/ignore.css b/plugins/postcss-nesting/test/ignore.css index 133a2e700..d1af9f263 100644 --- a/plugins/postcss-nesting/test/ignore.css +++ b/plugins/postcss-nesting/test/ignore.css @@ -14,18 +14,18 @@ f { } a, b { order: 1; - @nest c, d { + c, d { order: 2; } } -@nest & e { +& e { order: 3; } f { - @nest &h { + &h { order: 5; } - @nest &|i { + &|i { order: 6; } } diff --git a/plugins/postcss-nesting/test/ignore.expect.css b/plugins/postcss-nesting/test/ignore.expect.css index 732184e15..32dcf5863 100644 --- a/plugins/postcss-nesting/test/ignore.expect.css +++ b/plugins/postcss-nesting/test/ignore.expect.css @@ -1,9 +1,9 @@ a, b { order: 1; - c, d { +} +a c, b c, a d, b d { order: 2; } -} :scope e { order: 3; } @@ -12,18 +12,18 @@ f:is(g) { } a, b { order: 1; - @nest c, d { +} +a c, b c, a d, b d { order: 2; } -} -@nest & e { +:scope e { order: 3; } f:is(h) { order: 5; -} + } f { - @nest &|i { + &|i { order: 6; } } diff --git a/plugins/postcss-nesting/test/ignore.no-is-pseudo-selector.expect.css b/plugins/postcss-nesting/test/ignore.no-is-pseudo-selector.expect.css index 732184e15..32dcf5863 100644 --- a/plugins/postcss-nesting/test/ignore.no-is-pseudo-selector.expect.css +++ b/plugins/postcss-nesting/test/ignore.no-is-pseudo-selector.expect.css @@ -1,9 +1,9 @@ a, b { order: 1; - c, d { +} +a c, b c, a d, b d { order: 2; } -} :scope e { order: 3; } @@ -12,18 +12,18 @@ f:is(g) { } a, b { order: 1; - @nest c, d { +} +a c, b c, a d, b d { order: 2; } -} -@nest & e { +:scope e { order: 3; } f:is(h) { order: 5; -} + } f { - @nest &|i { + &|i { order: 6; } } diff --git a/plugins/postcss-nesting/test/invalid-selector.css b/plugins/postcss-nesting/test/invalid-selector.css index 1cac2bc97..c1dcf1d28 100644 --- a/plugins/postcss-nesting/test/invalid-selector.css +++ b/plugins/postcss-nesting/test/invalid-selector.css @@ -5,7 +5,7 @@ } .foo : bar { - @nest &.child { + &.child { order: 2; } } @@ -17,7 +17,7 @@ } .foo { - @nest &.child : bar { + &.child : bar { order: 4; } } diff --git a/plugins/postcss-nesting/test/invalid-selector.expect.css b/plugins/postcss-nesting/test/invalid-selector.expect.css index 1cac2bc97..c1dcf1d28 100644 --- a/plugins/postcss-nesting/test/invalid-selector.expect.css +++ b/plugins/postcss-nesting/test/invalid-selector.expect.css @@ -5,7 +5,7 @@ } .foo : bar { - @nest &.child { + &.child { order: 2; } } @@ -17,7 +17,7 @@ } .foo { - @nest &.child : bar { + &.child : bar { order: 4; } } diff --git a/plugins/postcss-nesting/test/media.css b/plugins/postcss-nesting/test/media.css index d8714e90a..79d72086e 100644 --- a/plugins/postcss-nesting/test/media.css +++ b/plugins/postcss-nesting/test/media.css @@ -27,7 +27,7 @@ a { @media (max-width: 200px) { order: 3; } - @nest & b { + & b { @media (max-width: 200px) { order: 4; } @@ -36,7 +36,7 @@ a { @media screen, print and speech { @media (max-width: 300px), (min-aspect-ratio: 16/9) { order: 5; - @nest & c { + & c { order: 6; } } diff --git a/plugins/postcss-nesting/test/media.expect.css b/plugins/postcss-nesting/test/media.expect.css index 88cfed624..46bbaa778 100644 --- a/plugins/postcss-nesting/test/media.expect.css +++ b/plugins/postcss-nesting/test/media.expect.css @@ -38,15 +38,15 @@ a { } } @media (min-width: 100px) and (max-width: 200px) { -a b { + a b { order: 4; -} + } } @media screen and (max-width: 300px), screen and (min-aspect-ratio: 16/9), print and speech and (max-width: 300px), print and speech and (min-aspect-ratio: 16/9) { a { order: 5; } -a c { + a c { order: 6; -} + } } diff --git a/plugins/postcss-nesting/test/media.no-is-pseudo-selector.expect.css b/plugins/postcss-nesting/test/media.no-is-pseudo-selector.expect.css index 88cfed624..46bbaa778 100644 --- a/plugins/postcss-nesting/test/media.no-is-pseudo-selector.expect.css +++ b/plugins/postcss-nesting/test/media.no-is-pseudo-selector.expect.css @@ -38,15 +38,15 @@ a { } } @media (min-width: 100px) and (max-width: 200px) { -a b { + a b { order: 4; -} + } } @media screen and (max-width: 300px), screen and (min-aspect-ratio: 16/9), print and speech and (max-width: 300px), print and speech and (min-aspect-ratio: 16/9) { a { order: 5; } -a c { + a c { order: 6; -} + } } diff --git a/plugins/postcss-nesting/test/pseudo-element.css b/plugins/postcss-nesting/test/pseudo-element.css index 03c528470..fc0119f4d 100644 --- a/plugins/postcss-nesting/test/pseudo-element.css +++ b/plugins/postcss-nesting/test/pseudo-element.css @@ -1,5 +1,5 @@ ::before { - @nest .foo&, &:focus { + .foo&, &:focus { order: 1; } } @@ -39,7 +39,7 @@ } .anything::before { - @nest .something_else > & { + .something_else > & { order: 9; } } diff --git a/plugins/postcss-nesting/test/pseudo-element.expect.css b/plugins/postcss-nesting/test/pseudo-element.expect.css index eb6414d65..50617a3b3 100644 --- a/plugins/postcss-nesting/test/pseudo-element.expect.css +++ b/plugins/postcss-nesting/test/pseudo-element.expect.css @@ -1,8 +1,7 @@ - -.foo::before, ::before:focus { + .foo::before, ::before:focus { order: 1; -} + } .a:hover::before, .b:focus::before, .a:hover::after, .b:focus::after { order: 2; @@ -34,4 +33,4 @@ .something_else > .anything::before { order: 9; -} + } diff --git a/plugins/postcss-nesting/test/spec-examples.css b/plugins/postcss-nesting/test/spec-examples.css index 05bf80244..66ecd7e79 100644 --- a/plugins/postcss-nesting/test/spec-examples.css +++ b/plugins/postcss-nesting/test/spec-examples.css @@ -205,7 +205,7 @@ figure { .foo { color: red; - @nest &>.bar { + &>.bar { color: blue; } } @@ -218,7 +218,7 @@ figure { .foo { color: red; - @nest .parent & { + .parent & { color: blue; } } @@ -231,7 +231,7 @@ figure { .foo { color: red; - @nest :not(&) { + :not(&) { color: blue; } } @@ -244,7 +244,7 @@ figure { .foo { color: red; - @nest .bar { + .bar { color: blue; } } @@ -254,7 +254,7 @@ figure { .foo { color: red; - @nest & .bar, + & .bar, .baz { color: blue; } @@ -267,7 +267,7 @@ figure { .foo { color: blue; - @nest .bar & { + .bar & { color: red; &.baz { @@ -359,14 +359,14 @@ figure { .foo { color: red; - @nest @media (min-width: 480px) { + @media (min-width: 480px) { & { color: blue; } } } -/* Invalid because @nest expects a selector prelude, +/* Invalid because expects a selector prelude, instead a conditional group rule was provided */ /* Example 7 */ diff --git a/plugins/postcss-nesting/test/spec-examples.expect.css b/plugins/postcss-nesting/test/spec-examples.expect.css index 9fa0b2ae4..76a6bff88 100644 --- a/plugins/postcss-nesting/test/spec-examples.expect.css +++ b/plugins/postcss-nesting/test/spec-examples.expect.css @@ -192,7 +192,7 @@ __bar.foo { } .foo>.bar { color: blue; -} + } /* equivalent to .foo { color: red; } @@ -205,7 +205,7 @@ __bar.foo { .parent .foo { color: blue; -} + } /* equivalent to .foo { color: red; } @@ -218,7 +218,7 @@ __bar.foo { :not(.foo) { color: blue; -} + } /* equivalent to .foo { color: red; } @@ -227,22 +227,22 @@ __bar.foo { .foo { color: red; +} - @nest .bar { +.foo .bar { color: blue; } -} /* Invalid because there’s no nesting selector */ .foo { color: red; +} - @nest & .bar, - .baz { +.foo .bar, + .foo .baz { color: blue; } -} /* Invalid because not all selectors in the list contain a nesting selector */ @@ -253,7 +253,7 @@ __bar.foo { } .bar .foo { color: red; -} + } .bar .foo.baz { color: green; } @@ -343,15 +343,15 @@ __bar.foo { .foo { color: red; +} - @nest @media (min-width: 480px) { - & { +@media (min-width: 480px) { + .foo { color: blue; } } -} -/* Invalid because @nest expects a selector prelude, +/* Invalid because expects a selector prelude, instead a conditional group rule was provided */ /* Example 7 */ diff --git a/plugins/postcss-nesting/test/spec-examples.no-is-pseudo-selector.expect.css b/plugins/postcss-nesting/test/spec-examples.no-is-pseudo-selector.expect.css index 2c0a34c04..0b937ab4a 100644 --- a/plugins/postcss-nesting/test/spec-examples.no-is-pseudo-selector.expect.css +++ b/plugins/postcss-nesting/test/spec-examples.no-is-pseudo-selector.expect.css @@ -192,7 +192,7 @@ __bar.foo { } .foo>.bar { color: blue; -} + } /* equivalent to .foo { color: red; } @@ -205,7 +205,7 @@ __bar.foo { .parent .foo { color: blue; -} + } /* equivalent to .foo { color: red; } @@ -218,7 +218,7 @@ __bar.foo { :not(.foo) { color: blue; -} + } /* equivalent to .foo { color: red; } @@ -227,22 +227,22 @@ __bar.foo { .foo { color: red; +} - @nest .bar { +.foo .bar { color: blue; } -} /* Invalid because there’s no nesting selector */ .foo { color: red; +} - @nest & .bar, - .baz { +.foo .bar, + .foo .baz { color: blue; } -} /* Invalid because not all selectors in the list contain a nesting selector */ @@ -253,7 +253,7 @@ __bar.foo { } .bar .foo { color: red; -} + } .bar .foo.baz { color: green; } @@ -343,15 +343,15 @@ __bar.foo { .foo { color: red; +} - @nest @media (min-width: 480px) { - & { +@media (min-width: 480px) { + .foo { color: blue; } } -} -/* Invalid because @nest expects a selector prelude, +/* Invalid because expects a selector prelude, instead a conditional group rule was provided */ /* Example 7 */