diff --git a/plugins/postcss-nesting/CHANGELOG.md b/plugins/postcss-nesting/CHANGELOG.md index 29c5c02af..f02fbcc6d 100644 --- a/plugins/postcss-nesting/CHANGELOG.md +++ b/plugins/postcss-nesting/CHANGELOG.md @@ -1,5 +1,10 @@ # Changes to PostCSS Nesting +### Unreleased (patch) + +- Skip nested rules that have a selector that begins with a letter +- Better warning when nested rules have a selector that begins with a letter + ### 11.2.0 (February 13, 2023) - Added: support for `&` at the root diff --git a/plugins/postcss-nesting/dist/index.cjs b/plugins/postcss-nesting/dist/index.cjs index abcb3f97d..58be29f45 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 m=!1;c.each((e=>"combinator"===e.type&&(m=!0,!1))),i?m&&c.insertBefore(c.at(0),e.nesting({})):(c.insertBefore(c.at(0),e.combinator({value:" "})),c.insertBefore(c.at(0),e.nesting({}))),f=s.toString()}let m=1,h=[],y=0;if(e().astSync(f).walkNesting((()=>{y++})),y>1&&i.length>1)h=combinationsWithSizeN(i,y),m=h.length;else{m=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){if(t===r-1)return void(t=r);n.remove(),e.insertAfter(t,n),t=e.index(n)}if("atrule"===n.type&&"mixin"===n.name.toLowerCase()){let o=n.prev();for(;o;){if(o&&("rule"===o.type||"atrule"===o.type))return;o=o.next()}if(t===r-1)return void(t=r);n.remove(),e.insertAfter(t,n),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;if(t===r-1)return void(t=r);n.remove(),e.insertAfter(t,n),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}"`)}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){shiftNodesBeforeParent(e,t);const s=t.clone().removeAll().append(e.nodes);s.raws.semicolon=!0;try{s.selectors=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}"`)}e.replaceWith(s),cleanupParent(t),r(s,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(${r.join(",")})`)];const c=[];for(let r=0;r{"nesting"===e.type&&(i=!0)}));const c=s.nodes[0];let m=!1;c.each((e=>"combinator"===e.type&&(m=!0,!1))),i?m&&c.insertBefore(c.at(0),e.nesting({})):(c.insertBefore(c.at(0),e.combinator({value:" "})),c.insertBefore(c.at(0),e.nesting({}))),f=s.toString()}let m=1,h=[],g=0;if(e().astSync(f).walkNesting((()=>{g++})),g>1&&i.length>1)h=combinationsWithSizeN(i,g),m=h.length;else{m=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){if(t===r-1)return void(t=r);n.remove(),e.insertAfter(t,n),t=e.index(n)}if("atrule"===n.type&&"mixin"===n.name.toLowerCase()){let o=n.prev();for(;o;){if(o&&("rule"===o.type||"atrule"===o.type))return;o=o.next()}if(t===r-1)return void(t=r);n.remove(),e.insertAfter(t,n),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;if(t===r-1)return void(t=r);n.remove(),e.insertAfter(t,n),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; diff --git a/plugins/postcss-nesting/dist/index.mjs b/plugins/postcss-nesting/dist/index.mjs index 53a716339..1df286f09 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 m=!1;c.each((e=>"combinator"===e.type&&(m=!0,!1))),i?m&&c.insertBefore(c.at(0),e.nesting({})):(c.insertBefore(c.at(0),e.combinator({value:" "})),c.insertBefore(c.at(0),e.nesting({}))),f=s.toString()}let m=1,h=[],g=0;if(e().astSync(f).walkNesting((()=>{g++})),g>1&&i.length>1)h=combinationsWithSizeN(i,g),m=h.length;else{m=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){if(t===r-1)return void(t=r);n.remove(),e.insertAfter(t,n),t=e.index(n)}if("atrule"===n.type&&"mixin"===n.name.toLowerCase()){let o=n.prev();for(;o;){if(o&&("rule"===o.type||"atrule"===o.type))return;o=o.next()}if(t===r-1)return void(t=r);n.remove(),e.insertAfter(t,n),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;if(t===r-1)return void(t=r);n.remove(),e.insertAfter(t,n),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}"`)}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){shiftNodesBeforeParent(e,t);const s=t.clone().removeAll().append(e.nodes);s.raws.semicolon=!0;try{s.selectors=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}"`)}e.replaceWith(s),cleanupParent(t),r(s,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(${r.join(",")})`)];const c=[];for(let r=0;r{"nesting"===e.type&&(i=!0)}));const c=s.nodes[0];let m=!1;c.each((e=>"combinator"===e.type&&(m=!0,!1))),i?m&&c.insertBefore(c.at(0),e.nesting({})):(c.insertBefore(c.at(0),e.combinator({value:" "})),c.insertBefore(c.at(0),e.nesting({}))),f=s.toString()}let m=1,h=[],g=0;if(e().astSync(f).walkNesting((()=>{g++})),g>1&&i.length>1)h=combinationsWithSizeN(i,g),m=h.length;else{m=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){if(t===r-1)return void(t=r);n.remove(),e.insertAfter(t,n),t=e.index(n)}if("atrule"===n.type&&"mixin"===n.name.toLowerCase()){let o=n.prev();for(;o;){if(o&&("rule"===o.type||"atrule"===o.type))return;o=o.next()}if(t===r-1)return void(t=r);n.remove(),e.insertAfter(t,n),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;if(t===r-1)return void(t=r);n.remove(),e.insertAfter(t,n),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}; 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 152ae046e..1f8e56557 100644 --- a/plugins/postcss-nesting/src/lib/merge-selectors/merge-selectors.ts +++ b/plugins/postcss-nesting/src/lib/merge-selectors/merge-selectors.ts @@ -29,7 +29,8 @@ export default function mergeSelectors(node: Node, postcssResult: Result, fromSe if (!fromAtNest) { if (x === 0 && toSelectorAST.nodes?.[0]?.nodes?.[0]?.type === 'tag') { - node.warn(postcssResult, `Nested selectors must start with a symbol : "${toSelectors[x]}"`); + node.warn(postcssResult, `Nested selectors must start with a symbol and "${toSelectors[x]}" begins with a letter.`); + return []; } } 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 3cb2ab197..3a5860b4e 100644 --- a/plugins/postcss-nesting/src/lib/nest-rule-within-rule.ts +++ b/plugins/postcss-nesting/src/lib/nest-rule-within-rule.ts @@ -7,6 +7,19 @@ import { walkFunc } from './walk-func.js'; import { options } from './options.js'; export default function transformNestRuleWithinRule(node: AtRule, parent: Rule, result: Result, walk: walkFunc, opts: options) { + let selectors = []; + + try { + selectors = mergeSelectors(node, result, parent.selectors, comma(node.params), opts, true); + } catch (err) { + node.warn(result, `Failed to parse selectors : "${parent.selector}" / "${node.params}" with message: "${err.message}"`); + return; + } + + if (!selectors.length) { + return; + } + // move previous siblings and the node to before the parent shiftNodesBeforeParent(node, parent); @@ -15,12 +28,7 @@ export default function transformNestRuleWithinRule(node: AtRule, parent: Rule, rule.raws.semicolon = true; /* nested rules end with "}" and do not have this flag set */ // update the selectors of the node to be merged with the parent - try { - rule.selectors = mergeSelectors(node, result, parent.selectors, comma(node.params), opts, true); - } catch (err) { - node.warn(result, `Failed to parse selectors : "${parent.selector}" / "${node.params}" with message: "${err.message}"`); - return; - } + rule.selectors = selectors; // replace the node with the new rule node.replaceWith(rule); diff --git a/plugins/postcss-nesting/src/lib/rule-within-rule.ts b/plugins/postcss-nesting/src/lib/rule-within-rule.ts index f6cec40a5..174a89ada 100644 --- a/plugins/postcss-nesting/src/lib/rule-within-rule.ts +++ b/plugins/postcss-nesting/src/lib/rule-within-rule.ts @@ -16,6 +16,10 @@ export default function transformRuleWithinRule(node: Rule, parent: Rule, result return; } + if (!selectors.length) { + return; + } + // Group all declarations after the first one. groupDeclarations(parent); diff --git a/plugins/postcss-nesting/test/ignore.expect.css b/plugins/postcss-nesting/test/ignore.expect.css index 01bbd969d..732184e15 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; -} -a c, b c, a d, b d { + c, d { order: 2; } +} :scope e { order: 3; } 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 01bbd969d..732184e15 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; -} -a c, b c, a d, b d { + c, d { order: 2; } +} :scope e { order: 3; } diff --git a/plugins/postcss-nesting/test/invalid-selector.expect.css b/plugins/postcss-nesting/test/invalid-selector.expect.css index dfc9adc90..1cac2bc97 100644 --- a/plugins/postcss-nesting/test/invalid-selector.expect.css +++ b/plugins/postcss-nesting/test/invalid-selector.expect.css @@ -4,10 +4,10 @@ } } -@nest &.child { - } - .foo : bar { + @nest &.child { + order: 2; + } } .foo { @@ -16,8 +16,8 @@ } } -@nest &.child : bar { - } - .foo { + @nest &.child : bar { + order: 4; + } }