🚀 Big News: Socket Acquires Coana to Bring Reachability Analysis to Every Appsec Team.Learn more
Socket
DemoInstallSign in
Socket

@csstools/css-parser-algorithms

Package Overview
Dependencies
Maintainers
3
Versions
23
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@csstools/css-parser-algorithms - npm Package Compare versions

Comparing version

to
2.5.0

14

CHANGELOG.md
# Changes to CSS Parser Algorithms
### 2.5.0
_December 31, 2023_
- Add a `forEach` and `walk` function.
- Improve documentation.
- Updated [`@csstools/css-tokenizer`](https://github.com/csstools/postcss-plugins/tree/main/packages/css-tokenizer) to [`2.2.3`](https://github.com/csstools/postcss-plugins/tree/main/packages/css-tokenizer/CHANGELOG.md#223) (patch)
### 2.4.0

@@ -19,8 +27,2 @@

### 2.3.1
_July 24, 2023_
- Updated [`@csstools/css-tokenizer`](https://github.com/csstools/postcss-plugins/tree/main/packages/css-tokenizer) to [`2.2.0`](https://github.com/csstools/postcss-plugins/tree/main/packages/css-tokenizer/CHANGELOG.md#220) (minor)
[Full CHANGELOG](https://github.com/csstools/postcss-plugins/tree/main/packages/css-parser-algorithms/CHANGELOG.md)

@@ -0,1 +1,78 @@

/**
* 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';
*
* 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';
*
* parseComponentValue(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';
*
* 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';
*
* 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 { CSSToken } from '@csstools/css-tokenizer';

@@ -6,7 +83,28 @@ import { ParseError } 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(): {

@@ -16,3 +114,9 @@ type: ComponentValueType;

};
/**
* @internal
*/
isCommentNode(): this is CommentNode;
/**
* @internal
*/
static isCommentNode(x: unknown): x is CommentNode;

@@ -62,5 +166,2 @@ }

* @returns `false` if the iteration was halted, `undefined` otherwise.
*
* @template T - The type of the `state` object.
* @template U - The type of the current node.
*/

@@ -86,5 +187,2 @@ forEach<T extends Record<string, unknown>, U extends ContainerNode>(this: U, cb: (entry: {

* @returns `false` if the iteration was halted, `undefined` otherwise.
*
* @template T - The type of the `state` object.
* @template U - The type of the current node.
*/

@@ -98,6 +196,38 @@ walk<T extends Record<string, unknown>, U extends ContainerNode>(this: U, cb: (entry: {

/**
* 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`
* The node type, always `ComponentValueType.Function`
*/

@@ -116,3 +246,3 @@ type: ComponentValueType;

/**
* Retrieve the name of the current Function.
* Retrieve the name of the current function.
* This is the parsed and unescaped name of the function.

@@ -122,8 +252,8 @@ */

/**
* Normalize the current Function:
* - if the "endToken" is EOF, replace with a ")-token"
* Normalize the current function:
* 1. if the "endToken" is EOF, replace with a ")-token"
*/
normalize(): void;
/**
* Retrieve the tokens for the current Function.
* Retrieve the tokens for the current function.
* This is the inverse of parsing from a list of tokens.

@@ -133,3 +263,3 @@ */

/**
* Convert the current Function to a string.
* Convert the current function to a string.
* This is not a true serialization.

@@ -140,2 +270,4 @@ * It is purely a concatenation of the string representation of the tokens.

/**
* @internal
*
* A debug helper to convert the current object to a JSON representation.

@@ -146,9 +278,7 @@ * This is useful in asserts and to store large ASTs in files.

/**
* Check if the current object is a FunctionNode.
* This is a type guard to help with type narrowing.
* @internal
*/
isFunctionNode(): this is FunctionNode;
/**
* Check if the given object is a FunctionNode.
* This is a type guard to help with type narrowing.
* @internal
*/

@@ -158,2 +288,28 @@ 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: {

@@ -166,12 +322,43 @@ walk(cb: (entry: {

/**
* 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;
/**
* Parse a comma-separated list of component values.
*
* @example
* ```js
* import { tokenize } from '@csstools/css-tokenizer';
* import { parseCommaSeparatedListOfComponentValues } from '@csstools/css-parser';
*
* parseCommaSeparatedListOfComponentValues(tokenize({ css: `20deg, 50%, 30%` }));
* ```
*/
export declare function parseCommaSeparatedListOfComponentValues(tokens: Array<CSSToken>, options?: {

@@ -181,2 +368,14 @@ onParseError?: (error: ParseError) => void;

/**
* Parse a single component value.
*
* @example
* ```js
* import { tokenize } from '@csstools/css-tokenizer';
* import { parseCommaSeparatedListOfComponentValues } from '@csstools/css-parser';
*
* parseCommaSeparatedListOfComponentValues(tokenize({ css: `10px` }));
* parseCommaSeparatedListOfComponentValues(tokenize({ css: `calc((10px + 1x) * 4)` }));
* ```
*/
export declare function parseComponentValue(tokens: Array<CSSToken>, options?: {

@@ -186,2 +385,13 @@ onParseError?: (error: ParseError) => void;

/**
* Parse a list of component values.
*
* @example
* ```js
* import { tokenize } from '@csstools/css-tokenizer';
* import { parseListOfComponentValues } from '@csstools/css-parser';
*
* parseListOfComponentValues(tokenize({ css: `20deg 30%` }));
* ```
*/
export declare function parseListOfComponentValues(tokens: Array<CSSToken>, options?: {

@@ -191,20 +401,65 @@ onParseError?: (error: ParseError) => void;

/**
* 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) => ComponentValue | void): 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:
* - if the "endToken" is EOF, replace with the mirror token of the "startToken"
* 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;
indexOf(item: ComponentValue): number | string;
at(index: number | string): ComponentValue | undefined;
/**
* @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;

@@ -222,10 +477,35 @@ }

/**
* 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(): Array<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(): {

@@ -235,3 +515,9 @@ type: ComponentValueType;

};
/**
* @internal
*/
isTokenNode(): this is TokenNode;
/**
* @internal
*/
static isTokenNode(x: unknown): x is TokenNode;

@@ -241,10 +527,55 @@ }

/**
* 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';
*
* 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 :
* - the previous iteration is used as a reference, so any checks are relative to the start of the current iteration.
* - the next element always appears after the current index.
* - the next element always exists in the list.
* - replacing an element does not cause the replaced element to be visited.
* - removing an element does not cause elements to be skipped.
* - an element added later in the list will be visited.
* 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.
*/

@@ -254,7 +585,28 @@ 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(): {

@@ -264,3 +616,9 @@ type: ComponentValueType;

};
/**
* @internal
*/
isWhitespaceNode(): this is WhitespaceNode;
/**
* @internal
*/
static isWhitespaceNode(x: unknown): x is WhitespaceNode;

@@ -267,0 +625,0 @@ }

{
"name": "@csstools/css-parser-algorithms",
"description": "Algorithms to help you parse CSS from an array of tokens.",
"version": "2.4.0",
"version": "2.5.0",
"contributors": [

@@ -51,3 +51,3 @@ {

"peerDependencies": {
"@csstools/css-tokenizer": "^2.2.2"
"@csstools/css-tokenizer": "^2.2.3"
},

@@ -54,0 +54,0 @@ "homepage": "https://github.com/csstools/postcss-plugins/tree/main/packages/css-parser-algorithms#readme",

@@ -9,2 +9,6 @@ # CSS Parser Algorithms

## API
[Read the API docs](./docs/css-parser-algorithms.md)
## Usage

@@ -11,0 +15,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet