From 86ae1ee205af59a6b769a8e7434593ed6e9fbe4f Mon Sep 17 00:00:00 2001 From: Romain Menke Date: Wed, 28 Feb 2024 19:24:34 +0100 Subject: [PATCH 1/5] nesting editions --- .../selector-resolve-nested/test/compound.mjs | 12 + plugins/postcss-nesting/dist/index.cjs | 2 +- plugins/postcss-nesting/dist/index.d.ts | 12 + plugins/postcss-nesting/dist/index.mjs | 2 +- plugins/postcss-nesting/package.json | 1 + .../src/editions/2021/index.ts | 38 ++ .../2021}/lib/ampersand-to-scope.ts | 0 .../2021}/lib/atrule-within-atrule.ts | 0 .../2021}/lib/atrule-within-rule.ts | 0 .../{ => editions/2021}/lib/cleanup-parent.ts | 0 .../2021}/lib/group-declarations.ts | 0 .../2021}/lib/is-type-of-rule.ts | 0 .../src/{ => editions/2021}/lib/list.ts | 0 .../{ => editions/2021}/lib/merge-params.ts | 0 .../merge-selectors/combinations-of-size-n.ts | 0 .../compound-selector-order.ts | 0 .../lib/merge-selectors/merge-selectors.ts | 0 .../2021}/lib/merge-selectors/specificity.ts | 0 .../2021}/lib/nest-rule-within-rule.ts | 0 .../src/{ => editions/2021}/lib/options.ts | 0 .../2021}/lib/rule-within-rule.ts | 0 .../2021}/lib/shift-nodes-before-parent.ts | 0 .../{ => editions/2021}/lib/valid-atrules.ts | 0 .../src/{ => editions/2021}/lib/walk-func.ts | 0 .../src/{ => editions/2021}/lib/walk.ts | 0 .../src/editions/2024-02/index.ts | 47 +++ .../2024-02/lib/ampersand-to-scope.ts | 35 ++ .../2024-02/lib/atrule-within-atrule.ts | 21 + .../2024-02/lib/atrule-within-rule.ts | 39 ++ .../editions/2024-02/lib/cleanup-parent.ts | 13 + .../editions/2024-02/lib/is-type-of-rule.ts | 9 + .../src/editions/2024-02/lib/list.ts | 46 +++ .../src/editions/2024-02/lib/merge-params.ts | 11 + .../editions/2024-02/lib/merge-selectors.ts | 27 ++ .../editions/2024-02/lib/rule-within-rule.ts | 33 ++ .../2024-02/lib/shift-nodes-before-parent.ts | 17 + .../src/editions/2024-02/lib/valid-atrules.ts | 1 + .../src/editions/2024-02/lib/walk-func.ts | 3 + .../src/editions/2024-02/lib/walk.ts | 35 ++ plugins/postcss-nesting/src/index.ts | 39 +- .../test/{_tape.mjs => _tape-2021.mjs} | 10 +- .../postcss-nesting/test/_tape-2024-02.mjs | 171 ++++++++ ...sand-everywhere.edition-2024-02.expect.css | 36 ++ .../test/basic.edition-2024-02.expect.css | 254 ++++++++++++ .../test/complex.edition-2024-02.expect.css | 77 ++++ .../test/container.edition-2024-02.expect.css | 16 + .../decl-order.edition-2024-02.expect.css | 71 ++++ .../test/direct.edition-2024-02.expect.css | 78 ++++ .../test/empty.edition-2024-02.expect.css | 31 ++ .../example.edition-2024-02.expect.css | 14 + .../test/ignore.edition-2024-02.expect.css | 29 ++ ...nvalid-selector.edition-2024-02.expect.css | 23 ++ .../test/layer.edition-2024-02.expect.css | 57 +++ plugins/postcss-nesting/test/media.css | 12 + .../test/media.edition-2024-02.expect.css | 66 +++ plugins/postcss-nesting/test/media.expect.css | 14 + .../media.no-is-pseudo-selector.expect.css | 14 + ...xin-declaration.edition-2024-02.expect.css | 79 ++++ ...in-nested-rules.edition-2024-02.expect.css | 7 + .../mixin-rule.edition-2024-02.expect.css | 14 + ...le-replacements.edition-2024-02.expect.css | 28 ++ .../pseudo-element.edition-2024-02.expect.css | 36 ++ ...ative-selectors.edition-2024-02.expect.css | 73 ++++ ...uires-is-pseudo.edition-2024-02.expect.css | 46 +++ .../spec-examples.edition-2024-02.expect.css | 380 ++++++++++++++++++ .../test/supports.edition-2024-02.expect.css | 13 + rollup/configs/externals.mjs | 2 + 67 files changed, 2065 insertions(+), 29 deletions(-) create mode 100644 plugins/postcss-nesting/src/editions/2021/index.ts rename plugins/postcss-nesting/src/{ => editions/2021}/lib/ampersand-to-scope.ts (100%) rename plugins/postcss-nesting/src/{ => editions/2021}/lib/atrule-within-atrule.ts (100%) rename plugins/postcss-nesting/src/{ => editions/2021}/lib/atrule-within-rule.ts (100%) rename plugins/postcss-nesting/src/{ => editions/2021}/lib/cleanup-parent.ts (100%) rename plugins/postcss-nesting/src/{ => editions/2021}/lib/group-declarations.ts (100%) rename plugins/postcss-nesting/src/{ => editions/2021}/lib/is-type-of-rule.ts (100%) rename plugins/postcss-nesting/src/{ => editions/2021}/lib/list.ts (100%) rename plugins/postcss-nesting/src/{ => editions/2021}/lib/merge-params.ts (100%) rename plugins/postcss-nesting/src/{ => editions/2021}/lib/merge-selectors/combinations-of-size-n.ts (100%) rename plugins/postcss-nesting/src/{ => editions/2021}/lib/merge-selectors/compound-selector-order.ts (100%) rename plugins/postcss-nesting/src/{ => editions/2021}/lib/merge-selectors/merge-selectors.ts (100%) rename plugins/postcss-nesting/src/{ => editions/2021}/lib/merge-selectors/specificity.ts (100%) rename plugins/postcss-nesting/src/{ => editions/2021}/lib/nest-rule-within-rule.ts (100%) rename plugins/postcss-nesting/src/{ => editions/2021}/lib/options.ts (100%) rename plugins/postcss-nesting/src/{ => editions/2021}/lib/rule-within-rule.ts (100%) rename plugins/postcss-nesting/src/{ => editions/2021}/lib/shift-nodes-before-parent.ts (100%) rename plugins/postcss-nesting/src/{ => editions/2021}/lib/valid-atrules.ts (100%) rename plugins/postcss-nesting/src/{ => editions/2021}/lib/walk-func.ts (100%) rename plugins/postcss-nesting/src/{ => editions/2021}/lib/walk.ts (100%) create mode 100644 plugins/postcss-nesting/src/editions/2024-02/index.ts create mode 100644 plugins/postcss-nesting/src/editions/2024-02/lib/ampersand-to-scope.ts create mode 100644 plugins/postcss-nesting/src/editions/2024-02/lib/atrule-within-atrule.ts create mode 100644 plugins/postcss-nesting/src/editions/2024-02/lib/atrule-within-rule.ts create mode 100644 plugins/postcss-nesting/src/editions/2024-02/lib/cleanup-parent.ts create mode 100644 plugins/postcss-nesting/src/editions/2024-02/lib/is-type-of-rule.ts create mode 100644 plugins/postcss-nesting/src/editions/2024-02/lib/list.ts create mode 100644 plugins/postcss-nesting/src/editions/2024-02/lib/merge-params.ts create mode 100644 plugins/postcss-nesting/src/editions/2024-02/lib/merge-selectors.ts create mode 100644 plugins/postcss-nesting/src/editions/2024-02/lib/rule-within-rule.ts create mode 100644 plugins/postcss-nesting/src/editions/2024-02/lib/shift-nodes-before-parent.ts create mode 100644 plugins/postcss-nesting/src/editions/2024-02/lib/valid-atrules.ts create mode 100644 plugins/postcss-nesting/src/editions/2024-02/lib/walk-func.ts create mode 100644 plugins/postcss-nesting/src/editions/2024-02/lib/walk.ts rename plugins/postcss-nesting/test/{_tape.mjs => _tape-2021.mjs} (96%) create mode 100644 plugins/postcss-nesting/test/_tape-2024-02.mjs create mode 100644 plugins/postcss-nesting/test/ampersand-everywhere.edition-2024-02.expect.css create mode 100644 plugins/postcss-nesting/test/basic.edition-2024-02.expect.css create mode 100644 plugins/postcss-nesting/test/complex.edition-2024-02.expect.css create mode 100644 plugins/postcss-nesting/test/container.edition-2024-02.expect.css create mode 100644 plugins/postcss-nesting/test/decl-order.edition-2024-02.expect.css create mode 100644 plugins/postcss-nesting/test/direct.edition-2024-02.expect.css create mode 100644 plugins/postcss-nesting/test/empty.edition-2024-02.expect.css create mode 100644 plugins/postcss-nesting/test/examples/example.edition-2024-02.expect.css create mode 100644 plugins/postcss-nesting/test/ignore.edition-2024-02.expect.css create mode 100644 plugins/postcss-nesting/test/invalid-selector.edition-2024-02.expect.css create mode 100644 plugins/postcss-nesting/test/layer.edition-2024-02.expect.css create mode 100644 plugins/postcss-nesting/test/media.edition-2024-02.expect.css create mode 100644 plugins/postcss-nesting/test/mixin-declaration.edition-2024-02.expect.css create mode 100644 plugins/postcss-nesting/test/mixin-nested-rules.edition-2024-02.expect.css create mode 100644 plugins/postcss-nesting/test/mixin-rule.edition-2024-02.expect.css create mode 100644 plugins/postcss-nesting/test/multiple-replacements.edition-2024-02.expect.css create mode 100644 plugins/postcss-nesting/test/pseudo-element.edition-2024-02.expect.css create mode 100644 plugins/postcss-nesting/test/relative-selectors.edition-2024-02.expect.css create mode 100644 plugins/postcss-nesting/test/requires-is-pseudo.edition-2024-02.expect.css create mode 100644 plugins/postcss-nesting/test/spec-examples.edition-2024-02.expect.css create mode 100644 plugins/postcss-nesting/test/supports.edition-2024-02.expect.css diff --git a/packages/selector-resolve-nested/test/compound.mjs b/packages/selector-resolve-nested/test/compound.mjs index ebd78fa23..7f6f54277 100644 --- a/packages/selector-resolve-nested/test/compound.mjs +++ b/packages/selector-resolve-nested/test/compound.mjs @@ -161,6 +161,18 @@ test('surrounding whitespace', async () => { expected_resolved: '#foo.foo.foo', expected_flat: '.foo#foo.foo', }, + { + a: '&', + b: 'a, .a', + expected_resolved: ':is(a,.a)', + expected_flat: 'a, .a', + }, + { + a: '&', + b: '::before', + expected_resolved: ':is(::before)', + expected_flat: '::before', + }, ]; for (const { a, b, expected_resolved, expected_flat } of testCases) { diff --git a/plugins/postcss-nesting/dist/index.cjs b/plugins/postcss-nesting/dist/index.cjs index 30af5d92e..cccf6f0aa 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 instanceof Error?e.message:e}"`)}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]),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(isDeclarationLike(e,n.length>0))t.push(e);else{if("comment"===e.type){let r=e.next();for(;r&&"comment"===r.type;)r=r.next();if(isDeclarationLike(r,n.length>0))return void t.push(e)}n.push(e)}})),e.removeAll(),t.forEach((t=>{e.append(t)})),n.forEach((t=>{e.append(t)}))}function isDeclarationLike(e,t){return!!e&&("decl"===e.type||"atrule"===e.type&&"mixin"===e.name.toLowerCase()&&!t)}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 instanceof Error?r.message:r}"`)}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 instanceof Error?r.message:r}"`)}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","starting-style"];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; +"use strict";var e=require("postcss-selector-parser"),t=require("@csstools/selector-specificity"),n=require("@csstools/selector-resolve-nested");const r=e.pseudo({value:":is"});function sortCompoundSelectorsInsideComplexSelector(t){if(!t||!t.nodes)return;const n=[];let o=[];for(let s=0;s"tag"===e.type))){const n=r.clone({}),o=t.nodes[s];o.replaceWith(n),n.append(e.selector({nodes:[o],value:void 0}))}o.push(t.nodes[s])}else n.push(o),n.push([t.nodes[s]]),o=[];n.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)?o.pseudoElement:o[t.type]}const o={universal:0,tag:1,pseudoElement:2,id:3,class:4,attribute:5,pseudo:6,selector:7,string:8,root:9,comment:10};function ampersandToScope$1(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 instanceof Error?e.message:e}"`)}r&&(r.walkNesting((t=>{const n=t.parent;t.replaceWith(e.pseudo({value:":scope"})),n&&sortCompoundSelectorsInsideComplexSelector(n)})),t.selector=r.toString())}function cleanupParent$1(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$1(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$1(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 a=[],c=0;if(e().astSync(l).walkNesting((()=>{c++})),c>1&&o.length>1)a=combinationsWithSizeN(o,c),i=a.length;else{i=o.length;for(let e=0;e{if("nesting"!==o.type)return;let s=a[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]),c=isCompoundSelector(s.nodes[0]),u=isSimpleSelector(o),p=isCompoundSelector(o);if(l&&u)return void o.replaceWith(s.clone());if((l||c)&&(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(c){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(isDeclarationLike(e,n.length>0))t.push(e);else{if("comment"===e.type){let r=e.next();for(;r&&"comment"===r.type;)r=r.next();if(isDeclarationLike(r,n.length>0))return void t.push(e)}n.push(e)}})),e.removeAll(),t.forEach((t=>{e.append(t)})),n.forEach((t=>{e.append(t)}))}function isDeclarationLike(e,t){return!!e&&("decl"===e.type||"atrule"===e.type&&"mixin"===e.name.toLowerCase()&&!t)}function transformRuleWithinRule$1(e,t,n,r){let o=[];try{o=mergeSelectors$1(t.selectors,e.selectors,r)}catch(r){return void e.warn(n,`Failed to parse selectors : "${t.selector}" / "${e.selector}" with message: "${r instanceof Error?r.message:r}"`)}if(!o.length)return;groupDeclarations(t),shiftNodesBeforeParent$1(e,t),e.selectors=o;"rule"===e.type&&"rule"===t.type&&e.selector===t.selector&&e.append(...t.nodes),cleanupParent$1(t)}function isValidRuleWithinRule$1(e){return e.selectors.every((e=>-1===e.indexOf("|")))}function comma$1(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$1(t.selectors,comma$1(e.params),o)}catch(r){return void e.warn(n,`Failed to parse selectors : "${t.selector}" / "${e.params}" with message: "${r instanceof Error?r.message:r}"`)}if(!s.length)return;shiftNodesBeforeParent$1(e,t);const i=t.clone().removeAll().append(e.nodes);i.raws.semicolon=!0,i.selectors=s,e.replaceWith(i),cleanupParent$1(t),r(i,n,o)}function isValidNestRuleWithinRule(e){return comma$1(e.params).every((e=>e.split("&").length>=2&&-1===e.indexOf("|")))}var s=["container","document","media","supports","layer","starting-style"];function atruleWithinRule$1(e,t,n,r,o){if(groupDeclarations(t),shiftNodesBeforeParent$1(e,t),e.nodes){const s=t.clone().removeAll().append(e.nodes);e.append(s),cleanupParent$1(t),r(s,n,o)}else cleanupParent$1(t)}function isAtruleWithinRule$1(e){return s.includes(e.name)}function transformAtruleWithinAtrule$1(e,t){var n,r;groupDeclarations(t),shiftNodesBeforeParent$1(e,t),e.params=(n=t.params,r=e.params,comma$1(n).map((e=>comma$1(r).map((t=>`${e} and ${t}`)).join(", "))).join(", ")),cleanupParent$1(t)}function isAtruleWithinAtrule$1(e,t){return s.includes(e.name)&&e.name===t.name}function isAtRule$1(e){return e&&"atrule"===e.type}function isNestRule(e){return e&&isAtRule$1(e)&&"nest"===e.name}function isRule$1(e){return e&&"rule"===e.type}function walk$1(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$1(r)&&isRule$1(o)&&isValidRuleWithinRule$1(r)?transformRuleWithinRule$1(r,o,t,n):isNestRule(r)&&isRule$1(o)&&isValidNestRuleWithinRule(r)?transformNestRuleWithinRule(r,o,t,walk$1,n):isAtRule$1(r)&&isRule$1(o)&&isAtruleWithinRule$1(r)?atruleWithinRule$1(r,o,t,walk$1,n):isAtRule$1(r)&&isAtRule$1(o)&&isAtruleWithinAtrule$1(r,o)&&transformAtruleWithinAtrule$1(r,o),"nodes"in r&&r.nodes.length&&walk$1(r,t,n)}))}const creator$2=e=>{const t=Object.assign({noIsPseudoSelector:!1,silenceAtNestWarning:!1},e);return{postcssPlugin:"postcss-nesting",Rule(e,{result:n}){walk$1(e,n,t),e.selector.includes("&")&&ersandToScope$1(e,n)}}};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 instanceof Error?e.message:e}"`)}r&&(r.walkNesting((t=>{t.replaceWith(e.pseudo({value:":scope"}))})),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)}creator$2.postcss=!0;const i=e();function mergeSelectors(e,t,r,o){let s;try{s=n.resolveNestedSelector(i.astSync(t),i.astSync(r))}catch(n){return e.warn(o,`Failed to parse selectors : "${r}" / "${t}" with message: "${n instanceof Error?n.message:n}"`),!1}return!!s&&s.toString()}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 transformRuleWithinRule(e,t,n){const r=mergeSelectors(e,e.selector,t.selector,n);if(!r)return;shiftNodesBeforeParent(e,t),e.selector=r;"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("|")))}var l=["container","document","media","supports","layer","starting-style"];function atruleWithinRule(e,t,n,r){if(shiftNodesBeforeParent(e,t),e.nodes){const o=t.clone().removeAll().append(e.nodes),s=mergeSelectors(e,"&",t.selector,n);if(!s)return;o.selector=s,e.append(o),cleanupParent(t),r(o,n)}else cleanupParent(t)}function isAtruleWithinRule(e){return l.includes(e.name)}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 transformAtruleWithinAtrule(e,t){var n,r;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 l.includes(e.name)&&e.name===t.name}function isAtRule(e){return e&&"atrule"===e.type}function isRule(e){return e&&"rule"===e.type}function walk(e,t){e.each((e=>{const n=e.parent;isRule(e)&&isRule(n)&&isValidRuleWithinRule(e)?transformRuleWithinRule(e,n,t):isAtRule(e)&&isRule(n)&&isAtruleWithinRule(e)?atruleWithinRule(e,n,t,walk):isAtRule(e)&&isAtRule(n)&&isAtruleWithinAtrule(e,n)&&transformAtruleWithinAtrule(e,n),"nodes"in e&&e.nodes.length&&walk(e,t)}))}const creator$1=e=>{if(Object.assign({noIsPseudoSelector:!1},e).noIsPseudoSelector)throw new Error("The `noIsPseudoSelector` option is no longer supported. Migrate your CSS to use the latest CSS nesting syntax.");return{postcssPlugin:"postcss-nesting",Rule(e,{result:t}){walk(e,t),e.selector.includes("&")&&ersandToScope(e,t)},AtRule:{nest(e){throw e.error(`\`@nest\` was removed from the CSS Nesting specification and will be removed from PostCSS Nesting in the next major version.\nChange \`@nest ${e.params} {}\` to \`${e.params} {}\` to migrate to the latest standard.`)}}}};creator$1.postcss=!0;const creator=e=>{const t=Object.assign({edition:"2021"},e);switch(t.edition){case"2021":return creator$2(e);case"2024-02":return creator$1(e);default:throw new Error(`Invalid edition: ${t.edition}`)}};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 c441b4179..bc1c0194e 100644 --- a/plugins/postcss-nesting/dist/index.d.ts +++ b/plugins/postcss-nesting/dist/index.d.ts @@ -5,10 +5,22 @@ export default creator; /** postcss-nesting plugin options */ export declare type pluginOptions = { + /** The implementation edition for CSS Nesting, default to '2021' */ + edition?: '2021' | '2024-02'; +} & pluginOptions2021 & pluginOptions2024_02; + +/** postcss-nesting plugin options */ +export declare type pluginOptions2021 = { /** Avoid the `:is()` pseudo class as much as possible. default: false */ noIsPseudoSelector?: boolean; /** Silence the `@nest` warning. */ silenceAtNestWarning?: boolean; }; +/** postcss-nesting plugin options */ +export declare type pluginOptions2024_02 = { + /** This option was removed. You must migrate your CSS to the latest speciation to continue using this plugin. */ + noIsPseudoSelector?: boolean; +}; + export { } diff --git a/plugins/postcss-nesting/dist/index.mjs b/plugins/postcss-nesting/dist/index.mjs index 9db68b45c..2e80aa1e1 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 o=[];let r=[];for(let s=0;s"tag"===e.type))){const o=n.clone({}),r=t.nodes[s];r.replaceWith(o),o.append(e.selector({nodes:[r],value:void 0}))}r.push(t.nodes[s])}else o.push(r),o.push([t.nodes[s]]),r=[];o.push(r);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)?o.pseudoElement:o[t.type]}const o={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 o,r=t.parent;for(;r;){if("rule"===r.type)return;r=r.parent}try{o=e().astSync(t.selector)}catch(e){return void t.warn(n,`Failed to parse selector : "${t.selector}" with message: "${e instanceof Error?e.message:e}"`)}o&&(o.walkNesting((t=>{const n=t.parent;t.replaceWith(e.pseudo({value:":scope"})),n&&sortCompoundSelectorsInsideComplexSelector(n)})),t.selector=o.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 o;n[s-1]+=1}r[s]=e[t]}o.push(r),n[n.length-1]++}}function nodesAreEquallySpecific(n){const o=n.map((t=>e().astSync(t))).map((e=>t(e))),r=o[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 o=t.nodes[0];let r=!1;o.each((e=>"combinator"===e.type&&(r=!0,!1))),n?r&&o.insertBefore(o.at(0),e.nesting({})):(o.insertBefore(o.at(0),e.combinator({value:" "})),o.insertBefore(o.at(0),e.nesting({}))),i=t.toString()}let c=[],a=0;if(e().astSync(i).walkNesting((()=>{a++})),a>1&&r.length>1)c=combinationsWithSizeN(r,a),l=c.length;else{l=r.length;for(let e=0;e{if("nesting"!==r.type)return;let s=c[t][n];n++,"root"===s.type&&1===s.nodes.length&&(s=s.nodes[0]);const l=e().astSync(`:is(${s.toString()})`),i=isSimpleSelector(s.nodes[0]),a=isCompoundSelector(s.nodes[0]),p=isSimpleSelector(r),u=isCompoundSelector(r);if(i&&p)return void r.replaceWith(s.clone());if((i||a)&&(p||u)){const e=r.parent;return i&&"selector"===s.type?r.replaceWith(s.clone().nodes[0]):r.replaceWith(...s.clone().nodes),void(e&&e.nodes.length>1&&sortCompoundSelectorsInsideComplexSelector(e))}if(i){const e=r.parent;return r.replaceWith(s.clone().nodes[0]),void(e&&sortCompoundSelectorsInsideComplexSelector(e))}if(a){const e=r.parent;return r.replaceWith(...s.clone().nodes),void(e&&sortCompoundSelectorsInsideComplexSelector(e))}if(nestingIsFirstAndOnlyInSelectorWithEitherSpaceOrChildCombinator(r)){const e=r.parent;return r.replaceWith(...s.clone().nodes),void(e&&sortCompoundSelectorsInsideComplexSelector(e))}if(nestingIsNotInsideCompoundSelector(r)){const e=r.parent;return r.replaceWith(...s.clone().nodes),void(e&&sortCompoundSelectorsInsideComplexSelector(e))}const d=r.parent;o.noIsPseudoSelector?r.replaceWith(...s.clone().nodes):r.replaceWith(...l.clone({}).nodes),d&&sortCompoundSelectorsInsideComplexSelector(d)})),s.push(r.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(isDeclarationLike(e,n.length>0))t.push(e);else{if("comment"===e.type){let o=e.next();for(;o&&"comment"===o.type;)o=o.next();if(isDeclarationLike(o,n.length>0))return void t.push(e)}n.push(e)}})),e.removeAll(),t.forEach((t=>{e.append(t)})),n.forEach((t=>{e.append(t)}))}function isDeclarationLike(e,t){return!!e&&("decl"===e.type||"atrule"===e.type&&"mixin"===e.name.toLowerCase()&&!t)}function transformRuleWithinRule(e,t,n,o){let r=[];try{r=mergeSelectors(t.selectors,e.selectors,o)}catch(o){return void e.warn(n,`Failed to parse selectors : "${t.selector}" / "${e.selector}" with message: "${o instanceof Error?o.message:o}"`)}if(!r.length)return;groupDeclarations(t),shiftNodesBeforeParent(e,t),e.selectors=r;"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="",o=!1,r=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?r+=1:")"===i?r>0&&(r-=1):0===r&&","===i&&(o=!0),o?(""!==n&&t.push(n.trim()),n="",o=!1):n+=i;return t.push(n.trim()),t}function transformNestRuleWithinRule(e,t,n,o,r){let s=[];try{s=mergeSelectors(t.selectors,comma(e.params),r)}catch(o){return void e.warn(n,`Failed to parse selectors : "${t.selector}" / "${e.params}" with message: "${o instanceof Error?o.message:o}"`)}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),o(l,n,r)}function isValidNestRuleWithinRule(e){return comma(e.params).every((e=>e.split("&").length>=2&&-1===e.indexOf("|")))}var r=["container","document","media","supports","layer","starting-style"];function atruleWithinRule(e,t,n,o,r){if(groupDeclarations(t),shiftNodesBeforeParent(e,t),e.nodes){const s=t.clone().removeAll().append(e.nodes);e.append(s),cleanupParent(t),o(s,n,r)}else cleanupParent(t)}function isAtruleWithinRule(e){return r.includes(e.name)}function transformAtruleWithinAtrule(e,t){var n,o;groupDeclarations(t),shiftNodesBeforeParent(e,t),e.params=(n=t.params,o=e.params,comma(n).map((e=>comma(o).map((t=>`${e} and ${t}`)).join(", "))).join(", ")),cleanupParent(t)}function isAtruleWithinAtrule(e,t){return r.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((o=>{const r=o.parent;isNestRule(o)&&!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 ${o.params} {}\` to \`${o.params} {}\` to migrate to the latest standard.`),isRule(o)&&isRule(r)&&isValidRuleWithinRule(o)?transformRuleWithinRule(o,r,t,n):isNestRule(o)&&isRule(r)&&isValidNestRuleWithinRule(o)?transformNestRuleWithinRule(o,r,t,walk,n):isAtRule(o)&&isRule(r)&&isAtruleWithinRule(o)?atruleWithinRule(o,r,t,walk,n):isAtRule(o)&&isAtRule(r)&&isAtruleWithinAtrule(o,r)&&transformAtruleWithinAtrule(o,r),"nodes"in o&&o.nodes.length&&walk(o,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}; +import e from"postcss-selector-parser";import{selectorSpecificity as t}from"@csstools/selector-specificity";import{resolveNestedSelector as n}from"@csstools/selector-resolve-nested";const r=e.pseudo({value:":is"});function sortCompoundSelectorsInsideComplexSelector(t){if(!t||!t.nodes)return;const n=[];let o=[];for(let s=0;s"tag"===e.type))){const n=r.clone({}),o=t.nodes[s];o.replaceWith(n),n.append(e.selector({nodes:[o],value:void 0}))}o.push(t.nodes[s])}else n.push(o),n.push([t.nodes[s]]),o=[];n.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)?o.pseudoElement:o[t.type]}const o={universal:0,tag:1,pseudoElement:2,id:3,class:4,attribute:5,pseudo:6,selector:7,string:8,root:9,comment:10};function ampersandToScope$1(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 instanceof Error?e.message:e}"`)}r&&(r.walkNesting((t=>{const n=t.parent;t.replaceWith(e.pseudo({value:":scope"})),n&&sortCompoundSelectorsInsideComplexSelector(n)})),t.selector=r.toString())}function cleanupParent$1(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$1(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$1(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 a=[],c=0;if(e().astSync(l).walkNesting((()=>{c++})),c>1&&o.length>1)a=combinationsWithSizeN(o,c),i=a.length;else{i=o.length;for(let e=0;e{if("nesting"!==o.type)return;let s=a[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]),c=isCompoundSelector(s.nodes[0]),u=isSimpleSelector(o),p=isCompoundSelector(o);if(l&&u)return void o.replaceWith(s.clone());if((l||c)&&(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(c){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(isDeclarationLike(e,n.length>0))t.push(e);else{if("comment"===e.type){let r=e.next();for(;r&&"comment"===r.type;)r=r.next();if(isDeclarationLike(r,n.length>0))return void t.push(e)}n.push(e)}})),e.removeAll(),t.forEach((t=>{e.append(t)})),n.forEach((t=>{e.append(t)}))}function isDeclarationLike(e,t){return!!e&&("decl"===e.type||"atrule"===e.type&&"mixin"===e.name.toLowerCase()&&!t)}function transformRuleWithinRule$1(e,t,n,r){let o=[];try{o=mergeSelectors$1(t.selectors,e.selectors,r)}catch(r){return void e.warn(n,`Failed to parse selectors : "${t.selector}" / "${e.selector}" with message: "${r instanceof Error?r.message:r}"`)}if(!o.length)return;groupDeclarations(t),shiftNodesBeforeParent$1(e,t),e.selectors=o;"rule"===e.type&&"rule"===t.type&&e.selector===t.selector&&e.append(...t.nodes),cleanupParent$1(t)}function isValidRuleWithinRule$1(e){return e.selectors.every((e=>-1===e.indexOf("|")))}function comma$1(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$1(t.selectors,comma$1(e.params),o)}catch(r){return void e.warn(n,`Failed to parse selectors : "${t.selector}" / "${e.params}" with message: "${r instanceof Error?r.message:r}"`)}if(!s.length)return;shiftNodesBeforeParent$1(e,t);const i=t.clone().removeAll().append(e.nodes);i.raws.semicolon=!0,i.selectors=s,e.replaceWith(i),cleanupParent$1(t),r(i,n,o)}function isValidNestRuleWithinRule(e){return comma$1(e.params).every((e=>e.split("&").length>=2&&-1===e.indexOf("|")))}var s=["container","document","media","supports","layer","starting-style"];function atruleWithinRule$1(e,t,n,r,o){if(groupDeclarations(t),shiftNodesBeforeParent$1(e,t),e.nodes){const s=t.clone().removeAll().append(e.nodes);e.append(s),cleanupParent$1(t),r(s,n,o)}else cleanupParent$1(t)}function isAtruleWithinRule$1(e){return s.includes(e.name)}function transformAtruleWithinAtrule$1(e,t){var n,r;groupDeclarations(t),shiftNodesBeforeParent$1(e,t),e.params=(n=t.params,r=e.params,comma$1(n).map((e=>comma$1(r).map((t=>`${e} and ${t}`)).join(", "))).join(", ")),cleanupParent$1(t)}function isAtruleWithinAtrule$1(e,t){return s.includes(e.name)&&e.name===t.name}function isAtRule$1(e){return e&&"atrule"===e.type}function isNestRule(e){return e&&isAtRule$1(e)&&"nest"===e.name}function isRule$1(e){return e&&"rule"===e.type}function walk$1(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$1(r)&&isRule$1(o)&&isValidRuleWithinRule$1(r)?transformRuleWithinRule$1(r,o,t,n):isNestRule(r)&&isRule$1(o)&&isValidNestRuleWithinRule(r)?transformNestRuleWithinRule(r,o,t,walk$1,n):isAtRule$1(r)&&isRule$1(o)&&isAtruleWithinRule$1(r)?atruleWithinRule$1(r,o,t,walk$1,n):isAtRule$1(r)&&isAtRule$1(o)&&isAtruleWithinAtrule$1(r,o)&&transformAtruleWithinAtrule$1(r,o),"nodes"in r&&r.nodes.length&&walk$1(r,t,n)}))}const creator$2=e=>{const t=Object.assign({noIsPseudoSelector:!1,silenceAtNestWarning:!1},e);return{postcssPlugin:"postcss-nesting",Rule(e,{result:n}){walk$1(e,n,t),e.selector.includes("&")&&ersandToScope$1(e,n)}}};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 instanceof Error?e.message:e}"`)}r&&(r.walkNesting((t=>{t.replaceWith(e.pseudo({value:":scope"}))})),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)}creator$2.postcss=!0;const i=e();function mergeSelectors(e,t,r,o){let s;try{s=n(i.astSync(t),i.astSync(r))}catch(n){return e.warn(o,`Failed to parse selectors : "${r}" / "${t}" with message: "${n instanceof Error?n.message:n}"`),!1}return!!s&&s.toString()}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 transformRuleWithinRule(e,t,n){const r=mergeSelectors(e,e.selector,t.selector,n);if(!r)return;shiftNodesBeforeParent(e,t),e.selector=r;"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("|")))}var l=["container","document","media","supports","layer","starting-style"];function atruleWithinRule(e,t,n,r){if(shiftNodesBeforeParent(e,t),e.nodes){const o=t.clone().removeAll().append(e.nodes),s=mergeSelectors(e,"&",t.selector,n);if(!s)return;o.selector=s,e.append(o),cleanupParent(t),r(o,n)}else cleanupParent(t)}function isAtruleWithinRule(e){return l.includes(e.name)}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 transformAtruleWithinAtrule(e,t){var n,r;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 l.includes(e.name)&&e.name===t.name}function isAtRule(e){return e&&"atrule"===e.type}function isRule(e){return e&&"rule"===e.type}function walk(e,t){e.each((e=>{const n=e.parent;isRule(e)&&isRule(n)&&isValidRuleWithinRule(e)?transformRuleWithinRule(e,n,t):isAtRule(e)&&isRule(n)&&isAtruleWithinRule(e)?atruleWithinRule(e,n,t,walk):isAtRule(e)&&isAtRule(n)&&isAtruleWithinAtrule(e,n)&&transformAtruleWithinAtrule(e,n),"nodes"in e&&e.nodes.length&&walk(e,t)}))}const creator$1=e=>{if(Object.assign({noIsPseudoSelector:!1},e).noIsPseudoSelector)throw new Error("The `noIsPseudoSelector` option is no longer supported. Migrate your CSS to use the latest CSS nesting syntax.");return{postcssPlugin:"postcss-nesting",Rule(e,{result:t}){walk(e,t),e.selector.includes("&")&&ersandToScope(e,t)},AtRule:{nest(e){throw e.error(`\`@nest\` was removed from the CSS Nesting specification and will be removed from PostCSS Nesting in the next major version.\nChange \`@nest ${e.params} {}\` to \`${e.params} {}\` to migrate to the latest standard.`)}}}};creator$1.postcss=!0;const creator=e=>{const t=Object.assign({edition:"2021"},e);switch(t.edition){case"2021":return creator$2(e);case"2024-02":return creator$1(e);default:throw new Error(`Invalid edition: ${t.edition}`)}};creator.postcss=!0;export{creator as default}; diff --git a/plugins/postcss-nesting/package.json b/plugins/postcss-nesting/package.json index 16c96b6bd..f9c06e25c 100644 --- a/plugins/postcss-nesting/package.json +++ b/plugins/postcss-nesting/package.json @@ -54,6 +54,7 @@ "dist" ], "dependencies": { + "@csstools/selector-resolve-nested": "^1.1.0", "@csstools/selector-specificity": "^3.0.2", "postcss-selector-parser": "^6.0.13" }, diff --git a/plugins/postcss-nesting/src/editions/2021/index.ts b/plugins/postcss-nesting/src/editions/2021/index.ts new file mode 100644 index 000000000..a4a36e4c6 --- /dev/null +++ b/plugins/postcss-nesting/src/editions/2021/index.ts @@ -0,0 +1,38 @@ +import type { PluginCreator } from 'postcss'; +import ampersandToScope from './lib/ampersand-to-scope.js'; +import walk from './lib/walk.js'; + +/** postcss-nesting plugin options */ +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) => { + const options = Object.assign( + // Default options + { + noIsPseudoSelector: false, + silenceAtNestWarning: false, + }, + // Provided options + opts, + ); + + return { + postcssPlugin: 'postcss-nesting', + Rule(rule, { result }) { + walk(rule, result, options); + + if (rule.selector.includes('&')) { + ampersandToScope(rule, result); + } + }, + }; +}; + +creator.postcss = true; + +export default creator; diff --git a/plugins/postcss-nesting/src/lib/ampersand-to-scope.ts b/plugins/postcss-nesting/src/editions/2021/lib/ampersand-to-scope.ts similarity index 100% rename from plugins/postcss-nesting/src/lib/ampersand-to-scope.ts rename to plugins/postcss-nesting/src/editions/2021/lib/ampersand-to-scope.ts diff --git a/plugins/postcss-nesting/src/lib/atrule-within-atrule.ts b/plugins/postcss-nesting/src/editions/2021/lib/atrule-within-atrule.ts similarity index 100% rename from plugins/postcss-nesting/src/lib/atrule-within-atrule.ts rename to plugins/postcss-nesting/src/editions/2021/lib/atrule-within-atrule.ts diff --git a/plugins/postcss-nesting/src/lib/atrule-within-rule.ts b/plugins/postcss-nesting/src/editions/2021/lib/atrule-within-rule.ts similarity index 100% rename from plugins/postcss-nesting/src/lib/atrule-within-rule.ts rename to plugins/postcss-nesting/src/editions/2021/lib/atrule-within-rule.ts diff --git a/plugins/postcss-nesting/src/lib/cleanup-parent.ts b/plugins/postcss-nesting/src/editions/2021/lib/cleanup-parent.ts similarity index 100% rename from plugins/postcss-nesting/src/lib/cleanup-parent.ts rename to plugins/postcss-nesting/src/editions/2021/lib/cleanup-parent.ts diff --git a/plugins/postcss-nesting/src/lib/group-declarations.ts b/plugins/postcss-nesting/src/editions/2021/lib/group-declarations.ts similarity index 100% rename from plugins/postcss-nesting/src/lib/group-declarations.ts rename to plugins/postcss-nesting/src/editions/2021/lib/group-declarations.ts diff --git a/plugins/postcss-nesting/src/lib/is-type-of-rule.ts b/plugins/postcss-nesting/src/editions/2021/lib/is-type-of-rule.ts similarity index 100% rename from plugins/postcss-nesting/src/lib/is-type-of-rule.ts rename to plugins/postcss-nesting/src/editions/2021/lib/is-type-of-rule.ts diff --git a/plugins/postcss-nesting/src/lib/list.ts b/plugins/postcss-nesting/src/editions/2021/lib/list.ts similarity index 100% rename from plugins/postcss-nesting/src/lib/list.ts rename to plugins/postcss-nesting/src/editions/2021/lib/list.ts diff --git a/plugins/postcss-nesting/src/lib/merge-params.ts b/plugins/postcss-nesting/src/editions/2021/lib/merge-params.ts similarity index 100% rename from plugins/postcss-nesting/src/lib/merge-params.ts rename to plugins/postcss-nesting/src/editions/2021/lib/merge-params.ts diff --git a/plugins/postcss-nesting/src/lib/merge-selectors/combinations-of-size-n.ts b/plugins/postcss-nesting/src/editions/2021/lib/merge-selectors/combinations-of-size-n.ts similarity index 100% rename from plugins/postcss-nesting/src/lib/merge-selectors/combinations-of-size-n.ts rename to plugins/postcss-nesting/src/editions/2021/lib/merge-selectors/combinations-of-size-n.ts diff --git a/plugins/postcss-nesting/src/lib/merge-selectors/compound-selector-order.ts b/plugins/postcss-nesting/src/editions/2021/lib/merge-selectors/compound-selector-order.ts similarity index 100% rename from plugins/postcss-nesting/src/lib/merge-selectors/compound-selector-order.ts rename to plugins/postcss-nesting/src/editions/2021/lib/merge-selectors/compound-selector-order.ts diff --git a/plugins/postcss-nesting/src/lib/merge-selectors/merge-selectors.ts b/plugins/postcss-nesting/src/editions/2021/lib/merge-selectors/merge-selectors.ts similarity index 100% rename from plugins/postcss-nesting/src/lib/merge-selectors/merge-selectors.ts rename to plugins/postcss-nesting/src/editions/2021/lib/merge-selectors/merge-selectors.ts diff --git a/plugins/postcss-nesting/src/lib/merge-selectors/specificity.ts b/plugins/postcss-nesting/src/editions/2021/lib/merge-selectors/specificity.ts similarity index 100% rename from plugins/postcss-nesting/src/lib/merge-selectors/specificity.ts rename to plugins/postcss-nesting/src/editions/2021/lib/merge-selectors/specificity.ts diff --git a/plugins/postcss-nesting/src/lib/nest-rule-within-rule.ts b/plugins/postcss-nesting/src/editions/2021/lib/nest-rule-within-rule.ts similarity index 100% rename from plugins/postcss-nesting/src/lib/nest-rule-within-rule.ts rename to plugins/postcss-nesting/src/editions/2021/lib/nest-rule-within-rule.ts diff --git a/plugins/postcss-nesting/src/lib/options.ts b/plugins/postcss-nesting/src/editions/2021/lib/options.ts similarity index 100% rename from plugins/postcss-nesting/src/lib/options.ts rename to plugins/postcss-nesting/src/editions/2021/lib/options.ts diff --git a/plugins/postcss-nesting/src/lib/rule-within-rule.ts b/plugins/postcss-nesting/src/editions/2021/lib/rule-within-rule.ts similarity index 100% rename from plugins/postcss-nesting/src/lib/rule-within-rule.ts rename to plugins/postcss-nesting/src/editions/2021/lib/rule-within-rule.ts diff --git a/plugins/postcss-nesting/src/lib/shift-nodes-before-parent.ts b/plugins/postcss-nesting/src/editions/2021/lib/shift-nodes-before-parent.ts similarity index 100% rename from plugins/postcss-nesting/src/lib/shift-nodes-before-parent.ts rename to plugins/postcss-nesting/src/editions/2021/lib/shift-nodes-before-parent.ts diff --git a/plugins/postcss-nesting/src/lib/valid-atrules.ts b/plugins/postcss-nesting/src/editions/2021/lib/valid-atrules.ts similarity index 100% rename from plugins/postcss-nesting/src/lib/valid-atrules.ts rename to plugins/postcss-nesting/src/editions/2021/lib/valid-atrules.ts diff --git a/plugins/postcss-nesting/src/lib/walk-func.ts b/plugins/postcss-nesting/src/editions/2021/lib/walk-func.ts similarity index 100% rename from plugins/postcss-nesting/src/lib/walk-func.ts rename to plugins/postcss-nesting/src/editions/2021/lib/walk-func.ts diff --git a/plugins/postcss-nesting/src/lib/walk.ts b/plugins/postcss-nesting/src/editions/2021/lib/walk.ts similarity index 100% rename from plugins/postcss-nesting/src/lib/walk.ts rename to plugins/postcss-nesting/src/editions/2021/lib/walk.ts diff --git a/plugins/postcss-nesting/src/editions/2024-02/index.ts b/plugins/postcss-nesting/src/editions/2024-02/index.ts new file mode 100644 index 000000000..795e8f1b6 --- /dev/null +++ b/plugins/postcss-nesting/src/editions/2024-02/index.ts @@ -0,0 +1,47 @@ +import type { PluginCreator } from 'postcss'; +import ampersandToScope from './lib/ampersand-to-scope.js'; +import walk from './lib/walk.js'; + +/** postcss-nesting plugin options */ +export type pluginOptions = { + /** This option was removed. You must migrate your CSS to the latest speciation to continue using this plugin. */ + noIsPseudoSelector?: boolean, +}; + +const creator: PluginCreator = (opts?: pluginOptions) => { + const options = Object.assign( + // Default options + { + noIsPseudoSelector: false, + }, + // Provided options + opts, + ); + + if (options.noIsPseudoSelector) { + throw new Error('The `noIsPseudoSelector` option is no longer supported. Migrate your CSS to use the latest CSS nesting syntax.'); + } + + return { + postcssPlugin: 'postcss-nesting', + Rule(rule, { result }) { + walk(rule, result); + + if (rule.selector.includes('&')) { + ampersandToScope(rule, result); + } + }, + AtRule: { + nest(rule) { + throw rule.error( + '`@nest` was removed from the CSS Nesting specification and will be removed from PostCSS Nesting in the next major version.\n' + + `Change \`@nest ${rule.params} {}\` to \`${rule.params} {}\` to migrate to the latest standard.`, + ); + }, + }, + }; +}; + +creator.postcss = true; + +export default creator; diff --git a/plugins/postcss-nesting/src/editions/2024-02/lib/ampersand-to-scope.ts b/plugins/postcss-nesting/src/editions/2024-02/lib/ampersand-to-scope.ts new file mode 100644 index 000000000..66b2cfef3 --- /dev/null +++ b/plugins/postcss-nesting/src/editions/2024-02/lib/ampersand-to-scope.ts @@ -0,0 +1,35 @@ +import type { Container, Node, Result, Rule } from 'postcss'; +import parser from 'postcss-selector-parser'; + +export default function ampersandToScope(rule: Rule, result: Result) { + let parent: Container = rule.parent; + + while (parent) { + if (parent.type === 'rule') { + // Skip any rules that nested. + // We only want to process "&" found in unnested rules. + return; + } + + parent = parent.parent; + } + + let selectorAST: parser.Root; + try { + selectorAST = parser().astSync(rule.selector); + } catch (err) { + rule.warn(result, `Failed to parse selector : "${rule.selector}" with message: "${(err instanceof Error) ? err.message : err}"`); + return; + } + if (!selectorAST) { + return; + } + + selectorAST.walkNesting((nesting) => { + nesting.replaceWith(parser.pseudo({ + value: ':scope', + })); + }); + + rule.selector = selectorAST.toString(); +} diff --git a/plugins/postcss-nesting/src/editions/2024-02/lib/atrule-within-atrule.ts b/plugins/postcss-nesting/src/editions/2024-02/lib/atrule-within-atrule.ts new file mode 100644 index 000000000..baea2b108 --- /dev/null +++ b/plugins/postcss-nesting/src/editions/2024-02/lib/atrule-within-atrule.ts @@ -0,0 +1,21 @@ +import type { AtRule } from 'postcss'; +import cleanupParent from './cleanup-parent.js'; +import mergeParams from './merge-params.js'; +import shiftNodesBeforeParent from './shift-nodes-before-parent.js'; +import validAtrules from './valid-atrules.js'; + +export default function transformAtruleWithinAtrule(node: AtRule, parent: AtRule) { + // move previous siblings and the node to before the parent + shiftNodesBeforeParent(node, parent); + + // update the params of the node to be merged with the parent + node.params = mergeParams(parent.params, node.params); + + // conditionally cleanup an empty parent rule + cleanupParent(parent); +} + +export function isAtruleWithinAtrule(node: AtRule, parent: AtRule) { + return validAtrules.includes(node.name) && + node.name === parent.name; +} diff --git a/plugins/postcss-nesting/src/editions/2024-02/lib/atrule-within-rule.ts b/plugins/postcss-nesting/src/editions/2024-02/lib/atrule-within-rule.ts new file mode 100644 index 000000000..01dc09c35 --- /dev/null +++ b/plugins/postcss-nesting/src/editions/2024-02/lib/atrule-within-rule.ts @@ -0,0 +1,39 @@ +import cleanupParent from './cleanup-parent.js'; +import shiftNodesBeforeParent from './shift-nodes-before-parent.js'; +import validAtrules from './valid-atrules.js'; +import { walkFunc } from './walk-func.js'; +import type { AtRule, Result, Rule } from 'postcss'; +import mergeSelectors from './merge-selectors.js'; + +export default function atruleWithinRule(node: AtRule, parent: Rule, result: Result, walk: walkFunc) { + // move previous siblings and the node to before the parent + shiftNodesBeforeParent(node, parent); + + // clone the parent as a new rule with children appended to it + if (node.nodes) { + const rule = parent.clone().removeAll().append(node.nodes); + + const selectors = mergeSelectors(node, '&', parent.selector, result); + if (!selectors) { + return; + } + + rule.selector = selectors; + + // append the new rule to the node + node.append(rule); + + // conditionally cleanup an empty parent rule + cleanupParent(parent); + + // walk the children of the new rule + walk(rule, result); + } else { + // conditionally cleanup an empty parent rule + cleanupParent(parent); + } +} + +export function isAtruleWithinRule(node: AtRule) { + return validAtrules.includes(node.name); +} diff --git a/plugins/postcss-nesting/src/editions/2024-02/lib/cleanup-parent.ts b/plugins/postcss-nesting/src/editions/2024-02/lib/cleanup-parent.ts new file mode 100644 index 000000000..815b1dd1c --- /dev/null +++ b/plugins/postcss-nesting/src/editions/2024-02/lib/cleanup-parent.ts @@ -0,0 +1,13 @@ +import type { ChildNode, Container } from 'postcss'; + +export default function cleanupParent(parent: Container) { + if (!parent.nodes.length) { + parent.remove(); + return; + } + + const commentNodes = parent.nodes.filter(node => node.type === 'comment'); + if (commentNodes.length === parent.nodes.length) { + parent.replaceWith(...commentNodes); + } +} diff --git a/plugins/postcss-nesting/src/editions/2024-02/lib/is-type-of-rule.ts b/plugins/postcss-nesting/src/editions/2024-02/lib/is-type-of-rule.ts new file mode 100644 index 000000000..1336f39eb --- /dev/null +++ b/plugins/postcss-nesting/src/editions/2024-02/lib/is-type-of-rule.ts @@ -0,0 +1,9 @@ +import type { AtRule, Node, Rule } from 'postcss'; + +export function isAtRule(node?: Node): node is AtRule { + return node && node.type === 'atrule'; +} + +export function isRule(node?: Node): node is Rule { + return node && node.type === 'rule'; +} diff --git a/plugins/postcss-nesting/src/editions/2024-02/lib/list.ts b/plugins/postcss-nesting/src/editions/2024-02/lib/list.ts new file mode 100644 index 000000000..76d4861d6 --- /dev/null +++ b/plugins/postcss-nesting/src/editions/2024-02/lib/list.ts @@ -0,0 +1,46 @@ +export function comma(string: string) { + const array: Array = []; + let current = ''; + let split = false; + + let func = 0; + let quote: string|false = false; + let escape = false; + + for (const letter of string) { + if (escape) { + escape = false; + } else if (letter === '\\') { + escape = true; + } else if (quote) { + if (letter === quote) { + quote = false; + } + } else if (letter === '"' || letter === '\'') { + quote = letter; + } else if (letter === '(') { + func += 1; + } else if (letter === ')') { + if (func > 0) { + func -= 1; + } + } else if (func === 0) { + if (letter === ',') { + split = true; + } + } + + if (split) { + if (current !== '') { + array.push(current.trim()); + } + current = ''; + split = false; + } else { + current += letter; + } + } + + array.push(current.trim()); + return array; +} diff --git a/plugins/postcss-nesting/src/editions/2024-02/lib/merge-params.ts b/plugins/postcss-nesting/src/editions/2024-02/lib/merge-params.ts new file mode 100644 index 000000000..5e97609b8 --- /dev/null +++ b/plugins/postcss-nesting/src/editions/2024-02/lib/merge-params.ts @@ -0,0 +1,11 @@ +import { comma } from './list.js'; + +export default function mergeParams(fromParams: string, toParams: string) { + return comma(fromParams) + .map((params1) => + comma(toParams) + .map((params2) => `${params1} and ${params2}`) + .join(', '), + ) + .join(', '); +} diff --git a/plugins/postcss-nesting/src/editions/2024-02/lib/merge-selectors.ts b/plugins/postcss-nesting/src/editions/2024-02/lib/merge-selectors.ts new file mode 100644 index 000000000..abbf815f0 --- /dev/null +++ b/plugins/postcss-nesting/src/editions/2024-02/lib/merge-selectors.ts @@ -0,0 +1,27 @@ +import parser from 'postcss-selector-parser'; +import type { Root } from 'postcss-selector-parser'; +import type { Node, Result } from 'postcss'; +import { resolveNestedSelector } from '@csstools/selector-resolve-nested'; + +const selectorParser = parser(); + +export default function mergeSelectors(node: Node, nodeSelector: string, parentSelector: string, result: Result): string|false { + let selectors: Root; + + // update the selectors of the node to be merged with the parent + try { + selectors = resolveNestedSelector( + selectorParser.astSync(nodeSelector), + selectorParser.astSync(parentSelector), + ); + } catch (err) { + node.warn(result, `Failed to parse selectors : "${parentSelector}" / "${nodeSelector}" with message: "${(err instanceof Error) ? err.message : err}"`); + return false; + } + + if (!selectors) { + return false; + } + + return selectors.toString(); +} diff --git a/plugins/postcss-nesting/src/editions/2024-02/lib/rule-within-rule.ts b/plugins/postcss-nesting/src/editions/2024-02/lib/rule-within-rule.ts new file mode 100644 index 000000000..50302d7f3 --- /dev/null +++ b/plugins/postcss-nesting/src/editions/2024-02/lib/rule-within-rule.ts @@ -0,0 +1,33 @@ +import cleanupParent from './cleanup-parent.js'; +import mergeSelectors from './merge-selectors.js'; +import shiftNodesBeforeParent from './shift-nodes-before-parent.js'; +import type { Result, Rule } from 'postcss'; + +export default function transformRuleWithinRule(node: Rule, parent: Rule, result: Result) { + const selectors = mergeSelectors(node, node.selector, parent.selector, result); + if (!selectors) { + return; + } + + // move previous siblings and the node to before the parent + shiftNodesBeforeParent(node, parent); + + // update the selectors of the node to be merged with the parent + node.selector = selectors; + + // merge similar rules back together + const areSameRule = (node.type === 'rule' && parent.type === 'rule' && node.selector === parent.selector); + + if (areSameRule) { + node.append(...parent.nodes); + } + + // conditionally cleanup an empty parent rule + cleanupParent(parent); +} + +export function isValidRuleWithinRule(node: Rule) { + return node.selectors.every((selector) => { + return selector.indexOf('|') === -1; + }); +} diff --git a/plugins/postcss-nesting/src/editions/2024-02/lib/shift-nodes-before-parent.ts b/plugins/postcss-nesting/src/editions/2024-02/lib/shift-nodes-before-parent.ts new file mode 100644 index 000000000..9a07f3ca3 --- /dev/null +++ b/plugins/postcss-nesting/src/editions/2024-02/lib/shift-nodes-before-parent.ts @@ -0,0 +1,17 @@ +import type { ChildNode, Container } from 'postcss'; +import cleanupParent from './cleanup-parent'; + +export default function shiftNodesBeforeParent(node: ChildNode, parent: Container) { + const index = parent.index(node); + + // conditionally move previous siblings into a clone of the parent + if (index) { + const newParent = parent.cloneBefore().removeAll().append(parent.nodes.slice(0, index)); + newParent.raws.semicolon = true; /* nested rules end with "}" and do not have this flag set */ + cleanupParent(newParent); + } + + // move the current node before the parent (and after the conditional clone) + parent.before(node); + parent.raws.semicolon = true; /* nested rules end with "}" and do not have this flag set */ +} diff --git a/plugins/postcss-nesting/src/editions/2024-02/lib/valid-atrules.ts b/plugins/postcss-nesting/src/editions/2024-02/lib/valid-atrules.ts new file mode 100644 index 000000000..8148d90b6 --- /dev/null +++ b/plugins/postcss-nesting/src/editions/2024-02/lib/valid-atrules.ts @@ -0,0 +1 @@ +export default ['container', 'document', 'media', 'supports', 'layer', 'starting-style']; diff --git a/plugins/postcss-nesting/src/editions/2024-02/lib/walk-func.ts b/plugins/postcss-nesting/src/editions/2024-02/lib/walk-func.ts new file mode 100644 index 000000000..f73a5d18b --- /dev/null +++ b/plugins/postcss-nesting/src/editions/2024-02/lib/walk-func.ts @@ -0,0 +1,3 @@ +import type { Container, Result } from 'postcss'; + +export type walkFunc = (node: Container, result: Result) => void; diff --git a/plugins/postcss-nesting/src/editions/2024-02/lib/walk.ts b/plugins/postcss-nesting/src/editions/2024-02/lib/walk.ts new file mode 100644 index 000000000..174504797 --- /dev/null +++ b/plugins/postcss-nesting/src/editions/2024-02/lib/walk.ts @@ -0,0 +1,35 @@ +import transformRuleWithinRule, { isValidRuleWithinRule } from './rule-within-rule.js'; +import transformAtruleWithinRule, { isAtruleWithinRule } from './atrule-within-rule.js'; +import transformAtruleWithinAtrule, { isAtruleWithinAtrule } from './atrule-within-atrule.js'; +import type { Container, Result } from 'postcss'; +import { isAtRule, isRule } from './is-type-of-rule.js'; + +export default function walk(node: Container, result: Result) { + node.each((child) => { + const parent = child.parent; + + if ( + isRule(child) && + isRule(parent) && + isValidRuleWithinRule(child) + ) { + transformRuleWithinRule(child, parent, result); + } else if ( + isAtRule(child) && + isRule(parent) && + isAtruleWithinRule(child) + ) { + transformAtruleWithinRule(child, parent, result, walk); + } else if ( + isAtRule(child) && + isAtRule(parent) && + isAtruleWithinAtrule(child, parent) + ) { + transformAtruleWithinAtrule(child, parent); + } + + if ('nodes' in child && child.nodes.length) { + walk(child, result); + } + }); +} diff --git a/plugins/postcss-nesting/src/index.ts b/plugins/postcss-nesting/src/index.ts index a4a36e4c6..e17244980 100644 --- a/plugins/postcss-nesting/src/index.ts +++ b/plugins/postcss-nesting/src/index.ts @@ -1,36 +1,37 @@ import type { PluginCreator } from 'postcss'; -import ampersandToScope from './lib/ampersand-to-scope.js'; -import walk from './lib/walk.js'; +import type { pluginOptions as pluginOptions2021 } from './editions/2021'; +import type { pluginOptions as pluginOptions2024_02 } from './editions/2024-02'; +import plugin2021 from './editions/2021'; +import plugin2024_02 from './editions/2024-02'; + +export type { pluginOptions as pluginOptions2021 } from './editions/2021'; +export type { pluginOptions as pluginOptions2024_02 } from './editions/2024-02'; + /** postcss-nesting plugin options */ export type pluginOptions = { - /** Avoid the `:is()` pseudo class as much as possible. default: false */ - noIsPseudoSelector?: boolean, - /** Silence the `@nest` warning. */ - silenceAtNestWarning?: boolean, -}; + /** The implementation edition for CSS Nesting, default to '2021' */ + edition?: '2021' | '2024-02', +} & pluginOptions2021 & pluginOptions2024_02; const creator: PluginCreator = (opts?: pluginOptions) => { const options = Object.assign( // Default options { - noIsPseudoSelector: false, - silenceAtNestWarning: false, + edition: '2021', }, // Provided options opts, ); - return { - postcssPlugin: 'postcss-nesting', - Rule(rule, { result }) { - walk(rule, result, options); - - if (rule.selector.includes('&')) { - ampersandToScope(rule, result); - } - }, - }; + switch (options.edition) { + case '2021': + return plugin2021(opts); + case '2024-02': + return plugin2024_02(opts); + default: + throw new Error(`Invalid edition: ${options.edition}`); + } }; creator.postcss = true; diff --git a/plugins/postcss-nesting/test/_tape.mjs b/plugins/postcss-nesting/test/_tape-2021.mjs similarity index 96% rename from plugins/postcss-nesting/test/_tape.mjs rename to plugins/postcss-nesting/test/_tape-2021.mjs index cdf523256..29578bd1f 100644 --- a/plugins/postcss-nesting/test/_tape.mjs +++ b/plugins/postcss-nesting/test/_tape-2021.mjs @@ -171,10 +171,7 @@ postcssTape(plugin)({ }, 'mixin-declaration:no-is-pseudo-selector': { message: 'supports other visitors (mixin declaration) { noIsPseudoSelector: true }', - plugins: [mixinPluginDeclaration(), plugin()], - options: { - noIsPseudoSelector: true, - }, + plugins: [mixinPluginDeclaration(), plugin({ noIsPseudoSelector: true })], }, 'mixin-rule': { message: 'supports other visitors (mixin rule)', @@ -182,10 +179,7 @@ postcssTape(plugin)({ }, 'mixin-rule:no-is-pseudo-selector': { message: 'supports other visitors (mixin rule) { noIsPseudoSelector: true }', - plugins: [mixinPluginRule(), plugin()], - options: { - noIsPseudoSelector: true, - }, + plugins: [mixinPluginRule(), plugin({ noIsPseudoSelector: true })], }, 'mixin-nested-rules': { message: 'supports mixin with nested rules', diff --git a/plugins/postcss-nesting/test/_tape-2024-02.mjs b/plugins/postcss-nesting/test/_tape-2024-02.mjs new file mode 100644 index 000000000..445fe12c3 --- /dev/null +++ b/plugins/postcss-nesting/test/_tape-2024-02.mjs @@ -0,0 +1,171 @@ +import { postcssTape } from '@csstools/postcss-tape'; +import plugin from 'postcss-nesting'; + +const mixinPluginRule = () => { + return { + postcssPlugin: 'mixin', + AtRule: { + mixin(node, { postcss }) { + node.replaceWith(postcss.parse('& .in{ &.deep { color: blue; }}', {from : 'mixin.css'})); + }, + }, + }; +}; + +mixinPluginRule.postcss = true; + +const mixinPluginDeclaration = () => { + return { + postcssPlugin: 'mixin', + AtRule: { + mixin(node, { postcss }) { + node.replaceWith(postcss.parse('color: blue;', {from : 'mixin.css'})); + }, + }, + }; +}; + +mixinPluginDeclaration.postcss = true; + +const mixinPluginNestedRules = () => { + return { + postcssPlugin: 'mixin', + AtRule: { + mixin(node, { postcss }) { + if (node.params === 'alpha') { + node.replaceWith(postcss.parse('@mixin alpha-1; @mixin alpha-2; & { color: white; }', {from : 'mixin.css'})); + } else if (node.params === 'alpha-1') { + node.replaceWith(postcss.parse('color: blue;', {from : 'mixin.css'})); + } else if (node.params === 'alpha-2') { + node.replaceWith(postcss.parse('display: flex;', {from : 'mixin.css'})); + } + }, + }, + }; +}; + +mixinPluginNestedRules.postcss = true; + +postcssTape(plugin)({ + 'basic:edition-2024-02': { + message: 'supports basic usage', + options: { + edition: '2024-02', + }, + }, + 'ampersand-everywhere:edition-2024-02': { + message: 'supports & at the root', + options: { + edition: '2024-02', + }, + }, + 'complex:edition-2024-02': { + message: 'supports complex entries', + options: { + edition: '2024-02', + }, + }, + 'direct:edition-2024-02': { + message: 'supports direct usage', + options: { + edition: '2024-02', + }, + }, + 'container:edition-2024-02': { + message: 'supports nested @container', + options: { + edition: '2024-02', + }, + }, + 'decl-order:edition-2024-02': { + message: 'resolves to the correct order', + options: { + edition: '2024-02', + }, + }, + 'invalid-selector:edition-2024-02': { + message: 'warns on invalid selectors', + options: { + edition: '2024-02', + }, + warnings: 4, + }, + 'media:edition-2024-02': { + message: 'supports nested @media', + options: { + edition: '2024-02', + }, + }, + 'multiple-replacements:edition-2024-02': { + message: 'supports multiple replacements', + options: { + edition: '2024-02', + }, + }, + 'pseudo-element:edition-2024-02': { + message: 'supports pseudo elements', + options: { + edition: '2024-02', + }, + }, + 'supports:edition-2024-02': { + message: 'supports nested @supports', + options: { + edition: '2024-02', + }, + }, + 'layer:edition-2024-02': { + message: 'supports nested @layer', + options: { + edition: '2024-02', + }, + }, + 'empty:edition-2024-02': { + message: 'removes empty rules', + options: { + edition: '2024-02', + }, + }, + 'ignore:edition-2024-02': { + message: 'ignores invalid entries', + options: { + edition: '2024-02', + }, + }, + 'mixin-declaration:edition-2024-02': { + message: 'supports other visitors (mixin declaration)', + plugins: [mixinPluginDeclaration(), plugin({ edition: '2024-02' })], + }, + 'mixin-rule:edition-2024-02': { + message: 'supports other visitors (mixin rule)', + plugins: [mixinPluginRule(), plugin({ edition: '2024-02' })], + }, + 'mixin-nested-rules:edition-2024-02': { + message: 'supports mixin with nested rules', + plugins: [mixinPluginNestedRules(), plugin({ edition: '2024-02' })], + }, + 'spec-examples:edition-2024-02': { + message: 'supports all spec examples', + options: { + edition: '2024-02', + }, + }, + 'relative-selectors:edition-2024-02': { + message: 'supports relative selectors', + options: { + edition: '2024-02', + }, + }, + 'requires-is-pseudo:edition-2024-02': { + message: 'examples of selector nesting that require :is to be correct', + options: { + edition: '2024-02', + }, + }, + 'examples/example:edition-2024-02': { + message: 'basic examples', + options: { + edition: '2024-02', + }, + }, +}); diff --git a/plugins/postcss-nesting/test/ampersand-everywhere.edition-2024-02.expect.css b/plugins/postcss-nesting/test/ampersand-everywhere.edition-2024-02.expect.css new file mode 100644 index 000000000..1527ae9bb --- /dev/null +++ b/plugins/postcss-nesting/test/ampersand-everywhere.edition-2024-02.expect.css @@ -0,0 +1,36 @@ +:scope { + order: 1; +} + +.foo :scope { + order: 2; +} + + +.foo:has(:scope, :not(:scope)) { + order: 3; +} + +:scope:scope+:scope { + order: 4; +} + +@media screen { + :scope { + order: 5; + } +} + +@container (.foo) { + :scope { + order: 6; + } +} + +:scopehtml { + order: 7; +} + +html:scope { + order: 8; +} diff --git a/plugins/postcss-nesting/test/basic.edition-2024-02.expect.css b/plugins/postcss-nesting/test/basic.edition-2024-02.expect.css new file mode 100644 index 000000000..42df43565 --- /dev/null +++ b/plugins/postcss-nesting/test/basic.edition-2024-02.expect.css @@ -0,0 +1,254 @@ +a { + order: 1; +} + +@media screen, print { + +a { + order: 2; +} + } + +@media screen and (min-width: 480px), print and (min-width: 480px) { + +a { + order: 3; +} + } + +@media screen, print { + +a { + + order: 4; +} + } + +a { + + order: 5; + order: 6; +} + +a b { + order: 7; + } + +:is(a b) c { + order: 8; + } + +a b { + + order: 9; + } + +a { + + order: 10; +} + +body a { + order: 11; + } + +html :is(body a) { + order: 12; + } + +body a { + + order: 13; + } + +a { + + order: 14; +} + +@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; + } + +:is(.c:is(.a,.b),.d:is(.a,.b))::before { + order: 41; + } + +:is(.a:hover,.b:focus)::before,:is(.a:hover,.b:focus)::after { + order: 51; + } + +/* leading : root */ +.comments { + /* leading : 1 */ + order: 61; + /* trailing: 2 */ +} +.comments .comment { + order: 62; + } +/* loose comment */ +.comments .comment { + order: 63; + } +.comments { + + /* leading : 4 */ + order: 64; + /* trailing: 5 */ +} +/* nested deeper */ +:is(.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 { + nested: 1; + + declaration: 1; + } + +.mixed-declarations-and-rules--b { + declaration: 1; +} + +.mixed-declarations-and-rules--b { + nested: 1; + + declaration: 2; + } + +@media screen { + +.mixed-declarations-and-rules--b { + nested: 2; + } + } + +.mixed-declarations-and-rules--b { + + declaration: 3; + } + +.node-less-at-rule { + declaration: 1; +} + +@layer foo; + +.node-less-at-rule { + + declaration: 2; +} + +h1 { + transition: background-color 1.5s; + background-color: green; +} + +@starting-style { + +h1 { + background-color: transparent; +} + } diff --git a/plugins/postcss-nesting/test/complex.edition-2024-02.expect.css b/plugins/postcss-nesting/test/complex.edition-2024-02.expect.css new file mode 100644 index 000000000..167fcf3b3 --- /dev/null +++ b/plugins/postcss-nesting/test/complex.edition-2024-02.expect.css @@ -0,0 +1,77 @@ +body > p, body > ul { + order: 1; +} + + +:is(body > p,body > ul) ~ :is(body > p,body > ul) { + order: 2; + } + + +:is(:is(body > p,body > ul) ~ :is(body > p,body > ul)):hover,:is(:is(body > p,body > ul) ~ :is(body > p,body > ul)):focus { + order: 3; + } + + +:is(:is(body > p,body > ul) ~ :is(body > p,body > ul)) *:hover,:is(:is(body > p,body > ul) ~ :is(body > p,body > ul)) *:focus { + order: 4; + } + + +:is(body > p,body > ul) > *:hover { + order: 5; + } + + +:is(body > p,body > ul) :is(body > p,body > ul) { + order: 6; + } + + +:is(body > p,body > ul):is(body > p,body > ul) { + order: 7; + } + + +:is(body > p,body > ul):is(body > p,body > ul):is(body > p,body > ul) { + order: 8; + } + + +body > p, body > .foo { + order: 101; +} + + +:is(body > p,body > .foo) ~ :is(body > p,body > .foo) { + order: 102; + } + + +:is(:is(body > p,body > .foo) ~ :is(body > p,body > .foo)):hover,:is(:is(body > p,body > .foo) ~ :is(body > p,body > .foo)):focus { + order: 103; + } + + +:is(:is(body > p,body > .foo) ~ :is(body > p,body > .foo)) *:hover,:is(:is(body > p,body > .foo) ~ :is(body > p,body > .foo)) *:focus { + order: 104; + } + + +:is(body > p,body > .foo) > *:hover { + order: 105; + } + + +:is(body > p,body > .foo):is(body > p,body > .foo) { + order: 106; + } + + +:is(body > p,body > .foo):is(body > p,body > .foo):is(body > p,body > .foo) { + order: 107; + } + +:is(body.foo,html.baz) + :is(body.foo,html.baz) { + order: 201; + } diff --git a/plugins/postcss-nesting/test/container.edition-2024-02.expect.css b/plugins/postcss-nesting/test/container.edition-2024-02.expect.css new file mode 100644 index 000000000..02aca6262 --- /dev/null +++ b/plugins/postcss-nesting/test/container.edition-2024-02.expect.css @@ -0,0 +1,16 @@ +.container { + contain: layout inline-size; +} + +.contained { + background-color: blue; + height: 100px; + width: 100px; +} + +@container (min-width: 640px) { + +.contained { + background-color: red; +} + } diff --git a/plugins/postcss-nesting/test/decl-order.edition-2024-02.expect.css b/plugins/postcss-nesting/test/decl-order.edition-2024-02.expect.css new file mode 100644 index 000000000..b4d73a5a5 --- /dev/null +++ b/plugins/postcss-nesting/test/decl-order.edition-2024-02.expect.css @@ -0,0 +1,71 @@ +.alpha { + /* comment 0 */ + prop-1: 1; + prop-2: 2; + + /* comment 1 */ + /* comment 2 */ +} + +.alpha .rule-1 {} + +.alpha .rule-2 {} + +/* comment 3 */ + +/* comment 4 */ + +@layer foo-1; + +@layer foo-2; + +/* comment 5 */ + +/* comment 6 */ + +@media (min-width: 3px) { + +.alpha { +}} + +@media (min-width: 4px) { + +.alpha { +}} + +.beta { + /* comment 0 */ + prop-1: 1; + /* comment 1 */ +} + +.beta .rule-1 {} + +/* comment 2 */ + +@layer foo-1; + +/* comment 3 */ + +@layer foo-2; + +.beta { + prop-2: 2; + /* comment 4 */ +} + +.beta .rule-2 {} + +/* comment 5 */ + +@media (min-width: 3px) { + +.beta { +}} + +/* comment 6 */ + +@media (min-width: 4px) { + +.beta { +}} diff --git a/plugins/postcss-nesting/test/direct.edition-2024-02.expect.css b/plugins/postcss-nesting/test/direct.edition-2024-02.expect.css new file mode 100644 index 000000000..9d14ada4c --- /dev/null +++ b/plugins/postcss-nesting/test/direct.edition-2024-02.expect.css @@ -0,0 +1,78 @@ +a, b { + order: 1; +} +:is(a,b) c,:is(a,b) d { + order: 2; + } +:is(:is(a,b) c,:is(a,b) d) e,:is(:is(a,b) c,:is(a,b) d) f { + order: 3; + } +:is(a,b) c,:is(a,b) d { + order: 4; + } +a, b { + order: 5; +} +a, b { + order: 1; +} +:is(a,b) c,:is(a,b) d { + order: 2; + } +:is(:is(a,b) c,:is(a,b) d) e,:is(:is(a,b) c,:is(a,b) d) f { + order: 3; + } +:is(a,b) c,:is(a,b) d { + order: 4; + } +a, b { + order: 5; +} + +a, +b { + order: 1; +} + +:is(a,b) c,:is(a,b) d { + order: 2; + } + +:is(:is(a,b) c,:is(a,b) d) e,:is(:is(a,b) c,:is(a,b) d) f { + order: 3; + } + +:is(a,b) c,:is(a,b) d { + + order: 4; + } + +a, +b { + + order: 5; +} + +.a, +.b { + order: 6; +} + +:is(.a,.b) .c,:is(.a,.b) .d { + order: 7; + } + +:is(:is(.a,.b) .c,:is(.a,.b) .d) .e,:is(:is(.a,.b) .c,:is(.a,.b) .d) .f { + order: 8; + } + +:is(.a,.b) .c,:is(.a,.b) .d { + + order: 9; + } + +.a, +.b { + + order: 10; +} diff --git a/plugins/postcss-nesting/test/empty.edition-2024-02.expect.css b/plugins/postcss-nesting/test/empty.edition-2024-02.expect.css new file mode 100644 index 000000000..61b3ccca7 --- /dev/null +++ b/plugins/postcss-nesting/test/empty.edition-2024-02.expect.css @@ -0,0 +1,31 @@ + + :is(a b) c { + order: 1; + } +d { + order: 2; +} +d e { + order: 3; + } +f g { + order: 4; + } +f { + order: 5; +} +:is(a b) c { + order: 1; + } +d { + order: 2; +} +d e { + order: 3; + } +f g { + order: 4; + } +f { + order: 5; +} diff --git a/plugins/postcss-nesting/test/examples/example.edition-2024-02.expect.css b/plugins/postcss-nesting/test/examples/example.edition-2024-02.expect.css new file mode 100644 index 000000000..94b8f30cc --- /dev/null +++ b/plugins/postcss-nesting/test/examples/example.edition-2024-02.expect.css @@ -0,0 +1,14 @@ +.foo { + color: red; +} +.foo:hover { + color: green; + } +.foo > .bar { + color: blue; + } +@media (prefers-color-scheme: dark) { + .foo { + color: cyan; +} + } diff --git a/plugins/postcss-nesting/test/ignore.edition-2024-02.expect.css b/plugins/postcss-nesting/test/ignore.edition-2024-02.expect.css new file mode 100644 index 000000000..92c5a47e5 --- /dev/null +++ b/plugins/postcss-nesting/test/ignore.edition-2024-02.expect.css @@ -0,0 +1,29 @@ +a, b { + order: 1; +} +:is(a,b) c,:is(a,b) d { + order: 2; + } +:scope e { + order: 3; +} +f:is(g) { + order: 5; + } +a, b { + order: 1; +} +:is(a,b) c,:is(a,b) d { + order: 2; + } +:scope e { + order: 3; +} +f:is(h) { + order: 5; + } +f { + &|i { + order: 6; + } +} diff --git a/plugins/postcss-nesting/test/invalid-selector.edition-2024-02.expect.css b/plugins/postcss-nesting/test/invalid-selector.edition-2024-02.expect.css new file mode 100644 index 000000000..c1dcf1d28 --- /dev/null +++ b/plugins/postcss-nesting/test/invalid-selector.edition-2024-02.expect.css @@ -0,0 +1,23 @@ +.foo : bar { + &.child { + order: 1; + } +} + +.foo : bar { + &.child { + order: 2; + } +} + +.foo { + &.child : bar { + order: 3; + } +} + +.foo { + &.child : bar { + order: 4; + } +} diff --git a/plugins/postcss-nesting/test/layer.edition-2024-02.expect.css b/plugins/postcss-nesting/test/layer.edition-2024-02.expect.css new file mode 100644 index 000000000..a782aba03 --- /dev/null +++ b/plugins/postcss-nesting/test/layer.edition-2024-02.expect.css @@ -0,0 +1,57 @@ +.unlayered { + contain: layout inline-size; +} + +.partially-layered { + background-color: blue; + height: 100px; + width: 100px; +} + +@layer A { + +.partially-layered { + background-color: red; +} + } + +/* see: https: //drafts.csswg.org/css-nesting/#nesting */ +/* Example usage with Cascade Layers */ +@layer base { + html { + block-size: 100%; + } + + html body { + min-block-size: 100%; + } +} + +/* equivalent to + @layer base { + html { block-size: 100%; } + html body { min-block-size: 100%; } + } +*/ + +/* Example nesting Cascade Layers */ +@layer base { + html { + block-size: 100%; + } + + @layer support { + html body { + min-block-size: 100%; + } + } +} + +/* equivalent to + @layer base { + html { block-size: 100%; } + } + @layer base.support { + html body { min-block-size: 100%; } + } +*/ diff --git a/plugins/postcss-nesting/test/media.css b/plugins/postcss-nesting/test/media.css index 79d72086e..e2c48336a 100644 --- a/plugins/postcss-nesting/test/media.css +++ b/plugins/postcss-nesting/test/media.css @@ -42,3 +42,15 @@ a { } } } + +a, .a { + @media screen { + order: 7; + } +} + +a, a::after { + @media screen { + order: 8; + } +} diff --git a/plugins/postcss-nesting/test/media.edition-2024-02.expect.css b/plugins/postcss-nesting/test/media.edition-2024-02.expect.css new file mode 100644 index 000000000..4ad873a18 --- /dev/null +++ b/plugins/postcss-nesting/test/media.edition-2024-02.expect.css @@ -0,0 +1,66 @@ +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) { + :is(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) { + :is(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; + } + } + +@media screen { + +:is(a,.a) { + order: 7; +} + } + +@media screen { + +:is(a,a::after) { + order: 8; +} + } diff --git a/plugins/postcss-nesting/test/media.expect.css b/plugins/postcss-nesting/test/media.expect.css index 46bbaa778..8aa660161 100644 --- a/plugins/postcss-nesting/test/media.expect.css +++ b/plugins/postcss-nesting/test/media.expect.css @@ -50,3 +50,17 @@ a { order: 6; } } + +@media screen { + +a, .a { + order: 7; +} + } + +@media screen { + +a, a::after { + order: 8; +} + } 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 46bbaa778..8aa660161 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 @@ -50,3 +50,17 @@ a { order: 6; } } + +@media screen { + +a, .a { + order: 7; +} + } + +@media screen { + +a, a::after { + order: 8; +} + } diff --git a/plugins/postcss-nesting/test/mixin-declaration.edition-2024-02.expect.css b/plugins/postcss-nesting/test/mixin-declaration.edition-2024-02.expect.css new file mode 100644 index 000000000..e8f1e6d5d --- /dev/null +++ b/plugins/postcss-nesting/test/mixin-declaration.edition-2024-02.expect.css @@ -0,0 +1,79 @@ +/* prevent regressions : https://github.com/csstools/postcss-nesting/issues/95 */ +body {color: blue; + + background: red; + font-size: 1rem; +} +@media (min-width: 64em) { +body { + background: green; +} + } +body { + + margin: 0; +} + + +:root { + /* a comment */color: blue; + + background: red; + font-size: 1rem; +} + + +@media (min-width: 64em) { + + +:root { + background: green; +} + } + + +:root { + + margin: 0; +} + +html { + color: pink;color: blue; + + background: red; + font-size: 1rem; +} + +@media (min-width: 64em) { + +html { + background: green; +} + } + +html { + + margin: 0; +} + +.other .a-rule { + color: cyan; + } + +.other {color: blue; + + background: red; + font-size: 1rem; +} + +@media (min-width: 64em) { + +.other { + background: green; +} + } + +.other { + + margin: 0; +} diff --git a/plugins/postcss-nesting/test/mixin-nested-rules.edition-2024-02.expect.css b/plugins/postcss-nesting/test/mixin-nested-rules.edition-2024-02.expect.css new file mode 100644 index 000000000..ee481d9cd --- /dev/null +++ b/plugins/postcss-nesting/test/mixin-nested-rules.edition-2024-02.expect.css @@ -0,0 +1,7 @@ + + a b {color: blue;display: flex; + } +:is(a b) { color: white; } +a b { + display: inline-flex; + } diff --git a/plugins/postcss-nesting/test/mixin-rule.edition-2024-02.expect.css b/plugins/postcss-nesting/test/mixin-rule.edition-2024-02.expect.css new file mode 100644 index 000000000..e04693352 --- /dev/null +++ b/plugins/postcss-nesting/test/mixin-rule.edition-2024-02.expect.css @@ -0,0 +1,14 @@ + + a b { + color: red; + } + +.deep:is(a .in) { color: blue; } + +f g { + color: red; + } + +/* a comment */ + +.deep:is(f .in) { color: blue; } diff --git a/plugins/postcss-nesting/test/multiple-replacements.edition-2024-02.expect.css b/plugins/postcss-nesting/test/multiple-replacements.edition-2024-02.expect.css new file mode 100644 index 000000000..c26179054 --- /dev/null +++ b/plugins/postcss-nesting/test/multiple-replacements.edition-2024-02.expect.css @@ -0,0 +1,28 @@ + + .a.b .a { + order: 1; + } + +.a.b :is(.a) { + order: 2; + } + +.a.b :not(.a + .a) { + order: 3; + } + +.a.b.a { + order: 4; + } + +.a.b:is(.a) { + order: 5; + } + +.a.b:not(.a + .a) { + order: 6; + } + +a + a { + order: 7 + } diff --git a/plugins/postcss-nesting/test/pseudo-element.edition-2024-02.expect.css b/plugins/postcss-nesting/test/pseudo-element.edition-2024-02.expect.css new file mode 100644 index 000000000..cf4bb5ef5 --- /dev/null +++ b/plugins/postcss-nesting/test/pseudo-element.edition-2024-02.expect.css @@ -0,0 +1,36 @@ + + .foo:is(::before),:is(::before):focus { + order: 1; + } + +:is(.a:hover,.b:focus)::before,:is(.a:hover,.b:focus)::after { + order: 2; + } + +.a::before { + order: 3; + } + +.a::after { + order: 4; + } + +.a::before { + order: 5; + } + +:is(.a::before):focus { + order: 6; + } + +.a::after { + order: 7; + } + +:is(.a::after):hover { + order: 8; + } + +.something_else > :is(.anything::before) { + order: 9; + } diff --git a/plugins/postcss-nesting/test/relative-selectors.edition-2024-02.expect.css b/plugins/postcss-nesting/test/relative-selectors.edition-2024-02.expect.css new file mode 100644 index 000000000..636ef31ad --- /dev/null +++ b/plugins/postcss-nesting/test/relative-selectors.edition-2024-02.expect.css @@ -0,0 +1,73 @@ + + .foo .bar { + order: 1; + } + + +.foo > .bar { + order: 2; + } + + +.foo + .bar { + order: 3; + } + + +.foo ~ .bar { + order: 4; + } + + +.foo ::before { + order: 5; + } + + +.foo .bar,.foo .other { + order: 11; + } + + +.foo > .bar,.foo .other { + order: 12; + } + + +.foo + .bar,.foo .other { + order: 13; + } + + +.foo ~ .bar,.foo .other { + order: 14; + } + + +.foo ::before,.foo .other { + order: 15; + } + +.bar .foo,.foo .other { + order: 21; + } + +.foo > .bar .foo,.foo .other { + order: 22; + } + +.foo + .bar .foo,.foo .other { + order: 23; + } + +.foo ~ .bar .foo,.foo .other { + order: 24; + } + +::before .foo,.foo .other { + order: 25; + } + +:is(.unrelated .foo,.bar) { + order: 31; + } diff --git a/plugins/postcss-nesting/test/requires-is-pseudo.edition-2024-02.expect.css b/plugins/postcss-nesting/test/requires-is-pseudo.edition-2024-02.expect.css new file mode 100644 index 000000000..cc21d0097 --- /dev/null +++ b/plugins/postcss-nesting/test/requires-is-pseudo.edition-2024-02.expect.css @@ -0,0 +1,46 @@ +/* + * two tag selectors in a compound selector + * would never match any elements selector, but :is required to have a correct selector + */ +body:is(html) { + order: 1; + } + +/* + * Adjacent element of parent/child selector + * Without :is it is not possible to match the selector + */ +:is(.alpha > .beta) + :is(.alpha > .beta) { + order: 2; + } + +/* Would be better written without nesting : */ +.alpha > .beta + .beta { + order: 2; +} + +/* + * Selector lists with mixed specificity + * In practice this will rarely be an issue. + * But we have to verify this once nesting is implemented in browsers. + */ +:is(#alpha,.beta):hover { + order: 3; + } + +/* will give unexpected results for
when the rule is followed by : */ +.delta:hover { + /* + * order will be 3 + * as the previous rule will have higher specificity + * + * only starts having effect once there is native support and when shipping untransformed code. + */ + order: 2; +} + +/* Would be better written without nesting : */ +#alpha:hover, +.beta:hover { + order: 3; +} diff --git a/plugins/postcss-nesting/test/spec-examples.edition-2024-02.expect.css b/plugins/postcss-nesting/test/spec-examples.edition-2024-02.expect.css new file mode 100644 index 000000000..5ddbf55f2 --- /dev/null +++ b/plugins/postcss-nesting/test/spec-examples.edition-2024-02.expect.css @@ -0,0 +1,380 @@ +/* https://www.w3.org/TR/css-nesting-1/ */ + +/* Example 2 */ + +table.colortable td { + text-align: center; + } + +.c:is(table.colortable td) { + text-transform: uppercase + } + +:is(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; +} +:is(.foo,.bar) + .baz,.qux:is(.foo,.bar) { + 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%); + } +:is(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; +} +:is(.foo,.bar) + .baz,:is(.foo,.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; +} + +.foo .bar { + color: blue; + } + +/* Invalid because there’s no nesting selector */ + +.foo { + color: red; +} + +.foo .bar,.foo .baz { + color: blue; + } + +/* Invalid because not all selectors in the list + contain a nesting selector */ + +/* Example 5 */ +.foo { + color: blue; +} +.bar .foo { + color: red; + } +.baz:is(.bar .foo) { + 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; +} + +@media (min-width: 480px) { + .foo { + color: blue; + } + } + +/* Invalid because expects a selector prelude, + instead a conditional group rule was provided */ + +/* Example 7 */ +article { + color: green; +} +article { + color: blue; + + /* + NOTE : We are more forgiving than the spec + This declaration is preserved + */ + color: red; + } + +article { + color: green; +} + +article { + color: blue; + + /* + NOTE : We are more forgiving than the spec + This declaration is preserved + */ + color: red; + } + +article.foo { + color: yellow; + } + +/* valid! */ diff --git a/plugins/postcss-nesting/test/supports.edition-2024-02.expect.css b/plugins/postcss-nesting/test/supports.edition-2024-02.expect.css new file mode 100644 index 000000000..2342752b3 --- /dev/null +++ b/plugins/postcss-nesting/test/supports.edition-2024-02.expect.css @@ -0,0 +1,13 @@ +a { + order: 1; +} +@supports (order: 2) { + a { + order: 2; +} + } +@supports (background-image: url('fo and o.png')) { + a { + order: 3; +} + } diff --git a/rollup/configs/externals.mjs b/rollup/configs/externals.mjs index 3c63875cb..74816a986 100644 --- a/rollup/configs/externals.mjs +++ b/rollup/configs/externals.mjs @@ -63,6 +63,7 @@ export const externalsForCLI = [ '@csstools/postcss-text-decoration-shorthand', '@csstools/postcss-trigonometric-functions', '@csstools/postcss-unset-value', + '@csstools/selector-resolve-nested', '@csstools/selector-specificity', '@csstools/utilities', 'autoprefixer', @@ -169,6 +170,7 @@ export const externalsForPlugin = [ '@csstools/postcss-text-decoration-shorthand', '@csstools/postcss-trigonometric-functions', '@csstools/postcss-unset-value', + '@csstools/selector-resolve-nested', '@csstools/selector-specificity', '@csstools/utilities', 'autoprefixer', From 70722d49cd8d14d75221c2f61a21684295ed263c Mon Sep 17 00:00:00 2001 From: Romain Menke Date: Tue, 5 Mar 2024 19:17:32 +0100 Subject: [PATCH 2/5] add examples from https://github.com/csstools/postcss-plugins/pull/1312 --- plugins/postcss-nesting/test/mixin-declaration.css | 9 +++++++++ .../test/mixin-declaration.edition-2024-02.expect.css | 8 ++++++++ .../postcss-nesting/test/mixin-declaration.expect.css | 11 +++++++++++ ...mixin-declaration.no-is-pseudo-selector.expect.css | 11 +++++++++++ 4 files changed, 39 insertions(+) diff --git a/plugins/postcss-nesting/test/mixin-declaration.css b/plugins/postcss-nesting/test/mixin-declaration.css index 06e1cdb13..5eb071599 100644 --- a/plugins/postcss-nesting/test/mixin-declaration.css +++ b/plugins/postcss-nesting/test/mixin-declaration.css @@ -58,3 +58,12 @@ html { margin: 0; } + +a { + & b { + color: cyan + } + + @mixin; + color: green; +} diff --git a/plugins/postcss-nesting/test/mixin-declaration.edition-2024-02.expect.css b/plugins/postcss-nesting/test/mixin-declaration.edition-2024-02.expect.css index e8f1e6d5d..ce7668d21 100644 --- a/plugins/postcss-nesting/test/mixin-declaration.edition-2024-02.expect.css +++ b/plugins/postcss-nesting/test/mixin-declaration.edition-2024-02.expect.css @@ -77,3 +77,11 @@ html { margin: 0; } + +a b { + color: cyan + } + +a {color: blue; + color: green; +} diff --git a/plugins/postcss-nesting/test/mixin-declaration.expect.css b/plugins/postcss-nesting/test/mixin-declaration.expect.css index 2576e5d1c..9e6acf23a 100644 --- a/plugins/postcss-nesting/test/mixin-declaration.expect.css +++ b/plugins/postcss-nesting/test/mixin-declaration.expect.css @@ -68,3 +68,14 @@ html { background: green; } } + +a { + color: green; +} + +a b { + color: cyan + } + +a {color: blue; +} diff --git a/plugins/postcss-nesting/test/mixin-declaration.no-is-pseudo-selector.expect.css b/plugins/postcss-nesting/test/mixin-declaration.no-is-pseudo-selector.expect.css index 2576e5d1c..9e6acf23a 100644 --- a/plugins/postcss-nesting/test/mixin-declaration.no-is-pseudo-selector.expect.css +++ b/plugins/postcss-nesting/test/mixin-declaration.no-is-pseudo-selector.expect.css @@ -68,3 +68,14 @@ html { background: green; } } + +a { + color: green; +} + +a b { + color: cyan + } + +a {color: blue; +} From 38c5ca06cca2cbdad45d41c045f3d84316378932 Mon Sep 17 00:00:00 2001 From: Romain Menke Date: Tue, 5 Mar 2024 19:53:20 +0100 Subject: [PATCH 3/5] cleanup --- README.md | 6 +-- plugin-packs/postcss-preset-env/README.md | 6 +-- plugins/postcss-nesting/CHANGELOG.md | 4 ++ plugins/postcss-nesting/dist/index.cjs | 2 +- plugins/postcss-nesting/dist/index.mjs | 2 +- .../editions/2021/lib/atrule-within-atrule.ts | 6 +-- .../editions/2021/lib/atrule-within-rule.ts | 10 ++-- .../2021/lib/nest-rule-within-rule.ts | 10 ++-- .../src/editions/2021/lib/rule-within-rule.ts | 8 ++-- .../src/editions/2021/lib/walk.ts | 10 ++-- .../2024-02/lib/atrule-within-atrule.ts | 21 --------- .../2024-02/lib/atrule-within-rule.ts | 8 ++-- .../editions/2024-02/lib/cleanup-parent.ts | 13 ------ .../editions/2024-02/lib/is-type-of-rule.ts | 9 ---- .../src/editions/2024-02/lib/list.ts | 46 ------------------- .../src/editions/2024-02/lib/merge-params.ts | 11 ----- .../editions/2024-02/lib/rule-within-rule.ts | 6 +-- .../2024-02/lib/shift-nodes-before-parent.ts | 17 ------- .../src/editions/2024-02/lib/valid-atrules.ts | 1 - .../src/editions/2024-02/lib/walk.ts | 13 ++---- .../{2021 => shared}/lib/cleanup-parent.ts | 0 .../{2021 => shared}/lib/is-type-of-rule.ts | 0 .../lib/shift-nodes-before-parent.ts | 0 .../{2021 => shared}/lib/valid-atrules.ts | 0 .../test/basic.edition-2024-02.expect.css | 9 ++-- plugins/postcss-nesting/test/media.css | 13 +++++- .../test/media.edition-2024-02.expect.css | 31 +++++++++---- plugins/postcss-nesting/test/media.expect.css | 13 +++++- .../media.no-is-pseudo-selector.expect.css | 13 +++++- .../spec-examples.edition-2024-02.expect.css | 5 +- sites/postcss-preset-env/index.njk | 6 +-- .../src/static/js/playground.js | 6 +-- 32 files changed, 106 insertions(+), 199 deletions(-) delete mode 100644 plugins/postcss-nesting/src/editions/2024-02/lib/atrule-within-atrule.ts delete mode 100644 plugins/postcss-nesting/src/editions/2024-02/lib/cleanup-parent.ts delete mode 100644 plugins/postcss-nesting/src/editions/2024-02/lib/is-type-of-rule.ts delete mode 100644 plugins/postcss-nesting/src/editions/2024-02/lib/list.ts delete mode 100644 plugins/postcss-nesting/src/editions/2024-02/lib/merge-params.ts delete mode 100644 plugins/postcss-nesting/src/editions/2024-02/lib/shift-nodes-before-parent.ts delete mode 100644 plugins/postcss-nesting/src/editions/2024-02/lib/valid-atrules.ts rename plugins/postcss-nesting/src/editions/{2021 => shared}/lib/cleanup-parent.ts (100%) rename plugins/postcss-nesting/src/editions/{2021 => shared}/lib/is-type-of-rule.ts (100%) rename plugins/postcss-nesting/src/editions/{2021 => shared}/lib/shift-nodes-before-parent.ts (100%) rename plugins/postcss-nesting/src/editions/{2021 => shared}/lib/valid-atrules.ts (100%) diff --git a/README.md b/README.md index 4a3e94bb0..bcf6138fd 100644 --- a/README.md +++ b/README.md @@ -23,11 +23,7 @@ const yourConfig = { /* remove autoprefixer if you had it here, it's part of postcss-preset-env */ postcssPresetEnv({ /* pluginOptions */ - features: { - 'nesting-rules': { - noIsPseudoSelector: false, - }, - }, + features: {}, }) ] } diff --git a/plugin-packs/postcss-preset-env/README.md b/plugin-packs/postcss-preset-env/README.md index 1d2493c3e..994807c7c 100644 --- a/plugin-packs/postcss-preset-env/README.md +++ b/plugin-packs/postcss-preset-env/README.md @@ -27,11 +27,7 @@ const yourConfig = { /* remove autoprefixer if you had it here, it's part of postcss-preset-env */ postcssPresetEnv({ /* pluginOptions */ - features: { - 'nesting-rules': ['auto', { - noIsPseudoSelector: false, - }], - }, + features: {}, }) ] } diff --git a/plugins/postcss-nesting/CHANGELOG.md b/plugins/postcss-nesting/CHANGELOG.md index 4ccb01211..94fece18d 100644 --- a/plugins/postcss-nesting/CHANGELOG.md +++ b/plugins/postcss-nesting/CHANGELOG.md @@ -1,5 +1,9 @@ # Changes to PostCSS Nesting +### Unreleased (minor) + +- Add the `edition` plugin option to control which CSS nesting specification version should be used. The default is `2021` but you can also set it to the newer `2024-02` edition to have more modern behavior. + ### 12.0.4 _February 26, 2024_ diff --git a/plugins/postcss-nesting/dist/index.cjs b/plugins/postcss-nesting/dist/index.cjs index cccf6f0aa..d27a0684a 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"),n=require("@csstools/selector-resolve-nested");const r=e.pseudo({value:":is"});function sortCompoundSelectorsInsideComplexSelector(t){if(!t||!t.nodes)return;const n=[];let o=[];for(let s=0;s"tag"===e.type))){const n=r.clone({}),o=t.nodes[s];o.replaceWith(n),n.append(e.selector({nodes:[o],value:void 0}))}o.push(t.nodes[s])}else n.push(o),n.push([t.nodes[s]]),o=[];n.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)?o.pseudoElement:o[t.type]}const o={universal:0,tag:1,pseudoElement:2,id:3,class:4,attribute:5,pseudo:6,selector:7,string:8,root:9,comment:10};function ampersandToScope$1(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 instanceof Error?e.message:e}"`)}r&&(r.walkNesting((t=>{const n=t.parent;t.replaceWith(e.pseudo({value:":scope"})),n&&sortCompoundSelectorsInsideComplexSelector(n)})),t.selector=r.toString())}function cleanupParent$1(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$1(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$1(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 a=[],c=0;if(e().astSync(l).walkNesting((()=>{c++})),c>1&&o.length>1)a=combinationsWithSizeN(o,c),i=a.length;else{i=o.length;for(let e=0;e{if("nesting"!==o.type)return;let s=a[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]),c=isCompoundSelector(s.nodes[0]),u=isSimpleSelector(o),p=isCompoundSelector(o);if(l&&u)return void o.replaceWith(s.clone());if((l||c)&&(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(c){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(isDeclarationLike(e,n.length>0))t.push(e);else{if("comment"===e.type){let r=e.next();for(;r&&"comment"===r.type;)r=r.next();if(isDeclarationLike(r,n.length>0))return void t.push(e)}n.push(e)}})),e.removeAll(),t.forEach((t=>{e.append(t)})),n.forEach((t=>{e.append(t)}))}function isDeclarationLike(e,t){return!!e&&("decl"===e.type||"atrule"===e.type&&"mixin"===e.name.toLowerCase()&&!t)}function transformRuleWithinRule$1(e,t,n,r){let o=[];try{o=mergeSelectors$1(t.selectors,e.selectors,r)}catch(r){return void e.warn(n,`Failed to parse selectors : "${t.selector}" / "${e.selector}" with message: "${r instanceof Error?r.message:r}"`)}if(!o.length)return;groupDeclarations(t),shiftNodesBeforeParent$1(e,t),e.selectors=o;"rule"===e.type&&"rule"===t.type&&e.selector===t.selector&&e.append(...t.nodes),cleanupParent$1(t)}function isValidRuleWithinRule$1(e){return e.selectors.every((e=>-1===e.indexOf("|")))}function comma$1(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$1(t.selectors,comma$1(e.params),o)}catch(r){return void e.warn(n,`Failed to parse selectors : "${t.selector}" / "${e.params}" with message: "${r instanceof Error?r.message:r}"`)}if(!s.length)return;shiftNodesBeforeParent$1(e,t);const i=t.clone().removeAll().append(e.nodes);i.raws.semicolon=!0,i.selectors=s,e.replaceWith(i),cleanupParent$1(t),r(i,n,o)}function isValidNestRuleWithinRule(e){return comma$1(e.params).every((e=>e.split("&").length>=2&&-1===e.indexOf("|")))}var s=["container","document","media","supports","layer","starting-style"];function atruleWithinRule$1(e,t,n,r,o){if(groupDeclarations(t),shiftNodesBeforeParent$1(e,t),e.nodes){const s=t.clone().removeAll().append(e.nodes);e.append(s),cleanupParent$1(t),r(s,n,o)}else cleanupParent$1(t)}function isAtruleWithinRule$1(e){return s.includes(e.name)}function transformAtruleWithinAtrule$1(e,t){var n,r;groupDeclarations(t),shiftNodesBeforeParent$1(e,t),e.params=(n=t.params,r=e.params,comma$1(n).map((e=>comma$1(r).map((t=>`${e} and ${t}`)).join(", "))).join(", ")),cleanupParent$1(t)}function isAtruleWithinAtrule$1(e,t){return s.includes(e.name)&&e.name===t.name}function isAtRule$1(e){return e&&"atrule"===e.type}function isNestRule(e){return e&&isAtRule$1(e)&&"nest"===e.name}function isRule$1(e){return e&&"rule"===e.type}function walk$1(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$1(r)&&isRule$1(o)&&isValidRuleWithinRule$1(r)?transformRuleWithinRule$1(r,o,t,n):isNestRule(r)&&isRule$1(o)&&isValidNestRuleWithinRule(r)?transformNestRuleWithinRule(r,o,t,walk$1,n):isAtRule$1(r)&&isRule$1(o)&&isAtruleWithinRule$1(r)?atruleWithinRule$1(r,o,t,walk$1,n):isAtRule$1(r)&&isAtRule$1(o)&&isAtruleWithinAtrule$1(r,o)&&transformAtruleWithinAtrule$1(r,o),"nodes"in r&&r.nodes.length&&walk$1(r,t,n)}))}const creator$2=e=>{const t=Object.assign({noIsPseudoSelector:!1,silenceAtNestWarning:!1},e);return{postcssPlugin:"postcss-nesting",Rule(e,{result:n}){walk$1(e,n,t),e.selector.includes("&")&&ersandToScope$1(e,n)}}};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 instanceof Error?e.message:e}"`)}r&&(r.walkNesting((t=>{t.replaceWith(e.pseudo({value:":scope"}))})),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)}creator$2.postcss=!0;const i=e();function mergeSelectors(e,t,r,o){let s;try{s=n.resolveNestedSelector(i.astSync(t),i.astSync(r))}catch(n){return e.warn(o,`Failed to parse selectors : "${r}" / "${t}" with message: "${n instanceof Error?n.message:n}"`),!1}return!!s&&s.toString()}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 transformRuleWithinRule(e,t,n){const r=mergeSelectors(e,e.selector,t.selector,n);if(!r)return;shiftNodesBeforeParent(e,t),e.selector=r;"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("|")))}var l=["container","document","media","supports","layer","starting-style"];function atruleWithinRule(e,t,n,r){if(shiftNodesBeforeParent(e,t),e.nodes){const o=t.clone().removeAll().append(e.nodes),s=mergeSelectors(e,"&",t.selector,n);if(!s)return;o.selector=s,e.append(o),cleanupParent(t),r(o,n)}else cleanupParent(t)}function isAtruleWithinRule(e){return l.includes(e.name)}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 transformAtruleWithinAtrule(e,t){var n,r;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 l.includes(e.name)&&e.name===t.name}function isAtRule(e){return e&&"atrule"===e.type}function isRule(e){return e&&"rule"===e.type}function walk(e,t){e.each((e=>{const n=e.parent;isRule(e)&&isRule(n)&&isValidRuleWithinRule(e)?transformRuleWithinRule(e,n,t):isAtRule(e)&&isRule(n)&&isAtruleWithinRule(e)?atruleWithinRule(e,n,t,walk):isAtRule(e)&&isAtRule(n)&&isAtruleWithinAtrule(e,n)&&transformAtruleWithinAtrule(e,n),"nodes"in e&&e.nodes.length&&walk(e,t)}))}const creator$1=e=>{if(Object.assign({noIsPseudoSelector:!1},e).noIsPseudoSelector)throw new Error("The `noIsPseudoSelector` option is no longer supported. Migrate your CSS to use the latest CSS nesting syntax.");return{postcssPlugin:"postcss-nesting",Rule(e,{result:t}){walk(e,t),e.selector.includes("&")&&ersandToScope(e,t)},AtRule:{nest(e){throw e.error(`\`@nest\` was removed from the CSS Nesting specification and will be removed from PostCSS Nesting in the next major version.\nChange \`@nest ${e.params} {}\` to \`${e.params} {}\` to migrate to the latest standard.`)}}}};creator$1.postcss=!0;const creator=e=>{const t=Object.assign({edition:"2021"},e);switch(t.edition){case"2021":return creator$2(e);case"2024-02":return creator$1(e);default:throw new Error(`Invalid edition: ${t.edition}`)}};creator.postcss=!0,module.exports=creator; +"use strict";var e=require("postcss-selector-parser"),t=require("@csstools/selector-specificity"),n=require("@csstools/selector-resolve-nested");const r=e.pseudo({value:":is"});function sortCompoundSelectorsInsideComplexSelector(t){if(!t||!t.nodes)return;const n=[];let o=[];for(let s=0;s"tag"===e.type))){const n=r.clone({}),o=t.nodes[s];o.replaceWith(n),n.append(e.selector({nodes:[o],value:void 0}))}o.push(t.nodes[s])}else n.push(o),n.push([t.nodes[s]]),o=[];n.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)?o.pseudoElement:o[t.type]}const o={universal:0,tag:1,pseudoElement:2,id:3,class:4,attribute:5,pseudo:6,selector:7,string:8,root:9,comment:10};function ampersandToScope$1(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 instanceof Error?e.message:e}"`)}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 groupDeclarations(e){const t=[],n=[];e.each((e=>{if(isDeclarationLike(e,n.length>0))t.push(e);else{if("comment"===e.type){let r=e.next();for(;r&&"comment"===r.type;)r=r.next();if(isDeclarationLike(r,n.length>0))return void t.push(e)}n.push(e)}})),e.removeAll(),t.forEach((t=>{e.append(t)})),n.forEach((t=>{e.append(t)}))}function isDeclarationLike(e,t){return!!e&&("decl"===e.type||"atrule"===e.type&&"mixin"===e.name.toLowerCase()&&!t)}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 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}var s=["container","document","media","supports","layer","starting-style"];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 s.includes(e.name)&&e.name===t.name}function atruleWithinRule$1(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$1(e){return s.includes(e.name)}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;te.split("&").length>=2&&-1===e.indexOf("|")))}function transformRuleWithinRule$1(e,t,n,r){let o=[];try{o=mergeSelectors$1(t.selectors,e.selectors,r)}catch(r){return void e.warn(n,`Failed to parse selectors : "${t.selector}" / "${e.selector}" with message: "${r instanceof Error?r.message:r}"`)}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$1(e){return e.selectors.every((e=>-1===e.indexOf("|")))}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$1(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$1(r)?transformRuleWithinRule$1(r,o,t,n):isNestRule(r)&&isRule(o)&&isValidNestRuleWithinRule(r)?transformNestRuleWithinRule(r,o,t,walk$1,n):isAtRule(r)&&isRule(o)&&isAtruleWithinRule$1(r)?atruleWithinRule$1(r,o,t,walk$1,n):isAtRule(r)&&isAtRule(o)&&isAtruleWithinAtrule(r,o)&&transformAtruleWithinAtrule(r,o),"nodes"in r&&r.nodes.length&&walk$1(r,t,n)}))}const creator$2=e=>{const t=Object.assign({noIsPseudoSelector:!1,silenceAtNestWarning:!1},e);return{postcssPlugin:"postcss-nesting",Rule(e,{result:n}){walk$1(e,n,t),e.selector.includes("&")&&ersandToScope$1(e,n)}}};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 instanceof Error?e.message:e}"`)}r&&(r.walkNesting((t=>{t.replaceWith(e.pseudo({value:":scope"}))})),t.selector=r.toString())}creator$2.postcss=!0;const i=e();function mergeSelectors(e,t,r,o){let s;try{s=n.resolveNestedSelector(i.astSync(t),i.astSync(r))}catch(n){return e.warn(o,`Failed to parse selectors : "${r}" / "${t}" with message: "${n instanceof Error?n.message:n}"`),!1}return!!s&&s.toString()}function atruleWithinRule(e,t,n,r){if(shiftNodesBeforeParent(e,t),e.nodes){const o=t.clone().removeAll().append(e.nodes),s=mergeSelectors(e,"&",t.selector,n);if(!s)return;o.selector=s,e.append(o),cleanupParent(t),r(o,n)}else cleanupParent(t)}function isAtruleWithinRule(e){return s.includes(e.name)}function transformRuleWithinRule(e,t,n){const r=mergeSelectors(e,e.selector,t.selector,n);if(!r)return;shiftNodesBeforeParent(e,t),e.selector=r;"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 walk(e,t){e.each((e=>{const n=e.parent;isRule(e)&&isRule(n)&&isValidRuleWithinRule(e)?transformRuleWithinRule(e,n,t):isAtRule(e)&&isRule(n)&&isAtruleWithinRule(e)&&atruleWithinRule(e,n,t,walk),"nodes"in e&&e.nodes.length&&walk(e,t)}))}const creator$1=e=>{if(Object.assign({noIsPseudoSelector:!1},e).noIsPseudoSelector)throw new Error("The `noIsPseudoSelector` option is no longer supported. Migrate your CSS to use the latest CSS nesting syntax.");return{postcssPlugin:"postcss-nesting",Rule(e,{result:t}){walk(e,t),e.selector.includes("&")&&ersandToScope(e,t)},AtRule:{nest(e){throw e.error(`\`@nest\` was removed from the CSS Nesting specification and will be removed from PostCSS Nesting in the next major version.\nChange \`@nest ${e.params} {}\` to \`${e.params} {}\` to migrate to the latest standard.`)}}}};creator$1.postcss=!0;const creator=e=>{const t=Object.assign({edition:"2021"},e);switch(t.edition){case"2021":return creator$2(e);case"2024-02":return creator$1(e);default:throw new Error(`Invalid edition: ${t.edition}`)}};creator.postcss=!0,module.exports=creator; diff --git a/plugins/postcss-nesting/dist/index.mjs b/plugins/postcss-nesting/dist/index.mjs index 2e80aa1e1..59b8c9f07 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";import{resolveNestedSelector as n}from"@csstools/selector-resolve-nested";const r=e.pseudo({value:":is"});function sortCompoundSelectorsInsideComplexSelector(t){if(!t||!t.nodes)return;const n=[];let o=[];for(let s=0;s"tag"===e.type))){const n=r.clone({}),o=t.nodes[s];o.replaceWith(n),n.append(e.selector({nodes:[o],value:void 0}))}o.push(t.nodes[s])}else n.push(o),n.push([t.nodes[s]]),o=[];n.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)?o.pseudoElement:o[t.type]}const o={universal:0,tag:1,pseudoElement:2,id:3,class:4,attribute:5,pseudo:6,selector:7,string:8,root:9,comment:10};function ampersandToScope$1(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 instanceof Error?e.message:e}"`)}r&&(r.walkNesting((t=>{const n=t.parent;t.replaceWith(e.pseudo({value:":scope"})),n&&sortCompoundSelectorsInsideComplexSelector(n)})),t.selector=r.toString())}function cleanupParent$1(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$1(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$1(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 a=[],c=0;if(e().astSync(l).walkNesting((()=>{c++})),c>1&&o.length>1)a=combinationsWithSizeN(o,c),i=a.length;else{i=o.length;for(let e=0;e{if("nesting"!==o.type)return;let s=a[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]),c=isCompoundSelector(s.nodes[0]),u=isSimpleSelector(o),p=isCompoundSelector(o);if(l&&u)return void o.replaceWith(s.clone());if((l||c)&&(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(c){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(isDeclarationLike(e,n.length>0))t.push(e);else{if("comment"===e.type){let r=e.next();for(;r&&"comment"===r.type;)r=r.next();if(isDeclarationLike(r,n.length>0))return void t.push(e)}n.push(e)}})),e.removeAll(),t.forEach((t=>{e.append(t)})),n.forEach((t=>{e.append(t)}))}function isDeclarationLike(e,t){return!!e&&("decl"===e.type||"atrule"===e.type&&"mixin"===e.name.toLowerCase()&&!t)}function transformRuleWithinRule$1(e,t,n,r){let o=[];try{o=mergeSelectors$1(t.selectors,e.selectors,r)}catch(r){return void e.warn(n,`Failed to parse selectors : "${t.selector}" / "${e.selector}" with message: "${r instanceof Error?r.message:r}"`)}if(!o.length)return;groupDeclarations(t),shiftNodesBeforeParent$1(e,t),e.selectors=o;"rule"===e.type&&"rule"===t.type&&e.selector===t.selector&&e.append(...t.nodes),cleanupParent$1(t)}function isValidRuleWithinRule$1(e){return e.selectors.every((e=>-1===e.indexOf("|")))}function comma$1(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$1(t.selectors,comma$1(e.params),o)}catch(r){return void e.warn(n,`Failed to parse selectors : "${t.selector}" / "${e.params}" with message: "${r instanceof Error?r.message:r}"`)}if(!s.length)return;shiftNodesBeforeParent$1(e,t);const i=t.clone().removeAll().append(e.nodes);i.raws.semicolon=!0,i.selectors=s,e.replaceWith(i),cleanupParent$1(t),r(i,n,o)}function isValidNestRuleWithinRule(e){return comma$1(e.params).every((e=>e.split("&").length>=2&&-1===e.indexOf("|")))}var s=["container","document","media","supports","layer","starting-style"];function atruleWithinRule$1(e,t,n,r,o){if(groupDeclarations(t),shiftNodesBeforeParent$1(e,t),e.nodes){const s=t.clone().removeAll().append(e.nodes);e.append(s),cleanupParent$1(t),r(s,n,o)}else cleanupParent$1(t)}function isAtruleWithinRule$1(e){return s.includes(e.name)}function transformAtruleWithinAtrule$1(e,t){var n,r;groupDeclarations(t),shiftNodesBeforeParent$1(e,t),e.params=(n=t.params,r=e.params,comma$1(n).map((e=>comma$1(r).map((t=>`${e} and ${t}`)).join(", "))).join(", ")),cleanupParent$1(t)}function isAtruleWithinAtrule$1(e,t){return s.includes(e.name)&&e.name===t.name}function isAtRule$1(e){return e&&"atrule"===e.type}function isNestRule(e){return e&&isAtRule$1(e)&&"nest"===e.name}function isRule$1(e){return e&&"rule"===e.type}function walk$1(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$1(r)&&isRule$1(o)&&isValidRuleWithinRule$1(r)?transformRuleWithinRule$1(r,o,t,n):isNestRule(r)&&isRule$1(o)&&isValidNestRuleWithinRule(r)?transformNestRuleWithinRule(r,o,t,walk$1,n):isAtRule$1(r)&&isRule$1(o)&&isAtruleWithinRule$1(r)?atruleWithinRule$1(r,o,t,walk$1,n):isAtRule$1(r)&&isAtRule$1(o)&&isAtruleWithinAtrule$1(r,o)&&transformAtruleWithinAtrule$1(r,o),"nodes"in r&&r.nodes.length&&walk$1(r,t,n)}))}const creator$2=e=>{const t=Object.assign({noIsPseudoSelector:!1,silenceAtNestWarning:!1},e);return{postcssPlugin:"postcss-nesting",Rule(e,{result:n}){walk$1(e,n,t),e.selector.includes("&")&&ersandToScope$1(e,n)}}};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 instanceof Error?e.message:e}"`)}r&&(r.walkNesting((t=>{t.replaceWith(e.pseudo({value:":scope"}))})),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)}creator$2.postcss=!0;const i=e();function mergeSelectors(e,t,r,o){let s;try{s=n(i.astSync(t),i.astSync(r))}catch(n){return e.warn(o,`Failed to parse selectors : "${r}" / "${t}" with message: "${n instanceof Error?n.message:n}"`),!1}return!!s&&s.toString()}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 transformRuleWithinRule(e,t,n){const r=mergeSelectors(e,e.selector,t.selector,n);if(!r)return;shiftNodesBeforeParent(e,t),e.selector=r;"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("|")))}var l=["container","document","media","supports","layer","starting-style"];function atruleWithinRule(e,t,n,r){if(shiftNodesBeforeParent(e,t),e.nodes){const o=t.clone().removeAll().append(e.nodes),s=mergeSelectors(e,"&",t.selector,n);if(!s)return;o.selector=s,e.append(o),cleanupParent(t),r(o,n)}else cleanupParent(t)}function isAtruleWithinRule(e){return l.includes(e.name)}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 transformAtruleWithinAtrule(e,t){var n,r;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 l.includes(e.name)&&e.name===t.name}function isAtRule(e){return e&&"atrule"===e.type}function isRule(e){return e&&"rule"===e.type}function walk(e,t){e.each((e=>{const n=e.parent;isRule(e)&&isRule(n)&&isValidRuleWithinRule(e)?transformRuleWithinRule(e,n,t):isAtRule(e)&&isRule(n)&&isAtruleWithinRule(e)?atruleWithinRule(e,n,t,walk):isAtRule(e)&&isAtRule(n)&&isAtruleWithinAtrule(e,n)&&transformAtruleWithinAtrule(e,n),"nodes"in e&&e.nodes.length&&walk(e,t)}))}const creator$1=e=>{if(Object.assign({noIsPseudoSelector:!1},e).noIsPseudoSelector)throw new Error("The `noIsPseudoSelector` option is no longer supported. Migrate your CSS to use the latest CSS nesting syntax.");return{postcssPlugin:"postcss-nesting",Rule(e,{result:t}){walk(e,t),e.selector.includes("&")&&ersandToScope(e,t)},AtRule:{nest(e){throw e.error(`\`@nest\` was removed from the CSS Nesting specification and will be removed from PostCSS Nesting in the next major version.\nChange \`@nest ${e.params} {}\` to \`${e.params} {}\` to migrate to the latest standard.`)}}}};creator$1.postcss=!0;const creator=e=>{const t=Object.assign({edition:"2021"},e);switch(t.edition){case"2021":return creator$2(e);case"2024-02":return creator$1(e);default:throw new Error(`Invalid edition: ${t.edition}`)}};creator.postcss=!0;export{creator as default}; +import e from"postcss-selector-parser";import{selectorSpecificity as t}from"@csstools/selector-specificity";import{resolveNestedSelector as n}from"@csstools/selector-resolve-nested";const r=e.pseudo({value:":is"});function sortCompoundSelectorsInsideComplexSelector(t){if(!t||!t.nodes)return;const n=[];let o=[];for(let s=0;s"tag"===e.type))){const n=r.clone({}),o=t.nodes[s];o.replaceWith(n),n.append(e.selector({nodes:[o],value:void 0}))}o.push(t.nodes[s])}else n.push(o),n.push([t.nodes[s]]),o=[];n.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)?o.pseudoElement:o[t.type]}const o={universal:0,tag:1,pseudoElement:2,id:3,class:4,attribute:5,pseudo:6,selector:7,string:8,root:9,comment:10};function ampersandToScope$1(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 instanceof Error?e.message:e}"`)}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 groupDeclarations(e){const t=[],n=[];e.each((e=>{if(isDeclarationLike(e,n.length>0))t.push(e);else{if("comment"===e.type){let r=e.next();for(;r&&"comment"===r.type;)r=r.next();if(isDeclarationLike(r,n.length>0))return void t.push(e)}n.push(e)}})),e.removeAll(),t.forEach((t=>{e.append(t)})),n.forEach((t=>{e.append(t)}))}function isDeclarationLike(e,t){return!!e&&("decl"===e.type||"atrule"===e.type&&"mixin"===e.name.toLowerCase()&&!t)}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 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}var s=["container","document","media","supports","layer","starting-style"];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 s.includes(e.name)&&e.name===t.name}function atruleWithinRule$1(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$1(e){return s.includes(e.name)}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]),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;te.split("&").length>=2&&-1===e.indexOf("|")))}function transformRuleWithinRule$1(e,t,n,r){let o=[];try{o=mergeSelectors$1(t.selectors,e.selectors,r)}catch(r){return void e.warn(n,`Failed to parse selectors : "${t.selector}" / "${e.selector}" with message: "${r instanceof Error?r.message:r}"`)}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$1(e){return e.selectors.every((e=>-1===e.indexOf("|")))}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$1(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$1(r)?transformRuleWithinRule$1(r,o,t,n):isNestRule(r)&&isRule(o)&&isValidNestRuleWithinRule(r)?transformNestRuleWithinRule(r,o,t,walk$1,n):isAtRule(r)&&isRule(o)&&isAtruleWithinRule$1(r)?atruleWithinRule$1(r,o,t,walk$1,n):isAtRule(r)&&isAtRule(o)&&isAtruleWithinAtrule(r,o)&&transformAtruleWithinAtrule(r,o),"nodes"in r&&r.nodes.length&&walk$1(r,t,n)}))}const creator$2=e=>{const t=Object.assign({noIsPseudoSelector:!1,silenceAtNestWarning:!1},e);return{postcssPlugin:"postcss-nesting",Rule(e,{result:n}){walk$1(e,n,t),e.selector.includes("&")&&ersandToScope$1(e,n)}}};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 instanceof Error?e.message:e}"`)}r&&(r.walkNesting((t=>{t.replaceWith(e.pseudo({value:":scope"}))})),t.selector=r.toString())}creator$2.postcss=!0;const i=e();function mergeSelectors(e,t,r,o){let s;try{s=n(i.astSync(t),i.astSync(r))}catch(n){return e.warn(o,`Failed to parse selectors : "${r}" / "${t}" with message: "${n instanceof Error?n.message:n}"`),!1}return!!s&&s.toString()}function atruleWithinRule(e,t,n,r){if(shiftNodesBeforeParent(e,t),e.nodes){const o=t.clone().removeAll().append(e.nodes),s=mergeSelectors(e,"&",t.selector,n);if(!s)return;o.selector=s,e.append(o),cleanupParent(t),r(o,n)}else cleanupParent(t)}function isAtruleWithinRule(e){return s.includes(e.name)}function transformRuleWithinRule(e,t,n){const r=mergeSelectors(e,e.selector,t.selector,n);if(!r)return;shiftNodesBeforeParent(e,t),e.selector=r;"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 walk(e,t){e.each((e=>{const n=e.parent;isRule(e)&&isRule(n)&&isValidRuleWithinRule(e)?transformRuleWithinRule(e,n,t):isAtRule(e)&&isRule(n)&&isAtruleWithinRule(e)&&atruleWithinRule(e,n,t,walk),"nodes"in e&&e.nodes.length&&walk(e,t)}))}const creator$1=e=>{if(Object.assign({noIsPseudoSelector:!1},e).noIsPseudoSelector)throw new Error("The `noIsPseudoSelector` option is no longer supported. Migrate your CSS to use the latest CSS nesting syntax.");return{postcssPlugin:"postcss-nesting",Rule(e,{result:t}){walk(e,t),e.selector.includes("&")&&ersandToScope(e,t)},AtRule:{nest(e){throw e.error(`\`@nest\` was removed from the CSS Nesting specification and will be removed from PostCSS Nesting in the next major version.\nChange \`@nest ${e.params} {}\` to \`${e.params} {}\` to migrate to the latest standard.`)}}}};creator$1.postcss=!0;const creator=e=>{const t=Object.assign({edition:"2021"},e);switch(t.edition){case"2021":return creator$2(e);case"2024-02":return creator$1(e);default:throw new Error(`Invalid edition: ${t.edition}`)}};creator.postcss=!0;export{creator as default}; diff --git a/plugins/postcss-nesting/src/editions/2021/lib/atrule-within-atrule.ts b/plugins/postcss-nesting/src/editions/2021/lib/atrule-within-atrule.ts index c33bc8151..7f3b6e20c 100644 --- a/plugins/postcss-nesting/src/editions/2021/lib/atrule-within-atrule.ts +++ b/plugins/postcss-nesting/src/editions/2021/lib/atrule-within-atrule.ts @@ -1,9 +1,9 @@ import type { AtRule } from 'postcss'; -import cleanupParent from './cleanup-parent.js'; +import cleanupParent from '../../shared/lib/cleanup-parent.js'; import groupDeclarations from './group-declarations.js'; import mergeParams from './merge-params.js'; -import shiftNodesBeforeParent from './shift-nodes-before-parent.js'; -import validAtrules from './valid-atrules.js'; +import shiftNodesBeforeParent from '../../shared/lib/shift-nodes-before-parent.js'; +import validAtrules from '../../shared/lib/valid-atrules.js'; export default function transformAtruleWithinAtrule(node: AtRule, parent: AtRule) { // Group all declarations after the first one. diff --git a/plugins/postcss-nesting/src/editions/2021/lib/atrule-within-rule.ts b/plugins/postcss-nesting/src/editions/2021/lib/atrule-within-rule.ts index b3a78e1a4..4dd27023e 100644 --- a/plugins/postcss-nesting/src/editions/2021/lib/atrule-within-rule.ts +++ b/plugins/postcss-nesting/src/editions/2021/lib/atrule-within-rule.ts @@ -1,10 +1,10 @@ -import cleanupParent from './cleanup-parent.js'; -import { options } from './options.js'; -import shiftNodesBeforeParent from './shift-nodes-before-parent.js'; -import validAtrules from './valid-atrules.js'; -import { walkFunc } from './walk-func.js'; import type { AtRule, Result, Rule } from 'postcss'; +import type { walkFunc } from './walk-func.js'; +import cleanupParent from '../../shared/lib/cleanup-parent.js'; import groupDeclarations from './group-declarations.js'; +import shiftNodesBeforeParent from '../../shared/lib/shift-nodes-before-parent.js'; +import validAtrules from '../../shared/lib/valid-atrules.js'; +import { options } from './options.js'; export default function atruleWithinRule(node: AtRule, parent: Rule, result: Result, walk: walkFunc, opts: options) { // Group all declarations after the first one. diff --git a/plugins/postcss-nesting/src/editions/2021/lib/nest-rule-within-rule.ts b/plugins/postcss-nesting/src/editions/2021/lib/nest-rule-within-rule.ts index e505fda06..4fabada1a 100644 --- a/plugins/postcss-nesting/src/editions/2021/lib/nest-rule-within-rule.ts +++ b/plugins/postcss-nesting/src/editions/2021/lib/nest-rule-within-rule.ts @@ -1,10 +1,10 @@ -import { comma } from './list.js'; -import shiftNodesBeforeParent from './shift-nodes-before-parent.js'; -import cleanupParent from './cleanup-parent.js'; -import mergeSelectors from './merge-selectors/merge-selectors.js'; import type { AtRule, Result, Rule } from 'postcss'; -import { walkFunc } from './walk-func.js'; +import type { walkFunc } from './walk-func.js'; +import mergeSelectors from './merge-selectors/merge-selectors.js'; +import shiftNodesBeforeParent from '../../shared/lib/shift-nodes-before-parent.js'; +import { comma } from './list.js'; import { options } from './options.js'; +import cleanupParent from '../../shared/lib/cleanup-parent.js'; export default function transformNestRuleWithinRule(node: AtRule, parent: Rule, result: Result, walk: walkFunc, opts: options) { let selectors = []; diff --git a/plugins/postcss-nesting/src/editions/2021/lib/rule-within-rule.ts b/plugins/postcss-nesting/src/editions/2021/lib/rule-within-rule.ts index 005effb31..d458db18f 100644 --- a/plugins/postcss-nesting/src/editions/2021/lib/rule-within-rule.ts +++ b/plugins/postcss-nesting/src/editions/2021/lib/rule-within-rule.ts @@ -1,9 +1,9 @@ -import shiftNodesBeforeParent from './shift-nodes-before-parent.js'; -import cleanupParent from './cleanup-parent.js'; -import mergeSelectors from './merge-selectors/merge-selectors.js'; import type { Result, Rule } from 'postcss'; -import { options } from './options.js'; +import cleanupParent from '../../shared/lib/cleanup-parent.js'; import groupDeclarations from './group-declarations.js'; +import mergeSelectors from './merge-selectors/merge-selectors.js'; +import shiftNodesBeforeParent from '../../shared/lib/shift-nodes-before-parent.js'; +import { options } from './options.js'; export default function transformRuleWithinRule(node: Rule, parent: Rule, result: Result, opts: options) { let selectors = []; diff --git a/plugins/postcss-nesting/src/editions/2021/lib/walk.ts b/plugins/postcss-nesting/src/editions/2021/lib/walk.ts index 30ffad425..b0782cc92 100644 --- a/plugins/postcss-nesting/src/editions/2021/lib/walk.ts +++ b/plugins/postcss-nesting/src/editions/2021/lib/walk.ts @@ -1,10 +1,10 @@ -import transformRuleWithinRule, { isValidRuleWithinRule } from './rule-within-rule.js'; -import transformNestRuleWithinRule, { isValidNestRuleWithinRule } from './nest-rule-within-rule.js'; -import transformAtruleWithinRule, { isAtruleWithinRule } from './atrule-within-rule.js'; -import transformAtruleWithinAtrule, { isAtruleWithinAtrule } from './atrule-within-atrule.js'; import type { Container, Result } from 'postcss'; +import transformAtruleWithinAtrule, { isAtruleWithinAtrule } from './atrule-within-atrule.js'; +import transformAtruleWithinRule, { isAtruleWithinRule } from './atrule-within-rule.js'; +import transformNestRuleWithinRule, { isValidNestRuleWithinRule } from './nest-rule-within-rule.js'; +import transformRuleWithinRule, { isValidRuleWithinRule } from './rule-within-rule.js'; +import { isAtRule, isNestRule, isRule } from '../../shared/lib/is-type-of-rule.js'; import { options } from './options.js'; -import { isAtRule, isNestRule, isRule } from './is-type-of-rule.js'; export default function walk(node: Container, result: Result, opts: options) { node.each((child) => { diff --git a/plugins/postcss-nesting/src/editions/2024-02/lib/atrule-within-atrule.ts b/plugins/postcss-nesting/src/editions/2024-02/lib/atrule-within-atrule.ts deleted file mode 100644 index baea2b108..000000000 --- a/plugins/postcss-nesting/src/editions/2024-02/lib/atrule-within-atrule.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { AtRule } from 'postcss'; -import cleanupParent from './cleanup-parent.js'; -import mergeParams from './merge-params.js'; -import shiftNodesBeforeParent from './shift-nodes-before-parent.js'; -import validAtrules from './valid-atrules.js'; - -export default function transformAtruleWithinAtrule(node: AtRule, parent: AtRule) { - // move previous siblings and the node to before the parent - shiftNodesBeforeParent(node, parent); - - // update the params of the node to be merged with the parent - node.params = mergeParams(parent.params, node.params); - - // conditionally cleanup an empty parent rule - cleanupParent(parent); -} - -export function isAtruleWithinAtrule(node: AtRule, parent: AtRule) { - return validAtrules.includes(node.name) && - node.name === parent.name; -} diff --git a/plugins/postcss-nesting/src/editions/2024-02/lib/atrule-within-rule.ts b/plugins/postcss-nesting/src/editions/2024-02/lib/atrule-within-rule.ts index 01dc09c35..06cb026ad 100644 --- a/plugins/postcss-nesting/src/editions/2024-02/lib/atrule-within-rule.ts +++ b/plugins/postcss-nesting/src/editions/2024-02/lib/atrule-within-rule.ts @@ -1,9 +1,9 @@ -import cleanupParent from './cleanup-parent.js'; -import shiftNodesBeforeParent from './shift-nodes-before-parent.js'; -import validAtrules from './valid-atrules.js'; -import { walkFunc } from './walk-func.js'; import type { AtRule, Result, Rule } from 'postcss'; +import type { walkFunc } from './walk-func.js'; +import cleanupParent from '../../shared/lib/cleanup-parent.js'; import mergeSelectors from './merge-selectors.js'; +import shiftNodesBeforeParent from '../../shared/lib/shift-nodes-before-parent.js'; +import validAtrules from '../../shared/lib/valid-atrules.js'; export default function atruleWithinRule(node: AtRule, parent: Rule, result: Result, walk: walkFunc) { // move previous siblings and the node to before the parent diff --git a/plugins/postcss-nesting/src/editions/2024-02/lib/cleanup-parent.ts b/plugins/postcss-nesting/src/editions/2024-02/lib/cleanup-parent.ts deleted file mode 100644 index 815b1dd1c..000000000 --- a/plugins/postcss-nesting/src/editions/2024-02/lib/cleanup-parent.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { ChildNode, Container } from 'postcss'; - -export default function cleanupParent(parent: Container) { - if (!parent.nodes.length) { - parent.remove(); - return; - } - - const commentNodes = parent.nodes.filter(node => node.type === 'comment'); - if (commentNodes.length === parent.nodes.length) { - parent.replaceWith(...commentNodes); - } -} diff --git a/plugins/postcss-nesting/src/editions/2024-02/lib/is-type-of-rule.ts b/plugins/postcss-nesting/src/editions/2024-02/lib/is-type-of-rule.ts deleted file mode 100644 index 1336f39eb..000000000 --- a/plugins/postcss-nesting/src/editions/2024-02/lib/is-type-of-rule.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { AtRule, Node, Rule } from 'postcss'; - -export function isAtRule(node?: Node): node is AtRule { - return node && node.type === 'atrule'; -} - -export function isRule(node?: Node): node is Rule { - return node && node.type === 'rule'; -} diff --git a/plugins/postcss-nesting/src/editions/2024-02/lib/list.ts b/plugins/postcss-nesting/src/editions/2024-02/lib/list.ts deleted file mode 100644 index 76d4861d6..000000000 --- a/plugins/postcss-nesting/src/editions/2024-02/lib/list.ts +++ /dev/null @@ -1,46 +0,0 @@ -export function comma(string: string) { - const array: Array = []; - let current = ''; - let split = false; - - let func = 0; - let quote: string|false = false; - let escape = false; - - for (const letter of string) { - if (escape) { - escape = false; - } else if (letter === '\\') { - escape = true; - } else if (quote) { - if (letter === quote) { - quote = false; - } - } else if (letter === '"' || letter === '\'') { - quote = letter; - } else if (letter === '(') { - func += 1; - } else if (letter === ')') { - if (func > 0) { - func -= 1; - } - } else if (func === 0) { - if (letter === ',') { - split = true; - } - } - - if (split) { - if (current !== '') { - array.push(current.trim()); - } - current = ''; - split = false; - } else { - current += letter; - } - } - - array.push(current.trim()); - return array; -} diff --git a/plugins/postcss-nesting/src/editions/2024-02/lib/merge-params.ts b/plugins/postcss-nesting/src/editions/2024-02/lib/merge-params.ts deleted file mode 100644 index 5e97609b8..000000000 --- a/plugins/postcss-nesting/src/editions/2024-02/lib/merge-params.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { comma } from './list.js'; - -export default function mergeParams(fromParams: string, toParams: string) { - return comma(fromParams) - .map((params1) => - comma(toParams) - .map((params2) => `${params1} and ${params2}`) - .join(', '), - ) - .join(', '); -} diff --git a/plugins/postcss-nesting/src/editions/2024-02/lib/rule-within-rule.ts b/plugins/postcss-nesting/src/editions/2024-02/lib/rule-within-rule.ts index 50302d7f3..9a7c58625 100644 --- a/plugins/postcss-nesting/src/editions/2024-02/lib/rule-within-rule.ts +++ b/plugins/postcss-nesting/src/editions/2024-02/lib/rule-within-rule.ts @@ -1,7 +1,7 @@ -import cleanupParent from './cleanup-parent.js'; -import mergeSelectors from './merge-selectors.js'; -import shiftNodesBeforeParent from './shift-nodes-before-parent.js'; import type { Result, Rule } from 'postcss'; +import cleanupParent from '../../shared/lib/cleanup-parent.js'; +import mergeSelectors from './merge-selectors.js'; +import shiftNodesBeforeParent from '../../shared/lib/shift-nodes-before-parent.js'; export default function transformRuleWithinRule(node: Rule, parent: Rule, result: Result) { const selectors = mergeSelectors(node, node.selector, parent.selector, result); diff --git a/plugins/postcss-nesting/src/editions/2024-02/lib/shift-nodes-before-parent.ts b/plugins/postcss-nesting/src/editions/2024-02/lib/shift-nodes-before-parent.ts deleted file mode 100644 index 9a07f3ca3..000000000 --- a/plugins/postcss-nesting/src/editions/2024-02/lib/shift-nodes-before-parent.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { ChildNode, Container } from 'postcss'; -import cleanupParent from './cleanup-parent'; - -export default function shiftNodesBeforeParent(node: ChildNode, parent: Container) { - const index = parent.index(node); - - // conditionally move previous siblings into a clone of the parent - if (index) { - const newParent = parent.cloneBefore().removeAll().append(parent.nodes.slice(0, index)); - newParent.raws.semicolon = true; /* nested rules end with "}" and do not have this flag set */ - cleanupParent(newParent); - } - - // move the current node before the parent (and after the conditional clone) - parent.before(node); - parent.raws.semicolon = true; /* nested rules end with "}" and do not have this flag set */ -} diff --git a/plugins/postcss-nesting/src/editions/2024-02/lib/valid-atrules.ts b/plugins/postcss-nesting/src/editions/2024-02/lib/valid-atrules.ts deleted file mode 100644 index 8148d90b6..000000000 --- a/plugins/postcss-nesting/src/editions/2024-02/lib/valid-atrules.ts +++ /dev/null @@ -1 +0,0 @@ -export default ['container', 'document', 'media', 'supports', 'layer', 'starting-style']; diff --git a/plugins/postcss-nesting/src/editions/2024-02/lib/walk.ts b/plugins/postcss-nesting/src/editions/2024-02/lib/walk.ts index 174504797..3a3ee2326 100644 --- a/plugins/postcss-nesting/src/editions/2024-02/lib/walk.ts +++ b/plugins/postcss-nesting/src/editions/2024-02/lib/walk.ts @@ -1,8 +1,7 @@ -import transformRuleWithinRule, { isValidRuleWithinRule } from './rule-within-rule.js'; -import transformAtruleWithinRule, { isAtruleWithinRule } from './atrule-within-rule.js'; -import transformAtruleWithinAtrule, { isAtruleWithinAtrule } from './atrule-within-atrule.js'; import type { Container, Result } from 'postcss'; -import { isAtRule, isRule } from './is-type-of-rule.js'; +import transformAtruleWithinRule, { isAtruleWithinRule } from './atrule-within-rule.js'; +import transformRuleWithinRule, { isValidRuleWithinRule } from './rule-within-rule.js'; +import { isAtRule, isRule } from '../../shared/lib/is-type-of-rule.js'; export default function walk(node: Container, result: Result) { node.each((child) => { @@ -20,12 +19,6 @@ export default function walk(node: Container, result: Result) { isAtruleWithinRule(child) ) { transformAtruleWithinRule(child, parent, result, walk); - } else if ( - isAtRule(child) && - isAtRule(parent) && - isAtruleWithinAtrule(child, parent) - ) { - transformAtruleWithinAtrule(child, parent); } if ('nodes' in child && child.nodes.length) { diff --git a/plugins/postcss-nesting/src/editions/2021/lib/cleanup-parent.ts b/plugins/postcss-nesting/src/editions/shared/lib/cleanup-parent.ts similarity index 100% rename from plugins/postcss-nesting/src/editions/2021/lib/cleanup-parent.ts rename to plugins/postcss-nesting/src/editions/shared/lib/cleanup-parent.ts diff --git a/plugins/postcss-nesting/src/editions/2021/lib/is-type-of-rule.ts b/plugins/postcss-nesting/src/editions/shared/lib/is-type-of-rule.ts similarity index 100% rename from plugins/postcss-nesting/src/editions/2021/lib/is-type-of-rule.ts rename to plugins/postcss-nesting/src/editions/shared/lib/is-type-of-rule.ts diff --git a/plugins/postcss-nesting/src/editions/2021/lib/shift-nodes-before-parent.ts b/plugins/postcss-nesting/src/editions/shared/lib/shift-nodes-before-parent.ts similarity index 100% rename from plugins/postcss-nesting/src/editions/2021/lib/shift-nodes-before-parent.ts rename to plugins/postcss-nesting/src/editions/shared/lib/shift-nodes-before-parent.ts diff --git a/plugins/postcss-nesting/src/editions/2021/lib/valid-atrules.ts b/plugins/postcss-nesting/src/editions/shared/lib/valid-atrules.ts similarity index 100% rename from plugins/postcss-nesting/src/editions/2021/lib/valid-atrules.ts rename to plugins/postcss-nesting/src/editions/shared/lib/valid-atrules.ts diff --git a/plugins/postcss-nesting/test/basic.edition-2024-02.expect.css b/plugins/postcss-nesting/test/basic.edition-2024-02.expect.css index 42df43565..ff0afaa7a 100644 --- a/plugins/postcss-nesting/test/basic.edition-2024-02.expect.css +++ b/plugins/postcss-nesting/test/basic.edition-2024-02.expect.css @@ -7,17 +7,14 @@ a { a { order: 2; } - } -@media screen and (min-width: 480px), print and (min-width: 480px) { + @media (min-width: 480px) { a { order: 3; } } -@media screen, print { - a { order: 4; @@ -71,14 +68,14 @@ a { a { order: 15; } - } -@media screen and (min-width: 480px) { + @media (min-width: 480px) { a { order: 16; } } + } body:is(a) { order: 17; diff --git a/plugins/postcss-nesting/test/media.css b/plugins/postcss-nesting/test/media.css index e2c48336a..54bbe51ab 100644 --- a/plugins/postcss-nesting/test/media.css +++ b/plugins/postcss-nesting/test/media.css @@ -11,7 +11,7 @@ a { } } } - @media screen, print and speech { + @media screen, print and (color) { @media (max-width: 300px), (min-aspect-ratio: 16/9) { order: 5; & c { @@ -33,7 +33,7 @@ a { } } } - @media screen, print and speech { + @media screen, print and (color) { @media (max-width: 300px), (min-aspect-ratio: 16/9) { order: 5; & c { @@ -54,3 +54,12 @@ a, a::after { order: 8; } } + + +.foo { + @media screen { + @media screen { + order: 9; + } + } +} diff --git a/plugins/postcss-nesting/test/media.edition-2024-02.expect.css b/plugins/postcss-nesting/test/media.edition-2024-02.expect.css index 4ad873a18..fae774a38 100644 --- a/plugins/postcss-nesting/test/media.edition-2024-02.expect.css +++ b/plugins/postcss-nesting/test/media.edition-2024-02.expect.css @@ -5,18 +5,19 @@ a { a { order: 2; } - } -@media (min-width: 100px) and (max-width: 200px) { + @media (max-width: 200px) { a { order: 3; } } -@media (min-width: 100px) and (max-width: 200px) { + @media (max-width: 200px) { :is(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) { + } +@media screen, print and (color) { + @media (max-width: 300px), (min-aspect-ratio: 16/9) { a { order: 5; } @@ -24,6 +25,7 @@ a { order: 6; } } + } a { order: 1; } @@ -31,18 +33,19 @@ a { a { order: 2; } - } -@media (min-width: 100px) and (max-width: 200px) { + @media (max-width: 200px) { a { order: 3; } } -@media (min-width: 100px) and (max-width: 200px) { + @media (max-width: 200px) { :is(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) { + } +@media screen, print and (color) { + @media (max-width: 300px), (min-aspect-ratio: 16/9) { a { order: 5; } @@ -50,6 +53,7 @@ a { order: 6; } } + } @media screen { @@ -64,3 +68,14 @@ a { order: 8; } } + + +@media screen { + @media screen { + + +.foo { + order: 9; +} + } + } diff --git a/plugins/postcss-nesting/test/media.expect.css b/plugins/postcss-nesting/test/media.expect.css index 8aa660161..c489e88b1 100644 --- a/plugins/postcss-nesting/test/media.expect.css +++ b/plugins/postcss-nesting/test/media.expect.css @@ -16,7 +16,7 @@ a { 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) { +@media screen and (max-width: 300px), screen and (min-aspect-ratio: 16/9), print and (color) and (max-width: 300px), print and (color) and (min-aspect-ratio: 16/9) { a { order: 5; } @@ -42,7 +42,7 @@ a { 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) { +@media screen and (max-width: 300px), screen and (min-aspect-ratio: 16/9), print and (color) and (max-width: 300px), print and (color) and (min-aspect-ratio: 16/9) { a { order: 5; } @@ -64,3 +64,12 @@ a, a::after { order: 8; } } + + +@media screen and screen { + + +.foo { + order: 9; +} + } 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 8aa660161..c489e88b1 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 @@ -16,7 +16,7 @@ a { 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) { +@media screen and (max-width: 300px), screen and (min-aspect-ratio: 16/9), print and (color) and (max-width: 300px), print and (color) and (min-aspect-ratio: 16/9) { a { order: 5; } @@ -42,7 +42,7 @@ a { 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) { +@media screen and (max-width: 300px), screen and (min-aspect-ratio: 16/9), print and (color) and (max-width: 300px), print and (color) and (min-aspect-ratio: 16/9) { a { order: 5; } @@ -64,3 +64,12 @@ a, a::after { order: 8; } } + + +@media screen and screen { + + +.foo { + order: 9; +} + } diff --git a/plugins/postcss-nesting/test/spec-examples.edition-2024-02.expect.css b/plugins/postcss-nesting/test/spec-examples.edition-2024-02.expect.css index 5ddbf55f2..cddb0b91b 100644 --- a/plugins/postcss-nesting/test/spec-examples.edition-2024-02.expect.css +++ b/plugins/postcss-nesting/test/spec-examples.edition-2024-02.expect.css @@ -295,12 +295,13 @@ __bar.foo { .foo { grid-auto-flow: column; } - } -@media (orientation: landscape) and (min-inline-size > 1024px) { + + @media (min-inline-size > 1024px) { .foo { max-inline-size: 1024px; } } + } /* equivalent to .foo { display: grid; } diff --git a/sites/postcss-preset-env/index.njk b/sites/postcss-preset-env/index.njk index f8eb06843..8f1641dd8 100644 --- a/sites/postcss-preset-env/index.njk +++ b/sites/postcss-preset-env/index.njk @@ -93,11 +93,7 @@ module.exports = { 'postcss-preset-env', { // Options - features: { - 'nesting-rules': { - noIsPseudoSelector: false, - }, - }, + features: {}, }, ], ] diff --git a/sites/postcss-preset-env/src/static/js/playground.js b/sites/postcss-preset-env/src/static/js/playground.js index a864c063c..699409b0f 100644 --- a/sites/postcss-preset-env/src/static/js/playground.js +++ b/sites/postcss-preset-env/src/static/js/playground.js @@ -42,9 +42,9 @@ const currentConfig = stateAtLoad.config ?? { blockDirection: 'top-to-bottom', }, features: { - 'nesting-rules': { - noIsPseudoSelector: false, - }, + 'nesting-rules': ['auto', { + edition: '2024-02', + }], }, }; From 9832e8884b2e7c976fc588301735ec0417430ad4 Mon Sep 17 00:00:00 2001 From: Romain Menke Date: Tue, 5 Mar 2024 19:57:27 +0100 Subject: [PATCH 4/5] docs --- plugins/postcss-nesting/README.md | 110 ++++++++++++++++++ plugins/postcss-nesting/docs/README.md | 42 +++++++ .../postcss-nesting/test/examples/example.css | 2 + .../example.edition-2024-02.expect.css | 4 + .../test/examples/example.expect.css | 2 + 5 files changed, 160 insertions(+) diff --git a/plugins/postcss-nesting/README.md b/plugins/postcss-nesting/README.md index e6b538459..68be2ca56 100644 --- a/plugins/postcss-nesting/README.md +++ b/plugins/postcss-nesting/README.md @@ -26,12 +26,16 @@ you might want to use [PostCSS Nested] instead. @media (prefers-color-scheme: dark) { color: cyan; } + + color: pink; } /* becomes */ .foo { color: red; + + color: pink; } .foo:hover { color: green; @@ -100,6 +104,112 @@ postcssNesting({ ## Options +### edition + +The default behavior is to transpile CSS following an older version of the CSS nesting specification. + +If you want to already use the latest version you can set the `edition` option to `2024-02`. + +```js +postcssNesting({ + edition: '2024-02' +}) +``` + +#### `2021` (default) + +This version is a continuation of what existed before CSS nesting was implemented in browsers. +It made a few non-invasive changes to keep up with implementations but it is falling behind. + +In a feature version of this plugin this will no longer be the default. + +```pcss +.foo { + color: red; + + &:hover { + color: green; + } + + > .bar { + color: blue; + } + + @media (prefers-color-scheme: dark) { + color: cyan; + } + + color: pink; +} + +/* becomes */ + +.foo { + color: red; + + color: pink; +} +.foo:hover { + color: green; + } +.foo > .bar { + color: blue; + } +@media (prefers-color-scheme: dark) { + .foo { + color: cyan; +} + } +``` + +#### `2024-02` + +- usage of `:is()` pseudo-class is no longer optional +- at rules are not combined with the `and` keyword +- `@nest` is removed from the specification +- declarations and nested rules/at-rules are no longer re-ordered + +```pcss +.foo { + color: red; + + &:hover { + color: green; + } + + > .bar { + color: blue; + } + + @media (prefers-color-scheme: dark) { + color: cyan; + } + + color: pink; +} + +/* becomes */ + +.foo { + color: red; +} +.foo:hover { + color: green; + } +.foo > .bar { + color: blue; + } +@media (prefers-color-scheme: dark) { + .foo { + color: cyan; +} + } +.foo { + + color: pink; +} +``` + ### noIsPseudoSelector #### Specificity diff --git a/plugins/postcss-nesting/docs/README.md b/plugins/postcss-nesting/docs/README.md index 06b06eefa..68a21b99a 100644 --- a/plugins/postcss-nesting/docs/README.md +++ b/plugins/postcss-nesting/docs/README.md @@ -58,6 +58,48 @@ You can silence this warning with a new `silenceAtNestWarning` plugin option. ## Options +### edition + +The default behavior is to transpile CSS following an older version of the CSS nesting specification. + +If you want to already use the latest version you can set the `edition` option to `2024-02`. + +```js +({ + edition: '2024-02' +}) +``` + +#### `2021` (default) + +This version is a continuation of what existed before CSS nesting was implemented in browsers. +It made a few non-invasive changes to keep up with implementations but it is falling behind. + +In a feature version of this plugin this will no longer be the default. + +```pcss + + +/* becomes */ + + +``` + +#### `2024-02` + +- usage of `:is()` pseudo-class is no longer optional +- at rules are not combined with the `and` keyword +- `@nest` is removed from the specification +- declarations and nested rules/at-rules are no longer re-ordered + +```pcss + + +/* becomes */ + + +``` + ### noIsPseudoSelector #### Specificity diff --git a/plugins/postcss-nesting/test/examples/example.css b/plugins/postcss-nesting/test/examples/example.css index b15aabb85..3e02e6c90 100644 --- a/plugins/postcss-nesting/test/examples/example.css +++ b/plugins/postcss-nesting/test/examples/example.css @@ -12,4 +12,6 @@ @media (prefers-color-scheme: dark) { color: cyan; } + + color: pink; } diff --git a/plugins/postcss-nesting/test/examples/example.edition-2024-02.expect.css b/plugins/postcss-nesting/test/examples/example.edition-2024-02.expect.css index 94b8f30cc..cc4dd9eef 100644 --- a/plugins/postcss-nesting/test/examples/example.edition-2024-02.expect.css +++ b/plugins/postcss-nesting/test/examples/example.edition-2024-02.expect.css @@ -12,3 +12,7 @@ color: cyan; } } +.foo { + + color: pink; +} diff --git a/plugins/postcss-nesting/test/examples/example.expect.css b/plugins/postcss-nesting/test/examples/example.expect.css index c2de30e41..305b4dc61 100644 --- a/plugins/postcss-nesting/test/examples/example.expect.css +++ b/plugins/postcss-nesting/test/examples/example.expect.css @@ -1,5 +1,7 @@ .foo { color: red; + + color: pink; } .foo:hover { color: green; From d8cd335b2f1f8ee7e74dd90081fc0da4bbe4a1d1 Mon Sep 17 00:00:00 2001 From: Romain Menke Date: Wed, 6 Mar 2024 09:03:36 +0100 Subject: [PATCH 5/5] do not warn on is pseudo option --- plugins/postcss-nesting/dist/index.cjs | 2 +- plugins/postcss-nesting/dist/index.d.ts | 2 +- plugins/postcss-nesting/dist/index.mjs | 2 +- .../src/editions/2024-02/index.ts | 17 ++--------------- 4 files changed, 5 insertions(+), 18 deletions(-) diff --git a/plugins/postcss-nesting/dist/index.cjs b/plugins/postcss-nesting/dist/index.cjs index d27a0684a..3a82e63cd 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"),n=require("@csstools/selector-resolve-nested");const r=e.pseudo({value:":is"});function sortCompoundSelectorsInsideComplexSelector(t){if(!t||!t.nodes)return;const n=[];let o=[];for(let s=0;s"tag"===e.type))){const n=r.clone({}),o=t.nodes[s];o.replaceWith(n),n.append(e.selector({nodes:[o],value:void 0}))}o.push(t.nodes[s])}else n.push(o),n.push([t.nodes[s]]),o=[];n.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)?o.pseudoElement:o[t.type]}const o={universal:0,tag:1,pseudoElement:2,id:3,class:4,attribute:5,pseudo:6,selector:7,string:8,root:9,comment:10};function ampersandToScope$1(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 instanceof Error?e.message:e}"`)}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 groupDeclarations(e){const t=[],n=[];e.each((e=>{if(isDeclarationLike(e,n.length>0))t.push(e);else{if("comment"===e.type){let r=e.next();for(;r&&"comment"===r.type;)r=r.next();if(isDeclarationLike(r,n.length>0))return void t.push(e)}n.push(e)}})),e.removeAll(),t.forEach((t=>{e.append(t)})),n.forEach((t=>{e.append(t)}))}function isDeclarationLike(e,t){return!!e&&("decl"===e.type||"atrule"===e.type&&"mixin"===e.name.toLowerCase()&&!t)}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 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}var s=["container","document","media","supports","layer","starting-style"];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 s.includes(e.name)&&e.name===t.name}function atruleWithinRule$1(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$1(e){return s.includes(e.name)}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;te.split("&").length>=2&&-1===e.indexOf("|")))}function transformRuleWithinRule$1(e,t,n,r){let o=[];try{o=mergeSelectors$1(t.selectors,e.selectors,r)}catch(r){return void e.warn(n,`Failed to parse selectors : "${t.selector}" / "${e.selector}" with message: "${r instanceof Error?r.message:r}"`)}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$1(e){return e.selectors.every((e=>-1===e.indexOf("|")))}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$1(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$1(r)?transformRuleWithinRule$1(r,o,t,n):isNestRule(r)&&isRule(o)&&isValidNestRuleWithinRule(r)?transformNestRuleWithinRule(r,o,t,walk$1,n):isAtRule(r)&&isRule(o)&&isAtruleWithinRule$1(r)?atruleWithinRule$1(r,o,t,walk$1,n):isAtRule(r)&&isAtRule(o)&&isAtruleWithinAtrule(r,o)&&transformAtruleWithinAtrule(r,o),"nodes"in r&&r.nodes.length&&walk$1(r,t,n)}))}const creator$2=e=>{const t=Object.assign({noIsPseudoSelector:!1,silenceAtNestWarning:!1},e);return{postcssPlugin:"postcss-nesting",Rule(e,{result:n}){walk$1(e,n,t),e.selector.includes("&")&&ersandToScope$1(e,n)}}};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 instanceof Error?e.message:e}"`)}r&&(r.walkNesting((t=>{t.replaceWith(e.pseudo({value:":scope"}))})),t.selector=r.toString())}creator$2.postcss=!0;const i=e();function mergeSelectors(e,t,r,o){let s;try{s=n.resolveNestedSelector(i.astSync(t),i.astSync(r))}catch(n){return e.warn(o,`Failed to parse selectors : "${r}" / "${t}" with message: "${n instanceof Error?n.message:n}"`),!1}return!!s&&s.toString()}function atruleWithinRule(e,t,n,r){if(shiftNodesBeforeParent(e,t),e.nodes){const o=t.clone().removeAll().append(e.nodes),s=mergeSelectors(e,"&",t.selector,n);if(!s)return;o.selector=s,e.append(o),cleanupParent(t),r(o,n)}else cleanupParent(t)}function isAtruleWithinRule(e){return s.includes(e.name)}function transformRuleWithinRule(e,t,n){const r=mergeSelectors(e,e.selector,t.selector,n);if(!r)return;shiftNodesBeforeParent(e,t),e.selector=r;"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 walk(e,t){e.each((e=>{const n=e.parent;isRule(e)&&isRule(n)&&isValidRuleWithinRule(e)?transformRuleWithinRule(e,n,t):isAtRule(e)&&isRule(n)&&isAtruleWithinRule(e)&&atruleWithinRule(e,n,t,walk),"nodes"in e&&e.nodes.length&&walk(e,t)}))}const creator$1=e=>{if(Object.assign({noIsPseudoSelector:!1},e).noIsPseudoSelector)throw new Error("The `noIsPseudoSelector` option is no longer supported. Migrate your CSS to use the latest CSS nesting syntax.");return{postcssPlugin:"postcss-nesting",Rule(e,{result:t}){walk(e,t),e.selector.includes("&")&&ersandToScope(e,t)},AtRule:{nest(e){throw e.error(`\`@nest\` was removed from the CSS Nesting specification and will be removed from PostCSS Nesting in the next major version.\nChange \`@nest ${e.params} {}\` to \`${e.params} {}\` to migrate to the latest standard.`)}}}};creator$1.postcss=!0;const creator=e=>{const t=Object.assign({edition:"2021"},e);switch(t.edition){case"2021":return creator$2(e);case"2024-02":return creator$1(e);default:throw new Error(`Invalid edition: ${t.edition}`)}};creator.postcss=!0,module.exports=creator; +"use strict";var e=require("postcss-selector-parser"),t=require("@csstools/selector-specificity"),n=require("@csstools/selector-resolve-nested");const r=e.pseudo({value:":is"});function sortCompoundSelectorsInsideComplexSelector(t){if(!t||!t.nodes)return;const n=[];let o=[];for(let s=0;s"tag"===e.type))){const n=r.clone({}),o=t.nodes[s];o.replaceWith(n),n.append(e.selector({nodes:[o],value:void 0}))}o.push(t.nodes[s])}else n.push(o),n.push([t.nodes[s]]),o=[];n.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)?o.pseudoElement:o[t.type]}const o={universal:0,tag:1,pseudoElement:2,id:3,class:4,attribute:5,pseudo:6,selector:7,string:8,root:9,comment:10};function ampersandToScope$1(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 instanceof Error?e.message:e}"`)}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 groupDeclarations(e){const t=[],n=[];e.each((e=>{if(isDeclarationLike(e,n.length>0))t.push(e);else{if("comment"===e.type){let r=e.next();for(;r&&"comment"===r.type;)r=r.next();if(isDeclarationLike(r,n.length>0))return void t.push(e)}n.push(e)}})),e.removeAll(),t.forEach((t=>{e.append(t)})),n.forEach((t=>{e.append(t)}))}function isDeclarationLike(e,t){return!!e&&("decl"===e.type||"atrule"===e.type&&"mixin"===e.name.toLowerCase()&&!t)}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 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}var s=["container","document","media","supports","layer","starting-style"];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 s.includes(e.name)&&e.name===t.name}function atruleWithinRule$1(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$1(e){return s.includes(e.name)}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;te.split("&").length>=2&&-1===e.indexOf("|")))}function transformRuleWithinRule$1(e,t,n,r){let o=[];try{o=mergeSelectors$1(t.selectors,e.selectors,r)}catch(r){return void e.warn(n,`Failed to parse selectors : "${t.selector}" / "${e.selector}" with message: "${r instanceof Error?r.message:r}"`)}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$1(e){return e.selectors.every((e=>-1===e.indexOf("|")))}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$1(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$1(r)?transformRuleWithinRule$1(r,o,t,n):isNestRule(r)&&isRule(o)&&isValidNestRuleWithinRule(r)?transformNestRuleWithinRule(r,o,t,walk$1,n):isAtRule(r)&&isRule(o)&&isAtruleWithinRule$1(r)?atruleWithinRule$1(r,o,t,walk$1,n):isAtRule(r)&&isAtRule(o)&&isAtruleWithinAtrule(r,o)&&transformAtruleWithinAtrule(r,o),"nodes"in r&&r.nodes.length&&walk$1(r,t,n)}))}const creator$2=e=>{const t=Object.assign({noIsPseudoSelector:!1,silenceAtNestWarning:!1},e);return{postcssPlugin:"postcss-nesting",Rule(e,{result:n}){walk$1(e,n,t),e.selector.includes("&")&&ersandToScope$1(e,n)}}};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 instanceof Error?e.message:e}"`)}r&&(r.walkNesting((t=>{t.replaceWith(e.pseudo({value:":scope"}))})),t.selector=r.toString())}creator$2.postcss=!0;const i=e();function mergeSelectors(e,t,r,o){let s;try{s=n.resolveNestedSelector(i.astSync(t),i.astSync(r))}catch(n){return e.warn(o,`Failed to parse selectors : "${r}" / "${t}" with message: "${n instanceof Error?n.message:n}"`),!1}return!!s&&s.toString()}function atruleWithinRule(e,t,n,r){if(shiftNodesBeforeParent(e,t),e.nodes){const o=t.clone().removeAll().append(e.nodes),s=mergeSelectors(e,"&",t.selector,n);if(!s)return;o.selector=s,e.append(o),cleanupParent(t),r(o,n)}else cleanupParent(t)}function isAtruleWithinRule(e){return s.includes(e.name)}function transformRuleWithinRule(e,t,n){const r=mergeSelectors(e,e.selector,t.selector,n);if(!r)return;shiftNodesBeforeParent(e,t),e.selector=r;"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 walk(e,t){e.each((e=>{const n=e.parent;isRule(e)&&isRule(n)&&isValidRuleWithinRule(e)?transformRuleWithinRule(e,n,t):isAtRule(e)&&isRule(n)&&isAtruleWithinRule(e)&&atruleWithinRule(e,n,t,walk),"nodes"in e&&e.nodes.length&&walk(e,t)}))}const creator$1=()=>({postcssPlugin:"postcss-nesting",Rule(e,{result:t}){walk(e,t),e.selector.includes("&")&&ersandToScope(e,t)},AtRule:{nest(e){throw e.error(`\`@nest\` was removed from the CSS Nesting specification and will be removed from PostCSS Nesting in the next major version.\nChange \`@nest ${e.params} {}\` to \`${e.params} {}\` to migrate to the latest standard.`)}}});creator$1.postcss=!0;const creator=e=>{const t=Object.assign({edition:"2021"},e);switch(t.edition){case"2021":return creator$2(e);case"2024-02":return creator$1();default:throw new Error(`Invalid edition: ${t.edition}`)}};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 bc1c0194e..0ce7888d8 100644 --- a/plugins/postcss-nesting/dist/index.d.ts +++ b/plugins/postcss-nesting/dist/index.d.ts @@ -19,7 +19,7 @@ export declare type pluginOptions2021 = { /** postcss-nesting plugin options */ export declare type pluginOptions2024_02 = { - /** This option was removed. You must migrate your CSS to the latest speciation to continue using this plugin. */ + /** @deprecated This option was removed. You must migrate your CSS to the latest speciation to continue using this plugin. */ noIsPseudoSelector?: boolean; }; diff --git a/plugins/postcss-nesting/dist/index.mjs b/plugins/postcss-nesting/dist/index.mjs index 59b8c9f07..781e66818 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";import{resolveNestedSelector as n}from"@csstools/selector-resolve-nested";const r=e.pseudo({value:":is"});function sortCompoundSelectorsInsideComplexSelector(t){if(!t||!t.nodes)return;const n=[];let o=[];for(let s=0;s"tag"===e.type))){const n=r.clone({}),o=t.nodes[s];o.replaceWith(n),n.append(e.selector({nodes:[o],value:void 0}))}o.push(t.nodes[s])}else n.push(o),n.push([t.nodes[s]]),o=[];n.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)?o.pseudoElement:o[t.type]}const o={universal:0,tag:1,pseudoElement:2,id:3,class:4,attribute:5,pseudo:6,selector:7,string:8,root:9,comment:10};function ampersandToScope$1(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 instanceof Error?e.message:e}"`)}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 groupDeclarations(e){const t=[],n=[];e.each((e=>{if(isDeclarationLike(e,n.length>0))t.push(e);else{if("comment"===e.type){let r=e.next();for(;r&&"comment"===r.type;)r=r.next();if(isDeclarationLike(r,n.length>0))return void t.push(e)}n.push(e)}})),e.removeAll(),t.forEach((t=>{e.append(t)})),n.forEach((t=>{e.append(t)}))}function isDeclarationLike(e,t){return!!e&&("decl"===e.type||"atrule"===e.type&&"mixin"===e.name.toLowerCase()&&!t)}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 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}var s=["container","document","media","supports","layer","starting-style"];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 s.includes(e.name)&&e.name===t.name}function atruleWithinRule$1(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$1(e){return s.includes(e.name)}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]),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;te.split("&").length>=2&&-1===e.indexOf("|")))}function transformRuleWithinRule$1(e,t,n,r){let o=[];try{o=mergeSelectors$1(t.selectors,e.selectors,r)}catch(r){return void e.warn(n,`Failed to parse selectors : "${t.selector}" / "${e.selector}" with message: "${r instanceof Error?r.message:r}"`)}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$1(e){return e.selectors.every((e=>-1===e.indexOf("|")))}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$1(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$1(r)?transformRuleWithinRule$1(r,o,t,n):isNestRule(r)&&isRule(o)&&isValidNestRuleWithinRule(r)?transformNestRuleWithinRule(r,o,t,walk$1,n):isAtRule(r)&&isRule(o)&&isAtruleWithinRule$1(r)?atruleWithinRule$1(r,o,t,walk$1,n):isAtRule(r)&&isAtRule(o)&&isAtruleWithinAtrule(r,o)&&transformAtruleWithinAtrule(r,o),"nodes"in r&&r.nodes.length&&walk$1(r,t,n)}))}const creator$2=e=>{const t=Object.assign({noIsPseudoSelector:!1,silenceAtNestWarning:!1},e);return{postcssPlugin:"postcss-nesting",Rule(e,{result:n}){walk$1(e,n,t),e.selector.includes("&")&&ersandToScope$1(e,n)}}};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 instanceof Error?e.message:e}"`)}r&&(r.walkNesting((t=>{t.replaceWith(e.pseudo({value:":scope"}))})),t.selector=r.toString())}creator$2.postcss=!0;const i=e();function mergeSelectors(e,t,r,o){let s;try{s=n(i.astSync(t),i.astSync(r))}catch(n){return e.warn(o,`Failed to parse selectors : "${r}" / "${t}" with message: "${n instanceof Error?n.message:n}"`),!1}return!!s&&s.toString()}function atruleWithinRule(e,t,n,r){if(shiftNodesBeforeParent(e,t),e.nodes){const o=t.clone().removeAll().append(e.nodes),s=mergeSelectors(e,"&",t.selector,n);if(!s)return;o.selector=s,e.append(o),cleanupParent(t),r(o,n)}else cleanupParent(t)}function isAtruleWithinRule(e){return s.includes(e.name)}function transformRuleWithinRule(e,t,n){const r=mergeSelectors(e,e.selector,t.selector,n);if(!r)return;shiftNodesBeforeParent(e,t),e.selector=r;"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 walk(e,t){e.each((e=>{const n=e.parent;isRule(e)&&isRule(n)&&isValidRuleWithinRule(e)?transformRuleWithinRule(e,n,t):isAtRule(e)&&isRule(n)&&isAtruleWithinRule(e)&&atruleWithinRule(e,n,t,walk),"nodes"in e&&e.nodes.length&&walk(e,t)}))}const creator$1=e=>{if(Object.assign({noIsPseudoSelector:!1},e).noIsPseudoSelector)throw new Error("The `noIsPseudoSelector` option is no longer supported. Migrate your CSS to use the latest CSS nesting syntax.");return{postcssPlugin:"postcss-nesting",Rule(e,{result:t}){walk(e,t),e.selector.includes("&")&&ersandToScope(e,t)},AtRule:{nest(e){throw e.error(`\`@nest\` was removed from the CSS Nesting specification and will be removed from PostCSS Nesting in the next major version.\nChange \`@nest ${e.params} {}\` to \`${e.params} {}\` to migrate to the latest standard.`)}}}};creator$1.postcss=!0;const creator=e=>{const t=Object.assign({edition:"2021"},e);switch(t.edition){case"2021":return creator$2(e);case"2024-02":return creator$1(e);default:throw new Error(`Invalid edition: ${t.edition}`)}};creator.postcss=!0;export{creator as default}; +import e from"postcss-selector-parser";import{selectorSpecificity as t}from"@csstools/selector-specificity";import{resolveNestedSelector as n}from"@csstools/selector-resolve-nested";const r=e.pseudo({value:":is"});function sortCompoundSelectorsInsideComplexSelector(t){if(!t||!t.nodes)return;const n=[];let o=[];for(let s=0;s"tag"===e.type))){const n=r.clone({}),o=t.nodes[s];o.replaceWith(n),n.append(e.selector({nodes:[o],value:void 0}))}o.push(t.nodes[s])}else n.push(o),n.push([t.nodes[s]]),o=[];n.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)?o.pseudoElement:o[t.type]}const o={universal:0,tag:1,pseudoElement:2,id:3,class:4,attribute:5,pseudo:6,selector:7,string:8,root:9,comment:10};function ampersandToScope$1(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 instanceof Error?e.message:e}"`)}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 groupDeclarations(e){const t=[],n=[];e.each((e=>{if(isDeclarationLike(e,n.length>0))t.push(e);else{if("comment"===e.type){let r=e.next();for(;r&&"comment"===r.type;)r=r.next();if(isDeclarationLike(r,n.length>0))return void t.push(e)}n.push(e)}})),e.removeAll(),t.forEach((t=>{e.append(t)})),n.forEach((t=>{e.append(t)}))}function isDeclarationLike(e,t){return!!e&&("decl"===e.type||"atrule"===e.type&&"mixin"===e.name.toLowerCase()&&!t)}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 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}var s=["container","document","media","supports","layer","starting-style"];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 s.includes(e.name)&&e.name===t.name}function atruleWithinRule$1(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$1(e){return s.includes(e.name)}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]),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;te.split("&").length>=2&&-1===e.indexOf("|")))}function transformRuleWithinRule$1(e,t,n,r){let o=[];try{o=mergeSelectors$1(t.selectors,e.selectors,r)}catch(r){return void e.warn(n,`Failed to parse selectors : "${t.selector}" / "${e.selector}" with message: "${r instanceof Error?r.message:r}"`)}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$1(e){return e.selectors.every((e=>-1===e.indexOf("|")))}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$1(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$1(r)?transformRuleWithinRule$1(r,o,t,n):isNestRule(r)&&isRule(o)&&isValidNestRuleWithinRule(r)?transformNestRuleWithinRule(r,o,t,walk$1,n):isAtRule(r)&&isRule(o)&&isAtruleWithinRule$1(r)?atruleWithinRule$1(r,o,t,walk$1,n):isAtRule(r)&&isAtRule(o)&&isAtruleWithinAtrule(r,o)&&transformAtruleWithinAtrule(r,o),"nodes"in r&&r.nodes.length&&walk$1(r,t,n)}))}const creator$2=e=>{const t=Object.assign({noIsPseudoSelector:!1,silenceAtNestWarning:!1},e);return{postcssPlugin:"postcss-nesting",Rule(e,{result:n}){walk$1(e,n,t),e.selector.includes("&")&&ersandToScope$1(e,n)}}};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 instanceof Error?e.message:e}"`)}r&&(r.walkNesting((t=>{t.replaceWith(e.pseudo({value:":scope"}))})),t.selector=r.toString())}creator$2.postcss=!0;const i=e();function mergeSelectors(e,t,r,o){let s;try{s=n(i.astSync(t),i.astSync(r))}catch(n){return e.warn(o,`Failed to parse selectors : "${r}" / "${t}" with message: "${n instanceof Error?n.message:n}"`),!1}return!!s&&s.toString()}function atruleWithinRule(e,t,n,r){if(shiftNodesBeforeParent(e,t),e.nodes){const o=t.clone().removeAll().append(e.nodes),s=mergeSelectors(e,"&",t.selector,n);if(!s)return;o.selector=s,e.append(o),cleanupParent(t),r(o,n)}else cleanupParent(t)}function isAtruleWithinRule(e){return s.includes(e.name)}function transformRuleWithinRule(e,t,n){const r=mergeSelectors(e,e.selector,t.selector,n);if(!r)return;shiftNodesBeforeParent(e,t),e.selector=r;"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 walk(e,t){e.each((e=>{const n=e.parent;isRule(e)&&isRule(n)&&isValidRuleWithinRule(e)?transformRuleWithinRule(e,n,t):isAtRule(e)&&isRule(n)&&isAtruleWithinRule(e)&&atruleWithinRule(e,n,t,walk),"nodes"in e&&e.nodes.length&&walk(e,t)}))}const creator$1=()=>({postcssPlugin:"postcss-nesting",Rule(e,{result:t}){walk(e,t),e.selector.includes("&")&&ersandToScope(e,t)},AtRule:{nest(e){throw e.error(`\`@nest\` was removed from the CSS Nesting specification and will be removed from PostCSS Nesting in the next major version.\nChange \`@nest ${e.params} {}\` to \`${e.params} {}\` to migrate to the latest standard.`)}}});creator$1.postcss=!0;const creator=e=>{const t=Object.assign({edition:"2021"},e);switch(t.edition){case"2021":return creator$2(e);case"2024-02":return creator$1();default:throw new Error(`Invalid edition: ${t.edition}`)}};creator.postcss=!0;export{creator as default}; diff --git a/plugins/postcss-nesting/src/editions/2024-02/index.ts b/plugins/postcss-nesting/src/editions/2024-02/index.ts index 795e8f1b6..568420e12 100644 --- a/plugins/postcss-nesting/src/editions/2024-02/index.ts +++ b/plugins/postcss-nesting/src/editions/2024-02/index.ts @@ -4,24 +4,11 @@ import walk from './lib/walk.js'; /** postcss-nesting plugin options */ export type pluginOptions = { - /** This option was removed. You must migrate your CSS to the latest speciation to continue using this plugin. */ + /** @deprecated This option was removed. You must migrate your CSS to the latest speciation to continue using this plugin. */ noIsPseudoSelector?: boolean, }; -const creator: PluginCreator = (opts?: pluginOptions) => { - const options = Object.assign( - // Default options - { - noIsPseudoSelector: false, - }, - // Provided options - opts, - ); - - if (options.noIsPseudoSelector) { - throw new Error('The `noIsPseudoSelector` option is no longer supported. Migrate your CSS to use the latest CSS nesting syntax.'); - } - +const creator: PluginCreator = () => { return { postcssPlugin: 'postcss-nesting', Rule(rule, { result }) {