Website Structure

This commit is contained in:
supalerk-ar66 2026-01-13 10:46:40 +07:00
parent 62812f2090
commit 71f0676a62
22365 changed files with 4265753 additions and 791 deletions

View file

@ -0,0 +1,9 @@
# Changes to Selector Resolve Nested
### 3.1.0
_June 6, 2025_
- Add `ignoreImplicitNesting` option to `resolveNestedSelector`
[Full CHANGELOG](https://github.com/csstools/postcss-plugins/tree/main/packages/selector-resolve-nested/CHANGELOG.md)

View file

@ -0,0 +1,18 @@
MIT No Attribution (MIT-0)
Copyright © CSSTools Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the “Software”), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,34 @@
# Selector Resolve Nested [<img src="https://postcss.github.io/postcss/logo.svg" alt="for PostCSS" width="90" height="90" align="right">][postcss]
[<img alt="npm version" src="https://img.shields.io/npm/v/@csstools/selector-resolve-nested.svg" height="20">][npm-url]
[<img alt="Build Status" src="https://github.com/csstools/postcss-plugins/actions/workflows/test.yml/badge.svg?branch=main" height="20">][cli-url]
[<img alt="Discord" src="https://shields.io/badge/Discord-5865F2?logo=discord&logoColor=white">][discord]
## API
[Read the API docs](./docs/selector-resolve-nested.md)
## Usage
Add [Selector Resolve Nested] to your project:
```bash
npm install @csstools/selector-resolve-nested --save-dev
```
```js
import { resolveNestedSelector } from '@csstools/selector-resolve-nested';
import parser from 'postcss-selector-parser';
const a = parser().astSync('.foo &');
const b = parser().astSync('.bar');
resolveNestedSelector(a, b); // '.foo .bar'
```
[cli-url]: https://github.com/csstools/postcss-plugins/actions/workflows/test.yml?query=workflow/test
[discord]: https://discord.gg/bUadyRwkJS
[npm-url]: https://www.npmjs.com/package/@csstools/selector-resolve-nested
[postcss]: https://github.com/postcss/postcss
[Selector Resolve Nested]: https://github.com/csstools/postcss-plugins/tree/main/packages/selector-resolve-nested

View file

