-
-
Notifications
You must be signed in to change notification settings - Fork 73
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
34e9e28
commit 67acd34
Showing
24 changed files
with
803 additions
and
170 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 r=[];let t=[];o.each((o=>{if("combinator"===o.type)return r.push(t,[o]),void(t=[]);if(e.isPseudoElement(o))return r.push(t),void(t=[o]);if("universal"===o.type&&t.find((e=>"universal"===e.type)))o.remove();else{if("tag"===o.type&&t.find((e=>"tag"===e.type))){o.remove();const r=e.selector({value:"",...sourceFrom(o)});r.append(o);const n=e.pseudo({value:":is",...sourceFrom(o)});return n.append(r),void t.push(n)}t.push(o)}})),r.push(t);const n=[];for(let e=0;e<r.length;e++){const o=r[e];o.sort(((e,o)=>selectorTypeOrder(e)-selectorTypeOrder(o))),n.push(...o)}o.removeAll();for(let e=n.length-1;e>=0;e--)n[e].remove(),o.prepend(n[e])}function selectorTypeOrder(r){return e.isPseudoElement(r)?o.pseudoElement:o[r.type]}const o={universal:0,tag:1,pseudoElement:2,nesting:3,id:4,class:5,attribute:6,pseudo:7,comment:8};function prepareParentSelectors(o,r=!1){if(r||!isCompoundSelector(o.nodes)){const r=e.pseudo({value:":is",...sourceFrom(o)});return o.nodes.forEach((e=>{r.append(e.clone())})),[r]}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)))}exports.resolveNestedSelector=function resolveNestedSelector(o,r){const t=[];for(let n=0;n<o.nodes.length;n++){const s=o.nodes[n].clone();{let o=!1;s.walkNesting((()=>(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 t=o.parent;e.add(t),"pseudo"===t.parent?.type&&":has"===t.parent.value?.toLowerCase()?o.replaceWith(...prepareParentSelectors(r,!0)):o.replaceWith(...prepareParentSelectors(r))}));for(const o of e)sortCompoundSelectorsInsideComplexSelector(o)}s.walk((e=>{"combinator"===e.type&&""!==e.value.trim()?(e.rawSpaceAfter=" ",e.rawSpaceBefore=" "):(e.rawSpaceAfter="",e.rawSpaceBefore="")})),t.push(s)}const n=e.root({value:"",...sourceFrom(o)});return t.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;e<t.length;e++){const o=t[e];o.sort(((e,o)=>selectorTypeOrder(e)-selectorTypeOrder(o))),n.push(...o)}o.removeAll();for(let e=n.length-1;e>=0;e--)n[e].remove(),o.prepend(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<o;e++)t[e]=0;const r=[];for(;;){const n=[];for(let s=o-1;s>=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.nodes.length;n++){const s=o.nodes[n].clone();let c,l=0;{let o=!1;s.walkNesting((()=>{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<t.nodes.length;e++){p.push([]);for(let o=0;o<l;o++)p[e].push(t.nodes[e].clone())}}for(let e=0;e<c;e++){let o=0;const t=s.clone();t.walkNesting((t=>{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.nodes.length;n++){const s=o.nodes[n].clone();{let o=!1;s.walkNesting((()=>(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;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}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
import e from"postcss-selector-parser";function sourceFrom(e){return{sourceIndex:e.sourceIndex??0,source:e.source}}function sortCompoundSelectorsInsideComplexSelector(o){const r=[];let t=[];o.each((o=>{if("combinator"===o.type)return r.push(t,[o]),void(t=[]);if(e.isPseudoElement(o))return r.push(t),void(t=[o]);if("universal"===o.type&&t.find((e=>"universal"===e.type)))o.remove();else{if("tag"===o.type&&t.find((e=>"tag"===e.type))){o.remove();const r=e.selector({value:"",...sourceFrom(o)});r.append(o);const n=e.pseudo({value:":is",...sourceFrom(o)});return n.append(r),void t.push(n)}t.push(o)}})),r.push(t);const n=[];for(let e=0;e<r.length;e++){const o=r[e];o.sort(((e,o)=>selectorTypeOrder(e)-selectorTypeOrder(o))),n.push(...o)}o.removeAll();for(let e=n.length-1;e>=0;e--)n[e].remove(),o.prepend(n[e])}function selectorTypeOrder(r){return e.isPseudoElement(r)?o.pseudoElement:o[r.type]}const o={universal:0,tag:1,pseudoElement:2,nesting:3,id:4,class:5,attribute:6,pseudo:7,comment:8};function resolveNestedSelector(o,r){const t=[];for(let n=0;n<o.nodes.length;n++){const s=o.nodes[n].clone();{let o=!1;s.walkNesting((()=>(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 t=o.parent;e.add(t),"pseudo"===t.parent?.type&&":has"===t.parent.value?.toLowerCase()?o.replaceWith(...prepareParentSelectors(r,!0)):o.replaceWith(...prepareParentSelectors(r))}));for(const o of e)sortCompoundSelectorsInsideComplexSelector(o)}s.walk((e=>{"combinator"===e.type&&""!==e.value.trim()?(e.rawSpaceAfter=" ",e.rawSpaceBefore=" "):(e.rawSpaceAfter="",e.rawSpaceBefore="")})),t.push(s)}const n=e.root({value:"",...sourceFrom(o)});return t.forEach((e=>{n.append(e)})),n}function prepareParentSelectors(o,r=!1){if(r||!isCompoundSelector(o.nodes)){const r=e.pseudo({value:":is",...sourceFrom(o)});return o.nodes.forEach((e=>{r.append(e.clone())})),[r]}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)))}export{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;e<t.length;e++){const o=t[e];o.sort(((e,o)=>selectorTypeOrder(e)-selectorTypeOrder(o))),n.push(...o)}o.removeAll();for(let e=n.length-1;e>=0;e--)n[e].remove(),o.prepend(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.nodes.length;n++){const s=o.nodes[n].clone();{let o=!1;s.walkNesting((()=>(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;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<o;e++)t[e]=0;const r=[];for(;;){const n=[];for(let s=o-1;s>=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.nodes.length;n++){const s=o.nodes[n].clone();let c,l=0;{let o=!1;s.walkNesting((()=>{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<t.nodes.length;e++){p.push([]);for(let o=0;o<l;o++)p[e].push(t.nodes[e].clone())}}for(let e=0;e<c;e++){let o=0;const t=s.clone();t.walkNesting((t=>{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}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
29 changes: 29 additions & 0 deletions
29
...s/selector-resolve-nested/docs/selector-resolve-nested.flattennestedselector.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
<!-- Do not edit this file. It is automatically generated by API Documenter. --> | ||
|
||
[Home](./index.md) > [@csstools/selector-resolve-nested](./selector-resolve-nested.md) > [flattenNestedSelector](./selector-resolve-nested.flattennestedselector.md) | ||
|
||
## flattenNestedSelector() function | ||
|
||
Flatten a nested selector against a given parent selector. | ||
|
||
⚠️ This is not a method to generate the equivalent un-nested selector. It is purely a method to construct a single selector AST that contains the parts of both the current and parent selector. It will not have the correct specificity and it will not match the right elements when used as a selector. It will not always serialize to a valid selector. | ||
|
||
**Signature:** | ||
|
||
```typescript | ||
export declare function flattenNestedSelector(selector: Root, parentSelector: Root): Root; | ||
``` | ||
|
||
## Parameters | ||
|
||
| Parameter | Type | Description | | ||
| --- | --- | --- | | ||
| selector | Root | The selector to resolve. | | ||
| parentSelector | Root | The parent selector to resolve against. | | ||
|
||
**Returns:** | ||
|
||
Root | ||
|
||
The resolved selector. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import type { Selector } from 'postcss-selector-parser'; | ||
|
||
export function combinationsWithSizeN(set: Array<Selector>, n: number): Array<Array<Selector>> { | ||
// set is the list of parent selectors | ||
// n is the amount of `&` selectors in the current selector. | ||
// all combinations of values in the set with an array size of n must be generated to match the nesting selector behavior. | ||
// | ||
// for example : | ||
// a current selector like: `& + & {}` | ||
// with parent : `.foo, .bar {}` | ||
// | ||
// the set is `['.foo', '.bar']` and n is 2, the resulting combinations are: | ||
// ['.foo', '.bar'] | ||
// ['.foo', '.foo'] | ||
// ['.bar', '.foo'] | ||
// ['.bar', '.bar'] | ||
// | ||
// outputted like : | ||
// .foo + .bar, | ||
// .foo + .foo, | ||
// .bar + .foo, | ||
// .bar + .bar {} | ||
|
||
|
||
/* node:coverage disable */ | ||
if (n < 2) { | ||
// should never happen and is checked by caller | ||
throw new Error('n must be greater than 1'); | ||
} | ||
|
||
if (set.length < 2) { | ||
// should never happen and is checked by caller | ||
throw new Error('s must be greater than 1'); | ||
} | ||
|
||
if (Math.pow(set.length, n) > 10000) { | ||
// Throwing is best here as a warning would be impossible to handle gracefully on our end. | ||
// This will error mid transform and there is no possible fallback at this point. | ||
// The user should reduce complexity. | ||
throw new Error('Too many combinations when trying to resolve a nested selector with lists, reduce the complexity of your selectors'); | ||
} | ||
/* node:coverage enable */ | ||
|
||
const counters: Array<number> = []; | ||
|
||
for (let i = 0; i < n; i++) { | ||
counters[i] = 0; | ||
} | ||
|
||
const result: Array<Array<Selector>> = []; | ||
|
||
// eslint-disable-next-line no-constant-condition | ||
while (true) { | ||
const ss: Array<Selector> = []; | ||
for (let i = n - 1; i >= 0; i--) { | ||
let currentCounter = counters[i]; | ||
if (currentCounter >= set.length) { | ||
currentCounter = 0; | ||
counters[i] = 0; | ||
|
||
if (i === 0) { | ||
return result; | ||
} else { | ||
counters[i - 1] += 1; | ||
} | ||
} | ||
|
||
ss[i] = set[currentCounter].clone(); | ||
} | ||
|
||
result.push(ss); | ||
counters[counters.length - 1]++; | ||
} | ||
} |
Oops, something went wrong.