From b2aa38b17699c121f9f5d85d68d30ec61c791280 Mon Sep 17 00:00:00 2001 From: Romain Menke Date: Fri, 6 Jun 2025 11:49:43 +0200 Subject: [PATCH] selector-resolve-nested: add ignoreImplicitNesting --- packages/selector-resolve-nested/CHANGELOG.md | 4 ++ .../selector-resolve-nested/dist/index.cjs | 2 +- .../selector-resolve-nested/dist/index.d.ts | 10 ++- .../selector-resolve-nested/dist/index.mjs | 2 +- .../docs/selector-resolve-nested.api.json | 68 ++++++++++++++++++- .../docs/selector-resolve-nested.md | 26 ++++++- ...or-resolve-nested.resolvenestedselector.md | 18 ++++- ...ed.resolveoptions.ignoreimplicitnesting.md | 13 ++++ .../selector-resolve-nested.resolveoptions.md | 55 +++++++++++++++ packages/selector-resolve-nested/src/index.ts | 1 + .../src/resolve-nested-selector.ts | 12 +++- .../selector-resolve-nested/test/index.mjs | 1 + .../test/resolve-nested-selector/at-scope.mjs | 24 +++++++ 13 files changed, 226 insertions(+), 10 deletions(-) create mode 100644 packages/selector-resolve-nested/docs/selector-resolve-nested.resolveoptions.ignoreimplicitnesting.md create mode 100644 packages/selector-resolve-nested/docs/selector-resolve-nested.resolveoptions.md create mode 100644 packages/selector-resolve-nested/test/resolve-nested-selector/at-scope.mjs diff --git a/packages/selector-resolve-nested/CHANGELOG.md b/packages/selector-resolve-nested/CHANGELOG.md index a54880708..415c3cd15 100644 --- a/packages/selector-resolve-nested/CHANGELOG.md +++ b/packages/selector-resolve-nested/CHANGELOG.md @@ -1,5 +1,9 @@ # Changes to Selector Resolve Nested +### Unreleased (minor) + +- Add `ignoreImplicitNesting` option to `resolveNestedSelector` + ### 3.0.0 _October 23, 2024_ diff --git a/packages/selector-resolve-nested/dist/index.cjs b/packages/selector-resolve-nested/dist/index.cjs index 614a19d15..4b57b9ac4 100644 --- a/packages/selector-resolve-nested/dist/index.cjs +++ b/packages/selector-resolve-nested/dist/index.cjs @@ -1 +1 @@ -"use strict";var e=require("postcss-selector-parser");function sourceFrom(e){return{sourceIndex:e.sourceIndex??0,source:e.source}}function sortCompoundSelectorsInsideComplexSelector(o){const t=[];let r=[];o.each((o=>{if("combinator"===o.type)return t.push(r,[o]),void(r=[]);if(e.isPseudoElement(o))return t.push(r),void(r=[o]);if("universal"===o.type&&r.find((e=>"universal"===e.type)))o.remove();else{if("tag"===o.type&&r.find((e=>"tag"===e.type))){o.remove();const t=e.selector({value:"",...sourceFrom(o)});t.append(o);const n=e.pseudo({value:":is",...sourceFrom(o)});return n.append(t),void r.push(n)}r.push(o)}})),t.push(r);const n=[];for(let e=0;eselectorTypeOrder(e)-selectorTypeOrder(o))),n.push(...o)}o.removeAll();for(let e=n.length-1;e>=0;e--)n[e].remove(),n[e].parent=o,o.nodes.unshift(n[e])}function selectorTypeOrder(t){return e.isPseudoElement(t)?o.pseudoElement:o[t.type]}const o={universal:0,tag:1,pseudoElement:2,nesting:3,id:4,class:5,attribute:6,pseudo:7,comment:8};function prepareParentSelectors(o,t=!1){if(t||!isCompoundSelector(o.nodes)){const t=e.pseudo({value:":is",...sourceFrom(o)});return o.nodes.forEach((e=>{t.append(e.clone())})),[t]}return o.nodes[0].nodes.map((e=>e.clone()))}function isCompoundSelector(o){return 1===o.length&&!o[0].nodes.some((o=>"combinator"===o.type||e.isPseudoElement(o)))}function combinationsWithSizeN(e,o){if(o<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,o)>1e4)throw new Error("Too many combinations when trying to resolve a nested selector with lists, reduce the complexity of your selectors");const t=[];for(let e=0;e=0;s--){let o=t[s];if(o>=e.length){if(o=0,t[s]=0,0===s)return r;t[s-1]+=1}n[s]=e[o].clone()}r.push(n),t[t.length-1]++}}exports.flattenNestedSelector=function flattenNestedSelector(o,t){const r=[];for(let n=0;n{o=!0,l++})),o?"combinator"===s.nodes[0]?.type&&(s.prepend(e.nesting({...sourceFrom(s)})),l++):(s.prepend(e.combinator({value:" ",...sourceFrom(s)})),s.prepend(e.nesting({...sourceFrom(s)})),l++)}let p=[];if(l>1&&t.nodes.length>1)p=combinationsWithSizeN(t.nodes,l),c=p.length;else{c=t.nodes.length;for(let e=0;e{const r=p[e][o];o++,t.replaceWith(...r.nodes)})),r.push(t)}}const n=e.root({value:"",...sourceFrom(o)});return r.forEach((e=>{n.append(e)})),n},exports.resolveNestedSelector=function resolveNestedSelector(o,t){const r=[];for(let n=0;n(o=!0,!1))),o?"combinator"===s.nodes[0]?.type&&s.prepend(e.nesting({...sourceFrom(s)})):(s.prepend(e.combinator({value:" ",...sourceFrom(s)})),s.prepend(e.nesting({...sourceFrom(s)})))}{const e=new Set;s.walkNesting((o=>{const r=o.parent;r&&(e.add(r),"pseudo"===r.parent?.type&&":has"===r.parent.value?.toLowerCase()?o.replaceWith(...prepareParentSelectors(t,!0)):o.replaceWith(...prepareParentSelectors(t)))}));for(const o of e)sortCompoundSelectorsInsideComplexSelector(o)}s.walk((e=>{"combinator"===e.type&&""!==e.value.trim()?(e.rawSpaceAfter=" ",e.rawSpaceBefore=" "):(e.rawSpaceAfter="",e.rawSpaceBefore="")})),r.push(s)}const n=e.root({value:"",...sourceFrom(o)});return r.forEach((e=>{n.append(e)})),n}; +"use strict";var e=require("postcss-selector-parser");function sourceFrom(e){return{sourceIndex:e.sourceIndex??0,source:e.source}}function sortCompoundSelectorsInsideComplexSelector(o){const t=[];let r=[];o.each((o=>{if("combinator"===o.type)return t.push(r,[o]),void(r=[]);if(e.isPseudoElement(o))return t.push(r),void(r=[o]);if("universal"===o.type&&r.find((e=>"universal"===e.type)))o.remove();else{if("tag"===o.type&&r.find((e=>"tag"===e.type))){o.remove();const t=e.selector({value:"",...sourceFrom(o)});t.append(o);const n=e.pseudo({value:":is",...sourceFrom(o)});return n.append(t),void r.push(n)}r.push(o)}})),t.push(r);const n=[];for(let e=0;eselectorTypeOrder(e)-selectorTypeOrder(o))),n.push(...o)}o.removeAll();for(let e=n.length-1;e>=0;e--)n[e].remove(),n[e].parent=o,o.nodes.unshift(n[e])}function selectorTypeOrder(t){return e.isPseudoElement(t)?o.pseudoElement:o[t.type]}const o={universal:0,tag:1,pseudoElement:2,nesting:3,id:4,class:5,attribute:6,pseudo:7,comment:8};function prepareParentSelectors(o,t=!1){if(t||!isCompoundSelector(o.nodes)){const t=e.pseudo({value:":is",...sourceFrom(o)});return o.nodes.forEach((e=>{t.append(e.clone())})),[t]}return o.nodes[0].nodes.map((e=>e.clone()))}function isCompoundSelector(o){return 1===o.length&&!o[0].nodes.some((o=>"combinator"===o.type||e.isPseudoElement(o)))}function combinationsWithSizeN(e,o){if(o<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,o)>1e4)throw new Error("Too many combinations when trying to resolve a nested selector with lists, reduce the complexity of your selectors");const t=[];for(let e=0;e=0;s--){let o=t[s];if(o>=e.length){if(o=0,t[s]=0,0===s)return r;t[s-1]+=1}n[s]=e[o].clone()}r.push(n),t[t.length-1]++}}exports.flattenNestedSelector=function flattenNestedSelector(o,t){const r=[];for(let n=0;n{o=!0,l++})),o?"combinator"===s.nodes[0]?.type&&(s.prepend(e.nesting({...sourceFrom(s)})),l++):(s.prepend(e.combinator({value:" ",...sourceFrom(s)})),s.prepend(e.nesting({...sourceFrom(s)})),l++)}let p=[];if(l>1&&t.nodes.length>1)p=combinationsWithSizeN(t.nodes,l),c=p.length;else{c=t.nodes.length;for(let e=0;e{const r=p[e][o];o++,t.replaceWith(...r.nodes)})),r.push(t)}}const n=e.root({value:"",...sourceFrom(o)});return r.forEach((e=>{n.append(e)})),n},exports.resolveNestedSelector=function resolveNestedSelector(o,t,r){const n=[];for(let s=0;s(o=!0,!1))),o?"combinator"===c.nodes[0]?.type&&c.prepend(e.nesting({...sourceFrom(c)})):(c.prepend(e.combinator({value:" ",...sourceFrom(c)})),c.prepend(e.nesting({...sourceFrom(c)})))}{const e=new Set;c.walkNesting((o=>{const r=o.parent;r&&(e.add(r),"pseudo"===r.parent?.type&&":has"===r.parent.value?.toLowerCase()?o.replaceWith(...prepareParentSelectors(t,!0)):o.replaceWith(...prepareParentSelectors(t)))}));for(const o of e)sortCompoundSelectorsInsideComplexSelector(o)}c.walk((e=>{"combinator"===e.type&&""!==e.value.trim()?(e.rawSpaceAfter=" ",e.rawSpaceBefore=" "):(e.rawSpaceAfter="",e.rawSpaceBefore="")})),n.push(c)}const s=e.root({value:"",...sourceFrom(o)});return n.forEach((e=>{s.append(e)})),s}; diff --git a/packages/selector-resolve-nested/dist/index.d.ts b/packages/selector-resolve-nested/dist/index.d.ts index 4150620b3..dfcae18b4 100644 --- a/packages/selector-resolve-nested/dist/index.d.ts +++ b/packages/selector-resolve-nested/dist/index.d.ts @@ -40,8 +40,16 @@ export declare function flattenNestedSelector(selector: Root, parentSelector: Ro * * @param selector - The selector to resolve. * @param parentSelector - The parent selector to resolve against. + * @param options - Change how resolving happens. * @returns The resolved selector. */ -export declare function resolveNestedSelector(selector: Root, parentSelector: Root): Root; +export declare function resolveNestedSelector(selector: Root, parentSelector: Root, options?: ResolveOptions): Root; + +export declare interface ResolveOptions { + /** + * If implicit `&` selectors should be prepended to the selector before resolving + */ + ignoreImplicitNesting: boolean; +} export { } diff --git a/packages/selector-resolve-nested/dist/index.mjs b/packages/selector-resolve-nested/dist/index.mjs index dea694e84..1bbcf07fe 100644 --- a/packages/selector-resolve-nested/dist/index.mjs +++ b/packages/selector-resolve-nested/dist/index.mjs @@ -1 +1 @@ -import e from"postcss-selector-parser";function sourceFrom(e){return{sourceIndex:e.sourceIndex??0,source:e.source}}function sortCompoundSelectorsInsideComplexSelector(o){const t=[];let r=[];o.each((o=>{if("combinator"===o.type)return t.push(r,[o]),void(r=[]);if(e.isPseudoElement(o))return t.push(r),void(r=[o]);if("universal"===o.type&&r.find((e=>"universal"===e.type)))o.remove();else{if("tag"===o.type&&r.find((e=>"tag"===e.type))){o.remove();const t=e.selector({value:"",...sourceFrom(o)});t.append(o);const n=e.pseudo({value:":is",...sourceFrom(o)});return n.append(t),void r.push(n)}r.push(o)}})),t.push(r);const n=[];for(let e=0;eselectorTypeOrder(e)-selectorTypeOrder(o))),n.push(...o)}o.removeAll();for(let e=n.length-1;e>=0;e--)n[e].remove(),n[e].parent=o,o.nodes.unshift(n[e])}function selectorTypeOrder(t){return e.isPseudoElement(t)?o.pseudoElement:o[t.type]}const o={universal:0,tag:1,pseudoElement:2,nesting:3,id:4,class:5,attribute:6,pseudo:7,comment:8};function resolveNestedSelector(o,t){const r=[];for(let n=0;n(o=!0,!1))),o?"combinator"===s.nodes[0]?.type&&s.prepend(e.nesting({...sourceFrom(s)})):(s.prepend(e.combinator({value:" ",...sourceFrom(s)})),s.prepend(e.nesting({...sourceFrom(s)})))}{const e=new Set;s.walkNesting((o=>{const r=o.parent;r&&(e.add(r),"pseudo"===r.parent?.type&&":has"===r.parent.value?.toLowerCase()?o.replaceWith(...prepareParentSelectors(t,!0)):o.replaceWith(...prepareParentSelectors(t)))}));for(const o of e)sortCompoundSelectorsInsideComplexSelector(o)}s.walk((e=>{"combinator"===e.type&&""!==e.value.trim()?(e.rawSpaceAfter=" ",e.rawSpaceBefore=" "):(e.rawSpaceAfter="",e.rawSpaceBefore="")})),r.push(s)}const n=e.root({value:"",...sourceFrom(o)});return r.forEach((e=>{n.append(e)})),n}function prepareParentSelectors(o,t=!1){if(t||!isCompoundSelector(o.nodes)){const t=e.pseudo({value:":is",...sourceFrom(o)});return o.nodes.forEach((e=>{t.append(e.clone())})),[t]}return o.nodes[0].nodes.map((e=>e.clone()))}function isCompoundSelector(o){return 1===o.length&&!o[0].nodes.some((o=>"combinator"===o.type||e.isPseudoElement(o)))}function combinationsWithSizeN(e,o){if(o<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,o)>1e4)throw new Error("Too many combinations when trying to resolve a nested selector with lists, reduce the complexity of your selectors");const t=[];for(let e=0;e=0;s--){let o=t[s];if(o>=e.length){if(o=0,t[s]=0,0===s)return r;t[s-1]+=1}n[s]=e[o].clone()}r.push(n),t[t.length-1]++}}function flattenNestedSelector(o,t){const r=[];for(let n=0;n{o=!0,l++})),o?"combinator"===s.nodes[0]?.type&&(s.prepend(e.nesting({...sourceFrom(s)})),l++):(s.prepend(e.combinator({value:" ",...sourceFrom(s)})),s.prepend(e.nesting({...sourceFrom(s)})),l++)}let p=[];if(l>1&&t.nodes.length>1)p=combinationsWithSizeN(t.nodes,l),c=p.length;else{c=t.nodes.length;for(let e=0;e{const r=p[e][o];o++,t.replaceWith(...r.nodes)})),r.push(t)}}const n=e.root({value:"",...sourceFrom(o)});return r.forEach((e=>{n.append(e)})),n}export{flattenNestedSelector,resolveNestedSelector}; +import e from"postcss-selector-parser";function sourceFrom(e){return{sourceIndex:e.sourceIndex??0,source:e.source}}function sortCompoundSelectorsInsideComplexSelector(o){const t=[];let r=[];o.each((o=>{if("combinator"===o.type)return t.push(r,[o]),void(r=[]);if(e.isPseudoElement(o))return t.push(r),void(r=[o]);if("universal"===o.type&&r.find((e=>"universal"===e.type)))o.remove();else{if("tag"===o.type&&r.find((e=>"tag"===e.type))){o.remove();const t=e.selector({value:"",...sourceFrom(o)});t.append(o);const n=e.pseudo({value:":is",...sourceFrom(o)});return n.append(t),void r.push(n)}r.push(o)}})),t.push(r);const n=[];for(let e=0;eselectorTypeOrder(e)-selectorTypeOrder(o))),n.push(...o)}o.removeAll();for(let e=n.length-1;e>=0;e--)n[e].remove(),n[e].parent=o,o.nodes.unshift(n[e])}function selectorTypeOrder(t){return e.isPseudoElement(t)?o.pseudoElement:o[t.type]}const o={universal:0,tag:1,pseudoElement:2,nesting:3,id:4,class:5,attribute:6,pseudo:7,comment:8};function resolveNestedSelector(o,t,r){const n=[];for(let s=0;s(o=!0,!1))),o?"combinator"===c.nodes[0]?.type&&c.prepend(e.nesting({...sourceFrom(c)})):(c.prepend(e.combinator({value:" ",...sourceFrom(c)})),c.prepend(e.nesting({...sourceFrom(c)})))}{const e=new Set;c.walkNesting((o=>{const r=o.parent;r&&(e.add(r),"pseudo"===r.parent?.type&&":has"===r.parent.value?.toLowerCase()?o.replaceWith(...prepareParentSelectors(t,!0)):o.replaceWith(...prepareParentSelectors(t)))}));for(const o of e)sortCompoundSelectorsInsideComplexSelector(o)}c.walk((e=>{"combinator"===e.type&&""!==e.value.trim()?(e.rawSpaceAfter=" ",e.rawSpaceBefore=" "):(e.rawSpaceAfter="",e.rawSpaceBefore="")})),n.push(c)}const s=e.root({value:"",...sourceFrom(o)});return n.forEach((e=>{s.append(e)})),s}function prepareParentSelectors(o,t=!1){if(t||!isCompoundSelector(o.nodes)){const t=e.pseudo({value:":is",...sourceFrom(o)});return o.nodes.forEach((e=>{t.append(e.clone())})),[t]}return o.nodes[0].nodes.map((e=>e.clone()))}function isCompoundSelector(o){return 1===o.length&&!o[0].nodes.some((o=>"combinator"===o.type||e.isPseudoElement(o)))}function combinationsWithSizeN(e,o){if(o<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,o)>1e4)throw new Error("Too many combinations when trying to resolve a nested selector with lists, reduce the complexity of your selectors");const t=[];for(let e=0;e=0;s--){let o=t[s];if(o>=e.length){if(o=0,t[s]=0,0===s)return r;t[s-1]+=1}n[s]=e[o].clone()}r.push(n),t[t.length-1]++}}function flattenNestedSelector(o,t){const r=[];for(let n=0;n{o=!0,l++})),o?"combinator"===s.nodes[0]?.type&&(s.prepend(e.nesting({...sourceFrom(s)})),l++):(s.prepend(e.combinator({value:" ",...sourceFrom(s)})),s.prepend(e.nesting({...sourceFrom(s)})),l++)}let p=[];if(l>1&&t.nodes.length>1)p=combinationsWithSizeN(t.nodes,l),c=p.length;else{c=t.nodes.length;for(let e=0;e{const r=p[e][o];o++,t.replaceWith(...r.nodes)})),r.push(t)}}const n=e.root({value:"",...sourceFrom(o)});return r.forEach((e=>{n.append(e)})),n}export{flattenNestedSelector,resolveNestedSelector}; diff --git a/packages/selector-resolve-nested/docs/selector-resolve-nested.api.json b/packages/selector-resolve-nested/docs/selector-resolve-nested.api.json index 2e7da3dbd..6c36f6723 100644 --- a/packages/selector-resolve-nested/docs/selector-resolve-nested.api.json +++ b/packages/selector-resolve-nested/docs/selector-resolve-nested.api.json @@ -238,7 +238,7 @@ { "kind": "Function", "canonicalReference": "@csstools/selector-resolve-nested!resolveNestedSelector:function(1)", - "docComment": "/**\n * Resolve a nested selector against a given parent selector.\n *\n * @param selector - The selector to resolve.\n *\n * @param parentSelector - The parent selector to resolve against.\n *\n * @returns The resolved selector.\n */\n", + "docComment": "/**\n * Resolve a nested selector against a given parent selector.\n *\n * @param selector - The selector to resolve.\n *\n * @param parentSelector - The parent selector to resolve against.\n *\n * @param options - Change how resolving happens.\n *\n * @returns The resolved selector.\n */\n", "excerptTokens": [ { "kind": "Content", @@ -258,6 +258,15 @@ "text": "Root", "canonicalReference": "postcss-selector-parser!parser.Root:interface" }, + { + "kind": "Content", + "text": ", options?: " + }, + { + "kind": "Reference", + "text": "ResolveOptions", + "canonicalReference": "@csstools/selector-resolve-nested!ResolveOptions:interface" + }, { "kind": "Content", "text": "): " @@ -274,8 +283,8 @@ ], "fileUrlPath": "dist/_types/resolve-nested-selector.d.ts", "returnTypeTokenRange": { - "startIndex": 5, - "endIndex": 6 + "startIndex": 7, + "endIndex": 8 }, "releaseTag": "Public", "overloadIndex": 1, @@ -295,9 +304,62 @@ "endIndex": 4 }, "isOptional": false + }, + { + "parameterName": "options", + "parameterTypeTokenRange": { + "startIndex": 5, + "endIndex": 6 + }, + "isOptional": true } ], "name": "resolveNestedSelector" + }, + { + "kind": "Interface", + "canonicalReference": "@csstools/selector-resolve-nested!ResolveOptions:interface", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "export interface ResolveOptions " + } + ], + "fileUrlPath": "dist/_types/resolve-nested-selector.d.ts", + "releaseTag": "Public", + "name": "ResolveOptions", + "preserveMemberOrder": false, + "members": [ + { + "kind": "PropertySignature", + "canonicalReference": "@csstools/selector-resolve-nested!ResolveOptions#ignoreImplicitNesting:member", + "docComment": "/**\n * If implicit `&` selectors should be prepended to the selector before resolving\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "ignoreImplicitNesting: " + }, + { + "kind": "Content", + "text": "boolean" + }, + { + "kind": "Content", + "text": ";" + } + ], + "isReadonly": false, + "isOptional": false, + "releaseTag": "Public", + "name": "ignoreImplicitNesting", + "propertyTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + } + ], + "extendsTokenRanges": [] } ] } diff --git a/packages/selector-resolve-nested/docs/selector-resolve-nested.md b/packages/selector-resolve-nested/docs/selector-resolve-nested.md index 75fde3a03..9f11ce4fa 100644 --- a/packages/selector-resolve-nested/docs/selector-resolve-nested.md +++ b/packages/selector-resolve-nested/docs/selector-resolve-nested.md @@ -50,7 +50,7 @@ Flatten a nested selector against a given parent selector. -[resolveNestedSelector(selector, parentSelector)](./selector-resolve-nested.resolvenestedselector.md) +[resolveNestedSelector(selector, parentSelector, options)](./selector-resolve-nested.resolvenestedselector.md) @@ -58,5 +58,29 @@ Flatten a nested selector against a given parent selector. Resolve a nested selector against a given parent selector. + + + +## Interfaces + + +
+ +Interface + + + + +Description + + +
+ +[ResolveOptions](./selector-resolve-nested.resolveoptions.md) + + + + +
diff --git a/packages/selector-resolve-nested/docs/selector-resolve-nested.resolvenestedselector.md b/packages/selector-resolve-nested/docs/selector-resolve-nested.resolvenestedselector.md index fe220ba89..4d0ad1524 100644 --- a/packages/selector-resolve-nested/docs/selector-resolve-nested.resolvenestedselector.md +++ b/packages/selector-resolve-nested/docs/selector-resolve-nested.resolvenestedselector.md @@ -9,7 +9,7 @@ Resolve a nested selector against a given parent selector. **Signature:** ```typescript -export declare function resolveNestedSelector(selector: Root, parentSelector: Root): Root; +export declare function resolveNestedSelector(selector: Root, parentSelector: Root, options?: ResolveOptions): Root; ``` ## Parameters @@ -61,6 +61,22 @@ Root The parent selector to resolve against. + + + +options + + + + +[ResolveOptions](./selector-resolve-nested.resolveoptions.md) + + + + +_(Optional)_ Change how resolving happens. + + **Returns:** diff --git a/packages/selector-resolve-nested/docs/selector-resolve-nested.resolveoptions.ignoreimplicitnesting.md b/packages/selector-resolve-nested/docs/selector-resolve-nested.resolveoptions.ignoreimplicitnesting.md new file mode 100644 index 000000000..93bc165e8 --- /dev/null +++ b/packages/selector-resolve-nested/docs/selector-resolve-nested.resolveoptions.ignoreimplicitnesting.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@csstools/selector-resolve-nested](./selector-resolve-nested.md) > [ResolveOptions](./selector-resolve-nested.resolveoptions.md) > [ignoreImplicitNesting](./selector-resolve-nested.resolveoptions.ignoreimplicitnesting.md) + +## ResolveOptions.ignoreImplicitNesting property + +If implicit `&` selectors should be prepended to the selector before resolving + +**Signature:** + +```typescript +ignoreImplicitNesting: boolean; +``` diff --git a/packages/selector-resolve-nested/docs/selector-resolve-nested.resolveoptions.md b/packages/selector-resolve-nested/docs/selector-resolve-nested.resolveoptions.md new file mode 100644 index 000000000..1884c8e9e --- /dev/null +++ b/packages/selector-resolve-nested/docs/selector-resolve-nested.resolveoptions.md @@ -0,0 +1,55 @@ + + +[Home](./index.md) > [@csstools/selector-resolve-nested](./selector-resolve-nested.md) > [ResolveOptions](./selector-resolve-nested.resolveoptions.md) + +## ResolveOptions interface + +**Signature:** + +```typescript +export interface ResolveOptions +``` + +## Properties + + + +
+ +Property + + + + +Modifiers + + + + +Type + + + + +Description + + +
+ +[ignoreImplicitNesting](./selector-resolve-nested.resolveoptions.ignoreimplicitnesting.md) + + + + + + + +boolean + + + + +If implicit `&` selectors should be prepended to the selector before resolving + + +
diff --git a/packages/selector-resolve-nested/src/index.ts b/packages/selector-resolve-nested/src/index.ts index a29413e52..4519694d1 100644 --- a/packages/selector-resolve-nested/src/index.ts +++ b/packages/selector-resolve-nested/src/index.ts @@ -19,5 +19,6 @@ * @packageDocumentation */ +export type { ResolveOptions } from './resolve-nested-selector'; export { resolveNestedSelector } from './resolve-nested-selector'; export { flattenNestedSelector } from './flatten-nested-selector'; diff --git a/packages/selector-resolve-nested/src/resolve-nested-selector.ts b/packages/selector-resolve-nested/src/resolve-nested-selector.ts index 0424900a7..c07636282 100644 --- a/packages/selector-resolve-nested/src/resolve-nested-selector.ts +++ b/packages/selector-resolve-nested/src/resolve-nested-selector.ts @@ -3,20 +3,28 @@ import parser from 'postcss-selector-parser'; import { sortCompoundSelectorsInsideComplexSelector } from './compound-selector-order'; import { sourceFrom } from './source'; +export interface ResolveOptions { + /** + * If implicit `&` selectors should be prepended to the selector before resolving + */ + ignoreImplicitNesting: boolean; +} + /** * Resolve a nested selector against a given parent selector. * * @param selector - The selector to resolve. * @param parentSelector - The parent selector to resolve against. + * @param options - Change how resolving happens. * @returns The resolved selector. */ -export function resolveNestedSelector(selector: Root, parentSelector: Root): Root { +export function resolveNestedSelector(selector: Root, parentSelector: Root, options?: ResolveOptions): Root { const result: Array = []; for (let x = 0; x < selector.nodes.length; x++) { const selectorAST = selector.nodes[x].clone(); - { + if (!options?.ignoreImplicitNesting) { let isNestContaining = false; selectorAST.walkNesting(() => { isNestContaining = true; diff --git a/packages/selector-resolve-nested/test/index.mjs b/packages/selector-resolve-nested/test/index.mjs index ed79e8168..748b01ac8 100644 --- a/packages/selector-resolve-nested/test/index.mjs +++ b/packages/selector-resolve-nested/test/index.mjs @@ -1,6 +1,7 @@ import './complex.mjs'; import './compound.mjs'; +import './resolve-nested-selector/at-scope.mjs'; import './resolve-nested-selector/lists.mjs'; import './resolve-nested-selector/multiple-resolves.mjs'; import './resolve-nested-selector/parent.mjs'; diff --git a/packages/selector-resolve-nested/test/resolve-nested-selector/at-scope.mjs b/packages/selector-resolve-nested/test/resolve-nested-selector/at-scope.mjs new file mode 100644 index 000000000..c7ff5818a --- /dev/null +++ b/packages/selector-resolve-nested/test/resolve-nested-selector/at-scope.mjs @@ -0,0 +1,24 @@ +import assert from 'node:assert/strict'; +import { test, describe } from 'node:test'; +import { resolveNestedSelector } from '@csstools/selector-resolve-nested'; +import parser from 'postcss-selector-parser'; + +describe('at-scope', () => { + test('explicit &', () => { + const a = parser().astSync('.a'); + const b = parser().astSync('& .b'); + + const ba = resolveNestedSelector(b, a, { ignoreImplicitNesting: true }); + + assert.equal(ba.toString(), '.a .b'); + }); + + test('implicit &', () => { + const a = parser().astSync('.a'); + const b = parser().astSync('.b'); + + const ba = resolveNestedSelector(b, a, { ignoreImplicitNesting: true }); + + assert.equal(ba.toString(), '.b'); + }); +});