@ -0,0 +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;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(),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<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,r){const n=[];for(let s=0;s<o.nodes.length;s++){const c=o.nodes[s].clone();if(!r?.ignoreImplicitNesting){let o=!1;c.walkNesting((()=>(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};

View file

@ -0,0 +1,55 @@
/**
* Resolve nested selectors following the CSS nesting specification.
*
* @example
*
* ```js
* import { resolveNestedSelector } from '@csstools/selector-resolve-nested';
* import parser from 'postcss-selector-parser';
*
* const selector = parser().astSync('.foo &');
* const parent = parser().astSync('.bar');
*
* // .foo .bar
* console.log(
* resolveNestedSelector(selector, parent).toString()
* )
* ```
*
* @packageDocumentation
*/
import type { Root } from 'postcss-selector-parser';
/**
* 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.
*
* @param selector - The selector to resolve.
* @param parentSelector - The parent selector to resolve against.
* @returns The resolved selector.
*/
export declare function flattenNestedSelector(selector: Root, parentSelector: Root): Root;
/**
* 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 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 { }

View file

@ -0,0 +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;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(),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.nodes.length;s++){const c=o.nodes[s].clone();if(!r?.ignoreImplicitNesting){let o=!1;c.walkNesting((()=>(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<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};

View file

@ -0,0 +1,66 @@
{
"name": "@csstools/selector-resolve-nested",
"description": "Resolve nested CSS selectors",
"version": "3.1.0",
"contributors": [
{
"name": "Antonio Laguna",
"email": "antonio@laguna.es",
"url": "https://antonio.laguna.es"
},
{
"name": "Romain Menke",
"email": "romainmenke@gmail.com"
}
],
"license": "MIT-0",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/csstools"
},
{
"type": "opencollective",
"url": "https://opencollective.com/csstools"
}
],
"engines": {
"node": ">=18"
},
"type": "module",
"main": "dist/index.cjs",
"module": "dist/index.mjs",
"exports": {
".": {
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.mjs"
},
"require": {
"default": "./dist/index.cjs"
}
}
},
"files": [
"CHANGELOG.md",
"LICENSE.md",
"README.md",
"dist"
],
"peerDependencies": {
"postcss-selector-parser": "^7.0.0"
},
"scripts": {},
"homepage": "https://github.com/csstools/postcss-plugins/tree/main/packages/selector-resolve-nested#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/csstools/postcss-plugins.git",
"directory": "packages/selector-resolve-nested"
},
"bugs": "https://github.com/csstools/postcss-plugins/issues",
"keywords": [
"css",
"nested",
"postcss-selector-parser"
]
}

View file

@ -0,0 +1,9 @@
# Changes to Selector Specificity
### 5.0.0
_October 23, 2024_
- Updated: `postcss-selector-parser`
[Full CHANGELOG](https://github.com/csstools/postcss-plugins/tree/main/packages/selector-specificity/CHANGELOG.md)

View file

@ -0,0 +1,18 @@
MIT No Attribution (MIT-0)
Copyright © CSSTools Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the “Software”), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,57 @@
# Selector Specificity
[<img alt="npm version" src="https://img.shields.io/npm/v/@csstools/selector-specificity.svg" height="20">][npm-url]
[<img alt="Build Status" src="https://github.com/csstools/postcss-plugins/workflows/test/badge.svg" height="20">][cli-url]
[<img alt="Discord" src="https://shields.io/badge/Discord-5865F2?logo=discord&logoColor=white">][discord]
## Usage
Add [Selector Specificity] to your project:
```bash
npm install @csstools/selector-specificity --save-dev
```
```js
import parser from 'postcss-selector-parser';
import { selectorSpecificity } from '@csstools/selector-specificity';
const selectorAST = parser().astSync('#foo:has(> .foo)');
const specificity = selectorSpecificity(selectorAST);
console.log(specificity.a); // 1
console.log(specificity.b); // 1
console.log(specificity.c); // 0
```
_`selectorSpecificity` takes a single selector, not a list of selectors (not : `a, b, c`).
To compare or otherwise manipulate lists of selectors you need to call `selectorSpecificity` on each part._
### Comparing
The package exports a utility function to compare two specificities.
```js
import { selectorSpecificity, compare } from '@csstools/selector-specificity';
const s1 = selectorSpecificity(ast1);
const s2 = selectorSpecificity(ast2);
compare(s1, s2); // -1 | 0 | 1
```
- if `s1 < s2` then `compare(s1, s2)` returns a negative number (`< 0`)
- if `s1 > s2` then `compare(s1, s2)` returns a positive number (`> 0`)
- if `s1 === s2` then `compare(s1, s2)` returns zero (`=== 0`)
## Prior Art
- [keeganstreet/specificity](https://github.com/keeganstreet/specificity)
- [bramus/specificity](https://github.com/bramus/specificity)
For CSSTools we always use `postcss-selector-parser` and want to calculate specificity from this AST.
[cli-url]: https://github.com/csstools/postcss-plugins/actions/workflows/test.yml?query=workflow/test
[discord]: https://discord.gg/bUadyRwkJS
[npm-url]: https://www.npmjs.com/package/@csstools/selector-specificity
[Selector Specificity]: https://github.com/csstools/postcss-plugins/tree/main/packages/selector-specificity

View file

@ -0,0 +1 @@
"use strict";var e=require("postcss-selector-parser");function compare(e,t){return e.a===t.a?e.b===t.b?e.c-t.c:e.b-t.b:e.a-t.a}function selectorSpecificity(t,s){const i=s?.customSpecificity?.(t);if(i)return i;if(!t)return{a:0,b:0,c:0};let c=0,n=0,o=0;if("universal"==t.type)return{a:0,b:0,c:0};if("id"===t.type)c+=1;else if("tag"===t.type)o+=1;else if("class"===t.type)n+=1;else if("attribute"===t.type)n+=1;else if(isPseudoElement(t))switch(t.value.toLowerCase()){case"::slotted":if(o+=1,t.nodes&&t.nodes.length>0){const e=specificityOfMostSpecificListItem(t.nodes,s);c+=e.a,n+=e.b,o+=e.c}break;case"::view-transition-group":case"::view-transition-image-pair":case"::view-transition-old":case"::view-transition-new":return t.nodes&&1===t.nodes.length&&"selector"===t.nodes[0].type&&selectorNodeContainsNothingOrOnlyUniversal(t.nodes[0])?{a:0,b:0,c:0}:{a:0,b:0,c:1};default:o+=1}else if(e.isPseudoClass(t))switch(t.value.toLowerCase()){case":-webkit-any":case":any":default:n+=1;break;case":-moz-any":case":has":case":is":case":matches":case":not":if(t.nodes&&t.nodes.length>0){const e=specificityOfMostSpecificListItem(t.nodes,s);c+=e.a,n+=e.b,o+=e.c}break;case":where":break;case":nth-child":case":nth-last-child":if(n+=1,t.nodes&&t.nodes.length>0){const i=t.nodes[0].nodes.findIndex((e=>"tag"===e.type&&"of"===e.value.toLowerCase()));if(i>-1){const a=e.selector({nodes:[],value:""});t.nodes[0].nodes.slice(i+1).forEach((e=>{a.append(e.clone())}));const r=[a];t.nodes.length>1&&r.push(...t.nodes.slice(1));const l=specificityOfMostSpecificListItem(r,s);c+=l.a,n+=l.b,o+=l.c}}break;case":local":case":global":t.nodes&&t.nodes.length>0&&t.nodes.forEach((e=>{const t=selectorSpecificity(e,s);c+=t.a,n+=t.b,o+=t.c}));break;case":host":case":host-context":if(n+=1,t.nodes&&t.nodes.length>0){const e=specificityOfMostSpecificListItem(t.nodes,s);c+=e.a,n+=e.b,o+=e.c}break;case":active-view-transition":case":active-view-transition-type":return{a:0,b:1,c:0}}else e.isContainer(t)&&t.nodes?.length>0&&t.nodes.forEach((e=>{const t=selectorSpecificity(e,s);c+=t.a,n+=t.b,o+=t.c}));return{a:c,b:n,c:o}}function specificityOfMostSpecificListItem(e,t){let s={a:0,b:0,c:0};return e.forEach((e=>{const i=selectorSpecificity(e,t);compare(i,s)<0||(s=i)})),s}function isPseudoElement(t){return e.isPseudoElement(t)}function selectorNodeContainsNothingOrOnlyUniversal(e){if(!e)return!1;if(!e.nodes)return!1;const t=e.nodes.filter((e=>"comment"!==e.type));return 0===t.length||1===t.length&&"universal"===t[0].type}exports.compare=compare,exports.selectorSpecificity=selectorSpecificity,exports.specificityOfMostSpecificListItem=specificityOfMostSpecificListItem;

View file

@ -0,0 +1,58 @@
import type { Node } from 'postcss-selector-parser';
/**
* Options for the calculation of the specificity of a selector
*/
export declare type CalculationOptions = {
/**
* The callback to calculate a custom specificity for a node
*/
customSpecificity?: CustomSpecificityCallback;
};
/**
* Compare two specificities
* @param s1 The first specificity
* @param s2 The second specificity
* @returns A value smaller than `0` if `s1` is less specific than `s2`, `0` if `s1` is equally specific as `s2`, a value larger than `0` if `s1` is more specific than `s2`
*/
export declare function compare(s1: Specificity, s2: Specificity): number;
/**
* Calculate a custom specificity for a node
*/
export declare type CustomSpecificityCallback = (node: Node) => Specificity | void | false | null | undefined;
/**
* Calculate the specificity for a selector
*/
export declare function selectorSpecificity(node: Node, options?: CalculationOptions): Specificity;
/**
* The specificity of a selector
*/
export declare type Specificity = {
/**
* The number of ID selectors in the selector
*/
a: number;
/**
* The number of class selectors, attribute selectors, and pseudo-classes in the selector
*/
b: number;
/**
* The number of type selectors and pseudo-elements in the selector
*/
c: number;
};
/**
* Calculate the most specific selector in a list
*/
export declare function specificityOfMostSpecificListItem(nodes: Array<Node>, options?: CalculationOptions): {
a: number;
b: number;
c: number;
};
export { }

View file

@ -0,0 +1 @@
import e from"postcss-selector-parser";function compare(e,t){return e.a===t.a?e.b===t.b?e.c-t.c:e.b-t.b:e.a-t.a}function selectorSpecificity(t,s){const i=s?.customSpecificity?.(t);if(i)return i;if(!t)return{a:0,b:0,c:0};let c=0,n=0,o=0;if("universal"==t.type)return{a:0,b:0,c:0};if("id"===t.type)c+=1;else if("tag"===t.type)o+=1;else if("class"===t.type)n+=1;else if("attribute"===t.type)n+=1;else if(isPseudoElement(t))switch(t.value.toLowerCase()){case"::slotted":if(o+=1,t.nodes&&t.nodes.length>0){const e=specificityOfMostSpecificListItem(t.nodes,s);c+=e.a,n+=e.b,o+=e.c}break;case"::view-transition-group":case"::view-transition-image-pair":case"::view-transition-old":case"::view-transition-new":return t.nodes&&1===t.nodes.length&&"selector"===t.nodes[0].type&&selectorNodeContainsNothingOrOnlyUniversal(t.nodes[0])?{a:0,b:0,c:0}:{a:0,b:0,c:1};default:o+=1}else if(e.isPseudoClass(t))switch(t.value.toLowerCase()){case":-webkit-any":case":any":default:n+=1;break;case":-moz-any":case":has":case":is":case":matches":case":not":if(t.nodes&&t.nodes.length>0){const e=specificityOfMostSpecificListItem(t.nodes,s);c+=e.a,n+=e.b,o+=e.c}break;case":where":break;case":nth-child":case":nth-last-child":if(n+=1,t.nodes&&t.nodes.length>0){const i=t.nodes[0].nodes.findIndex((e=>"tag"===e.type&&"of"===e.value.toLowerCase()));if(i>-1){const a=e.selector({nodes:[],value:""});t.nodes[0].nodes.slice(i+1).forEach((e=>{a.append(e.clone())}));const r=[a];t.nodes.length>1&&r.push(...t.nodes.slice(1));const l=specificityOfMostSpecificListItem(r,s);c+=l.a,n+=l.b,o+=l.c}}break;case":local":case":global":t.nodes&&t.nodes.length>0&&t.nodes.forEach((e=>{const t=selectorSpecificity(e,s);c+=t.a,n+=t.b,o+=t.c}));break;case":host":case":host-context":if(n+=1,t.nodes&&t.nodes.length>0){const e=specificityOfMostSpecificListItem(t.nodes,s);c+=e.a,n+=e.b,o+=e.c}break;case":active-view-transition":case":active-view-transition-type":return{a:0,b:1,c:0}}else e.isContainer(t)&&t.nodes?.length>0&&t.nodes.forEach((e=>{const t=selectorSpecificity(e,s);c+=t.a,n+=t.b,o+=t.c}));return{a:c,b:n,c:o}}function specificityOfMostSpecificListItem(e,t){let s={a:0,b:0,c:0};return e.forEach((e=>{const i=selectorSpecificity(e,t);compare(i,s)<0||(s=i)})),s}function isPseudoElement(t){return e.isPseudoElement(t)}function selectorNodeContainsNothingOrOnlyUniversal(e){if(!e)return!1;if(!e.nodes)return!1;const t=e.nodes.filter((e=>"comment"!==e.type));return 0===t.length||1===t.length&&"universal"===t[0].type}export{compare,selectorSpecificity,specificityOfMostSpecificListItem};

View file

@ -0,0 +1,66 @@
{
"name": "@csstools/selector-specificity",
"description": "Determine selector specificity with postcss-selector-parser",
"version": "5.0.0",
"contributors": [
{
"name": "Antonio Laguna",
"email": "antonio@laguna.es",
"url": "https://antonio.laguna.es"
},
{
"name": "Romain Menke",
"email": "romainmenke@gmail.com"
}
],
"license": "MIT-0",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/csstools"
},
{
"type": "opencollective",
"url": "https://opencollective.com/csstools"
}
],
"engines": {
"node": ">=18"
},
"type": "module",
"main": "dist/index.cjs",
"module": "dist/index.mjs",
"exports": {
".": {
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.mjs"
},
"require": {
"default": "./dist/index.cjs"
}
}
},
"files": [
"CHANGELOG.md",
"LICENSE.md",
"README.md",
"dist"
],
"peerDependencies": {
"postcss-selector-parser": "^7.0.0"
},
"scripts": {},
"homepage": "https://github.com/csstools/postcss-plugins/tree/main/packages/selector-specificity#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/csstools/postcss-plugins.git",
"directory": "packages/selector-specificity"
},
"bugs": "https://github.com/csstools/postcss-plugins/issues",
"keywords": [
"css",
"postcss-selector-parser",
"specificity"
]
}