first commit

This commit is contained in:
2025-03-07 22:27:18 +08:00
commit 912da26042
4457 changed files with 306818 additions and 0 deletions

View File

@@ -0,0 +1,9 @@
# Changes to CSS Parser Algorithms
### 3.0.4
_November 1, 2024_
- Updated [`@csstools/css-tokenizer`](https://github.com/csstools/postcss-plugins/tree/main/packages/css-tokenizer) to [`3.0.3`](https://github.com/csstools/postcss-plugins/tree/main/packages/css-tokenizer/CHANGELOG.md#303) (patch)
[Full CHANGELOG](https://github.com/csstools/postcss-plugins/tree/main/packages/css-parser-algorithms/CHANGELOG.md)

View File

@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright 2022 Romain Menke, Antonio Laguna <antonio@laguna.es>
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,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
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.

119
node_modules/@csstools/css-parser-algorithms/README.md generated vendored Normal file
View File

@@ -0,0 +1,119 @@
# CSS Parser Algorithms
[<img alt="npm version" src="https://img.shields.io/npm/v/@csstools/css-parser-algorithms.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]
Implemented from : https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/
## API
[Read the API docs](./docs/css-parser-algorithms.md)
## Usage
Add [CSS Parser Algorithms] to your project:
```bash
npm install @csstools/css-parser-algorithms @csstools/css-tokenizer --save-dev
```
[CSS Parser Algorithms] only accepts tokenized CSS.
It must be used together with `@csstools/css-tokenizer`.
```js
import { tokenizer, TokenType } from '@csstools/css-tokenizer';
import { parseComponentValue } from '@csstools/css-parser-algorithms';
const myCSS = `@media only screen and (min-width: 768rem) {
.foo {
content: 'Some content!' !important;
}
}
`;
const t = tokenizer({
css: myCSS,
});
const tokens = [];
{
while (!t.endOfFile()) {
tokens.push(t.nextToken());
}
tokens.push(t.nextToken()); // EOF-token
}
const options = {
onParseError: ((err) => {
throw err;
}),
};
const result = parseComponentValue(tokens, options);
console.log(result);
```
### Available functions
- [`parseComponentValue`](https://www.w3.org/TR/css-syntax-3/#parse-component-value)
- [`parseListOfComponentValues`](https://www.w3.org/TR/css-syntax-3/#parse-list-of-component-values)
- [`parseCommaSeparatedListOfComponentValues`](https://www.w3.org/TR/css-syntax-3/#parse-comma-separated-list-of-component-values)
### Utilities
#### `gatherNodeAncestry`
The AST does not expose the entire ancestry of each node.
The walker methods do provide access to the current parent, but also not the entire ancestry.
To gather the entire ancestry for a a given sub tree of the AST you can use `gatherNodeAncestry`.
The result is a `Map` with the child nodes as keys and the parents as values.
This allows you to lookup any ancestor of any node.
```js
import { parseComponentValue } from '@csstools/css-parser-algorithms';
const result = parseComponentValue(tokens, options);
const ancestry = gatherNodeAncestry(result);
```
### Options
```ts
{
onParseError?: (error: ParseError) => void
}
```
#### `onParseError`
The parser algorithms are forgiving and won't stop when a parse error is encountered.
Parse errors also aren't tokens.
To receive parsing error information you can set a callback.
Parser errors will try to inform you about the point in the parsing logic the error happened.
This tells you the kind of error.
## Goals and non-goals
Things this package aims to be:
- specification compliant CSS parser
- a reliable low level package to be used in CSS sub-grammars
What it is not:
- opinionated
- fast
- small
- a replacement for PostCSS (PostCSS is fast and also an ecosystem)
[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/css-parser-algorithms
[CSS Parser Algorithms]: https://github.com/csstools/postcss-plugins/tree/main/packages/css-parser-algorithms

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,604 @@
/**
* Parse CSS following the {@link https://drafts.csswg.org/css-syntax/#parsing | CSS Syntax Level 3 specification}.
*
* @remarks
* The tokenizing and parsing tools provided by CSS Tools are designed to be low level and generic with strong ties to their respective specifications.
*
* Any analysis or mutation of CSS source code should be done with the least powerful tool that can accomplish the task.
* For many applications it is sufficient to work with tokens.
* For others you might need to use {@link https://github.com/csstools/postcss-plugins/tree/main/packages/css-parser-algorithms | @csstools/css-parser-algorithms} or a more specific parser.
*
* The implementation of the AST nodes is kept lightweight and simple.
* Do not expect magic methods, instead assume that arrays and class instances behave like any other JavaScript.
*
* @example
* Parse a string of CSS into a component value:
* ```js
* import { tokenize } from '@csstools/css-tokenizer';
* import { parseComponentValue } from '@csstools/css-parser-algorithms';
*
* const myCSS = `calc(1px * 2)`;
*
* const componentValue = parseComponentValue(tokenize({
* css: myCSS,
* }));
*
* console.log(componentValue);
* ```
*
* @example
* Use the right algorithm for the job.
*
* Algorithms that can parse larger structures (comma-separated lists, ...) can also parse smaller structures.
* However, the opposite is not true.
*
* If your context allows a list of component values, use {@link parseListOfComponentValues}:
* ```js
* import { tokenize } from '@csstools/css-tokenizer';
* import { parseListOfComponentValues } from '@csstools/css-parser-algorithms';
*
* parseListOfComponentValues(tokenize({ css: `10x 20px` }));
* ```
*
* If your context allows a comma-separated list of component values, use {@link parseCommaSeparatedListOfComponentValues}:
* ```js
* import { tokenize } from '@csstools/css-tokenizer';
* import { parseCommaSeparatedListOfComponentValues } from '@csstools/css-parser-algorithms';
*
* parseCommaSeparatedListOfComponentValues(tokenize({ css: `20deg, 50%, 30%` }));
* ```
*
* @example
* Use the stateful walkers to keep track of the context of a given component value.
*
* ```js
* import { tokenize } from '@csstools/css-tokenizer';
* import { parseComponentValue, isSimpleBlockNode } from '@csstools/css-parser-algorithms';
*
* const myCSS = `calc(1px * (5 / 2))`;
*
* const componentValue = parseComponentValue(tokenize({ css: myCSS }));
*
* let state = { inSimpleBlock: false };
* componentValue.walk((entry) => {
* if (isSimpleBlockNode(entry)) {
* entry.state.inSimpleBlock = true;
* return;
* }
*
* if (entry.state.inSimpleBlock) {
* console.log(entry.node.toString()); // `5`, ...
* }
* }, state);
* ```
*
* @packageDocumentation
*/
import type { CSSToken } from '@csstools/css-tokenizer';
import { ParseError } from '@csstools/css-tokenizer';
import type { TokenFunction } from '@csstools/css-tokenizer';
export declare class CommentNode {
/**
* The node type, always `ComponentValueType.Comment`
*/
type: ComponentValueType;
/**
* The comment token.
*/
value: CSSToken;
constructor(value: CSSToken);
/**
* Retrieve the tokens for the current comment.
* This is the inverse of parsing from a list of tokens.
*/
tokens(): Array<CSSToken>;
/**
* Convert the current comment to a string.
* This is not a true serialization.
* It is purely a concatenation of the string representation of the tokens.
*/
toString(): string;
/**
* @internal
*
* A debug helper to convert the current object to a JSON representation.
* This is useful in asserts and to store large ASTs in files.
*/
toJSON(): Record<string, unknown>;
/**
* @internal
*/
isCommentNode(): this is CommentNode;
/**
* @internal
*/
static isCommentNode(x: unknown): x is CommentNode;
}
export declare type ComponentValue = FunctionNode | SimpleBlockNode | WhitespaceNode | CommentNode | TokenNode;
export declare enum ComponentValueType {
Function = "function",
SimpleBlock = "simple-block",
Whitespace = "whitespace",
Comment = "comment",
Token = "token"
}
export declare type ContainerNode = FunctionNode | SimpleBlockNode;
export declare abstract class ContainerNodeBaseClass {
/**
* The contents of the `Function` or `Simple Block`.
* This is a list of component values.
*/
value: Array<ComponentValue>;
/**
* Retrieve the index of the given item in the current node.
* For most node types this will be trivially implemented as `this.value.indexOf(item)`.
*/
indexOf(item: ComponentValue): number | string;
/**
* Retrieve the item at the given index in the current node.
* For most node types this will be trivially implemented as `this.value[index]`.
*/
at(index: number | string): ComponentValue | undefined;
/**
* Iterates over each item in the `value` array of the current node.
*
* @param cb - The callback function to execute for each item.
* The function receives an object containing the current node (`node`), its parent (`parent`),
* and an optional `state` object.
* A second parameter is the index of the current node.
* The function can return `false` to stop the iteration.
*
* @param state - An optional state object that can be used to pass additional information to the callback function.
* The state object is cloned for each iteration. This means that changes to the state object are not reflected in the next iteration.
*
* @returns `false` if the iteration was halted, `undefined` otherwise.
*/
forEach<T extends Record<string, unknown>, U extends ContainerNode>(this: U, cb: (entry: {
node: ComponentValue;
parent: ContainerNode;
state?: T;
}, index: number | string) => boolean | void, state?: T): false | undefined;
/**
* Walks the current node and all its children.
*
* @param cb - The callback function to execute for each item.
* The function receives an object containing the current node (`node`), its parent (`parent`),
* and an optional `state` object.
* A second parameter is the index of the current node.
* The function can return `false` to stop the iteration.
*
* @param state - An optional state object that can be used to pass additional information to the callback function.
* The state object is cloned for each iteration. This means that changes to the state object are not reflected in the next iteration.
* However changes are passed down to child node iterations.
*
* @returns `false` if the iteration was halted, `undefined` otherwise.
*/
walk<T extends Record<string, unknown>, U extends ContainerNode>(this: U, cb: (entry: {
node: ComponentValue;
parent: ContainerNode;
state?: T;
}, index: number | string) => boolean | void, state?: T): false | undefined;
}
/**
* Iterates over each item in a list of component values.
*
* @param cb - The callback function to execute for each item.
* The function receives an object containing the current node (`node`), its parent (`parent`),
* and an optional `state` object.
* A second parameter is the index of the current node.
* The function can return `false` to stop the iteration.
*
* @param state - An optional state object that can be used to pass additional information to the callback function.
* The state object is cloned for each iteration. This means that changes to the state object are not reflected in the next iteration.
*
* @returns `false` if the iteration was halted, `undefined` otherwise.
*/
export declare function forEach<T extends Record<string, unknown>>(componentValues: Array<ComponentValue>, cb: (entry: {
node: ComponentValue;
parent: ContainerNode | {
value: Array<ComponentValue>;
};
state?: T;
}, index: number | string) => boolean | void, state?: T): false | undefined;
/**
* A function node.
*
* @example
* ```js
* const node = parseComponentValue(tokenize('calc(1 + 1)'));
*
* isFunctionNode(node); // true
* node.getName(); // 'calc'
* ```
*/
export declare class FunctionNode extends ContainerNodeBaseClass {
/**
* The node type, always `ComponentValueType.Function`
*/
type: ComponentValueType;
/**
* The token for the name of the function.
*/
name: TokenFunction;
/**
* The token for the closing parenthesis of the function.
* If the function is unclosed, this will be an EOF token.
*/
endToken: CSSToken;
constructor(name: TokenFunction, endToken: CSSToken, value: Array<ComponentValue>);
/**
* Retrieve the name of the current function.
* This is the parsed and unescaped name of the function.
*/
getName(): string;
/**
* Normalize the current function:
* 1. if the "endToken" is EOF, replace with a ")-token"
*/
normalize(): void;
/**
* Retrieve the tokens for the current function.
* This is the inverse of parsing from a list of tokens.
*/
tokens(): Array<CSSToken>;
/**
* Convert the current function to a string.
* This is not a true serialization.
* It is purely a concatenation of the string representation of the tokens.
*/
toString(): string;
/**
* @internal
*
* A debug helper to convert the current object to a JSON representation.
* This is useful in asserts and to store large ASTs in files.
*/
toJSON(): unknown;
/**
* @internal
*/
isFunctionNode(): this is FunctionNode;
/**
* @internal
*/
static isFunctionNode(x: unknown): x is FunctionNode;
}
/**
* AST nodes do not have a `parent` property or method.
* This makes it harder to traverse the AST upwards.
* This function builds a `Map<Child, Parent>` that can be used to lookup ancestors of a node.
*
* @remarks
* There is no magic behind this or the map it returns.
* Mutating the AST will not update the map.
*
* Types are erased and any content of the map has type `unknown`.
* If someone knows a clever way to type this, please let us know.
*
* @example
* ```js
* const ancestry = gatherNodeAncestry(mediaQuery);
* mediaQuery.walk((entry) => {
* const node = entry.node; // directly exposed
* const parent = entry.parent; // directly exposed
* const grandParent: unknown = ancestry.get(parent); // lookup
*
* console.log('node', node);
* console.log('parent', parent);
* console.log('grandParent', grandParent);
* });
* ```
*/
export declare function gatherNodeAncestry(node: {
walk(cb: (entry: {
node: unknown;
parent: unknown;
}, index: number | string) => boolean | void): false | undefined;
}): Map<unknown, unknown>;
/**
* Check if the current object is a `CommentNode`.
* This is a type guard.
*/
export declare function isCommentNode(x: unknown): x is CommentNode;
/**
* Check if the current object is a `FunctionNode`.
* This is a type guard.
*/
export declare function isFunctionNode(x: unknown): x is FunctionNode;
/**
* Check if the current object is a `SimpleBlockNode`.
* This is a type guard.
*/
export declare function isSimpleBlockNode(x: unknown): x is SimpleBlockNode;
/**
* Check if the current object is a `TokenNode`.
* This is a type guard.
*/
export declare function isTokenNode(x: unknown): x is TokenNode;
/**
* Check if the current object is a `WhitespaceNode`.
* This is a type guard.
*/
export declare function isWhitespaceNode(x: unknown): x is WhitespaceNode;
/**
* Check if the current object is a `WhiteSpaceNode` or a `CommentNode`.
* This is a type guard.
*/
export declare function isWhiteSpaceOrCommentNode(x: unknown): x is WhitespaceNode | CommentNode;
/**
* Parse a comma-separated list of component values.
*
* @example
* ```js
* import { tokenize } from '@csstools/css-tokenizer';
* import { parseCommaSeparatedListOfComponentValues } from '@csstools/css-parser-algorithms';
*
* parseCommaSeparatedListOfComponentValues(tokenize({ css: `20deg, 50%, 30%` }));
* ```
*/
export declare function parseCommaSeparatedListOfComponentValues(tokens: Array<CSSToken>, options?: {
onParseError?: (error: ParseError) => void;
}): Array<Array<ComponentValue>>;
/**
* Parse a single component value.
*
* @example
* ```js
* import { tokenize } from '@csstools/css-tokenizer';
* import { parseCommaSeparatedListOfComponentValues } from '@csstools/css-parser-algorithms';
*
* parseCommaSeparatedListOfComponentValues(tokenize({ css: `10px` }));
* parseCommaSeparatedListOfComponentValues(tokenize({ css: `calc((10px + 1x) * 4)` }));
* ```
*/
export declare function parseComponentValue(tokens: Array<CSSToken>, options?: {
onParseError?: (error: ParseError) => void;
}): ComponentValue | undefined;
/**
* Parse a list of component values.
*
* @example
* ```js
* import { tokenize } from '@csstools/css-tokenizer';
* import { parseListOfComponentValues } from '@csstools/css-parser-algorithms';
*
* parseListOfComponentValues(tokenize({ css: `20deg 30%` }));
* ```
*/
export declare function parseListOfComponentValues(tokens: Array<CSSToken>, options?: {
onParseError?: (error: ParseError) => void;
}): Array<ComponentValue>;
/**
* Replace specific component values in a list of component values.
* A helper for the most common and simplistic cases when mutating an AST.
*/
export declare function replaceComponentValues(componentValuesList: Array<Array<ComponentValue>>, replaceWith: (componentValue: ComponentValue) => Array<ComponentValue> | ComponentValue | void): Array<Array<ComponentValue>>;
/**
* A simple block node.
*
* @example
* ```js
* const node = parseComponentValue(tokenize('[foo=bar]'));
*
* isSimpleBlockNode(node); // true
* node.startToken; // [TokenType.OpenSquare, '[', 0, 0, undefined]
* ```
*/
export declare class SimpleBlockNode extends ContainerNodeBaseClass {
/**
* The node type, always `ComponentValueType.SimpleBlock`
*/
type: ComponentValueType;
/**
* The token for the opening token of the block.
*/
startToken: CSSToken;
/**
* The token for the closing token of the block.
* If the block is closed it will be the mirror variant of the `startToken`.
* If the block is unclosed, this will be an EOF token.
*/
endToken: CSSToken;
constructor(startToken: CSSToken, endToken: CSSToken, value: Array<ComponentValue>);
/**
* Normalize the current simple block
* 1. if the "endToken" is EOF, replace with the mirror token of the "startToken"
*/
normalize(): void;
/**
* Retrieve the tokens for the current simple block.
* This is the inverse of parsing from a list of tokens.
*/
tokens(): Array<CSSToken>;
/**
* Convert the current simple block to a string.
* This is not a true serialization.
* It is purely a concatenation of the string representation of the tokens.
*/
toString(): string;
/**
* @internal
*
* A debug helper to convert the current object to a JSON representation.
* This is useful in asserts and to store large ASTs in files.
*/
toJSON(): unknown;
/**
* @internal
*/
isSimpleBlockNode(): this is SimpleBlockNode;
/**
* @internal
*/
static isSimpleBlockNode(x: unknown): x is SimpleBlockNode;
}
/**
* Returns the start and end index of a node in the CSS source string.
*/
export declare function sourceIndices(x: {
tokens(): Array<CSSToken>;
} | Array<{
tokens(): Array<CSSToken>;
}>): [number, number];
/**
* Concatenate the string representation of a collection of component values.
* This is not a proper serializer that will handle escaping and whitespace.
* It only produces valid CSS for token lists that are also valid.
*/
export declare function stringify(componentValueLists: Array<Array<ComponentValue>>): string;
export declare class TokenNode {
/**
* The node type, always `ComponentValueType.Token`
*/
type: ComponentValueType;
/**
* The token.
*/
value: CSSToken;
constructor(value: CSSToken);
/**
* This is the inverse of parsing from a list of tokens.
*/
tokens(): [CSSToken];
/**
* Convert the current token to a string.
* This is not a true serialization.
* It is purely the string representation of token.
*/
toString(): string;
/**
* @internal
*
* A debug helper to convert the current object to a JSON representation.
* This is useful in asserts and to store large ASTs in files.
*/
toJSON(): Record<string, unknown>;
/**
* @internal
*/
isTokenNode(): this is TokenNode;
/**
* @internal
*/
static isTokenNode(x: unknown): x is TokenNode;
}
/**
* Walks each item in a list of component values all of their children.
*
* @param cb - The callback function to execute for each item.
* The function receives an object containing the current node (`node`), its parent (`parent`),
* and an optional `state` object.
* A second parameter is the index of the current node.
* The function can return `false` to stop the iteration.
*
* @param state - An optional state object that can be used to pass additional information to the callback function.
* The state object is cloned for each iteration. This means that changes to the state object are not reflected in the next iteration.
* However changes are passed down to child node iterations.
*
* @returns `false` if the iteration was halted, `undefined` otherwise.
*
* @example
* ```js
* import { tokenize } from '@csstools/css-tokenizer';
* import { parseListOfComponentValues, isSimpleBlockNode } from '@csstools/css-parser-algorithms';
*
* const myCSS = `calc(1px * (5 / 2)) 10px`;
*
* const componentValues = parseListOfComponentValues(tokenize({ css: myCSS }));
*
* let state = { inSimpleBlock: false };
* walk(componentValues, (entry) => {
* if (isSimpleBlockNode(entry)) {
* entry.state.inSimpleBlock = true;
* return;
* }
*
* if (entry.state.inSimpleBlock) {
* console.log(entry.node.toString()); // `5`, ...
* }
* }, state);
* ```
*/
export declare function walk<T extends Record<string, unknown>>(componentValues: Array<ComponentValue>, cb: (entry: {
node: ComponentValue;
parent: ContainerNode | {
value: Array<ComponentValue>;
};
state?: T;
}, index: number | string) => boolean | void, state?: T): false | undefined;
/**
* Generate a function that finds the next element that should be visited when walking an AST.
* Rules :
* 1. the previous iteration is used as a reference, so any checks are relative to the start of the current iteration.
* 2. the next element always appears after the current index.
* 3. the next element always exists in the list.
* 4. replacing an element does not cause the replaced element to be visited.
* 5. removing an element does not cause elements to be skipped.
* 6. an element added later in the list will be visited.
*/
export declare function walkerIndexGenerator<T>(initialList: Array<T>): (list: Array<T>, child: T, index: number) => number;
export declare class WhitespaceNode {
/**
* The node type, always `ComponentValueType.WhiteSpace`
*/
type: ComponentValueType;
/**
* The list of consecutive whitespace tokens.
*/
value: Array<CSSToken>;
constructor(value: Array<CSSToken>);
/**
* Retrieve the tokens for the current whitespace.
* This is the inverse of parsing from a list of tokens.
*/
tokens(): Array<CSSToken>;
/**
* Convert the current whitespace to a string.
* This is not a true serialization.
* It is purely a concatenation of the string representation of the tokens.
*/
toString(): string;
/**
* @internal
*
* A debug helper to convert the current object to a JSON representation.
* This is useful in asserts and to store large ASTs in files.
*/
toJSON(): Record<string, unknown>;
/**
* @internal
*/
isWhitespaceNode(): this is WhitespaceNode;
/**
* @internal
*/
static isWhitespaceNode(x: unknown): x is WhitespaceNode;
}
export { }

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,65 @@
{
"name": "@csstools/css-parser-algorithms",
"description": "Algorithms to help you parse CSS from an array of tokens.",
"version": "3.0.4",
"contributors": [
{
"name": "Antonio Laguna",
"email": "antonio@laguna.es",
"url": "https://antonio.laguna.es"
},
{
"name": "Romain Menke",
"email": "romainmenke@gmail.com"
}
],
"license": "MIT",
"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": {
"@csstools/css-tokenizer": "^3.0.3"
},
"scripts": {},
"homepage": "https://github.com/csstools/postcss-plugins/tree/main/packages/css-parser-algorithms#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/csstools/postcss-plugins.git",
"directory": "packages/css-parser-algorithms"
},
"bugs": "https://github.com/csstools/postcss-plugins/issues",
"keywords": [
"css",
"parser"
]
}

9
node_modules/@csstools/css-tokenizer/CHANGELOG.md generated vendored Normal file
View File

@@ -0,0 +1,9 @@
# Changes to CSS Tokenizer
### 3.0.3
_October 25, 2024_
- Fix input preprocessing
[Full CHANGELOG](https://github.com/csstools/postcss-plugins/tree/main/packages/css-tokenizer/CHANGELOG.md)

20
node_modules/@csstools/css-tokenizer/LICENSE.md generated vendored Normal file
View File

@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright 2022 Romain Menke, Antonio Laguna <antonio@laguna.es>
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,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
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.

111
node_modules/@csstools/css-tokenizer/README.md generated vendored Normal file
View File

@@ -0,0 +1,111 @@
# CSS Tokenizer
[<img alt="npm version" src="https://img.shields.io/npm/v/@csstools/css-tokenizer.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]
Implemented from : https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/
## API
[Read the API docs](./docs/css-tokenizer.md)
## Usage
Add [CSS Tokenizer] to your project:
```bash
npm install @csstools/css-tokenizer --save-dev
```
```js
import { tokenizer, TokenType } from '@csstools/css-tokenizer';
const myCSS = `@media only screen and (min-width: 768rem) {
.foo {
content: 'Some content!' !important;
}
}
`;
const t = tokenizer({
css: myCSS,
});
while (true) {
const token = t.nextToken();
if (token[0] === TokenType.EOF) {
break;
}
console.log(token);
}
```
Or use the `tokenize` helper function:
```js
import { tokenize } from '@csstools/css-tokenizer';
const myCSS = `@media only screen and (min-width: 768rem) {
.foo {
content: 'Some content!' !important;
}
}
`;
const tokens = tokenize({
css: myCSS,
});
console.log(tokens);
```
### Options
```ts
{
onParseError?: (error: ParseError) => void
}
```
#### `onParseError`
The tokenizer is forgiving and won't stop when a parse error is encountered.
To receive parsing error information you can set a callback.
```js
import { tokenizer, TokenType } from '@csstools/css-tokenizer';
const t = tokenizer({
css: '\\',
}, { onParseError: (err) => console.warn(err) });
while (true) {
const token = t.nextToken();
if (token[0] === TokenType.EOF) {
break;
}
}
```
Parser errors will try to inform you where in the tokenizer logic the error happened.
This tells you what kind of error occurred.
## Goals and non-goals
Things this package aims to be:
- specification compliant CSS tokenizer
- a reliable low level package to be used in CSS parsers
What it is not:
- opinionated
- fast
- small
[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/css-tokenizer
[CSS Tokenizer]: https://github.com/csstools/postcss-plugins/tree/main/packages/css-tokenizer

1
node_modules/@csstools/css-tokenizer/dist/index.cjs generated vendored Normal file

File diff suppressed because one or more lines are too long

593
node_modules/@csstools/css-tokenizer/dist/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,593 @@
/**
* Tokenize CSS following the {@link https://drafts.csswg.org/css-syntax/#tokenization | CSS Syntax Level 3 specification}.
*
* @remarks
* The tokenizing and parsing tools provided by CSS Tools are designed to be low level and generic with strong ties to their respective specifications.
*
* Any analysis or mutation of CSS source code should be done with the least powerful tool that can accomplish the task.
* For many applications it is sufficient to work with tokens.
* For others you might need to use {@link https://github.com/csstools/postcss-plugins/tree/main/packages/css-parser-algorithms | @csstools/css-parser-algorithms} or a more specific parser.
*
* @example
* Tokenize a string of CSS into an array of tokens:
* ```js
* import { tokenize } from '@csstools/css-tokenizer';
*
* const myCSS = `@media only screen and (min-width: 768rem) {
* .foo {
* content: 'Some content!' !important;
* }
* }
* `;
*
* const tokens = tokenize({
* css: myCSS,
* });
*
* console.log(tokens);
* ```
*
* @packageDocumentation
*/
/**
* Deep clone a list of tokens.
* Useful for mutations without altering the original list.
*/
export declare function cloneTokens(tokens: Array<CSSToken>): Array<CSSToken>;
/**
* The union of all possible CSS tokens
*/
export declare type CSSToken = TokenAtKeyword | TokenBadString | TokenBadURL | TokenCDC | TokenCDO | TokenColon | TokenComma | TokenComment | TokenDelim | TokenDimension | TokenEOF | TokenFunction | TokenHash | TokenIdent | TokenNumber | TokenPercentage | TokenSemicolon | TokenString | TokenURL | TokenWhitespace | TokenOpenParen | TokenCloseParen | TokenOpenSquare | TokenCloseSquare | TokenOpenCurly | TokenCloseCurly | TokenUnicodeRange;
/**
* The type of hash token
*/
export declare enum HashType {
/**
* The hash token did not start with an ident sequence (e.g. `#-2`)
*/
Unrestricted = "unrestricted",
/**
* The hash token started with an ident sequence (e.g. `#foo`)
* Only hash tokens with the "id" type are valid ID selectors.
*/
ID = "id"
}
/**
* Assert that a given value has the general structure of a CSS token:
* 1. is an array.
* 2. has at least four items.
* 3. has a known token type.
* 4. has a string representation.
* 5. has a start position.
* 6. has an end position.
*/
export declare function isToken(x: any): x is CSSToken;
export declare function isTokenAtKeyword(x?: CSSToken | null): x is TokenAtKeyword;
export declare function isTokenBadString(x?: CSSToken | null): x is TokenBadString;
export declare function isTokenBadURL(x?: CSSToken | null): x is TokenBadURL;
export declare function isTokenCDC(x?: CSSToken | null): x is TokenCDC;
export declare function isTokenCDO(x?: CSSToken | null): x is TokenCDO;
export declare function isTokenCloseCurly(x?: CSSToken | null): x is TokenCloseCurly;
export declare function isTokenCloseParen(x?: CSSToken | null): x is TokenCloseParen;
export declare function isTokenCloseSquare(x?: CSSToken | null): x is TokenCloseSquare;
export declare function isTokenColon(x?: CSSToken | null): x is TokenColon;
export declare function isTokenComma(x?: CSSToken | null): x is TokenComma;
export declare function isTokenComment(x?: CSSToken | null): x is TokenComment;
export declare function isTokenDelim(x?: CSSToken | null): x is TokenDelim;
export declare function isTokenDimension(x?: CSSToken | null): x is TokenDimension;
export declare function isTokenEOF(x?: CSSToken | null): x is TokenEOF;
export declare function isTokenFunction(x?: CSSToken | null): x is TokenFunction;
export declare function isTokenHash(x?: CSSToken | null): x is TokenHash;
export declare function isTokenIdent(x?: CSSToken | null): x is TokenIdent;
export declare function isTokenNumber(x?: CSSToken | null): x is TokenNumber;
/**
* Assert that a token is a numeric token
*/
export declare function isTokenNumeric(x?: CSSToken | null): x is NumericToken;
export declare function isTokenOpenCurly(x?: CSSToken | null): x is TokenOpenCurly;
export declare function isTokenOpenParen(x?: CSSToken | null): x is TokenOpenParen;
export declare function isTokenOpenSquare(x?: CSSToken | null): x is TokenOpenSquare;
export declare function isTokenPercentage(x?: CSSToken | null): x is TokenPercentage;
export declare function isTokenSemicolon(x?: CSSToken | null): x is TokenSemicolon;
export declare function isTokenString(x?: CSSToken | null): x is TokenString;
export declare function isTokenUnicodeRange(x?: CSSToken | null): x is TokenUnicodeRange;
export declare function isTokenURL(x?: CSSToken | null): x is TokenURL;
export declare function isTokenWhitespace(x?: CSSToken | null): x is TokenWhitespace;
/**
* Assert that a token is a whitespace or comment token
*/
export declare function isTokenWhiteSpaceOrComment(x?: CSSToken | null): x is TokenWhitespace | TokenComment;
/**
* Get the mirror variant of a given token
*
* @example
*
* ```js
* const input = [TokenType.OpenParen, '(', 0, 1, undefined];
* const output = mirrorVariant(input);
*
* console.log(output); // [TokenType.CloseParen, ')', -1, -1, undefined]
* ```
*/
export declare function mirrorVariant(token: CSSToken): CSSToken | null;
/**
* Get the mirror variant type of a given token type
*
* @example
*
* ```js
* const input = TokenType.OpenParen;
* const output = mirrorVariantType(input);
*
* console.log(output); // TokenType.CloseParen
* ```
*/
export declare function mirrorVariantType(type: TokenType): TokenType | null;
/**
* Set the ident value and update the string representation.
* This handles escaping.
*/
export declare function mutateIdent(ident: TokenIdent, newValue: string): void;
/**
* Set the unit and update the string representation.
* This handles escaping.
*/
export declare function mutateUnit(ident: TokenDimension, newUnit: string): void;
/**
* The type of number token
* Either `integer` or `number`
*/
export declare enum NumberType {
Integer = "integer",
Number = "number"
}
/**
* The union of all possible CSS tokens that represent a numeric value
*/
export declare type NumericToken = TokenDimension | TokenNumber | TokenPercentage;
/**
* The CSS Tokenizer is forgiving and will never throw on invalid input.
* Any errors are reported through the `onParseError` callback.
*/
export declare class ParseError extends Error {
/** The index of the start character of the current token. */
sourceStart: number;
/** The index of the end character of the current token. */
sourceEnd: number;
/** The parser steps that preceded the error. */
parserState: Array<string>;
constructor(message: string, sourceStart: number, sourceEnd: number, parserState: Array<string>);
}
export declare const ParseErrorMessage: {
UnexpectedNewLineInString: string;
UnexpectedEOFInString: string;
UnexpectedEOFInComment: string;
UnexpectedEOFInURL: string;
UnexpectedEOFInEscapedCodePoint: string;
UnexpectedCharacterInURL: string;
InvalidEscapeSequenceInURL: string;
InvalidEscapeSequenceAfterBackslash: string;
};
export declare class ParseErrorWithToken extends ParseError {
/** The associated token. */
token: CSSToken;
constructor(message: string, sourceStart: number, sourceEnd: number, parserState: Array<string>, token: CSSToken);
}
/**
* Concatenate the string representation of a list of tokens.
* This is not a proper serializer that will handle escaping and whitespace.
* It only produces valid CSS for a token list that is also valid.
*/
export declare function stringify(...tokens: Array<CSSToken>): string;
/**
* The CSS Token interface
*
* @remarks
* CSS Tokens are fully typed and have a strict structure.
* This makes it easier to iterate and analyze a token stream.
*
* The string representation and the parsed value are stored separately for many token types.
* It is always assumed that the string representation will be used when stringifying, while the parsed value should be used when analyzing tokens.
*/
export declare interface Token<T extends TokenType, U> extends Array<T | string | number | U> {
/**
* The type of token
*/
0: T;
/**
* The token representation
*
* @remarks
* This field will be used when stringifying the token.
* Any stored value is assumed to be valid CSS.
*
* You should never use this field when analyzing the token when there is a parsed value available.
* But you must store mutated values here.
*/
1: string;
/**
* Start position of representation
*/
2: number;
/**
* End position of representation
*/
3: number;
/**
* Extra data
*
* @remarks
* This holds the parsed value of each token.
* These values are unescaped, unquoted, converted to numbers, etc.
*
* You should always use this field when analyzing the token.
* But you must not assume that mutating only this field will have any effect.
*/
4: U;
}
export declare interface TokenAtKeyword extends Token<TokenType.AtKeyword, {
/**
* The unescaped at-keyword name without the leading `@`.
*/
value: string;
}> {
}
export declare interface TokenBadString extends Token<TokenType.BadString, undefined> {
}
export declare interface TokenBadURL extends Token<TokenType.BadURL, undefined> {
}
export declare interface TokenCDC extends Token<TokenType.CDC, undefined> {
}
export declare interface TokenCDO extends Token<TokenType.CDO, undefined> {
}
export declare interface TokenCloseCurly extends Token<TokenType.CloseCurly, undefined> {
}
export declare interface TokenCloseParen extends Token<TokenType.CloseParen, undefined> {
}
export declare interface TokenCloseSquare extends Token<TokenType.CloseSquare, undefined> {
}
export declare interface TokenColon extends Token<TokenType.Colon, undefined> {
}
export declare interface TokenComma extends Token<TokenType.Comma, undefined> {
}
export declare interface TokenComment extends Token<TokenType.Comment, undefined> {
}
export declare interface TokenDelim extends Token<TokenType.Delim, {
/**
* The delim character.
*/
value: string;
}> {
}
export declare interface TokenDimension extends Token<TokenType.Dimension, {
/**
* The numeric value.
*/
value: number;
/**
* The unescaped unit name.
*/
unit: string;
/**
* `integer` or `number`
*/
type: NumberType;
/**
* The sign character as it appeared in the source.
* This is only useful if you need to determine if a value was written as "2px" or "+2px".
*/
signCharacter?: '+' | '-';
}> {
}
export declare interface TokenEOF extends Token<TokenType.EOF, undefined> {
}
export declare interface TokenFunction extends Token<TokenType.Function, {
/**
* The unescaped function name without the trailing `(`.
*/
value: string;
}> {
}
export declare interface TokenHash extends Token<TokenType.Hash, {
/**
* The unescaped hash value without the leading `#`.
*/
value: string;
/**
* The hash type.
*/
type: HashType;
}> {
}
export declare interface TokenIdent extends Token<TokenType.Ident, {
/**
* The unescaped ident value.
*/
value: string;
}> {
}
/**
* Tokenize a CSS string into a list of tokens.
*/
export declare function tokenize(input: {
css: {
valueOf(): string;
};
unicodeRangesAllowed?: boolean;
}, options?: {
onParseError?: (error: ParseError) => void;
}): Array<CSSToken>;
/**
* Create a tokenizer for a CSS string.
*/
export declare function tokenizer(input: {
css: {
valueOf(): string;
};
unicodeRangesAllowed?: boolean;
}, options?: {
onParseError?: (error: ParseError) => void;
}): {
nextToken: () => CSSToken;
endOfFile: () => boolean;
};
export declare interface TokenNumber extends Token<TokenType.Number, {
/**
* The numeric value.
*/
value: number;
/**
* `integer` or `number`
*/
type: NumberType;
/**
* The sign character as it appeared in the source.
* This is only useful if you need to determine if a value was written as "2" or "+2".
*/
signCharacter?: '+' | '-';
}> {
}
export declare interface TokenOpenCurly extends Token<TokenType.OpenCurly, undefined> {
}
export declare interface TokenOpenParen extends Token<TokenType.OpenParen, undefined> {
}
export declare interface TokenOpenSquare extends Token<TokenType.OpenSquare, undefined> {
}
export declare interface TokenPercentage extends Token<TokenType.Percentage, {
/**
* The numeric value.
*/
value: number;
/**
* The sign character as it appeared in the source.
* This is only useful if you need to determine if a value was written as "2%" or "+2%".
*/
signCharacter?: '+' | '-';
}> {
}
export declare interface TokenSemicolon extends Token<TokenType.Semicolon, undefined> {
}
export declare interface TokenString extends Token<TokenType.String, {
/**
* The unescaped string value without the leading and trailing quotes.
*/
value: string;
}> {
}
/**
* All possible CSS token types
*/
export declare enum TokenType {
/**
* @see {@link https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#comment-diagram}
*/
Comment = "comment",
/**
* @see {@link https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#typedef-at-keyword-token}
*/
AtKeyword = "at-keyword-token",
/**
* @see {@link https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#typedef-bad-string-token}
*/
BadString = "bad-string-token",
/**
* @see {@link https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#typedef-bad-url-token}
*/
BadURL = "bad-url-token",
/**
* @see {@link https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#typedef-cdc-token}
*/
CDC = "CDC-token",
/**
* @see {@link https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#typedef-cdo-token}
*/
CDO = "CDO-token",
/**
* @see {@link https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#typedef-colon-token}
*/
Colon = "colon-token",
/**
* @see {@link https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#typedef-comma-token}
*/
Comma = "comma-token",
/**
* @see {@link https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#typedef-delim-token}
*/
Delim = "delim-token",
/**
* @see {@link https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#typedef-dimension-token}
*/
Dimension = "dimension-token",
/**
* @see {@link https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#typedef-eof-token}
*/
EOF = "EOF-token",
/**
* @see {@link https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#typedef-function-token}
*/
Function = "function-token",
/**
* @see {@link https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#typedef-hash-token}
*/
Hash = "hash-token",
/**
* @see {@link https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#typedef-ident-token}
*/
Ident = "ident-token",
/**
* @see {@link https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#typedef-percentage-token}
*/
Number = "number-token",
/**
* @see {@link https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#typedef-percentage-token}
*/
Percentage = "percentage-token",
/**
* @see {@link https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#typedef-semicolon-token}
*/
Semicolon = "semicolon-token",
/**
* @see {@link https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#typedef-string-token}
*/
String = "string-token",
/**
* @see {@link https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#typedef-url-token}
*/
URL = "url-token",
/**
* @see {@link https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#typedef-whitespace-token}
*/
Whitespace = "whitespace-token",
/**
* @see {@link https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#tokendef-open-paren}
*/
OpenParen = "(-token",
/**
* @see {@link https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#tokendef-close-paren}
*/
CloseParen = ")-token",
/**
* @see {@link https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#tokendef-open-square}
*/
OpenSquare = "[-token",
/**
* @see {@link https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#tokendef-close-square}
*/
CloseSquare = "]-token",
/**
* @see {@link https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#tokendef-open-curly}
*/
OpenCurly = "{-token",
/**
* @see {@link https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#tokendef-close-curly}
*/
CloseCurly = "}-token",
/**
* Only appears in the token stream when the `unicodeRangesAllowed` option is set to true.
*
* @example
* ```js
* import { tokenize } from '@csstools/css-tokenizer';
*
* const tokens = tokenize({
* css: `U+0025-00FF, U+4??`,
* unicodeRangesAllowed: true,
* });
*
* console.log(tokens);
* ```
*
* @see {@link https://drafts.csswg.org/css-syntax/#typedef-unicode-range-token}
*/
UnicodeRange = "unicode-range-token"
}
export declare interface TokenUnicodeRange extends Token<TokenType.UnicodeRange, {
startOfRange: number;
endOfRange: number;
}> {
}
export declare interface TokenURL extends Token<TokenType.URL, {
/**
* The unescaped URL value without the leading `url(` and trailing `)`.
*/
value: string;
}> {
}
export declare interface TokenWhitespace extends Token<TokenType.Whitespace, undefined> {
}
export { }

1
node_modules/@csstools/css-tokenizer/dist/index.mjs generated vendored Normal file

File diff suppressed because one or more lines are too long

62
node_modules/@csstools/css-tokenizer/package.json generated vendored Normal file
View File

@@ -0,0 +1,62 @@
{
"name": "@csstools/css-tokenizer",
"description": "Tokenize CSS",
"version": "3.0.3",
"contributors": [
{
"name": "Antonio Laguna",
"email": "antonio@laguna.es",
"url": "https://antonio.laguna.es"
},
{
"name": "Romain Menke",
"email": "romainmenke@gmail.com"
}
],
"license": "MIT",
"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"
],
"scripts": {},
"homepage": "https://github.com/csstools/postcss-plugins/tree/main/packages/css-tokenizer#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/csstools/postcss-plugins.git",
"directory": "packages/css-tokenizer"
},
"bugs": "https://github.com/csstools/postcss-plugins/issues",
"keywords": [
"css",
"tokenizer"
]
}

View File

@@ -0,0 +1,10 @@
# Changes to Media Query List Parser
### 4.0.2
_November 1, 2024_
- Updated [`@csstools/css-tokenizer`](https://github.com/csstools/postcss-plugins/tree/main/packages/css-tokenizer) to [`3.0.3`](https://github.com/csstools/postcss-plugins/tree/main/packages/css-tokenizer/CHANGELOG.md#303) (patch)
- Updated [`@csstools/css-parser-algorithms`](https://github.com/csstools/postcss-plugins/tree/main/packages/css-parser-algorithms) to [`3.0.4`](https://github.com/csstools/postcss-plugins/tree/main/packages/css-parser-algorithms/CHANGELOG.md#304) (patch)
[Full CHANGELOG](https://github.com/csstools/postcss-plugins/tree/main/packages/media-query-list-parser/CHANGELOG.md)

View File

@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright 2022 Romain Menke, Antonio Laguna <antonio@laguna.es>
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,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
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,61 @@
# Media Query List Parser
[<img alt="npm version" src="https://img.shields.io/npm/v/@csstools/media-query-list-parser.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]
Implemented from : https://www.w3.org/TR/mediaqueries-5/
## Usage
Add [Media Query List Parser] to your project:
```bash
npm install @csstools/media-query-list-parser @csstools/css-parser-algorithms @csstools/css-tokenizer --save-dev
```
[Media Query List Parser] depends on our CSS tokenizer and parser algorithms.
It must be used together with `@csstools/css-tokenizer` and `@csstools/css-parser-algorithms`.
```ts
import { parse } from '@csstools/media-query-list-parser';
export function parseCustomMedia() {
const mediaQueryList = parse('screen and (min-width: 300px), (50px < height < 30vw)');
mediaQueryList.forEach((mediaQuery) => {
mediaQuery.walk((entry, index) => {
// Index of the current Node in `parent`.
console.log(index);
// Type of `parent`.
console.log(entry.parent.type);
// Type of `node`
{
// Sometimes nodes can be arrays.
if (Array.isArray(entry.node)) {
entry.node.forEach((item) => {
console.log(item.type);
});
}
if ('type' in entry.node) {
console.log(entry.node.type);
}
}
// stringified version of the current node.
console.log(entry.node.toString());
// Return `false` to stop the walker.
return false;
});
});
}
```
[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/media-query-list-parser
[Media Query List Parser]: https://github.com/csstools/postcss-plugins/tree/main/packages/media-query-list-parser

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,775 @@
import type { ComponentValue } from '@csstools/css-parser-algorithms';
import type { ContainerNode } from '@csstools/css-parser-algorithms';
import { CSSToken } from '@csstools/css-tokenizer';
import type { ParseError } from '@csstools/css-tokenizer';
import type { TokenColon } from '@csstools/css-tokenizer';
import type { TokenDelim } from '@csstools/css-tokenizer';
import type { TokenIdent } from '@csstools/css-tokenizer';
export declare function cloneMediaQuery<T extends MediaQueryWithType | MediaQueryWithoutType | MediaQueryInvalid>(x: T): T;
export declare function comparisonFromTokens(tokens: [TokenDelim, TokenDelim] | [TokenDelim]): MediaFeatureComparison | false;
export declare class CustomMedia {
type: NodeType;
name: Array<CSSToken>;
mediaQueryList: Array<MediaQuery> | null;
trueOrFalseKeyword: Array<CSSToken> | null;
constructor(name: Array<CSSToken>, mediaQueryList: Array<MediaQuery> | null, trueOrFalseKeyword?: Array<CSSToken>);
getName(): string;
getNameToken(): CSSToken | null;
hasMediaQueryList(): boolean;
hasTrueKeyword(): boolean;
hasFalseKeyword(): boolean;
tokens(): Array<CSSToken>;
toString(): string;
/**
* @internal
*/
toJSON(): Record<string, unknown>;
/**
* @internal
*/
isCustomMedia(): this is CustomMedia;
static isCustomMedia(x: unknown): x is CustomMedia;
}
export declare class GeneralEnclosed {
type: NodeType;
value: ComponentValue;
constructor(value: ComponentValue);
tokens(): Array<CSSToken>;
toString(): string;
/**
* @internal
*/
hasLeadingSpace(): boolean;
indexOf(item: ComponentValue): number | string;
at(index: number | string): ComponentValue | undefined;
walk<T extends Record<string, unknown>>(cb: (entry: {
node: GeneralEnclosedWalkerEntry;
parent: GeneralEnclosedWalkerParent;
state?: T;
}, index: number | string) => boolean | void, state?: T): false | undefined;
/**
* @internal
*/
toJSON(): Record<string, unknown>;
/**
* @internal
*/
isGeneralEnclosed(): this is GeneralEnclosed;
static isGeneralEnclosed(x: unknown): x is GeneralEnclosed;
}
export declare type GeneralEnclosedWalkerEntry = ComponentValue;
export declare type GeneralEnclosedWalkerParent = ContainerNode | GeneralEnclosed;
export declare function invertComparison(operator: MediaFeatureComparison): MediaFeatureComparison | false;
export declare function isCustomMedia(x: unknown): x is GeneralEnclosed;
export declare function isGeneralEnclosed(x: unknown): x is GeneralEnclosed;
export declare function isMediaAnd(x: unknown): x is MediaAnd;
export declare function isMediaCondition(x: unknown): x is MediaCondition;
export declare function isMediaConditionList(x: unknown): x is MediaConditionList;
export declare function isMediaConditionListWithAnd(x: unknown): x is MediaConditionListWithAnd;
export declare function isMediaConditionListWithOr(x: unknown): x is MediaConditionListWithOr;
export declare function isMediaFeature(x: unknown): x is MediaFeature;
export declare function isMediaFeatureBoolean(x: unknown): x is MediaFeatureBoolean;
export declare function isMediaFeatureName(x: unknown): x is MediaFeatureName;
export declare function isMediaFeaturePlain(x: unknown): x is MediaFeaturePlain;
export declare function isMediaFeatureRange(x: unknown): x is MediaFeatureRange;
export declare function isMediaFeatureRangeNameValue(x: unknown): x is MediaFeatureRangeNameValue;
export declare function isMediaFeatureRangeValueName(x: unknown): x is MediaFeatureRangeValueName;
export declare function isMediaFeatureRangeValueNameValue(x: unknown): x is MediaFeatureRangeValueNameValue;
export declare function isMediaFeatureValue(x: unknown): x is MediaFeatureValue;
export declare function isMediaInParens(x: unknown): x is MediaInParens;
export declare function isMediaNot(x: unknown): x is MediaNot;
export declare function isMediaOr(x: unknown): x is MediaOr;
export declare function isMediaQuery(x: unknown): x is MediaQuery;
export declare function isMediaQueryInvalid(x: unknown): x is MediaQueryInvalid;
export declare function isMediaQueryWithoutType(x: unknown): x is MediaQueryWithoutType;
export declare function isMediaQueryWithType(x: unknown): x is MediaQueryWithType;
export declare function matchesComparison(componentValues: Array<ComponentValue>): false | [number, number];
export declare function matchesRatio(componentValues: Array<ComponentValue>): -1 | [number, number];
export declare function matchesRatioExactly(componentValues: Array<ComponentValue>): -1 | [number, number];
export declare class MediaAnd {
type: NodeType;
modifier: Array<CSSToken>;
media: MediaInParens;
constructor(modifier: Array<CSSToken>, media: MediaInParens);
tokens(): Array<CSSToken>;
toString(): string;
/**
* @internal
*/
hasLeadingSpace(): boolean;
indexOf(item: MediaInParens): number | string;
at(index: number | string): MediaInParens | null;
walk<T extends Record<string, unknown>>(cb: (entry: {
node: MediaAndWalkerEntry;
parent: MediaAndWalkerParent;
state?: T;
}, index: number | string) => boolean | void, state?: T): false | undefined;
/**
* @internal
*/
toJSON(): unknown;
/**
* @internal
*/
isMediaAnd(): this is MediaAnd;
static isMediaAnd(x: unknown): x is MediaAnd;
}
export declare type MediaAndWalkerEntry = MediaInParensWalkerEntry | MediaInParens;
export declare type MediaAndWalkerParent = MediaInParensWalkerParent | MediaAnd;
export declare class MediaCondition {
type: NodeType;
media: MediaNot | MediaInParens | MediaConditionListWithAnd | MediaConditionListWithOr;
constructor(media: MediaNot | MediaInParens | MediaConditionListWithAnd | MediaConditionListWithOr);
tokens(): Array<CSSToken>;
toString(): string;
/**
* @internal
*/
hasLeadingSpace(): boolean;
indexOf(item: MediaNot | MediaInParens | MediaConditionListWithAnd | MediaConditionListWithOr): number | string;
at(index: number | string): MediaNot | MediaInParens | MediaConditionListWithAnd | MediaConditionListWithOr | undefined;
walk<T extends Record<string, unknown>>(cb: (entry: {
node: MediaConditionWalkerEntry;
parent: MediaConditionWalkerParent;
state?: T;
}, index: number | string) => boolean | void, state?: T): false | undefined;
/**
* @internal
*/
toJSON(): unknown;
/**
* @internal
*/
isMediaCondition(): this is MediaCondition;
static isMediaCondition(x: unknown): x is MediaCondition;
}
export declare type MediaConditionList = MediaConditionListWithAnd | MediaConditionListWithOr;
export declare class MediaConditionListWithAnd {
type: NodeType;
leading: MediaInParens;
list: Array<MediaAnd>;
before: Array<CSSToken>;
after: Array<CSSToken>;
constructor(leading: MediaInParens, list: Array<MediaAnd>, before?: Array<CSSToken>, after?: Array<CSSToken>);
tokens(): Array<CSSToken>;
toString(): string;
/**
* @internal
*/
hasLeadingSpace(): boolean;
indexOf(item: MediaInParens | MediaAnd): number | string;
at(index: number | string): MediaInParens | MediaAnd | undefined;
walk<T extends Record<string, unknown>>(cb: (entry: {
node: MediaConditionListWithAndWalkerEntry;
parent: MediaConditionListWithAndWalkerParent;
state?: T;
}, index: number | string) => boolean | void, state?: T): false | undefined;
toJSON(): unknown;
isMediaConditionListWithAnd(): this is MediaConditionListWithAnd;
static isMediaConditionListWithAnd(x: unknown): x is MediaConditionListWithAnd;
}
export declare type MediaConditionListWithAndWalkerEntry = MediaAndWalkerEntry | MediaAnd;
export declare type MediaConditionListWithAndWalkerParent = MediaAndWalkerParent | MediaConditionListWithAnd;
export declare class MediaConditionListWithOr {
type: NodeType;
leading: MediaInParens;
list: Array<MediaOr>;
before: Array<CSSToken>;
after: Array<CSSToken>;
constructor(leading: MediaInParens, list: Array<MediaOr>, before?: Array<CSSToken>, after?: Array<CSSToken>);
tokens(): Array<CSSToken>;
toString(): string;
/**
* @internal
*/
hasLeadingSpace(): boolean;
indexOf(item: MediaInParens | MediaOr): number | string;
at(index: number | string): MediaInParens | MediaOr | undefined;
walk<T extends Record<string, unknown>>(cb: (entry: {
node: MediaConditionListWithOrWalkerEntry;
parent: MediaConditionListWithOrWalkerParent;
state?: T;
}, index: number | string) => boolean | void, state?: T): false | undefined;
/**
* @internal
*/
toJSON(): unknown;
/**
* @internal
*/
isMediaConditionListWithOr(): this is MediaConditionListWithOr;
static isMediaConditionListWithOr(x: unknown): x is MediaConditionListWithOr;
}
export declare type MediaConditionListWithOrWalkerEntry = MediaOrWalkerEntry | MediaOr;
export declare type MediaConditionListWithOrWalkerParent = MediaOrWalkerParent | MediaConditionListWithOr;
export declare type MediaConditionWalkerEntry = MediaNotWalkerEntry | MediaConditionListWithAndWalkerEntry | MediaConditionListWithOrWalkerEntry | MediaNot | MediaConditionListWithAnd | MediaConditionListWithOr;
export declare type MediaConditionWalkerParent = MediaNotWalkerParent | MediaConditionListWithAndWalkerParent | MediaConditionListWithOrWalkerParent | MediaCondition;
export declare class MediaFeature {
type: NodeType;
feature: MediaFeaturePlain | MediaFeatureBoolean | MediaFeatureRange;
before: Array<CSSToken>;
after: Array<CSSToken>;
constructor(feature: MediaFeaturePlain | MediaFeatureBoolean | MediaFeatureRange, before?: Array<CSSToken>, after?: Array<CSSToken>);
getName(): string;
getNameToken(): CSSToken;
tokens(): Array<CSSToken>;
toString(): string;
/**
* @internal
*/
hasLeadingSpace(): boolean;
indexOf(item: MediaFeaturePlain | MediaFeatureBoolean | MediaFeatureRange): number | string;
at(index: number | string): MediaFeatureBoolean | MediaFeaturePlain | MediaFeatureRange | undefined;
walk<T extends Record<string, unknown>>(cb: (entry: {
node: MediaFeatureWalkerEntry;
parent: MediaFeatureWalkerParent;
state?: T;
}, index: number | string) => boolean | void, state?: T): false | undefined;
/**
* @internal
*/
toJSON(): Record<string, unknown>;
/**
* @internal
*/
isMediaFeature(): this is MediaFeature;
static isMediaFeature(x: unknown): x is MediaFeature;
}
export declare class MediaFeatureBoolean {
type: NodeType;
name: MediaFeatureName;
constructor(name: MediaFeatureName);
getName(): string;
getNameToken(): CSSToken;
tokens(): Array<CSSToken>;
toString(): string;
indexOf(item: MediaFeatureName): number | string;
at(index: number | string): MediaFeatureName | undefined;
/**
* @internal
*/
toJSON(): Record<string, unknown>;
/**
* @internal
*/
isMediaFeatureBoolean(): this is MediaFeatureBoolean;
static isMediaFeatureBoolean(x: unknown): x is MediaFeatureBoolean;
}
export declare type MediaFeatureComparison = MediaFeatureLT | MediaFeatureGT | MediaFeatureEQ;
export declare enum MediaFeatureEQ {
EQ = "="
}
export declare enum MediaFeatureGT {
GT = ">",
GT_OR_EQ = ">="
}
export declare enum MediaFeatureLT {
LT = "<",
LT_OR_EQ = "<="
}
export declare class MediaFeatureName {
type: NodeType;
name: ComponentValue;
before: Array<CSSToken>;
after: Array<CSSToken>;
constructor(name: ComponentValue, before?: Array<CSSToken>, after?: Array<CSSToken>);
getName(): string;
getNameToken(): CSSToken;
tokens(): Array<CSSToken>;
toString(): string;
indexOf(item: ComponentValue): number | string;
at(index: number | string): ComponentValue | undefined;
/**
* @internal
*/
toJSON(): Record<string, unknown>;
/**
* @internal
*/
isMediaFeatureName(): this is MediaFeatureName;
static isMediaFeatureName(x: unknown): x is MediaFeatureName;
}
export declare class MediaFeaturePlain {
type: NodeType;
name: MediaFeatureName;
colon: TokenColon;
value: MediaFeatureValue;
constructor(name: MediaFeatureName, colon: TokenColon, value: MediaFeatureValue);
getName(): string;
getNameToken(): CSSToken;
tokens(): Array<CSSToken>;
toString(): string;
indexOf(item: MediaFeatureName | MediaFeatureValue): number | string;
at(index: number | string): MediaFeatureName | MediaFeatureValue | undefined;
walk<T extends Record<string, unknown>>(cb: (entry: {
node: MediaFeaturePlainWalkerEntry;
parent: MediaFeaturePlainWalkerParent;
state?: T;
}, index: number | string) => boolean | void, state?: T): false | undefined;
/**
* @internal
*/
toJSON(): Record<string, unknown>;
/**
* @internal
*/
isMediaFeaturePlain(): this is MediaFeaturePlain;
static isMediaFeaturePlain(x: unknown): x is MediaFeaturePlain;
}
export declare type MediaFeaturePlainWalkerEntry = MediaFeatureValueWalkerEntry | MediaFeatureValue;
export declare type MediaFeaturePlainWalkerParent = MediaFeatureValueWalkerParent | MediaFeaturePlain;
export declare type MediaFeatureRange = MediaFeatureRangeNameValue | MediaFeatureRangeValueName | MediaFeatureRangeValueNameValue;
export declare class MediaFeatureRangeNameValue {
type: NodeType;
name: MediaFeatureName;
operator: [TokenDelim, TokenDelim] | [TokenDelim];
value: MediaFeatureValue;
constructor(name: MediaFeatureName, operator: [TokenDelim, TokenDelim] | [TokenDelim], value: MediaFeatureValue);
operatorKind(): MediaFeatureComparison | false;
getName(): string;
getNameToken(): CSSToken;
tokens(): Array<CSSToken>;
toString(): string;
indexOf(item: MediaFeatureName | MediaFeatureValue): number | string;
at(index: number | string): MediaFeatureName | MediaFeatureValue | undefined;
walk<T extends Record<string, unknown>>(cb: (entry: {
node: MediaFeatureRangeWalkerEntry;
parent: MediaFeatureRangeWalkerParent;
state?: T;
}, index: number | string) => boolean | void, state?: T): false | undefined;
/**
* @internal
*/
toJSON(): Record<string, unknown>;
/**
* @internal
*/
isMediaFeatureRangeNameValue(): this is MediaFeatureRangeNameValue;
static isMediaFeatureRangeNameValue(x: unknown): x is MediaFeatureRangeNameValue;
}
export declare class MediaFeatureRangeValueName {
type: NodeType;
name: MediaFeatureName;
operator: [TokenDelim, TokenDelim] | [TokenDelim];
value: MediaFeatureValue;
constructor(name: MediaFeatureName, operator: [TokenDelim, TokenDelim] | [TokenDelim], value: MediaFeatureValue);
operatorKind(): MediaFeatureComparison | false;
getName(): string;
getNameToken(): CSSToken;
tokens(): Array<CSSToken>;
toString(): string;
indexOf(item: MediaFeatureName | MediaFeatureValue): number | string;
at(index: number | string): MediaFeatureName | MediaFeatureValue | undefined;
walk<T extends Record<string, unknown>>(cb: (entry: {
node: MediaFeatureRangeWalkerEntry;
parent: MediaFeatureRangeWalkerParent;
state?: T;
}, index: number | string) => boolean | void, state?: T): false | undefined;
/**
* @internal
*/
toJSON(): Record<string, unknown>;
/**
* @internal
*/
isMediaFeatureRangeValueName(): this is MediaFeatureRangeValueName;
static isMediaFeatureRangeValueName(x: unknown): x is MediaFeatureRangeValueName;
}
export declare class MediaFeatureRangeValueNameValue {
type: NodeType;
name: MediaFeatureName;
valueOne: MediaFeatureValue;
valueOneOperator: [TokenDelim, TokenDelim] | [TokenDelim];
valueTwo: MediaFeatureValue;
valueTwoOperator: [TokenDelim, TokenDelim] | [TokenDelim];
constructor(name: MediaFeatureName, valueOne: MediaFeatureValue, valueOneOperator: [TokenDelim, TokenDelim] | [TokenDelim], valueTwo: MediaFeatureValue, valueTwoOperator: [TokenDelim, TokenDelim] | [TokenDelim]);
valueOneOperatorKind(): MediaFeatureComparison | false;
valueTwoOperatorKind(): MediaFeatureComparison | false;
getName(): string;
getNameToken(): CSSToken;
tokens(): Array<CSSToken>;
toString(): string;
indexOf(item: MediaFeatureName | MediaFeatureValue): number | string;
at(index: number | string): MediaFeatureName | MediaFeatureValue | undefined;
walk<T extends Record<string, unknown>>(cb: (entry: {
node: MediaFeatureRangeWalkerEntry;
parent: MediaFeatureRangeWalkerParent;
state?: T;
}, index: number | string) => boolean | void, state?: T): false | undefined;
/**
* @internal
*/
toJSON(): Record<string, unknown>;
/**
* @internal
*/
isMediaFeatureRangeValueNameValue(): this is MediaFeatureRangeValueNameValue;
static isMediaFeatureRangeValueNameValue(x: unknown): x is MediaFeatureRangeValueNameValue;
}
export declare type MediaFeatureRangeWalkerEntry = MediaFeatureValueWalkerEntry | MediaFeatureValue;
export declare type MediaFeatureRangeWalkerParent = MediaFeatureValueWalkerParent | MediaFeatureRange;
export declare class MediaFeatureValue {
type: NodeType;
value: ComponentValue | Array<ComponentValue>;
before: Array<CSSToken>;
after: Array<CSSToken>;
constructor(value: ComponentValue | Array<ComponentValue>, before?: Array<CSSToken>, after?: Array<CSSToken>);
tokens(): Array<CSSToken>;
toString(): string;
indexOf(item: ComponentValue): number | string;
at(index: number | string): ComponentValue | Array<ComponentValue> | undefined;
walk<T extends Record<string, unknown>>(cb: (entry: {
node: MediaFeatureValueWalkerEntry;
parent: MediaFeatureValueWalkerParent;
state?: T;
}, index: number | string) => boolean | void, state?: T): false | undefined;
/**
* @internal
*/
toJSON(): Record<string, unknown>;
/**
* @internal
*/
isMediaFeatureValue(): this is MediaFeatureValue;
static isMediaFeatureValue(x: unknown): x is MediaFeatureValue;
}
export declare type MediaFeatureValueWalkerEntry = ComponentValue;
export declare type MediaFeatureValueWalkerParent = ContainerNode | MediaFeatureValue;
export declare type MediaFeatureWalkerEntry = MediaFeaturePlainWalkerEntry | MediaFeatureRangeWalkerEntry | MediaFeaturePlain | MediaFeatureBoolean | MediaFeatureRange;
export declare type MediaFeatureWalkerParent = MediaFeaturePlainWalkerParent | MediaFeatureRangeWalkerParent | MediaFeature;
export declare class MediaInParens {
type: NodeType;
media: MediaCondition | MediaFeature | GeneralEnclosed;
before: Array<CSSToken>;
after: Array<CSSToken>;
constructor(media: MediaCondition | MediaFeature | GeneralEnclosed, before?: Array<CSSToken>, after?: Array<CSSToken>);
tokens(): Array<CSSToken>;
toString(): string;
/**
* @internal
*/
hasLeadingSpace(): boolean;
indexOf(item: MediaCondition | MediaFeature | GeneralEnclosed): number | string;
at(index: number | string): MediaCondition | MediaFeature | GeneralEnclosed | undefined;
walk<T extends Record<string, unknown>>(cb: (entry: {
node: MediaInParensWalkerEntry;
parent: MediaInParensWalkerParent;
state?: T;
}, index: number | string) => boolean | void, state?: T): false | undefined;
/**
* @internal
*/
toJSON(): Record<string, unknown>;
/**
* @internal
*/
isMediaInParens(): this is MediaInParens;
static isMediaInParens(x: unknown): x is MediaInParens;
}
export declare type MediaInParensWalkerEntry = ComponentValue | GeneralEnclosed | MediaAnd | MediaNot | MediaOr | MediaConditionList | MediaCondition | MediaFeatureBoolean | MediaFeatureName | MediaFeaturePlain | MediaFeatureRange | MediaFeatureValue | MediaFeature | MediaInParens;
export declare type MediaInParensWalkerParent = ContainerNode | GeneralEnclosed | MediaAnd | MediaNot | MediaOr | MediaConditionList | MediaCondition | MediaFeatureBoolean | MediaFeatureName | MediaFeaturePlain | MediaFeatureRange | MediaFeatureValue | MediaFeature | MediaInParens;
export declare class MediaNot {
type: NodeType;
modifier: Array<CSSToken>;
media: MediaInParens;
constructor(modifier: Array<CSSToken>, media: MediaInParens);
tokens(): Array<CSSToken>;
toString(): string;
/**
* @internal
*/
hasLeadingSpace(): boolean;
indexOf(item: MediaInParens): number | string;
at(index: number | string): MediaInParens | undefined;
walk<T extends Record<string, unknown>>(cb: (entry: {
node: MediaNotWalkerEntry;
parent: MediaNotWalkerParent;
state?: T;
}, index: number | string) => boolean | void, state?: T): false | undefined;
/**
* @internal
*/
toJSON(): Record<string, unknown>;
/**
* @internal
*/
isMediaNot(): this is MediaNot;
static isMediaNot(x: unknown): x is MediaNot;
}
export declare type MediaNotWalkerEntry = MediaInParensWalkerEntry | MediaInParens;
export declare type MediaNotWalkerParent = MediaInParensWalkerParent | MediaNot;
export declare class MediaOr {
type: NodeType;
modifier: Array<CSSToken>;
media: MediaInParens;
constructor(modifier: Array<CSSToken>, media: MediaInParens);
tokens(): Array<CSSToken>;
toString(): string;
indexOf(item: MediaInParens): number | string;
at(index: number | string): MediaInParens | undefined;
walk<T extends Record<string, unknown>>(cb: (entry: {
node: MediaOrWalkerEntry;
parent: MediaOrWalkerParent;
state?: T;
}, index: number | string) => boolean | void, state?: T): false | undefined;
/**
* @internal
*/
toJSON(): Record<string, unknown>;
/**
* @internal
*/
isMediaOr(): this is MediaOr;
static isMediaOr(x: unknown): x is MediaOr;
}
export declare type MediaOrWalkerEntry = MediaInParensWalkerEntry | MediaInParens;
export declare type MediaOrWalkerParent = MediaInParensWalkerParent | MediaOr;
export declare type MediaQuery = MediaQueryWithType | MediaQueryWithoutType | MediaQueryInvalid;
export declare class MediaQueryInvalid {
type: NodeType;
media: Array<ComponentValue>;
constructor(media: Array<ComponentValue>);
negateQuery(): Array<MediaQuery>;
tokens(): Array<CSSToken>;
toString(): string;
walk<T extends Record<string, unknown>>(cb: (entry: {
node: MediaQueryInvalidWalkerEntry;
parent: MediaQueryInvalidWalkerParent;
state?: T;
}, index: number | string) => boolean | void, state?: T): false | undefined;
/**
* @internal
*/
toJSON(): Record<string, unknown>;
/**
* @internal
*/
isMediaQueryInvalid(): this is MediaQueryInvalid;
static isMediaQueryInvalid(x: unknown): x is MediaQueryInvalid;
}
export declare type MediaQueryInvalidWalkerEntry = ComponentValue;
export declare type MediaQueryInvalidWalkerParent = ComponentValue | MediaQueryInvalid;
export declare enum MediaQueryModifier {
Not = "not",
Only = "only"
}
export declare class MediaQueryWithoutType {
type: NodeType;
media: MediaCondition;
constructor(media: MediaCondition);
negateQuery(): Array<MediaQuery>;
tokens(): Array<CSSToken>;
toString(): string;
indexOf(item: MediaCondition): number | string;
at(index: number | string): MediaCondition | undefined;
walk<T extends Record<string, unknown>>(cb: (entry: {
node: MediaQueryWithoutTypeWalkerEntry;
parent: MediaQueryWithoutTypeWalkerParent;
state?: T;
}, index: number | string) => boolean | void, state?: T): false | undefined;
/**
* @internal
*/
toJSON(): Record<string, unknown>;
/**
* @internal
*/
isMediaQueryWithoutType(): this is MediaQueryWithoutType;
static isMediaQueryWithoutType(x: unknown): x is MediaQueryWithoutType;
}
export declare type MediaQueryWithoutTypeWalkerEntry = MediaConditionWalkerEntry | MediaCondition;
export declare type MediaQueryWithoutTypeWalkerParent = MediaConditionWalkerParent | MediaQueryWithoutType;
export declare class MediaQueryWithType {
type: NodeType;
modifier: Array<CSSToken>;
mediaType: Array<CSSToken>;
and: Array<CSSToken> | undefined;
media: MediaCondition | undefined;
constructor(modifier: Array<CSSToken>, mediaType: Array<CSSToken>, and?: Array<CSSToken>, media?: MediaCondition);
getModifier(): string;
negateQuery(): Array<MediaQuery>;
getMediaType(): string;
tokens(): Array<CSSToken>;
toString(): string;
indexOf(item: MediaCondition): number | string;
at(index: number | string): MediaCondition | undefined;
walk<T extends Record<string, unknown>>(cb: (entry: {
node: MediaQueryWithTypeWalkerEntry;
parent: MediaQueryWithTypeWalkerParent;
state?: T;
}, index: number | string) => boolean | void, state?: T): false | undefined;
/**
* @internal
*/
toJSON(): Record<string, unknown>;
/**
* @internal
*/
isMediaQueryWithType(): this is MediaQueryWithType;
static isMediaQueryWithType(x: unknown): x is MediaQueryWithType;
}
export declare type MediaQueryWithTypeWalkerEntry = MediaConditionWalkerEntry | MediaCondition;
export declare type MediaQueryWithTypeWalkerParent = MediaConditionWalkerParent | MediaQueryWithType;
export declare enum MediaType {
/** Always matches */
All = "all",
Print = "print",
Screen = "screen",
/** Never matches */
Tty = "tty",
/** Never matches */
Tv = "tv",
/** Never matches */
Projection = "projection",
/** Never matches */
Handheld = "handheld",
/** Never matches */
Braille = "braille",
/** Never matches */
Embossed = "embossed",
/** Never matches */
Aural = "aural",
/** Never matches */
Speech = "speech"
}
export declare function modifierFromToken(token: TokenIdent): MediaQueryModifier | false;
export declare function newMediaFeatureBoolean(name: string): MediaFeature;
export declare function newMediaFeaturePlain(name: string, ...value: Array<CSSToken>): MediaFeature;
export declare enum NodeType {
CustomMedia = "custom-media",
GeneralEnclosed = "general-enclosed",
MediaAnd = "media-and",
MediaCondition = "media-condition",
MediaConditionListWithAnd = "media-condition-list-and",
MediaConditionListWithOr = "media-condition-list-or",
MediaFeature = "media-feature",
MediaFeatureBoolean = "mf-boolean",
MediaFeatureName = "mf-name",
MediaFeaturePlain = "mf-plain",
MediaFeatureRangeNameValue = "mf-range-name-value",
MediaFeatureRangeValueName = "mf-range-value-name",
MediaFeatureRangeValueNameValue = "mf-range-value-name-value",
MediaFeatureValue = "mf-value",
MediaInParens = "media-in-parens",
MediaNot = "media-not",
MediaOr = "media-or",
MediaQueryWithType = "media-query-with-type",
MediaQueryWithoutType = "media-query-without-type",
MediaQueryInvalid = "media-query-invalid"
}
export declare function parse(source: string, options?: {
preserveInvalidMediaQueries?: boolean;
onParseError?: (error: ParseError) => void;
}): Array<MediaQuery>;
export declare function parseCustomMedia(source: string, options?: {
preserveInvalidMediaQueries?: boolean;
onParseError?: (error: ParseError) => void;
}): CustomMedia | false;
export declare function parseCustomMediaFromTokens(tokens: Array<CSSToken>, options?: {
preserveInvalidMediaQueries?: boolean;
onParseError?: (error: ParseError) => void;
}): CustomMedia | false;
export declare function parseFromTokens(tokens: Array<CSSToken>, options?: {
preserveInvalidMediaQueries?: boolean;
onParseError?: (error: ParseError) => void;
}): Array<MediaQuery>;
export declare function typeFromToken(token: TokenIdent): MediaType | false;
export { }

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,67 @@
{
"name": "@csstools/media-query-list-parser",
"description": "Parse CSS media query lists.",
"version": "4.0.2",
"contributors": [
{
"name": "Antonio Laguna",
"email": "antonio@laguna.es",
"url": "https://antonio.laguna.es"
},
{
"name": "Romain Menke",
"email": "romainmenke@gmail.com"
}
],
"license": "MIT",
"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": {
"@csstools/css-parser-algorithms": "^3.0.4",
"@csstools/css-tokenizer": "^3.0.3"
},
"scripts": {},
"homepage": "https://github.com/csstools/postcss-plugins/tree/main/packages/media-query-list-parser#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/csstools/postcss-plugins.git",
"directory": "packages/media-query-list-parser"
},
"bugs": "https://github.com/csstools/postcss-plugins/issues",
"keywords": [
"css",
"media query",
"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)

18
node_modules/@csstools/selector-specificity/LICENSE.md generated vendored Normal file
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.

57
node_modules/@csstools/selector-specificity/README.md generated vendored Normal file
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"
]
}