diff --git a/package-lock.json b/package-lock.json index 6c331be13..6d733e7ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2311,6 +2311,12 @@ "node": ">= 8" } }, + "node_modules/@rmenke/css-tokenizer-tests": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rmenke/css-tokenizer-tests/-/css-tokenizer-tests-1.0.1.tgz", + "integrity": "sha512-/vtsR3e96S1tedQm5Ud84tdKeTg4X361mnWM0iO/B9Bm3wzLQPqDzGRfJo8tqsk+Rc8/nb9Cg+A6Tgs6EF4pDQ==", + "dev": true + }, "node_modules/@rollup/plugin-babel": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-6.0.3.tgz", @@ -7066,6 +7072,7 @@ "version": "1.0.0", "license": "MIT", "devDependencies": { + "@rmenke/css-tokenizer-tests": "^1.0.1", "postcss-parser-tests": "^8.5.1" }, "engines": { @@ -9226,6 +9233,7 @@ "@csstools/css-tokenizer": { "version": "file:packages/css-tokenizer", "requires": { + "@rmenke/css-tokenizer-tests": "^1.0.1", "postcss-parser-tests": "^8.5.1" } }, @@ -9713,6 +9721,12 @@ "fastq": "^1.6.0" } }, + "@rmenke/css-tokenizer-tests": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rmenke/css-tokenizer-tests/-/css-tokenizer-tests-1.0.1.tgz", + "integrity": "sha512-/vtsR3e96S1tedQm5Ud84tdKeTg4X361mnWM0iO/B9Bm3wzLQPqDzGRfJo8tqsk+Rc8/nb9Cg+A6Tgs6EF4pDQ==", + "dev": true + }, "@rollup/plugin-babel": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-6.0.3.tgz", diff --git a/packages/cascade-layer-name-parser/dist/index.cjs b/packages/cascade-layer-name-parser/dist/index.cjs index bc1f8c66a..30638cc13 100644 --- a/packages/cascade-layer-name-parser/dist/index.cjs +++ b/packages/cascade-layer-name-parser/dist/index.cjs @@ -1 +1 @@ -"use strict";var e=require("@csstools/css-tokenizer"),t=require("@csstools/css-parser-algorithms");class LayerName{parts;constructor(e){this.parts=e}tokens(){return[...this.parts]}slice(t,n){const r=[];for(let t=0;tt[0]===e.TokenType.Ident||t[0]===e.TokenType.Delim)),n,...t.parts.filter((t=>t[0]===e.TokenType.Ident||t[0]===e.TokenType.Delim))])}segments(){return this.parts.filter((t=>t[0]===e.TokenType.Ident)).map((e=>e[4].value))}name(){return this.parts.filter((t=>t[0]===e.TokenType.Ident||t[0]===e.TokenType.Delim)).map((e=>e[1])).join("")}equal(e){const t=this.segments(),n=e.segments();if(t.length!==n.length)return!1;for(let e=0;e{}),genericError=e=>({message:`Invalid cascade layer name. ${e}`,start:n[0][2],end:n[n.length-1][3],state:["6.4.2. Layer Naming and Nesting","Layer name syntax"," = [ '.' ]*"]}),a=[];for(let n=0;ne.tokens()));let p,l=!1,m=!1;for(let t=0;t{const n=t.segments();e:for(let r=0;r=a&&(o=t,a=r)}-1===o?e.push(n):e.splice(o+1,0,n)}})),e},exports.parse=function parse(t,n){const r=e.tokenizer({css:t},{commentsAreTokens:!0,onParseError:null==n?void 0:n.onParseError}),s=[];for(;!r.endOfFile();)s.push(r.nextToken());return s.push(r.nextToken()),parseFromTokens(s,n)},exports.parseFromTokens=parseFromTokens; +"use strict";var e=require("@csstools/css-tokenizer"),n=require("@csstools/css-parser-algorithms");class LayerName{parts;constructor(e){this.parts=e}tokens(){return[...this.parts]}slice(n,r){const t=[];for(let n=0;nn[0]===e.TokenType.Ident||n[0]===e.TokenType.Delim)),r,...n.parts.filter((n=>n[0]===e.TokenType.Ident||n[0]===e.TokenType.Delim))])}segments(){return this.parts.filter((n=>n[0]===e.TokenType.Ident)).map((e=>e[4].value))}name(){return this.parts.filter((n=>n[0]===e.TokenType.Ident||n[0]===e.TokenType.Delim)).map((e=>e[1])).join("")}equal(e){const n=this.segments(),r=e.segments();if(n.length!==r.length)return!1;for(let e=0;e{}),o=["6.4.2. Layer Naming and Nesting","Layer name syntax"," = [ '.' ]*"],i=r[0][2],l=r[r.length-1][3],p=[];for(let r=0;re.tokens()));let c,T=!1,y=!1;for(let n=0;n{const r=n.segments();e:for(let t=0;t=o&&(s=n,o=t)}-1===s?e.push(r):e.splice(s+1,0,r)}})),e},exports.parse=function parse(n,r){const t=e.tokenizer({css:n},{commentsAreTokens:!0,onParseError:null==r?void 0:r.onParseError}),a=[];for(;!t.endOfFile();)a.push(t.nextToken());return a.push(t.nextToken()),parseFromTokens(a,r)},exports.parseFromTokens=parseFromTokens; diff --git a/packages/cascade-layer-name-parser/dist/index.mjs b/packages/cascade-layer-name-parser/dist/index.mjs index 6dc6faf7e..5a42d30b5 100644 --- a/packages/cascade-layer-name-parser/dist/index.mjs +++ b/packages/cascade-layer-name-parser/dist/index.mjs @@ -1 +1 @@ -import{TokenType as e,stringify as t,tokenizer as n}from"@csstools/css-tokenizer";import{parseCommaSeparatedListOfComponentValues as r,isTokenNode as s,isCommentNode as a,isWhitespaceNode as o}from"@csstools/css-parser-algorithms";class LayerName{parts;constructor(e){this.parts=e}tokens(){return[...this.parts]}slice(t,n){const r=[];for(let t=0;tt[0]===e.Ident||t[0]===e.Delim)),n,...t.parts.filter((t=>t[0]===e.Ident||t[0]===e.Delim))])}segments(){return this.parts.filter((t=>t[0]===e.Ident)).map((e=>e[4].value))}name(){return this.parts.filter((t=>t[0]===e.Ident||t[0]===e.Delim)).map((e=>e[1])).join("")}equal(e){const t=this.segments(),n=e.segments();if(t.length!==n.length)return!1;for(let e=0;e{const n=t.segments();e:for(let r=0;r=o&&(a=t,o=r)}-1===a?e.push(n):e.splice(a+1,0,n)}})),e}function parseFromTokens(t,n){const i=r(t,{onParseError:null==n?void 0:n.onParseError}),l=(null==n?void 0:n.onParseError)??(()=>{}),genericError=e=>({message:`Invalid cascade layer name. ${e}`,start:t[0][2],end:t[t.length-1][3],state:["6.4.2. Layer Naming and Nesting","Layer name syntax"," = [ '.' ]*"]}),m=[];for(let t=0;te.tokens()));let c,u=!1,p=!1;for(let t=0;tt[0]===e.Ident||t[0]===e.Delim)),n,...t.parts.filter((t=>t[0]===e.Ident||t[0]===e.Delim))])}segments(){return this.parts.filter((t=>t[0]===e.Ident)).map((e=>e[4].value))}name(){return this.parts.filter((t=>t[0]===e.Ident||t[0]===e.Delim)).map((e=>e[1])).join("")}equal(e){const t=this.segments(),n=e.segments();if(t.length!==n.length)return!1;for(let e=0;e{const n=t.segments();e:for(let r=0;r=o&&(s=t,o=r)}-1===s?e.push(n):e.splice(s+1,0,n)}})),e}function parseFromTokens(t,r){const l=a(t,{onParseError:null==r?void 0:r.onParseError}),m=(null==r?void 0:r.onParseError)??(()=>{}),c=["6.4.2. Layer Naming and Nesting","Layer name syntax"," = [ '.' ]*"],d=t[0][2],u=t[t.length-1][3],p=[];for(let t=0;te.tokens()));let h,f=!1,y=!1;for(let t=0;t void; + onParseError?: (error: ParseError) => void; }; export declare function parseFromTokens(tokens: Array, options?: Options): LayerName[]; export declare function parse(source: string, options?: Options): LayerName[]; diff --git a/packages/cascade-layer-name-parser/src/parser/parse.ts b/packages/cascade-layer-name-parser/src/parser/parse.ts index 238dc0a22..d81094dca 100644 --- a/packages/cascade-layer-name-parser/src/parser/parse.ts +++ b/packages/cascade-layer-name-parser/src/parser/parse.ts @@ -1,11 +1,10 @@ import { isCommentNode, isTokenNode, isWhitespaceNode } from '@csstools/css-parser-algorithms'; import { parseCommaSeparatedListOfComponentValues } from '@csstools/css-parser-algorithms'; -import { ParserError } from '@csstools/css-parser-algorithms/dist/interfaces/error'; -import { CSSToken, tokenizer, TokenType } from '@csstools/css-tokenizer'; +import { CSSToken, tokenizer, TokenType, ParseError } from '@csstools/css-tokenizer'; import { LayerName } from '../nodes/layer-name'; export type Options = { - onParseError?: (error: ParserError) => void + onParseError?: (error: ParseError) => void } export function parseFromTokens(tokens: Array, options?: Options) { @@ -20,18 +19,14 @@ export function parseFromTokens(tokens: Array, options?: Options) { // There is no error recovery when parsing layer names. // They are either fully valid or fully invalid. - const genericError = (message) => { - return { - message: `Invalid cascade layer name. ${message}`, - start: tokens[0][2], - end: tokens[tokens.length - 1][3], - state: [ - '6.4.2. Layer Naming and Nesting', - 'Layer name syntax', - ' = [ \'.\' ]*', - ], - }; - }; + const genericErrorParseState = [ + '6.4.2. Layer Naming and Nesting', + 'Layer name syntax', + ' = [ \'.\' ]*', + ]; + + const sourceStart = tokens[0][2]; + const sourceEnd = tokens[tokens.length - 1][3]; const result: Array = []; @@ -40,7 +35,13 @@ export function parseFromTokens(tokens: Array, options?: Options) { for (let j = 0; j < componentValuesList.length; j++) { const componentValue = componentValuesList[j]; if (!isTokenNode(componentValue) && !isCommentNode(componentValue) && !isWhitespaceNode(componentValue)) { - onParseError(genericError(`Invalid layer name part "${componentValue.toString()}"`)); + onParseError(new ParseError( + `Invalid cascade layer name. Invalid layer name part "${componentValue.toString()}"`, + sourceStart, + sourceEnd, + genericErrorParseState, + )); + return []; } } @@ -61,13 +62,25 @@ export function parseFromTokens(tokens: Array, options?: Options) { token[4].value === '.' ) )) { - onParseError(genericError(`Invalid character "${token[1]}"`)); + onParseError(new ParseError( + `Invalid cascade layer name. Invalid character "${token[1]}"`, + sourceStart, + sourceEnd, + genericErrorParseState, + )); + return []; } if (!inLayerNameSequence) { if (token[0] === TokenType.Delim) { - onParseError(genericError('Layer names can not start with a dot.')); + onParseError(new ParseError( + 'Invalid cascade layer name. Layer names can not start with a dot.', + sourceStart, + sourceEnd, + genericErrorParseState, + )); + return []; } } @@ -83,20 +96,38 @@ export function parseFromTokens(tokens: Array, options?: Options) { } if (sawWhiteSpaceAfterIdent) { - onParseError(genericError('Encountered unexpected whitespace between layer name parts.')); + onParseError(new ParseError( + 'Invalid cascade layer name. Encountered unexpected whitespace between layer name parts.', + sourceStart, + sourceEnd, + genericErrorParseState, + )); + return []; } if (lastToken[0] === TokenType.Ident) { if (token[0] === TokenType.Ident) { - onParseError(genericError('Layer name parts must be separated by dots.')); + onParseError(new ParseError( + 'Invalid cascade layer name. Layer name parts must be separated by dots.', + sourceStart, + sourceEnd, + genericErrorParseState, + )); + return []; } } if (lastToken[0] === TokenType.Delim) { if (token[0] === TokenType.Delim) { - onParseError(genericError('Layer name parts must not be empty.')); + onParseError(new ParseError( + 'Invalid cascade layer name. Layer name parts must not be empty.', + sourceStart, + sourceEnd, + genericErrorParseState, + )); + return []; } } @@ -112,12 +143,24 @@ export function parseFromTokens(tokens: Array, options?: Options) { } if (!lastToken) { - onParseError(genericError('Empty layer name.')); + onParseError(new ParseError( + 'Invalid cascade layer name. Empty layer name.', + sourceStart, + sourceEnd, + genericErrorParseState, + )); + return []; } if (lastToken[0] === TokenType.Delim) { - onParseError(genericError('Layer name must not end with a dot.')); + onParseError(new ParseError( + 'Invalid cascade layer name. Layer name must not end with a dot.', + sourceStart, + sourceEnd, + genericErrorParseState, + )); + return []; } diff --git a/packages/css-parser-algorithms/CHANGELOG.md b/packages/css-parser-algorithms/CHANGELOG.md index 6e25f75aa..37992fcd3 100644 --- a/packages/css-parser-algorithms/CHANGELOG.md +++ b/packages/css-parser-algorithms/CHANGELOG.md @@ -1,5 +1,8 @@ ### Unreleased +- Fix: Removes `UnclosedFunctionNode` and `UnclosedSimpleBlockNode`. (breaking) +- Change the `ParseError` interface, this is now a subclass of `Error` (breaking) +- Change `nameTokenValue` in `FunctionNode` to `getName` (breaking) - Fix: Do not discard empty items in comma separated lists. ### 1.0.0 (November 14, 2022) diff --git a/packages/css-parser-algorithms/README.md b/packages/css-parser-algorithms/README.md index 4d3f11218..0ba9b0af6 100644 --- a/packages/css-parser-algorithms/README.md +++ b/packages/css-parser-algorithms/README.md @@ -82,7 +82,7 @@ const ancestry = gatherNodeAncestry(result); ```ts { - onParseError?: (error: ParserError) => void + onParseError?: (error: ParseError) => void } ``` @@ -96,11 +96,6 @@ 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. -`start` and `end` are the location in your CSS source code. - -`UnclosedSimpleBlockNode` and `UnclosedFunctionNode` entries will be added to the output. -This allows you to recover from errors and/or show warnings. - ## Goals and non-goals Things this package aims to be: diff --git a/packages/css-parser-algorithms/dist/consume/consume-component-block-function.d.ts b/packages/css-parser-algorithms/dist/consume/consume-component-block-function.d.ts index 24e87f789..10d09860a 100644 --- a/packages/css-parser-algorithms/dist/consume/consume-component-block-function.d.ts +++ b/packages/css-parser-algorithms/dist/consume/consume-component-block-function.d.ts @@ -2,7 +2,7 @@ import { CSSToken, TokenFunction } from '@csstools/css-tokenizer'; import { Context } from '../interfaces/context'; import { ComponentValueType } from '../util/component-value-type'; export type ContainerNode = FunctionNode | SimpleBlockNode; -export type ComponentValue = FunctionNode | SimpleBlockNode | WhitespaceNode | CommentNode | TokenNode | UnclosedSimpleBlockNode | UnclosedFunctionNode; +export type ComponentValue = FunctionNode | SimpleBlockNode | WhitespaceNode | CommentNode | TokenNode; export declare function consumeComponentValue(ctx: Context, tokens: Array): { advance: number; node: ComponentValue; @@ -13,11 +13,16 @@ export declare class FunctionNode { endToken: CSSToken; value: Array; constructor(name: TokenFunction, endToken: CSSToken, value: Array); - nameTokenValue(): string; + getName(): string; + /** + * Normalize the current Function: + * - if the "endToken" is EOF, replace with a ")-token" + */ + normalize(): void; tokens(): Array; toString(): string; indexOf(item: ComponentValue): number | string; - at(index: number | string): ComponentValue; + at(index: number | string): ComponentValue | undefined; walk(cb: (entry: { node: ComponentValue; parent: ContainerNode; @@ -28,7 +33,7 @@ export declare class FunctionNode { } export declare function consumeFunction(ctx: Context, tokens: Array): { advance: number; - node: FunctionNode | UnclosedFunctionNode; + node: FunctionNode; }; export declare class SimpleBlockNode { type: ComponentValueType; @@ -36,10 +41,15 @@ export declare class SimpleBlockNode { endToken: CSSToken; value: Array; constructor(startToken: CSSToken, endToken: CSSToken, value: Array); + /** + * Normalize the current Simple Block: + * - if the "endToken" is EOF, replace with the mirror token of the "startToken" + */ + normalize(): void; tokens(): Array; toString(): string; indexOf(item: ComponentValue): number | string; - at(index: number | string): ComponentValue; + at(index: number | string): ComponentValue | undefined; walk(cb: (entry: { node: ComponentValue; parent: ContainerNode; @@ -51,7 +61,7 @@ export declare class SimpleBlockNode { /** https://www.w3.org/TR/css-syntax-3/#consume-simple-block */ export declare function consumeSimpleBlock(ctx: Context, tokens: Array): { advance: number; - node: SimpleBlockNode | UnclosedSimpleBlockNode; + node: SimpleBlockNode; }; export declare class WhitespaceNode { type: ComponentValueType; @@ -104,29 +114,3 @@ export declare class TokenNode { isTokenNode(): this is TokenNode; static isTokenNode(x: unknown): x is TokenNode; } -export declare class UnclosedFunctionNode { - type: ComponentValueType; - value: Array; - constructor(value: Array); - tokens(): Array; - toString(): string; - toJSON(): { - type: ComponentValueType; - tokens: CSSToken[]; - }; - isUnclosedFunctionNode(): this is UnclosedFunctionNode; - static isUnclosedFunctionNode(x: unknown): x is UnclosedFunctionNode; -} -export declare class UnclosedSimpleBlockNode { - type: ComponentValueType; - value: Array; - constructor(value: Array); - tokens(): Array; - toString(): string; - toJSON(): { - type: ComponentValueType; - tokens: CSSToken[]; - }; - isUnclosedSimpleBlockNode(): this is UnclosedSimpleBlockNode; - static isUnclosedSimpleBlockNode(x: unknown): x is UnclosedSimpleBlockNode; -} diff --git a/packages/css-parser-algorithms/dist/index.cjs b/packages/css-parser-algorithms/dist/index.cjs index 2ad9b2515..6056c8b81 100644 --- a/packages/css-parser-algorithms/dist/index.cjs +++ b/packages/css-parser-algorithms/dist/index.cjs @@ -1 +1 @@ -"use strict";var e,n=require("@csstools/css-tokenizer");function consumeComponentValue(e,o){const t=o[0];if(t[0]===n.TokenType.OpenParen||t[0]===n.TokenType.OpenCurly||t[0]===n.TokenType.OpenSquare){const n=consumeSimpleBlock(e,o);return{advance:n.advance,node:n.node}}if(t[0]===n.TokenType.Function){const n=consumeFunction(e,o);return{advance:n.advance,node:n.node}}if(t[0]===n.TokenType.Whitespace){const n=consumeWhitespace(e,o);return{advance:n.advance,node:n.node}}if(t[0]===n.TokenType.Comment){const n=consumeComment(e,o);return{advance:n.advance,node:n.node}}return{advance:1,node:new TokenNode(t)}}exports.ComponentValueType=void 0,(e=exports.ComponentValueType||(exports.ComponentValueType={})).Function="function",e.SimpleBlock="simple-block",e.Whitespace="whitespace",e.Comment="comment",e.Token="token",e.UnclosedFunction="unclosed-function",e.UnclosedSimpleBlock="unclosed-simple-block";class FunctionNode{type=exports.ComponentValueType.Function;name;endToken;value;constructor(e,n,o){this.name=e,this.endToken=n,this.value=o}nameTokenValue(){return this.name[4].value}tokens(){return[this.name,...this.value.flatMap((e=>n.isToken(e)?e:e.tokens())),this.endToken]}toString(){const e=this.value.map((e=>n.isToken(e)?n.stringify(e):e.toString())).join("");return n.stringify(this.name)+e+n.stringify(this.endToken)}indexOf(e){return this.value.indexOf(e)}at(e){if("number"==typeof e)return e<0&&(e=this.value.length+e),this.value[e]}walk(e){let n=!1;if(this.value.forEach(((o,t)=>{n||(!1!==e({node:o,parent:this},t)?"walk"in o&&!1===o.walk(e)&&(n=!0):n=!0)})),n)return!1}toJSON(){return{type:this.type,name:this.nameTokenValue(),tokens:this.tokens(),value:this.value.map((e=>e.toJSON()))}}isFunctionNode(){return FunctionNode.isFunctionNode(this)}static isFunctionNode(e){return!!e&&(e instanceof FunctionNode&&e.type===exports.ComponentValueType.Function)}}function consumeFunction(e,o){const t=[];let s=1;for(;;){const i=o[s];if(!i||i[0]===n.TokenType.EOF)return e.onParseError({message:"Unexpected EOF while consuming a function.",start:o[0][2],end:o[o.length-1][3],state:["5.4.9. Consume a function","Unexpected EOF"]}),{advance:o.length,node:new UnclosedFunctionNode(o)};if(i[0]===n.TokenType.CloseParen)return{advance:s+1,node:new FunctionNode(o[0],i,t)};if(i[0]===n.TokenType.Comment||i[0]===n.TokenType.Whitespace){const n=consumeAllCommentsAndWhitespace(e,o.slice(s));s+=n.advance,t.push(...n.nodes);continue}const c=consumeComponentValue(e,o.slice(s));s+=c.advance,t.push(c.node)}}class SimpleBlockNode{type=exports.ComponentValueType.SimpleBlock;startToken;endToken;value;constructor(e,n,o){this.startToken=e,this.endToken=n,this.value=o}tokens(){return[this.startToken,...this.value.flatMap((e=>n.isToken(e)?e:e.tokens())),this.endToken]}toString(){const e=this.value.map((e=>n.isToken(e)?n.stringify(e):e.toString())).join("");return n.stringify(this.startToken)+e+n.stringify(this.endToken)}indexOf(e){return this.value.indexOf(e)}at(e){if("number"==typeof e)return e<0&&(e=this.value.length+e),this.value[e]}walk(e){let n=!1;if(this.value.forEach(((o,t)=>{n||(!1!==e({node:o,parent:this},t)?"walk"in o&&!1===o.walk(e)&&(n=!0):n=!0)})),n)return!1}toJSON(){return{type:this.type,startToken:this.startToken,tokens:this.tokens(),value:this.value.map((e=>e.toJSON()))}}isSimpleBlockNode(){return SimpleBlockNode.isSimpleBlockNode(this)}static isSimpleBlockNode(e){return!!e&&(e instanceof SimpleBlockNode&&e.type===exports.ComponentValueType.SimpleBlock)}}function consumeSimpleBlock(e,o){const t=n.mirrorVariantType(o[0][0]);if(!t)throw new Error("Failed to parse, a mirror variant must exist for all block open tokens.");const s=[];let i=1;for(;;){const c=o[i];if(!c||c[0]===n.TokenType.EOF)return e.onParseError({message:"Unexpected EOF while consuming a simple block.",start:o[0][2],end:o[o.length-1][3],state:["5.4.8. Consume a simple block","Unexpected EOF"]}),{advance:o.length,node:new UnclosedSimpleBlockNode(o)};if(c[0]===t)return{advance:i+1,node:new SimpleBlockNode(o[0],c,s)};if(c[0]===n.TokenType.Comment||c[0]===n.TokenType.Whitespace){const n=consumeAllCommentsAndWhitespace(e,o.slice(i));i+=n.advance,s.push(...n.nodes);continue}const r=consumeComponentValue(e,o.slice(i));i+=r.advance,s.push(r.node)}}class WhitespaceNode{type=exports.ComponentValueType.Whitespace;value;constructor(e){this.value=e}tokens(){return this.value}toString(){return n.stringify(...this.value)}toJSON(){return{type:this.type,tokens:this.tokens()}}isWhitespaceNode(){return WhitespaceNode.isWhitespaceNode(this)}static isWhitespaceNode(e){return!!e&&(e instanceof WhitespaceNode&&e.type===exports.ComponentValueType.Whitespace)}}function consumeWhitespace(e,o){let t=0;for(;;){if(o[t][0]!==n.TokenType.Whitespace)return{advance:t,node:new WhitespaceNode(o.slice(0,t))};t++}}class CommentNode{type=exports.ComponentValueType.Comment;value;constructor(e){this.value=e}tokens(){return[this.value]}toString(){return n.stringify(this.value)}toJSON(){return{type:this.type,tokens:this.tokens()}}isCommentNode(){return CommentNode.isCommentNode(this)}static isCommentNode(e){return!!e&&(e instanceof CommentNode&&e.type===exports.ComponentValueType.Comment)}}function consumeComment(e,n){return{advance:1,node:new CommentNode(n[0])}}function consumeAllCommentsAndWhitespace(e,o){const t=[];let s=0;for(;;)if(o[s][0]!==n.TokenType.Whitespace){if(o[s][0]!==n.TokenType.Comment)return{advance:s,nodes:t};t.push(new CommentNode(o[s])),s++}else{const e=consumeWhitespace(0,o.slice(s));s+=e.advance,t.push(e.node)}}class TokenNode{type=exports.ComponentValueType.Token;value;constructor(e){this.value=e}tokens(){return[this.value]}toString(){return n.stringify(this.value)}toJSON(){return{type:this.type,tokens:this.tokens()}}isTokenNode(){return TokenNode.isTokenNode(this)}static isTokenNode(e){return!!e&&(e instanceof TokenNode&&e.type===exports.ComponentValueType.Token)}}class UnclosedFunctionNode{type=exports.ComponentValueType.UnclosedFunction;value;constructor(e){this.value=e}tokens(){return this.value}toString(){return n.stringify(...this.value)}toJSON(){return{type:this.type,tokens:this.tokens()}}isUnclosedFunctionNode(){return UnclosedFunctionNode.isUnclosedFunctionNode(this)}static isUnclosedFunctionNode(e){return!!e&&(e instanceof UnclosedFunctionNode&&e.type===exports.ComponentValueType.UnclosedFunction)}}class UnclosedSimpleBlockNode{type=exports.ComponentValueType.UnclosedSimpleBlock;value;constructor(e){this.value=e}tokens(){return this.value}toString(){return n.stringify(...this.value)}toJSON(){return{type:this.type,tokens:this.tokens()}}isUnclosedSimpleBlockNode(){return UnclosedSimpleBlockNode.isUnclosedSimpleBlockNode(this)}static isUnclosedSimpleBlockNode(e){return!!e&&(e instanceof UnclosedSimpleBlockNode&&e.type===exports.ComponentValueType.UnclosedSimpleBlock)}}exports.CommentNode=CommentNode,exports.FunctionNode=FunctionNode,exports.SimpleBlockNode=SimpleBlockNode,exports.TokenNode=TokenNode,exports.UnclosedFunctionNode=UnclosedFunctionNode,exports.UnclosedSimpleBlockNode=UnclosedSimpleBlockNode,exports.WhitespaceNode=WhitespaceNode,exports.consumeAllCommentsAndWhitespace=consumeAllCommentsAndWhitespace,exports.consumeComment=consumeComment,exports.consumeComponentValue=consumeComponentValue,exports.consumeFunction=consumeFunction,exports.consumeSimpleBlock=consumeSimpleBlock,exports.consumeWhitespace=consumeWhitespace,exports.gatherNodeAncestry=function gatherNodeAncestry(e){const n=new Map;return e.walk((e=>{Array.isArray(e.node)?e.node.forEach((o=>{n.set(o,e.parent)})):n.set(e.node,e.parent)})),n},exports.isCommentNode=function isCommentNode(e){return CommentNode.isCommentNode(e)},exports.isFunctionNode=function isFunctionNode(e){return FunctionNode.isFunctionNode(e)},exports.isSimpleBlockNode=function isSimpleBlockNode(e){return SimpleBlockNode.isSimpleBlockNode(e)},exports.isTokenNode=function isTokenNode(e){return TokenNode.isTokenNode(e)},exports.isUnclosedFunctionNode=function isUnclosedFunctionNode(e){return UnclosedFunctionNode.isUnclosedFunctionNode(e)},exports.isUnclosedSimpleBlockNode=function isUnclosedSimpleBlockNode(e){return UnclosedSimpleBlockNode.isUnclosedSimpleBlockNode(e)},exports.isWhitespaceNode=function isWhitespaceNode(e){return WhitespaceNode.isWhitespaceNode(e)},exports.parseCommaSeparatedListOfComponentValues=function parseCommaSeparatedListOfComponentValues(e,o){const t={onParseError:(null==o?void 0:o.onParseError)??(()=>{})},s=[...e];if(0===e.length)return[];s[s.length-1][0]!==n.TokenType.EOF&&s.push([n.TokenType.EOF,"",s[s.length-1][2],s[s.length-1][3],void 0]);const i=[];let c=[],r=0;for(;;){if(!s[r]||s[r][0]===n.TokenType.EOF)return c.length&&i.push(c),i;if(s[r][0]===n.TokenType.Comma){i.push(c),c=[],r++;continue}const o=consumeComponentValue(t,e.slice(r));c.push(o.node),r+=o.advance}},exports.parseComponentValue=function parseComponentValue(e,o){const t={onParseError:(null==o?void 0:o.onParseError)??(()=>{})},s=[...e];s[s.length-1][0]!==n.TokenType.EOF&&s.push([n.TokenType.EOF,"",s[s.length-1][2],s[s.length-1][3],void 0]);const i=consumeComponentValue(t,s);if(s[Math.min(i.advance,s.length-1)][0]===n.TokenType.EOF)return i.node;t.onParseError({message:"Expected EOF after parsing a component value.",start:e[0][2],end:e[e.length-1][3],state:["5.3.9. Parse a component value","Expected EOF"]})},exports.parseListOfComponentValues=function parseListOfComponentValues(e,o){const t={onParseError:(null==o?void 0:o.onParseError)??(()=>{})},s=[...e];s[s.length-1][0]!==n.TokenType.EOF&&s.push([n.TokenType.EOF,"",s[s.length-1][2],s[s.length-1][3],void 0]);const i=[];let c=0;for(;;){if(!s[c]||s[c][0]===n.TokenType.EOF)return i;const e=consumeComponentValue(t,s.slice(c));i.push(e.node),c+=e.advance}}; +"use strict";var e,n=require("@csstools/css-tokenizer");function consumeComponentValue(e,o){const t=o[0];if(t[0]===n.TokenType.OpenParen||t[0]===n.TokenType.OpenCurly||t[0]===n.TokenType.OpenSquare){const n=consumeSimpleBlock(e,o);return{advance:n.advance,node:n.node}}if(t[0]===n.TokenType.Function){const n=consumeFunction(e,o);return{advance:n.advance,node:n.node}}if(t[0]===n.TokenType.Whitespace){const n=consumeWhitespace(e,o);return{advance:n.advance,node:n.node}}if(t[0]===n.TokenType.Comment){const n=consumeComment(e,o);return{advance:n.advance,node:n.node}}return{advance:1,node:new TokenNode(t)}}exports.ComponentValueType=void 0,(e=exports.ComponentValueType||(exports.ComponentValueType={})).Function="function",e.SimpleBlock="simple-block",e.Whitespace="whitespace",e.Comment="comment",e.Token="token";class FunctionNode{type=exports.ComponentValueType.Function;name;endToken;value;constructor(e,n,o){this.name=e,this.endToken=n,this.value=o}getName(){return this.name[4].value}normalize(){this.endToken[0]===n.TokenType.EOF&&(this.endToken=[n.TokenType.CloseParen,")",-1,-1,void 0])}tokens(){return this.endToken[0]===n.TokenType.EOF?[this.name,...this.value.flatMap((e=>n.isToken(e)?e:e.tokens()))]:[this.name,...this.value.flatMap((e=>n.isToken(e)?e:e.tokens())),this.endToken]}toString(){const e=this.value.map((e=>n.isToken(e)?n.stringify(e):e.toString())).join("");return n.stringify(this.name)+e+n.stringify(this.endToken)}indexOf(e){return this.value.indexOf(e)}at(e){if("number"==typeof e)return e<0&&(e=this.value.length+e),this.value[e]}walk(e){let n=!1;if(this.value.forEach(((o,t)=>{n||(!1!==e({node:o,parent:this},t)?"walk"in o&&!1===o.walk(e)&&(n=!0):n=!0)})),n)return!1}toJSON(){return{type:this.type,name:this.getName(),tokens:this.tokens(),value:this.value.map((e=>e.toJSON()))}}isFunctionNode(){return FunctionNode.isFunctionNode(this)}static isFunctionNode(e){return!!e&&(e instanceof FunctionNode&&e.type===exports.ComponentValueType.Function)}}function consumeFunction(e,o){const t=[];let s=1;for(;;){const i=o[s];if(!i||i[0]===n.TokenType.EOF)return e.onParseError(new n.ParseError("Unexpected EOF while consuming a function.",o[0][2],o[o.length-1][3],["5.4.9. Consume a function","Unexpected EOF"])),{advance:o.length,node:new FunctionNode(o[0],i,t)};if(i[0]===n.TokenType.CloseParen)return{advance:s+1,node:new FunctionNode(o[0],i,t)};if(i[0]===n.TokenType.Comment||i[0]===n.TokenType.Whitespace){const n=consumeAllCommentsAndWhitespace(e,o.slice(s));s+=n.advance,t.push(...n.nodes);continue}const r=consumeComponentValue(e,o.slice(s));s+=r.advance,t.push(r.node)}}class SimpleBlockNode{type=exports.ComponentValueType.SimpleBlock;startToken;endToken;value;constructor(e,n,o){this.startToken=e,this.endToken=n,this.value=o}normalize(){if(this.endToken[0]===n.TokenType.EOF){const e=n.mirrorVariant(this.startToken);e&&(this.endToken=e)}}tokens(){return this.endToken[0]===n.TokenType.EOF?[this.startToken,...this.value.flatMap((e=>n.isToken(e)?e:e.tokens()))]:[this.startToken,...this.value.flatMap((e=>n.isToken(e)?e:e.tokens())),this.endToken]}toString(){const e=this.value.map((e=>n.isToken(e)?n.stringify(e):e.toString())).join("");return n.stringify(this.startToken)+e+n.stringify(this.endToken)}indexOf(e){return this.value.indexOf(e)}at(e){if("number"==typeof e)return e<0&&(e=this.value.length+e),this.value[e]}walk(e){let n=!1;if(this.value.forEach(((o,t)=>{n||(!1!==e({node:o,parent:this},t)?"walk"in o&&!1===o.walk(e)&&(n=!0):n=!0)})),n)return!1}toJSON(){return{type:this.type,startToken:this.startToken,tokens:this.tokens(),value:this.value.map((e=>e.toJSON()))}}isSimpleBlockNode(){return SimpleBlockNode.isSimpleBlockNode(this)}static isSimpleBlockNode(e){return!!e&&(e instanceof SimpleBlockNode&&e.type===exports.ComponentValueType.SimpleBlock)}}function consumeSimpleBlock(e,o){const t=n.mirrorVariantType(o[0][0]);if(!t)throw new Error("Failed to parse, a mirror variant must exist for all block open tokens.");const s=[];let i=1;for(;;){const r=o[i];if(!r||r[0]===n.TokenType.EOF)return e.onParseError(new n.ParseError("Unexpected EOF while consuming a simple block.",o[0][2],o[o.length-1][3],["5.4.8. Consume a simple block","Unexpected EOF"])),{advance:o.length,node:new SimpleBlockNode(o[0],r,s)};if(r[0]===t)return{advance:i+1,node:new SimpleBlockNode(o[0],r,s)};if(r[0]===n.TokenType.Comment||r[0]===n.TokenType.Whitespace){const n=consumeAllCommentsAndWhitespace(e,o.slice(i));i+=n.advance,s.push(...n.nodes);continue}const a=consumeComponentValue(e,o.slice(i));i+=a.advance,s.push(a.node)}}class WhitespaceNode{type=exports.ComponentValueType.Whitespace;value;constructor(e){this.value=e}tokens(){return this.value}toString(){return n.stringify(...this.value)}toJSON(){return{type:this.type,tokens:this.tokens()}}isWhitespaceNode(){return WhitespaceNode.isWhitespaceNode(this)}static isWhitespaceNode(e){return!!e&&(e instanceof WhitespaceNode&&e.type===exports.ComponentValueType.Whitespace)}}function consumeWhitespace(e,o){let t=0;for(;;){if(o[t][0]!==n.TokenType.Whitespace)return{advance:t,node:new WhitespaceNode(o.slice(0,t))};t++}}class CommentNode{type=exports.ComponentValueType.Comment;value;constructor(e){this.value=e}tokens(){return[this.value]}toString(){return n.stringify(this.value)}toJSON(){return{type:this.type,tokens:this.tokens()}}isCommentNode(){return CommentNode.isCommentNode(this)}static isCommentNode(e){return!!e&&(e instanceof CommentNode&&e.type===exports.ComponentValueType.Comment)}}function consumeComment(e,n){return{advance:1,node:new CommentNode(n[0])}}function consumeAllCommentsAndWhitespace(e,o){const t=[];let s=0;for(;;)if(o[s][0]!==n.TokenType.Whitespace){if(o[s][0]!==n.TokenType.Comment)return{advance:s,nodes:t};t.push(new CommentNode(o[s])),s++}else{const e=consumeWhitespace(0,o.slice(s));s+=e.advance,t.push(e.node)}}class TokenNode{type=exports.ComponentValueType.Token;value;constructor(e){this.value=e}tokens(){return[this.value]}toString(){return n.stringify(this.value)}toJSON(){return{type:this.type,tokens:this.tokens()}}isTokenNode(){return TokenNode.isTokenNode(this)}static isTokenNode(e){return!!e&&(e instanceof TokenNode&&e.type===exports.ComponentValueType.Token)}}exports.CommentNode=CommentNode,exports.FunctionNode=FunctionNode,exports.SimpleBlockNode=SimpleBlockNode,exports.TokenNode=TokenNode,exports.WhitespaceNode=WhitespaceNode,exports.consumeAllCommentsAndWhitespace=consumeAllCommentsAndWhitespace,exports.consumeComment=consumeComment,exports.consumeComponentValue=consumeComponentValue,exports.consumeFunction=consumeFunction,exports.consumeSimpleBlock=consumeSimpleBlock,exports.consumeWhitespace=consumeWhitespace,exports.gatherNodeAncestry=function gatherNodeAncestry(e){const n=new Map;return e.walk((e=>{Array.isArray(e.node)?e.node.forEach((o=>{n.set(o,e.parent)})):n.set(e.node,e.parent)})),n},exports.isCommentNode=function isCommentNode(e){return CommentNode.isCommentNode(e)},exports.isFunctionNode=function isFunctionNode(e){return FunctionNode.isFunctionNode(e)},exports.isSimpleBlockNode=function isSimpleBlockNode(e){return SimpleBlockNode.isSimpleBlockNode(e)},exports.isTokenNode=function isTokenNode(e){return TokenNode.isTokenNode(e)},exports.isWhitespaceNode=function isWhitespaceNode(e){return WhitespaceNode.isWhitespaceNode(e)},exports.parseCommaSeparatedListOfComponentValues=function parseCommaSeparatedListOfComponentValues(e,o){const t={onParseError:(null==o?void 0:o.onParseError)??(()=>{})},s=[...e];if(0===e.length)return[];s[s.length-1][0]!==n.TokenType.EOF&&s.push([n.TokenType.EOF,"",s[s.length-1][2],s[s.length-1][3],void 0]);const i=[];let r=[],a=0;for(;;){if(!s[a]||s[a][0]===n.TokenType.EOF)return r.length&&i.push(r),i;if(s[a][0]===n.TokenType.Comma){i.push(r),r=[],a++;continue}const o=consumeComponentValue(t,e.slice(a));r.push(o.node),a+=o.advance}},exports.parseComponentValue=function parseComponentValue(e,o){const t={onParseError:(null==o?void 0:o.onParseError)??(()=>{})},s=[...e];s[s.length-1][0]!==n.TokenType.EOF&&s.push([n.TokenType.EOF,"",s[s.length-1][2],s[s.length-1][3],void 0]);const i=consumeComponentValue(t,s);if(s[Math.min(i.advance,s.length-1)][0]===n.TokenType.EOF)return i.node;t.onParseError(new n.ParseError("Expected EOF after parsing a component value.",e[0][2],e[e.length-1][3],["5.3.9. Parse a component value","Expected EOF"]))},exports.parseListOfComponentValues=function parseListOfComponentValues(e,o){const t={onParseError:(null==o?void 0:o.onParseError)??(()=>{})},s=[...e];s[s.length-1][0]!==n.TokenType.EOF&&s.push([n.TokenType.EOF,"",s[s.length-1][2],s[s.length-1][3],void 0]);const i=[];let r=0;for(;;){if(!s[r]||s[r][0]===n.TokenType.EOF)return i;const e=consumeComponentValue(t,s.slice(r));i.push(e.node),r+=e.advance}}; diff --git a/packages/css-parser-algorithms/dist/index.d.ts b/packages/css-parser-algorithms/dist/index.d.ts index 5b32227e3..929a3ff48 100644 --- a/packages/css-parser-algorithms/dist/index.d.ts +++ b/packages/css-parser-algorithms/dist/index.d.ts @@ -3,6 +3,5 @@ export { parseComponentValue } from './parse/parse-component-value'; export { parseListOfComponentValues } from './parse/parse-list-of-component-values'; export { parseCommaSeparatedListOfComponentValues } from './parse/parse-comma-separated-list-of-component-values'; export { gatherNodeAncestry } from './util/node-ancestry'; -export { ParserError } from './interfaces/error'; export { ComponentValueType } from './util/component-value-type'; -export { isCommentNode, isFunctionNode, isSimpleBlockNode, isTokenNode, isUnclosedFunctionNode, isUnclosedSimpleBlockNode, isWhitespaceNode, } from './util/type-predicates'; +export { isCommentNode, isFunctionNode, isSimpleBlockNode, isTokenNode, isWhitespaceNode, } from './util/type-predicates'; diff --git a/packages/css-parser-algorithms/dist/index.mjs b/packages/css-parser-algorithms/dist/index.mjs index cb6b5c249..6cd7b5d71 100644 --- a/packages/css-parser-algorithms/dist/index.mjs +++ b/packages/css-parser-algorithms/dist/index.mjs @@ -1 +1 @@ -import{TokenType as e,isToken as n,stringify as o,mirrorVariantType as t}from"@csstools/css-tokenizer";var s;function consumeComponentValue(n,o){const t=o[0];if(t[0]===e.OpenParen||t[0]===e.OpenCurly||t[0]===e.OpenSquare){const e=consumeSimpleBlock(n,o);return{advance:e.advance,node:e.node}}if(t[0]===e.Function){const e=consumeFunction(n,o);return{advance:e.advance,node:e.node}}if(t[0]===e.Whitespace){const e=consumeWhitespace(n,o);return{advance:e.advance,node:e.node}}if(t[0]===e.Comment){const e=consumeComment(n,o);return{advance:e.advance,node:e.node}}return{advance:1,node:new TokenNode(t)}}!function(e){e.Function="function",e.SimpleBlock="simple-block",e.Whitespace="whitespace",e.Comment="comment",e.Token="token",e.UnclosedFunction="unclosed-function",e.UnclosedSimpleBlock="unclosed-simple-block"}(s||(s={}));class FunctionNode{type=s.Function;name;endToken;value;constructor(e,n,o){this.name=e,this.endToken=n,this.value=o}nameTokenValue(){return this.name[4].value}tokens(){return[this.name,...this.value.flatMap((e=>n(e)?e:e.tokens())),this.endToken]}toString(){const e=this.value.map((e=>n(e)?o(e):e.toString())).join("");return o(this.name)+e+o(this.endToken)}indexOf(e){return this.value.indexOf(e)}at(e){if("number"==typeof e)return e<0&&(e=this.value.length+e),this.value[e]}walk(e){let n=!1;if(this.value.forEach(((o,t)=>{n||(!1!==e({node:o,parent:this},t)?"walk"in o&&!1===o.walk(e)&&(n=!0):n=!0)})),n)return!1}toJSON(){return{type:this.type,name:this.nameTokenValue(),tokens:this.tokens(),value:this.value.map((e=>e.toJSON()))}}isFunctionNode(){return FunctionNode.isFunctionNode(this)}static isFunctionNode(e){return!!e&&(e instanceof FunctionNode&&e.type===s.Function)}}function consumeFunction(n,o){const t=[];let s=1;for(;;){const i=o[s];if(!i||i[0]===e.EOF)return n.onParseError({message:"Unexpected EOF while consuming a function.",start:o[0][2],end:o[o.length-1][3],state:["5.4.9. Consume a function","Unexpected EOF"]}),{advance:o.length,node:new UnclosedFunctionNode(o)};if(i[0]===e.CloseParen)return{advance:s+1,node:new FunctionNode(o[0],i,t)};if(i[0]===e.Comment||i[0]===e.Whitespace){const e=consumeAllCommentsAndWhitespace(n,o.slice(s));s+=e.advance,t.push(...e.nodes);continue}const c=consumeComponentValue(n,o.slice(s));s+=c.advance,t.push(c.node)}}class SimpleBlockNode{type=s.SimpleBlock;startToken;endToken;value;constructor(e,n,o){this.startToken=e,this.endToken=n,this.value=o}tokens(){return[this.startToken,...this.value.flatMap((e=>n(e)?e:e.tokens())),this.endToken]}toString(){const e=this.value.map((e=>n(e)?o(e):e.toString())).join("");return o(this.startToken)+e+o(this.endToken)}indexOf(e){return this.value.indexOf(e)}at(e){if("number"==typeof e)return e<0&&(e=this.value.length+e),this.value[e]}walk(e){let n=!1;if(this.value.forEach(((o,t)=>{n||(!1!==e({node:o,parent:this},t)?"walk"in o&&!1===o.walk(e)&&(n=!0):n=!0)})),n)return!1}toJSON(){return{type:this.type,startToken:this.startToken,tokens:this.tokens(),value:this.value.map((e=>e.toJSON()))}}isSimpleBlockNode(){return SimpleBlockNode.isSimpleBlockNode(this)}static isSimpleBlockNode(e){return!!e&&(e instanceof SimpleBlockNode&&e.type===s.SimpleBlock)}}function consumeSimpleBlock(n,o){const s=t(o[0][0]);if(!s)throw new Error("Failed to parse, a mirror variant must exist for all block open tokens.");const i=[];let c=1;for(;;){const t=o[c];if(!t||t[0]===e.EOF)return n.onParseError({message:"Unexpected EOF while consuming a simple block.",start:o[0][2],end:o[o.length-1][3],state:["5.4.8. Consume a simple block","Unexpected EOF"]}),{advance:o.length,node:new UnclosedSimpleBlockNode(o)};if(t[0]===s)return{advance:c+1,node:new SimpleBlockNode(o[0],t,i)};if(t[0]===e.Comment||t[0]===e.Whitespace){const e=consumeAllCommentsAndWhitespace(n,o.slice(c));c+=e.advance,i.push(...e.nodes);continue}const r=consumeComponentValue(n,o.slice(c));c+=r.advance,i.push(r.node)}}class WhitespaceNode{type=s.Whitespace;value;constructor(e){this.value=e}tokens(){return this.value}toString(){return o(...this.value)}toJSON(){return{type:this.type,tokens:this.tokens()}}isWhitespaceNode(){return WhitespaceNode.isWhitespaceNode(this)}static isWhitespaceNode(e){return!!e&&(e instanceof WhitespaceNode&&e.type===s.Whitespace)}}function consumeWhitespace(n,o){let t=0;for(;;){if(o[t][0]!==e.Whitespace)return{advance:t,node:new WhitespaceNode(o.slice(0,t))};t++}}class CommentNode{type=s.Comment;value;constructor(e){this.value=e}tokens(){return[this.value]}toString(){return o(this.value)}toJSON(){return{type:this.type,tokens:this.tokens()}}isCommentNode(){return CommentNode.isCommentNode(this)}static isCommentNode(e){return!!e&&(e instanceof CommentNode&&e.type===s.Comment)}}function consumeComment(e,n){return{advance:1,node:new CommentNode(n[0])}}function consumeAllCommentsAndWhitespace(n,o){const t=[];let s=0;for(;;)if(o[s][0]!==e.Whitespace){if(o[s][0]!==e.Comment)return{advance:s,nodes:t};t.push(new CommentNode(o[s])),s++}else{const e=consumeWhitespace(0,o.slice(s));s+=e.advance,t.push(e.node)}}class TokenNode{type=s.Token;value;constructor(e){this.value=e}tokens(){return[this.value]}toString(){return o(this.value)}toJSON(){return{type:this.type,tokens:this.tokens()}}isTokenNode(){return TokenNode.isTokenNode(this)}static isTokenNode(e){return!!e&&(e instanceof TokenNode&&e.type===s.Token)}}class UnclosedFunctionNode{type=s.UnclosedFunction;value;constructor(e){this.value=e}tokens(){return this.value}toString(){return o(...this.value)}toJSON(){return{type:this.type,tokens:this.tokens()}}isUnclosedFunctionNode(){return UnclosedFunctionNode.isUnclosedFunctionNode(this)}static isUnclosedFunctionNode(e){return!!e&&(e instanceof UnclosedFunctionNode&&e.type===s.UnclosedFunction)}}class UnclosedSimpleBlockNode{type=s.UnclosedSimpleBlock;value;constructor(e){this.value=e}tokens(){return this.value}toString(){return o(...this.value)}toJSON(){return{type:this.type,tokens:this.tokens()}}isUnclosedSimpleBlockNode(){return UnclosedSimpleBlockNode.isUnclosedSimpleBlockNode(this)}static isUnclosedSimpleBlockNode(e){return!!e&&(e instanceof UnclosedSimpleBlockNode&&e.type===s.UnclosedSimpleBlock)}}function parseComponentValue(n,o){const t={onParseError:(null==o?void 0:o.onParseError)??(()=>{})},s=[...n];s[s.length-1][0]!==e.EOF&&s.push([e.EOF,"",s[s.length-1][2],s[s.length-1][3],void 0]);const i=consumeComponentValue(t,s);if(s[Math.min(i.advance,s.length-1)][0]===e.EOF)return i.node;t.onParseError({message:"Expected EOF after parsing a component value.",start:n[0][2],end:n[n.length-1][3],state:["5.3.9. Parse a component value","Expected EOF"]})}function parseListOfComponentValues(n,o){const t={onParseError:(null==o?void 0:o.onParseError)??(()=>{})},s=[...n];s[s.length-1][0]!==e.EOF&&s.push([e.EOF,"",s[s.length-1][2],s[s.length-1][3],void 0]);const i=[];let c=0;for(;;){if(!s[c]||s[c][0]===e.EOF)return i;const n=consumeComponentValue(t,s.slice(c));i.push(n.node),c+=n.advance}}function parseCommaSeparatedListOfComponentValues(n,o){const t={onParseError:(null==o?void 0:o.onParseError)??(()=>{})},s=[...n];if(0===n.length)return[];s[s.length-1][0]!==e.EOF&&s.push([e.EOF,"",s[s.length-1][2],s[s.length-1][3],void 0]);const i=[];let c=[],r=0;for(;;){if(!s[r]||s[r][0]===e.EOF)return c.length&&i.push(c),i;if(s[r][0]===e.Comma){i.push(c),c=[],r++;continue}const o=consumeComponentValue(t,n.slice(r));c.push(o.node),r+=o.advance}}function gatherNodeAncestry(e){const n=new Map;return e.walk((e=>{Array.isArray(e.node)?e.node.forEach((o=>{n.set(o,e.parent)})):n.set(e.node,e.parent)})),n}function isSimpleBlockNode(e){return SimpleBlockNode.isSimpleBlockNode(e)}function isFunctionNode(e){return FunctionNode.isFunctionNode(e)}function isUnclosedSimpleBlockNode(e){return UnclosedSimpleBlockNode.isUnclosedSimpleBlockNode(e)}function isUnclosedFunctionNode(e){return UnclosedFunctionNode.isUnclosedFunctionNode(e)}function isWhitespaceNode(e){return WhitespaceNode.isWhitespaceNode(e)}function isCommentNode(e){return CommentNode.isCommentNode(e)}function isTokenNode(e){return TokenNode.isTokenNode(e)}export{CommentNode,s as ComponentValueType,FunctionNode,SimpleBlockNode,TokenNode,UnclosedFunctionNode,UnclosedSimpleBlockNode,WhitespaceNode,consumeAllCommentsAndWhitespace,consumeComment,consumeComponentValue,consumeFunction,consumeSimpleBlock,consumeWhitespace,gatherNodeAncestry,isCommentNode,isFunctionNode,isSimpleBlockNode,isTokenNode,isUnclosedFunctionNode,isUnclosedSimpleBlockNode,isWhitespaceNode,parseCommaSeparatedListOfComponentValues,parseComponentValue,parseListOfComponentValues}; +import{TokenType as e,isToken as n,stringify as t,ParseError as o,mirrorVariant as s,mirrorVariantType as i}from"@csstools/css-tokenizer";var a;function consumeComponentValue(n,t){const o=t[0];if(o[0]===e.OpenParen||o[0]===e.OpenCurly||o[0]===e.OpenSquare){const e=consumeSimpleBlock(n,t);return{advance:e.advance,node:e.node}}if(o[0]===e.Function){const e=consumeFunction(n,t);return{advance:e.advance,node:e.node}}if(o[0]===e.Whitespace){const e=consumeWhitespace(n,t);return{advance:e.advance,node:e.node}}if(o[0]===e.Comment){const e=consumeComment(n,t);return{advance:e.advance,node:e.node}}return{advance:1,node:new TokenNode(o)}}!function(e){e.Function="function",e.SimpleBlock="simple-block",e.Whitespace="whitespace",e.Comment="comment",e.Token="token"}(a||(a={}));class FunctionNode{type=a.Function;name;endToken;value;constructor(e,n,t){this.name=e,this.endToken=n,this.value=t}getName(){return this.name[4].value}normalize(){this.endToken[0]===e.EOF&&(this.endToken=[e.CloseParen,")",-1,-1,void 0])}tokens(){return this.endToken[0]===e.EOF?[this.name,...this.value.flatMap((e=>n(e)?e:e.tokens()))]:[this.name,...this.value.flatMap((e=>n(e)?e:e.tokens())),this.endToken]}toString(){const e=this.value.map((e=>n(e)?t(e):e.toString())).join("");return t(this.name)+e+t(this.endToken)}indexOf(e){return this.value.indexOf(e)}at(e){if("number"==typeof e)return e<0&&(e=this.value.length+e),this.value[e]}walk(e){let n=!1;if(this.value.forEach(((t,o)=>{n||(!1!==e({node:t,parent:this},o)?"walk"in t&&!1===t.walk(e)&&(n=!0):n=!0)})),n)return!1}toJSON(){return{type:this.type,name:this.getName(),tokens:this.tokens(),value:this.value.map((e=>e.toJSON()))}}isFunctionNode(){return FunctionNode.isFunctionNode(this)}static isFunctionNode(e){return!!e&&(e instanceof FunctionNode&&e.type===a.Function)}}function consumeFunction(n,t){const s=[];let i=1;for(;;){const a=t[i];if(!a||a[0]===e.EOF)return n.onParseError(new o("Unexpected EOF while consuming a function.",t[0][2],t[t.length-1][3],["5.4.9. Consume a function","Unexpected EOF"])),{advance:t.length,node:new FunctionNode(t[0],a,s)};if(a[0]===e.CloseParen)return{advance:i+1,node:new FunctionNode(t[0],a,s)};if(a[0]===e.Comment||a[0]===e.Whitespace){const e=consumeAllCommentsAndWhitespace(n,t.slice(i));i+=e.advance,s.push(...e.nodes);continue}const r=consumeComponentValue(n,t.slice(i));i+=r.advance,s.push(r.node)}}class SimpleBlockNode{type=a.SimpleBlock;startToken;endToken;value;constructor(e,n,t){this.startToken=e,this.endToken=n,this.value=t}normalize(){if(this.endToken[0]===e.EOF){const e=s(this.startToken);e&&(this.endToken=e)}}tokens(){return this.endToken[0]===e.EOF?[this.startToken,...this.value.flatMap((e=>n(e)?e:e.tokens()))]:[this.startToken,...this.value.flatMap((e=>n(e)?e:e.tokens())),this.endToken]}toString(){const e=this.value.map((e=>n(e)?t(e):e.toString())).join("");return t(this.startToken)+e+t(this.endToken)}indexOf(e){return this.value.indexOf(e)}at(e){if("number"==typeof e)return e<0&&(e=this.value.length+e),this.value[e]}walk(e){let n=!1;if(this.value.forEach(((t,o)=>{n||(!1!==e({node:t,parent:this},o)?"walk"in t&&!1===t.walk(e)&&(n=!0):n=!0)})),n)return!1}toJSON(){return{type:this.type,startToken:this.startToken,tokens:this.tokens(),value:this.value.map((e=>e.toJSON()))}}isSimpleBlockNode(){return SimpleBlockNode.isSimpleBlockNode(this)}static isSimpleBlockNode(e){return!!e&&(e instanceof SimpleBlockNode&&e.type===a.SimpleBlock)}}function consumeSimpleBlock(n,t){const s=i(t[0][0]);if(!s)throw new Error("Failed to parse, a mirror variant must exist for all block open tokens.");const a=[];let r=1;for(;;){const i=t[r];if(!i||i[0]===e.EOF)return n.onParseError(new o("Unexpected EOF while consuming a simple block.",t[0][2],t[t.length-1][3],["5.4.8. Consume a simple block","Unexpected EOF"])),{advance:t.length,node:new SimpleBlockNode(t[0],i,a)};if(i[0]===s)return{advance:r+1,node:new SimpleBlockNode(t[0],i,a)};if(i[0]===e.Comment||i[0]===e.Whitespace){const e=consumeAllCommentsAndWhitespace(n,t.slice(r));r+=e.advance,a.push(...e.nodes);continue}const c=consumeComponentValue(n,t.slice(r));r+=c.advance,a.push(c.node)}}class WhitespaceNode{type=a.Whitespace;value;constructor(e){this.value=e}tokens(){return this.value}toString(){return t(...this.value)}toJSON(){return{type:this.type,tokens:this.tokens()}}isWhitespaceNode(){return WhitespaceNode.isWhitespaceNode(this)}static isWhitespaceNode(e){return!!e&&(e instanceof WhitespaceNode&&e.type===a.Whitespace)}}function consumeWhitespace(n,t){let o=0;for(;;){if(t[o][0]!==e.Whitespace)return{advance:o,node:new WhitespaceNode(t.slice(0,o))};o++}}class CommentNode{type=a.Comment;value;constructor(e){this.value=e}tokens(){return[this.value]}toString(){return t(this.value)}toJSON(){return{type:this.type,tokens:this.tokens()}}isCommentNode(){return CommentNode.isCommentNode(this)}static isCommentNode(e){return!!e&&(e instanceof CommentNode&&e.type===a.Comment)}}function consumeComment(e,n){return{advance:1,node:new CommentNode(n[0])}}function consumeAllCommentsAndWhitespace(n,t){const o=[];let s=0;for(;;)if(t[s][0]!==e.Whitespace){if(t[s][0]!==e.Comment)return{advance:s,nodes:o};o.push(new CommentNode(t[s])),s++}else{const e=consumeWhitespace(0,t.slice(s));s+=e.advance,o.push(e.node)}}class TokenNode{type=a.Token;value;constructor(e){this.value=e}tokens(){return[this.value]}toString(){return t(this.value)}toJSON(){return{type:this.type,tokens:this.tokens()}}isTokenNode(){return TokenNode.isTokenNode(this)}static isTokenNode(e){return!!e&&(e instanceof TokenNode&&e.type===a.Token)}}function parseComponentValue(n,t){const s={onParseError:(null==t?void 0:t.onParseError)??(()=>{})},i=[...n];i[i.length-1][0]!==e.EOF&&i.push([e.EOF,"",i[i.length-1][2],i[i.length-1][3],void 0]);const a=consumeComponentValue(s,i);if(i[Math.min(a.advance,i.length-1)][0]===e.EOF)return a.node;s.onParseError(new o("Expected EOF after parsing a component value.",n[0][2],n[n.length-1][3],["5.3.9. Parse a component value","Expected EOF"]))}function parseListOfComponentValues(n,t){const o={onParseError:(null==t?void 0:t.onParseError)??(()=>{})},s=[...n];s[s.length-1][0]!==e.EOF&&s.push([e.EOF,"",s[s.length-1][2],s[s.length-1][3],void 0]);const i=[];let a=0;for(;;){if(!s[a]||s[a][0]===e.EOF)return i;const n=consumeComponentValue(o,s.slice(a));i.push(n.node),a+=n.advance}}function parseCommaSeparatedListOfComponentValues(n,t){const o={onParseError:(null==t?void 0:t.onParseError)??(()=>{})},s=[...n];if(0===n.length)return[];s[s.length-1][0]!==e.EOF&&s.push([e.EOF,"",s[s.length-1][2],s[s.length-1][3],void 0]);const i=[];let a=[],r=0;for(;;){if(!s[r]||s[r][0]===e.EOF)return a.length&&i.push(a),i;if(s[r][0]===e.Comma){i.push(a),a=[],r++;continue}const t=consumeComponentValue(o,n.slice(r));a.push(t.node),r+=t.advance}}function gatherNodeAncestry(e){const n=new Map;return e.walk((e=>{Array.isArray(e.node)?e.node.forEach((t=>{n.set(t,e.parent)})):n.set(e.node,e.parent)})),n}function isSimpleBlockNode(e){return SimpleBlockNode.isSimpleBlockNode(e)}function isFunctionNode(e){return FunctionNode.isFunctionNode(e)}function isWhitespaceNode(e){return WhitespaceNode.isWhitespaceNode(e)}function isCommentNode(e){return CommentNode.isCommentNode(e)}function isTokenNode(e){return TokenNode.isTokenNode(e)}export{CommentNode,a as ComponentValueType,FunctionNode,SimpleBlockNode,TokenNode,WhitespaceNode,consumeAllCommentsAndWhitespace,consumeComment,consumeComponentValue,consumeFunction,consumeSimpleBlock,consumeWhitespace,gatherNodeAncestry,isCommentNode,isFunctionNode,isSimpleBlockNode,isTokenNode,isWhitespaceNode,parseCommaSeparatedListOfComponentValues,parseComponentValue,parseListOfComponentValues}; diff --git a/packages/css-parser-algorithms/dist/interfaces/context.d.ts b/packages/css-parser-algorithms/dist/interfaces/context.d.ts index e73055642..dc246e000 100644 --- a/packages/css-parser-algorithms/dist/interfaces/context.d.ts +++ b/packages/css-parser-algorithms/dist/interfaces/context.d.ts @@ -1,4 +1,4 @@ -import { ParserError } from './error'; +import { ParseError } from '@csstools/css-tokenizer'; export type Context = { - onParseError: (error: ParserError) => void; + onParseError: (error: ParseError) => void; }; diff --git a/packages/css-parser-algorithms/dist/interfaces/error.d.ts b/packages/css-parser-algorithms/dist/interfaces/error.d.ts deleted file mode 100644 index feff4c468..000000000 --- a/packages/css-parser-algorithms/dist/interfaces/error.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -export type ParserError = { - message: string; - start: number; - end: number; - state: Array; -}; diff --git a/packages/css-parser-algorithms/dist/parse/parse-comma-separated-list-of-component-values.d.ts b/packages/css-parser-algorithms/dist/parse/parse-comma-separated-list-of-component-values.d.ts index 62fcdc01a..67dff1d04 100644 --- a/packages/css-parser-algorithms/dist/parse/parse-comma-separated-list-of-component-values.d.ts +++ b/packages/css-parser-algorithms/dist/parse/parse-comma-separated-list-of-component-values.d.ts @@ -1,6 +1,5 @@ -import { ParserError } from '../interfaces/error'; -import { CSSToken } from '@csstools/css-tokenizer'; +import { CSSToken, ParseError } from '@csstools/css-tokenizer'; import { ComponentValue } from '../consume/consume-component-block-function'; export declare function parseCommaSeparatedListOfComponentValues(tokens: Array, options?: { - onParseError?: (error: ParserError) => void; + onParseError?: (error: ParseError) => void; }): ComponentValue[][]; diff --git a/packages/css-parser-algorithms/dist/parse/parse-component-value.d.ts b/packages/css-parser-algorithms/dist/parse/parse-component-value.d.ts index c89a438ed..7860d7948 100644 --- a/packages/css-parser-algorithms/dist/parse/parse-component-value.d.ts +++ b/packages/css-parser-algorithms/dist/parse/parse-component-value.d.ts @@ -1,5 +1,4 @@ -import { ParserError } from '../interfaces/error'; -import { CSSToken } from '@csstools/css-tokenizer'; +import { CSSToken, ParseError } from '@csstools/css-tokenizer'; export declare function parseComponentValue(tokens: Array, options?: { - onParseError?: (error: ParserError) => void; + onParseError?: (error: ParseError) => void; }): import("../consume/consume-component-block-function").ComponentValue; diff --git a/packages/css-parser-algorithms/dist/parse/parse-list-of-component-values.d.ts b/packages/css-parser-algorithms/dist/parse/parse-list-of-component-values.d.ts index 7c823cfd2..737f554d2 100644 --- a/packages/css-parser-algorithms/dist/parse/parse-list-of-component-values.d.ts +++ b/packages/css-parser-algorithms/dist/parse/parse-list-of-component-values.d.ts @@ -1,6 +1,5 @@ -import { ParserError } from '../interfaces/error'; -import { CSSToken } from '@csstools/css-tokenizer'; +import { CSSToken, ParseError } from '@csstools/css-tokenizer'; import { ComponentValue } from '../consume/consume-component-block-function'; export declare function parseListOfComponentValues(tokens: Array, options?: { - onParseError?: (error: ParserError) => void; + onParseError?: (error: ParseError) => void; }): ComponentValue[]; diff --git a/packages/css-parser-algorithms/dist/util/component-value-type.d.ts b/packages/css-parser-algorithms/dist/util/component-value-type.d.ts index a34707707..eda7ee93c 100644 --- a/packages/css-parser-algorithms/dist/util/component-value-type.d.ts +++ b/packages/css-parser-algorithms/dist/util/component-value-type.d.ts @@ -3,7 +3,5 @@ export declare enum ComponentValueType { SimpleBlock = "simple-block", Whitespace = "whitespace", Comment = "comment", - Token = "token", - UnclosedFunction = "unclosed-function", - UnclosedSimpleBlock = "unclosed-simple-block" + Token = "token" } diff --git a/packages/css-parser-algorithms/dist/util/type-predicates.d.ts b/packages/css-parser-algorithms/dist/util/type-predicates.d.ts index 1cbf896d4..896d3036b 100644 --- a/packages/css-parser-algorithms/dist/util/type-predicates.d.ts +++ b/packages/css-parser-algorithms/dist/util/type-predicates.d.ts @@ -1,8 +1,6 @@ -import { CommentNode, FunctionNode, SimpleBlockNode, TokenNode, UnclosedFunctionNode, UnclosedSimpleBlockNode, WhitespaceNode } from '../consume/consume-component-block-function'; +import { CommentNode, FunctionNode, SimpleBlockNode, TokenNode, WhitespaceNode } from '../consume/consume-component-block-function'; export declare function isSimpleBlockNode(x: unknown): x is SimpleBlockNode; export declare function isFunctionNode(x: unknown): x is FunctionNode; -export declare function isUnclosedSimpleBlockNode(x: unknown): x is UnclosedSimpleBlockNode; -export declare function isUnclosedFunctionNode(x: unknown): x is UnclosedFunctionNode; export declare function isWhitespaceNode(x: unknown): x is WhitespaceNode; export declare function isCommentNode(x: unknown): x is CommentNode; export declare function isTokenNode(x: unknown): x is TokenNode; diff --git a/packages/css-parser-algorithms/src/consume/consume-component-block-function.ts b/packages/css-parser-algorithms/src/consume/consume-component-block-function.ts index 726b51843..a18a4dcd8 100644 --- a/packages/css-parser-algorithms/src/consume/consume-component-block-function.ts +++ b/packages/css-parser-algorithms/src/consume/consume-component-block-function.ts @@ -1,10 +1,10 @@ -import { CSSToken, mirrorVariantType, stringify, TokenType, isToken, TokenFunction } from '@csstools/css-tokenizer'; +import { CSSToken, mirrorVariantType, mirrorVariant, stringify, TokenType, isToken, TokenFunction, ParseError } from '@csstools/css-tokenizer'; import { Context } from '../interfaces/context'; import { ComponentValueType } from '../util/component-value-type'; export type ContainerNode = FunctionNode | SimpleBlockNode; -export type ComponentValue = FunctionNode | SimpleBlockNode | WhitespaceNode | CommentNode | TokenNode | UnclosedSimpleBlockNode | UnclosedFunctionNode; +export type ComponentValue = FunctionNode | SimpleBlockNode | WhitespaceNode | CommentNode | TokenNode; // https://www.w3.org/TR/css-syntax-3/#consume-a-component-value export function consumeComponentValue(ctx: Context, tokens: Array): { advance: number, node: ComponentValue } { @@ -64,11 +64,34 @@ export class FunctionNode { this.value = value; } - nameTokenValue(): string { + getName(): string { return this.name[4].value; } + /** + * Normalize the current Function: + * - if the "endToken" is EOF, replace with a ")-token" + */ + normalize() { + if (this.endToken[0] === TokenType.EOF) { + this.endToken = [TokenType.CloseParen, ')', -1, -1, undefined]; + } + } + tokens(): Array { + if (this.endToken[0] === TokenType.EOF) { + return [ + this.name, + ...this.value.flatMap((x) => { + if (isToken(x)) { + return x; + } + + return x.tokens(); + }), + ]; + } + return [ this.name, ...this.value.flatMap((x) => { @@ -98,7 +121,7 @@ export class FunctionNode { return this.value.indexOf(item); } - at(index: number | string) { + at(index: number | string): ComponentValue | undefined { if (typeof index === 'number') { if (index < 0) { index = this.value.length + index; @@ -136,7 +159,7 @@ export class FunctionNode { toJSON() { return { type: this.type, - name: this.nameTokenValue(), + name: this.getName(), tokens: this.tokens(), value: this.value.map((x) => x.toJSON()), }; @@ -160,7 +183,7 @@ export class FunctionNode { } // https://www.w3.org/TR/css-syntax-3/#consume-function -export function consumeFunction(ctx: Context, tokens: Array): { advance: number, node: FunctionNode | UnclosedFunctionNode } { +export function consumeFunction(ctx: Context, tokens: Array): { advance: number, node: FunctionNode } { const value: Array = []; let i = 1; @@ -169,19 +192,19 @@ export function consumeFunction(ctx: Context, tokens: Array): { advanc while (true) { const token = tokens[i]; if (!token || token[0] === TokenType.EOF) { - ctx.onParseError({ - message: 'Unexpected EOF while consuming a function.', - start: tokens[0][2], - end: tokens[tokens.length - 1][3], - state: [ + ctx.onParseError(new ParseError( + 'Unexpected EOF while consuming a function.', + tokens[0][2], + tokens[tokens.length - 1][3], + [ '5.4.9. Consume a function', 'Unexpected EOF', ], - }); + )); return { advance: tokens.length, - node: new UnclosedFunctionNode(tokens), + node: new FunctionNode(tokens[0] as TokenFunction, token, value), }; } @@ -218,7 +241,33 @@ export class SimpleBlockNode { this.value = value; } + /** + * Normalize the current Simple Block: + * - if the "endToken" is EOF, replace with the mirror token of the "startToken" + */ + normalize() { + if (this.endToken[0] === TokenType.EOF) { + const mirror = mirrorVariant(this.startToken); + if (mirror) { + this.endToken = mirror; + } + } + } + tokens(): Array { + if (this.endToken[0] === TokenType.EOF) { + return [ + this.startToken, + ...this.value.flatMap((x) => { + if (isToken(x)) { + return x; + } + + return x.tokens(); + }), + ]; + } + return [ this.startToken, ...this.value.flatMap((x) => { @@ -248,7 +297,7 @@ export class SimpleBlockNode { return this.value.indexOf(item); } - at(index: number | string) { + at(index: number | string): ComponentValue | undefined { if (typeof index === 'number') { if (index < 0) { index = this.value.length + index; @@ -310,7 +359,7 @@ export class SimpleBlockNode { } /** https://www.w3.org/TR/css-syntax-3/#consume-simple-block */ -export function consumeSimpleBlock(ctx: Context, tokens: Array): { advance: number, node: SimpleBlockNode | UnclosedSimpleBlockNode } { +export function consumeSimpleBlock(ctx: Context, tokens: Array): { advance: number, node: SimpleBlockNode } { const endingTokenType = mirrorVariantType(tokens[0][0]); if (!endingTokenType) { throw new Error('Failed to parse, a mirror variant must exist for all block open tokens.'); @@ -324,19 +373,19 @@ export function consumeSimpleBlock(ctx: Context, tokens: Array): { adv while (true) { const token = tokens[i]; if (!token || token[0] === TokenType.EOF) { - ctx.onParseError({ - message: 'Unexpected EOF while consuming a simple block.', - start: tokens[0][2], - end: tokens[tokens.length - 1][3], - state: [ + ctx.onParseError(new ParseError( + 'Unexpected EOF while consuming a simple block.', + tokens[0][2], + tokens[tokens.length - 1][3], + [ '5.4.8. Consume a simple block', 'Unexpected EOF', ], - }); + )); return { advance: tokens.length, - node: new UnclosedSimpleBlockNode(tokens), + node: new SimpleBlockNode(tokens[0], token, value), }; } @@ -537,85 +586,3 @@ export class TokenNode { return x.type === ComponentValueType.Token; } } - -export class UnclosedFunctionNode { - type: ComponentValueType = ComponentValueType.UnclosedFunction; - - value: Array; - - constructor(value: Array) { - this.value = value; - } - - tokens(): Array { - return this.value; - } - - toString(): string { - return stringify(...this.value); - } - - toJSON() { - return { - type: this.type, - tokens: this.tokens(), - }; - } - - isUnclosedFunctionNode(): this is UnclosedFunctionNode { - return UnclosedFunctionNode.isUnclosedFunctionNode(this); - } - - static isUnclosedFunctionNode(x: unknown): x is UnclosedFunctionNode { - if (!x) { - return false; - } - - if (!(x instanceof UnclosedFunctionNode)) { - return false; - } - - return x.type === ComponentValueType.UnclosedFunction; - } -} - -export class UnclosedSimpleBlockNode { - type: ComponentValueType = ComponentValueType.UnclosedSimpleBlock; - - value: Array; - - constructor(value: Array) { - this.value = value; - } - - tokens(): Array { - return this.value; - } - - toString(): string { - return stringify(...this.value); - } - - toJSON() { - return { - type: this.type, - tokens: this.tokens(), - }; - } - - isUnclosedSimpleBlockNode(): this is UnclosedSimpleBlockNode { - return UnclosedSimpleBlockNode.isUnclosedSimpleBlockNode(this); - } - - static isUnclosedSimpleBlockNode(x: unknown): x is UnclosedSimpleBlockNode { - if (!x) { - return false; - } - - if (!(x instanceof UnclosedSimpleBlockNode)) { - return false; - } - - return x.type === ComponentValueType.UnclosedSimpleBlock; - } -} diff --git a/packages/css-parser-algorithms/src/index.ts b/packages/css-parser-algorithms/src/index.ts index 5bb92c097..82bf6ff6c 100644 --- a/packages/css-parser-algorithms/src/index.ts +++ b/packages/css-parser-algorithms/src/index.ts @@ -3,14 +3,11 @@ export { parseComponentValue } from './parse/parse-component-value'; export { parseListOfComponentValues } from './parse/parse-list-of-component-values'; export { parseCommaSeparatedListOfComponentValues } from './parse/parse-comma-separated-list-of-component-values'; export { gatherNodeAncestry } from './util/node-ancestry'; -export { ParserError } from './interfaces/error'; export { ComponentValueType } from './util/component-value-type'; export { isCommentNode, isFunctionNode, isSimpleBlockNode, isTokenNode, - isUnclosedFunctionNode, - isUnclosedSimpleBlockNode, isWhitespaceNode, } from './util/type-predicates'; diff --git a/packages/css-parser-algorithms/src/interfaces/context.ts b/packages/css-parser-algorithms/src/interfaces/context.ts index 27eb77de6..dd261f6b9 100644 --- a/packages/css-parser-algorithms/src/interfaces/context.ts +++ b/packages/css-parser-algorithms/src/interfaces/context.ts @@ -1,5 +1,5 @@ -import { ParserError } from './error'; +import { ParseError } from '@csstools/css-tokenizer'; export type Context = { - onParseError: (error: ParserError) => void + onParseError: (error: ParseError) => void } diff --git a/packages/css-parser-algorithms/src/interfaces/error.ts b/packages/css-parser-algorithms/src/interfaces/error.ts deleted file mode 100644 index e8c677745..000000000 --- a/packages/css-parser-algorithms/src/interfaces/error.ts +++ /dev/null @@ -1,6 +0,0 @@ -export type ParserError = { - message: string, - start: number, - end: number, - state: Array -} diff --git a/packages/css-parser-algorithms/src/parse/parse-comma-separated-list-of-component-values.ts b/packages/css-parser-algorithms/src/parse/parse-comma-separated-list-of-component-values.ts index 67b5425c9..96c87fcd3 100644 --- a/packages/css-parser-algorithms/src/parse/parse-comma-separated-list-of-component-values.ts +++ b/packages/css-parser-algorithms/src/parse/parse-comma-separated-list-of-component-values.ts @@ -1,8 +1,7 @@ -import { ParserError } from '../interfaces/error'; -import { CSSToken, TokenType } from '@csstools/css-tokenizer'; +import { CSSToken, ParseError, TokenType } from '@csstools/css-tokenizer'; import { ComponentValue, consumeComponentValue } from '../consume/consume-component-block-function'; -export function parseCommaSeparatedListOfComponentValues(tokens: Array, options?: { onParseError?: (error: ParserError) => void }) { +export function parseCommaSeparatedListOfComponentValues(tokens: Array, options?: { onParseError?: (error: ParseError) => void }) { const ctx = { onParseError: options?.onParseError ?? (() => { /* noop */ }), }; diff --git a/packages/css-parser-algorithms/src/parse/parse-component-value.ts b/packages/css-parser-algorithms/src/parse/parse-component-value.ts index 1fbb8ca01..a86ae4931 100644 --- a/packages/css-parser-algorithms/src/parse/parse-component-value.ts +++ b/packages/css-parser-algorithms/src/parse/parse-component-value.ts @@ -1,8 +1,7 @@ -import { ParserError } from '../interfaces/error'; -import { CSSToken, TokenType } from '@csstools/css-tokenizer'; +import { CSSToken, ParseError, TokenType } from '@csstools/css-tokenizer'; import { consumeComponentValue } from '../consume/consume-component-block-function'; -export function parseComponentValue(tokens: Array, options?: { onParseError?: (error: ParserError) => void }) { +export function parseComponentValue(tokens: Array, options?: { onParseError?: (error: ParseError) => void }) { const ctx = { onParseError: options?.onParseError ?? (() => { /* noop */ }), }; @@ -28,13 +27,13 @@ export function parseComponentValue(tokens: Array, options?: { onParse return result.node; } - ctx.onParseError({ - message: 'Expected EOF after parsing a component value.', - start: tokens[0][2], - end: tokens[tokens.length - 1][3], - state: [ + ctx.onParseError(new ParseError( + 'Expected EOF after parsing a component value.', + tokens[0][2], + tokens[tokens.length - 1][3], + [ '5.3.9. Parse a component value', 'Expected EOF', ], - }); + )); } diff --git a/packages/css-parser-algorithms/src/parse/parse-list-of-component-values.ts b/packages/css-parser-algorithms/src/parse/parse-list-of-component-values.ts index 833a5d17a..d9d9ab63e 100644 --- a/packages/css-parser-algorithms/src/parse/parse-list-of-component-values.ts +++ b/packages/css-parser-algorithms/src/parse/parse-list-of-component-values.ts @@ -1,8 +1,7 @@ -import { ParserError } from '../interfaces/error'; -import { CSSToken, TokenType } from '@csstools/css-tokenizer'; +import { CSSToken, ParseError, TokenType } from '@csstools/css-tokenizer'; import { ComponentValue, consumeComponentValue } from '../consume/consume-component-block-function'; -export function parseListOfComponentValues(tokens: Array, options?: { onParseError?: (error: ParserError) => void }) { +export function parseListOfComponentValues(tokens: Array, options?: { onParseError?: (error: ParseError) => void }) { const ctx = { onParseError: options?.onParseError ?? (() => { /* noop */ }), }; diff --git a/packages/css-parser-algorithms/src/util/component-value-type.ts b/packages/css-parser-algorithms/src/util/component-value-type.ts index 228ae5fc7..5f278782e 100644 --- a/packages/css-parser-algorithms/src/util/component-value-type.ts +++ b/packages/css-parser-algorithms/src/util/component-value-type.ts @@ -4,6 +4,4 @@ export enum ComponentValueType { Whitespace = 'whitespace', Comment = 'comment', Token = 'token', - UnclosedFunction = 'unclosed-function', - UnclosedSimpleBlock = 'unclosed-simple-block' } diff --git a/packages/css-parser-algorithms/src/util/type-predicates.ts b/packages/css-parser-algorithms/src/util/type-predicates.ts index a5cb96eda..79ec240e5 100644 --- a/packages/css-parser-algorithms/src/util/type-predicates.ts +++ b/packages/css-parser-algorithms/src/util/type-predicates.ts @@ -1,4 +1,4 @@ -import { CommentNode, FunctionNode, SimpleBlockNode, TokenNode, UnclosedFunctionNode, UnclosedSimpleBlockNode, WhitespaceNode } from '../consume/consume-component-block-function'; +import { CommentNode, FunctionNode, SimpleBlockNode, TokenNode, WhitespaceNode } from '../consume/consume-component-block-function'; export function isSimpleBlockNode(x: unknown): x is SimpleBlockNode { return SimpleBlockNode.isSimpleBlockNode(x); @@ -8,14 +8,6 @@ export function isFunctionNode(x: unknown): x is FunctionNode { return FunctionNode.isFunctionNode(x); } -export function isUnclosedSimpleBlockNode(x: unknown): x is UnclosedSimpleBlockNode { - return UnclosedSimpleBlockNode.isUnclosedSimpleBlockNode(x); -} - -export function isUnclosedFunctionNode(x: unknown): x is UnclosedFunctionNode { - return UnclosedFunctionNode.isUnclosedFunctionNode(x); -} - export function isWhitespaceNode(x: unknown): x is WhitespaceNode { return WhitespaceNode.isWhitespaceNode(x); } diff --git a/packages/css-parser-algorithms/test/cases/various/0021.list-comma.expect.json b/packages/css-parser-algorithms/test/cases/various/0021.list-comma.expect.json new file mode 100644 index 000000000..91cf1b1e6 --- /dev/null +++ b/packages/css-parser-algorithms/test/cases/various/0021.list-comma.expect.json @@ -0,0 +1,113 @@ +[ + [ + { + "type": "simple-block", + "startToken": [ + "(-token", + "(", + 0, + 0, + null + ], + "tokens": [ + [ + "(-token", + "(", + 0, + 0, + null + ], + [ + "ident-token", + "min-width", + 1, + 9, + { + "value": "min-width" + } + ], + [ + "colon-token", + ":", + 10, + 10, + null + ], + [ + "whitespace-token", + " ", + 11, + 11, + null + ], + [ + "dimension-token", + "300px", + 12, + 16, + { + "value": 300, + "type": "integer", + "unit": "px" + } + ] + ], + "value": [ + { + "type": "token", + "tokens": [ + [ + "ident-token", + "min-width", + 1, + 9, + { + "value": "min-width" + } + ] + ] + }, + { + "type": "token", + "tokens": [ + [ + "colon-token", + ":", + 10, + 10, + null + ] + ] + }, + { + "type": "whitespace", + "tokens": [ + [ + "whitespace-token", + " ", + 11, + 11, + null + ] + ] + }, + { + "type": "token", + "tokens": [ + [ + "dimension-token", + "300px", + 12, + 16, + { + "value": 300, + "type": "integer", + "unit": "px" + } + ] + ] + } + ] + } + ] +] \ No newline at end of file diff --git a/packages/css-parser-algorithms/test/cases/various/0021.list-space.expect.json b/packages/css-parser-algorithms/test/cases/various/0021.list-space.expect.json new file mode 100644 index 000000000..efa006b93 --- /dev/null +++ b/packages/css-parser-algorithms/test/cases/various/0021.list-space.expect.json @@ -0,0 +1,111 @@ +[ + { + "type": "simple-block", + "startToken": [ + "(-token", + "(", + 0, + 0, + null + ], + "tokens": [ + [ + "(-token", + "(", + 0, + 0, + null + ], + [ + "ident-token", + "min-width", + 1, + 9, + { + "value": "min-width" + } + ], + [ + "colon-token", + ":", + 10, + 10, + null + ], + [ + "whitespace-token", + " ", + 11, + 11, + null + ], + [ + "dimension-token", + "300px", + 12, + 16, + { + "value": 300, + "type": "integer", + "unit": "px" + } + ] + ], + "value": [ + { + "type": "token", + "tokens": [ + [ + "ident-token", + "min-width", + 1, + 9, + { + "value": "min-width" + } + ] + ] + }, + { + "type": "token", + "tokens": [ + [ + "colon-token", + ":", + 10, + 10, + null + ] + ] + }, + { + "type": "whitespace", + "tokens": [ + [ + "whitespace-token", + " ", + 11, + 11, + null + ] + ] + }, + { + "type": "token", + "tokens": [ + [ + "dimension-token", + "300px", + 12, + 16, + { + "value": 300, + "type": "integer", + "unit": "px" + } + ] + ] + } + ] + } +] \ No newline at end of file diff --git a/packages/css-parser-algorithms/test/cases/various/0021.mjs b/packages/css-parser-algorithms/test/cases/various/0021.mjs new file mode 100644 index 000000000..a0d9cb818 --- /dev/null +++ b/packages/css-parser-algorithms/test/cases/various/0021.mjs @@ -0,0 +1,14 @@ +import assert from 'assert'; +import { runTest } from '../../util/run-test.mjs'; + +runTest( + '(min-width: 300px', + 'various/0021', + (actual, expected) => { + assert.deepStrictEqual( + actual, + expected, + ); + }, + true, +); diff --git a/packages/css-parser-algorithms/test/cases/wpt/0001.list-comma.expect.json b/packages/css-parser-algorithms/test/cases/wpt/0001.list-comma.expect.json new file mode 100644 index 000000000..88632bb58 --- /dev/null +++ b/packages/css-parser-algorithms/test/cases/wpt/0001.list-comma.expect.json @@ -0,0 +1,48 @@ +[ + [ + { + "type": "simple-block", + "startToken": [ + "[-token", + "[", + 0, + 0, + null + ], + "tokens": [ + [ + "[-token", + "[", + 0, + 0, + null + ], + [ + "ident-token", + "foo", + 1, + 3, + { + "value": "foo" + } + ] + ], + "value": [ + { + "type": "token", + "tokens": [ + [ + "ident-token", + "foo", + 1, + 3, + { + "value": "foo" + } + ] + ] + } + ] + } + ] +] \ No newline at end of file diff --git a/packages/css-parser-algorithms/test/cases/wpt/0001.list-space.expect.json b/packages/css-parser-algorithms/test/cases/wpt/0001.list-space.expect.json new file mode 100644 index 000000000..1c8119cee --- /dev/null +++ b/packages/css-parser-algorithms/test/cases/wpt/0001.list-space.expect.json @@ -0,0 +1,46 @@ +[ + { + "type": "simple-block", + "startToken": [ + "[-token", + "[", + 0, + 0, + null + ], + "tokens": [ + [ + "[-token", + "[", + 0, + 0, + null + ], + [ + "ident-token", + "foo", + 1, + 3, + { + "value": "foo" + } + ] + ], + "value": [ + { + "type": "token", + "tokens": [ + [ + "ident-token", + "foo", + 1, + 3, + { + "value": "foo" + } + ] + ] + } + ] + } +] \ No newline at end of file diff --git a/packages/css-parser-algorithms/test/cases/wpt/0001.mjs b/packages/css-parser-algorithms/test/cases/wpt/0001.mjs new file mode 100644 index 000000000..a68369f07 --- /dev/null +++ b/packages/css-parser-algorithms/test/cases/wpt/0001.mjs @@ -0,0 +1,15 @@ +import assert from 'assert'; +import { runTest } from '../../util/run-test.mjs'; + +// css/css-syntax/unclosed-constructs.html +runTest( + '[foo', + 'wpt/0001', + (actual, expected) => { + assert.deepStrictEqual( + actual, + expected, + ); + }, + true, +); diff --git a/packages/css-parser-algorithms/test/cases/wpt/0002.list-comma.expect.json b/packages/css-parser-algorithms/test/cases/wpt/0002.list-comma.expect.json new file mode 100644 index 000000000..f2ea7e57f --- /dev/null +++ b/packages/css-parser-algorithms/test/cases/wpt/0002.list-comma.expect.json @@ -0,0 +1,48 @@ +[ + [ + { + "type": "simple-block", + "startToken": [ + "(-token", + "(", + 0, + 0, + null + ], + "tokens": [ + [ + "(-token", + "(", + 0, + 0, + null + ], + [ + "ident-token", + "foo", + 1, + 3, + { + "value": "foo" + } + ] + ], + "value": [ + { + "type": "token", + "tokens": [ + [ + "ident-token", + "foo", + 1, + 3, + { + "value": "foo" + } + ] + ] + } + ] + } + ] +] \ No newline at end of file diff --git a/packages/css-parser-algorithms/test/cases/wpt/0002.list-space.expect.json b/packages/css-parser-algorithms/test/cases/wpt/0002.list-space.expect.json new file mode 100644 index 000000000..da80a7ade --- /dev/null +++ b/packages/css-parser-algorithms/test/cases/wpt/0002.list-space.expect.json @@ -0,0 +1,46 @@ +[ + { + "type": "simple-block", + "startToken": [ + "(-token", + "(", + 0, + 0, + null + ], + "tokens": [ + [ + "(-token", + "(", + 0, + 0, + null + ], + [ + "ident-token", + "foo", + 1, + 3, + { + "value": "foo" + } + ] + ], + "value": [ + { + "type": "token", + "tokens": [ + [ + "ident-token", + "foo", + 1, + 3, + { + "value": "foo" + } + ] + ] + } + ] + } +] \ No newline at end of file diff --git a/packages/css-parser-algorithms/test/cases/wpt/0002.mjs b/packages/css-parser-algorithms/test/cases/wpt/0002.mjs new file mode 100644 index 000000000..8028a02b7 --- /dev/null +++ b/packages/css-parser-algorithms/test/cases/wpt/0002.mjs @@ -0,0 +1,15 @@ +import assert from 'assert'; +import { runTest } from '../../util/run-test.mjs'; + +// css/css-syntax/unclosed-constructs.html +runTest( + '(foo', + 'wpt/0002', + (actual, expected) => { + assert.deepStrictEqual( + actual, + expected, + ); + }, + true, +); diff --git a/packages/css-parser-algorithms/test/cases/wpt/0003.list-comma.expect.json b/packages/css-parser-algorithms/test/cases/wpt/0003.list-comma.expect.json new file mode 100644 index 000000000..504cac274 --- /dev/null +++ b/packages/css-parser-algorithms/test/cases/wpt/0003.list-comma.expect.json @@ -0,0 +1,48 @@ +[ + [ + { + "type": "simple-block", + "startToken": [ + "{-token", + "{", + 0, + 0, + null + ], + "tokens": [ + [ + "{-token", + "{", + 0, + 0, + null + ], + [ + "ident-token", + "foo", + 1, + 3, + { + "value": "foo" + } + ] + ], + "value": [ + { + "type": "token", + "tokens": [ + [ + "ident-token", + "foo", + 1, + 3, + { + "value": "foo" + } + ] + ] + } + ] + } + ] +] \ No newline at end of file diff --git a/packages/css-parser-algorithms/test/cases/wpt/0003.list-space.expect.json b/packages/css-parser-algorithms/test/cases/wpt/0003.list-space.expect.json new file mode 100644 index 000000000..5039bbe16 --- /dev/null +++ b/packages/css-parser-algorithms/test/cases/wpt/0003.list-space.expect.json @@ -0,0 +1,46 @@ +[ + { + "type": "simple-block", + "startToken": [ + "{-token", + "{", + 0, + 0, + null + ], + "tokens": [ + [ + "{-token", + "{", + 0, + 0, + null + ], + [ + "ident-token", + "foo", + 1, + 3, + { + "value": "foo" + } + ] + ], + "value": [ + { + "type": "token", + "tokens": [ + [ + "ident-token", + "foo", + 1, + 3, + { + "value": "foo" + } + ] + ] + } + ] + } +] \ No newline at end of file diff --git a/packages/css-parser-algorithms/test/cases/wpt/0003.mjs b/packages/css-parser-algorithms/test/cases/wpt/0003.mjs new file mode 100644 index 000000000..67af48370 --- /dev/null +++ b/packages/css-parser-algorithms/test/cases/wpt/0003.mjs @@ -0,0 +1,15 @@ +import assert from 'assert'; +import { runTest } from '../../util/run-test.mjs'; + +// css/css-syntax/unclosed-constructs.html +runTest( + '{foo', + 'wpt/0003', + (actual, expected) => { + assert.deepStrictEqual( + actual, + expected, + ); + }, + true, +); diff --git a/packages/css-parser-algorithms/test/cases/wpt/0004.list-comma.expect.json b/packages/css-parser-algorithms/test/cases/wpt/0004.list-comma.expect.json new file mode 100644 index 000000000..ae41ab866 --- /dev/null +++ b/packages/css-parser-algorithms/test/cases/wpt/0004.list-comma.expect.json @@ -0,0 +1,44 @@ +[ + [ + { + "type": "function", + "name": "bar", + "tokens": [ + [ + "function-token", + "bar(", + 0, + 3, + { + "value": "bar" + } + ], + [ + "ident-token", + "foo", + 4, + 6, + { + "value": "foo" + } + ] + ], + "value": [ + { + "type": "token", + "tokens": [ + [ + "ident-token", + "foo", + 4, + 6, + { + "value": "foo" + } + ] + ] + } + ] + } + ] +] \ No newline at end of file diff --git a/packages/css-parser-algorithms/test/cases/wpt/0004.list-space.expect.json b/packages/css-parser-algorithms/test/cases/wpt/0004.list-space.expect.json new file mode 100644 index 000000000..056e61766 --- /dev/null +++ b/packages/css-parser-algorithms/test/cases/wpt/0004.list-space.expect.json @@ -0,0 +1,42 @@ +[ + { + "type": "function", + "name": "bar", + "tokens": [ + [ + "function-token", + "bar(", + 0, + 3, + { + "value": "bar" + } + ], + [ + "ident-token", + "foo", + 4, + 6, + { + "value": "foo" + } + ] + ], + "value": [ + { + "type": "token", + "tokens": [ + [ + "ident-token", + "foo", + 4, + 6, + { + "value": "foo" + } + ] + ] + } + ] + } +] \ No newline at end of file diff --git a/packages/css-parser-algorithms/test/cases/wpt/0004.mjs b/packages/css-parser-algorithms/test/cases/wpt/0004.mjs new file mode 100644 index 000000000..fcb6d5702 --- /dev/null +++ b/packages/css-parser-algorithms/test/cases/wpt/0004.mjs @@ -0,0 +1,15 @@ +import assert from 'assert'; +import { runTest } from '../../util/run-test.mjs'; + +// css/css-syntax/unclosed-constructs.html +runTest( + 'bar(foo', + 'wpt/0004', + (actual, expected) => { + assert.deepStrictEqual( + actual, + expected, + ); + }, + true, +); diff --git a/packages/css-parser-algorithms/test/test.mjs b/packages/css-parser-algorithms/test/test.mjs index 2eef7a9a5..91e5cc05e 100644 --- a/packages/css-parser-algorithms/test/test.mjs +++ b/packages/css-parser-algorithms/test/test.mjs @@ -36,3 +36,9 @@ import './cases/various/0017.mjs'; import './cases/various/0018.mjs'; import './cases/various/0019.mjs'; import './cases/various/0020.mjs'; +import './cases/various/0021.mjs'; + +import './cases/wpt/0001.mjs'; +import './cases/wpt/0002.mjs'; +import './cases/wpt/0003.mjs'; +import './cases/wpt/0004.mjs'; diff --git a/packages/css-parser-algorithms/test/util/run-test.mjs b/packages/css-parser-algorithms/test/util/run-test.mjs index 5d58211c9..525580a41 100644 --- a/packages/css-parser-algorithms/test/util/run-test.mjs +++ b/packages/css-parser-algorithms/test/util/run-test.mjs @@ -3,8 +3,12 @@ import path from 'path'; import { parseCommaSeparatedListOfComponentValues, parseListOfComponentValues } from '@csstools/css-parser-algorithms'; import { tokenizer } from '@csstools/css-tokenizer'; -export function runTest(source, testPath, assertEqual) { +export function runTest(source, testPath, assertEqual, expectParseError = false) { const onParseError = (err) => { + if (expectParseError) { + return; + } + console.warn(err); throw new Error(`Unable to parse "${source}"`); }; diff --git a/packages/css-tokenizer/CHANGELOG.md b/packages/css-tokenizer/CHANGELOG.md index cd7fc18e7..3412c20f0 100644 --- a/packages/css-tokenizer/CHANGELOG.md +++ b/packages/css-tokenizer/CHANGELOG.md @@ -1,3 +1,9 @@ +### Unreleased + +- Simplify `Reader` interface (breaking) +- Change the `ParseError` interface, this is now a subclass of `Error` (breaking) +- Improve performance + ### 1.0.0 (November 14, 2022) - Initial version diff --git a/packages/css-tokenizer/README.md b/packages/css-tokenizer/README.md index 3fc1cef61..9117513b9 100644 --- a/packages/css-tokenizer/README.md +++ b/packages/css-tokenizer/README.md @@ -43,7 +43,7 @@ while (true) { ```ts { commentsAreTokens?: false, - onParseError?: (error: ParserError) => void + onParseError?: (error: ParseError) => void } ``` @@ -94,22 +94,9 @@ while (true) { } ``` -logs : - -```js -{ - message: 'Unexpected EOF while consuming an escaped code point.', - start: 0, - end: 0, - state: ['4.3.7. Consume an escaped code point', 'Unexpected EOF'], -} -``` - Parser errors will try to inform you about the point in the tokenizer logic the error happened. This tells you the kind of error. -`start` and `end` are the location in your CSS source code. - ## Goals and non-goals Things this package aims to be: diff --git a/packages/css-tokenizer/dist/code-points/code-points-to-string.d.ts b/packages/css-tokenizer/dist/code-points/code-points-to-string.d.ts deleted file mode 100644 index bb5e034c7..000000000 --- a/packages/css-tokenizer/dist/code-points/code-points-to-string.d.ts +++ /dev/null @@ -1 +0,0 @@ -export declare function codePointsToString(codePoints: Array): string; diff --git a/packages/css-tokenizer/dist/code-points/code-points.d.ts b/packages/css-tokenizer/dist/code-points/code-points.d.ts index 66cbce284..c920f6193 100644 --- a/packages/css-tokenizer/dist/code-points/code-points.d.ts +++ b/packages/css-tokenizer/dist/code-points/code-points.d.ts @@ -1,78 +1,78 @@ /** ' */ -export declare const APOSTROPHE: number; +export declare const APOSTROPHE = 39; /** * */ -export declare const ASTERISK: number; +export declare const ASTERISK = 42; /** \b */ -export declare const BACKSPACE: number; +export declare const BACKSPACE = 8; /** \r */ -export declare const CARRIAGE_RETURN: number; +export declare const CARRIAGE_RETURN = 13; /** \t */ -export declare const CHARACTER_TABULATION: number; +export declare const CHARACTER_TABULATION = 9; /** : */ -export declare const COLON: number; +export declare const COLON = 58; /** , */ -export declare const COMMA: number; +export declare const COMMA = 44; /** @ */ -export declare const COMMERCIAL_AT: number; +export declare const COMMERCIAL_AT = 64; /** \x7F */ -export declare const DELETE: number; +export declare const DELETE = 127; /** ! */ -export declare const EXCLAMATION_MARK: number; +export declare const EXCLAMATION_MARK = 33; /** \f */ -export declare const FORM_FEED: number; +export declare const FORM_FEED = 12; /** . */ -export declare const FULL_STOP: number; +export declare const FULL_STOP = 46; /** > */ -export declare const GREATER_THAN_SIGN: number; +export declare const GREATER_THAN_SIGN = 62; /** - */ -export declare const HYPHEN_MINUS: number; +export declare const HYPHEN_MINUS = 45; /** \x1F */ -export declare const INFORMATION_SEPARATOR_ONE: number; +export declare const INFORMATION_SEPARATOR_ONE = 31; /** E */ -export declare const LATIN_CAPITAL_LETTER_E: number; +export declare const LATIN_CAPITAL_LETTER_E = 69; /** e */ -export declare const LATIN_SMALL_LETTER_E: number; +export declare const LATIN_SMALL_LETTER_E = 101; /** { */ -export declare const LEFT_CURLY_BRACKET: number; +export declare const LEFT_CURLY_BRACKET = 123; /** ( */ -export declare const LEFT_PARENTHESIS: number; +export declare const LEFT_PARENTHESIS = 40; /** [ */ -export declare const LEFT_SQUARE_BRACKET: number; +export declare const LEFT_SQUARE_BRACKET = 91; /** < */ -export declare const LESS_THAN_SIGN: number; +export declare const LESS_THAN_SIGN = 60; /** \n */ -export declare const LINE_FEED: number; +export declare const LINE_FEED = 10; /** \v */ -export declare const LINE_TABULATION: number; +export declare const LINE_TABULATION = 11; /** _ */ -export declare const LOW_LINE: number; +export declare const LOW_LINE = 95; /** \x10FFFF */ -export declare const MAXIMUM_ALLOWED_CODEPOINT: number; +export declare const MAXIMUM_ALLOWED_CODEPOINT = 1114111; /** \x00 */ -export declare const NULL: number; +export declare const NULL = 0; /** # */ -export declare const NUMBER_SIGN: number; +export declare const NUMBER_SIGN = 35; /** % */ -export declare const PERCENTAGE_SIGN: number; +export declare const PERCENTAGE_SIGN = 37; /** + */ -export declare const PLUS_SIGN: number; +export declare const PLUS_SIGN = 43; /** " */ -export declare const QUOTATION_MARK: number; +export declare const QUOTATION_MARK = 34; /** � */ -export declare const REPLACEMENT_CHARACTER: number; +export declare const REPLACEMENT_CHARACTER = 65533; /** \ */ -export declare const REVERSE_SOLIDUS: number; +export declare const REVERSE_SOLIDUS = 92; /** } */ -export declare const RIGHT_CURLY_BRACKET: number; +export declare const RIGHT_CURLY_BRACKET = 125; /** ) */ -export declare const RIGHT_PARENTHESIS: number; +export declare const RIGHT_PARENTHESIS = 41; /** ] */ -export declare const RIGHT_SQUARE_BRACKET: number; +export declare const RIGHT_SQUARE_BRACKET = 93; /** ; */ -export declare const SEMICOLON: number; +export declare const SEMICOLON = 59; /** \u0E */ -export declare const SHIFT_OUT: number; +export declare const SHIFT_OUT = 14; /** / */ -export declare const SOLIDUS: number; +export declare const SOLIDUS = 47; /** \u20 */ -export declare const SPACE: number; +export declare const SPACE = 32; diff --git a/packages/css-tokenizer/dist/consume/whitespace-token.d.ts b/packages/css-tokenizer/dist/consume/whitespace-token.d.ts index 76930adea..86585f70d 100644 --- a/packages/css-tokenizer/dist/consume/whitespace-token.d.ts +++ b/packages/css-tokenizer/dist/consume/whitespace-token.d.ts @@ -1,4 +1,4 @@ import { CodePointReader } from '../interfaces/code-point-reader'; import { Context } from '../interfaces/context'; import { TokenWhitespace } from '../interfaces/token'; -export declare function consumeWhiteSpace(ctx: Context, reader: CodePointReader, max?: number): TokenWhitespace; +export declare function consumeWhiteSpace(ctx: Context, reader: CodePointReader): TokenWhitespace; diff --git a/packages/css-tokenizer/dist/index.cjs b/packages/css-tokenizer/dist/index.cjs index 0b87d2cd1..decfae264 100644 --- a/packages/css-tokenizer/dist/index.cjs +++ b/packages/css-tokenizer/dist/index.cjs @@ -1 +1 @@ -"use strict";class Reader{#e;#t="";#n=[];#o=0;#r=0;#i=-1;constructor(e){this.#e=0,this.#t=e,this.#o=e.length;for(let e=0;e=j}function isIdentStartCodePoint(e){return!!isLetterCodePoint(e)||(!!isNonASCIICodePoint(e)||e===v)}function isIdentCodePoint(e){return!!isIdentStartCodePoint(e)||(!!isDigitCodePoint(e)||e===k)}function isNewLine(e){switch(e){case y:case s:case h:return!0;default:return!1}}function isWhitespace(e){switch(e){case y:case s:case h:case c:case H:return!0;default:return!1}}const G="\ud800".charCodeAt(0),X="\udfff".charCodeAt(0);function checkIfTwoCodePointsAreAValidEscape(e,t){const n=t.peekTwoCodePoints();return n[0]===L&&n[1]!==y}function checkIfThreeCodePointsWouldStartAnIdentSequence(e,t){const n=t.peekThreeCodePoints(),[o,r,i]=n;return o===k?r===k||(!!isIdentStartCodePoint(r)||r===L&&i!==y):!!isIdentStartCodePoint(o)||o===L&&checkIfTwoCodePointsAreAValidEscape(0,t)}function checkIfThreeCodePointsWouldStartANumber(e,t){const n=t.peekThreeCodePoints(),[o,r,i]=n;return o===U||o===k?!!isDigitCodePoint(r)||r===P&&isDigitCodePoint(i):o===P?isDigitCodePoint(r):!!isDigitCodePoint(o)}function checkIfTwoCodePointsStartAComment(e,t){const n=t.peekTwoCodePoints();return n[0]===B&&n[1]===r}function checkIfThreeCodePointsWouldStartCDC(e,t){const n=t.peekThreeCodePoints(),[o,r,i]=n;return o===k&&r===k&&i===T}function consumeComment(e,t){for(t.readCodePoint(),t.readCodePoint();;){const n=t.readCodePoint();if(!1===n){const n=t.representation();e.onParseError({message:"Unexpected EOF while consuming a comment.",start:n[0],end:n[1],state:["4.3.2. Consume comments","Unexpected EOF"]});break}if(n!==r)continue;const o=t.peekOneCodePoint();if(!1!==o&&o===B){t.readCodePoint();break}}const n=t.representation();return[exports.TokenType.Comment,t.representationString(),n[0],n[1],void 0]}function codePointsToString(e){let t="";for(let n=0;nO?b:i}var o;return n}function consumeIdentSequence(e,t){const n=[];for(;;){const o=t.peekOneCodePoint();if(!1===o)return n;if(isIdentCodePoint(o))t.readCodePoint(),n.push(o);else{if(o!==L||!checkIfTwoCodePointsAreAValidEscape(0,t))return n;t.readCodePoint(),n.push(consumeEscapedCodePoint(e,t))}}}function consumeHashToken(e,t){t.readCodePoint();const o=t.peekOneCodePoint();if(!1!==o&&isIdentCodePoint(o)||checkIfTwoCodePointsAreAValidEscape(0,t)){let o=n.Unrestricted;checkIfThreeCodePointsWouldStartAnIdentSequence(0,t)&&(o=n.ID);const r=consumeIdentSequence(e,t),i=t.representation();return[exports.TokenType.Hash,t.representationString(),i[0],i[1],{value:codePointsToString(r),type:o}]}const r=t.representation();return[exports.TokenType.Delim,t.representationString(),r[0],r[1],{value:"#"}]}function consumeNumber(e,t){let n=exports.NumberType.Integer;const o=[];{const e=t.peekOneCodePoint();e!==U&&e!==k||(t.readCodePoint(),o.push(e));const n=consumeDigits(t);for(let e=0;e{})};return{nextToken:function nextToken(){if(r.resetRepresentation(),checkIfTwoCodePointsStartAComment(0,r)){if(null!=t&&t.commentsAreTokens)return consumeComment(i,r);consumeComment(i,r)}r.resetRepresentation();const e=r.peekOneCodePoint();if(!1===e)return[exports.TokenType.EOF,"",-1,-1,void 0];switch(e){case d:{r.readCodePoint();const e=r.representation();return[exports.TokenType.Comma,r.representationString(),e[0],e[1],void 0]}case a:{r.readCodePoint();const e=r.representation();return[exports.TokenType.Colon,r.representationString(),e[0],e[1],void 0]}case q:{r.readCodePoint();const e=r.representation();return[exports.TokenType.Semicolon,r.representationString(),e[0],e[1],void 0]}case S:{r.readCodePoint();const e=r.representation();return[exports.TokenType.OpenParen,r.representationString(),e[0],e[1],void 0]}case W:{r.readCodePoint();const e=r.representation();return[exports.TokenType.CloseParen,r.representationString(),e[0],e[1],void 0]}case A:{r.readCodePoint();const e=r.representation();return[exports.TokenType.OpenSquare,r.representationString(),e[0],e[1],void 0]}case F:{r.readCodePoint();const e=r.representation();return[exports.TokenType.CloseSquare,r.representationString(),e[0],e[1],void 0]}case g:{r.readCodePoint();const e=r.representation();return[exports.TokenType.OpenCurly,r.representationString(),e[0],e[1],void 0]}case R:{r.readCodePoint();const e=r.representation();return[exports.TokenType.CloseCurly,r.representationString(),e[0],e[1],void 0]}}switch(e){case o:case N:return consumeStringToken(i,r);case w:return consumeHashToken(i,r);case U:case P:{if(checkIfThreeCodePointsWouldStartANumber(0,r))return consumeNumericToken(i,r);r.readCodePoint();const t=r.representation();return[exports.TokenType.Delim,r.representationString(),t[0],t[1],{value:String.fromCharCode(e)}]}case k:{if(checkIfThreeCodePointsWouldStartANumber(0,r))return consumeNumericToken(i,r);if(checkIfThreeCodePointsWouldStartCDC(0,r)){r.readCodePoint(),r.readCodePoint(),r.readCodePoint();const e=r.representation();return[exports.TokenType.CDC,r.representationString(),e[0],e[1],void 0]}if(checkIfThreeCodePointsWouldStartAnIdentSequence(0,r))return consumeIdentLikeToken(i,r);r.readCodePoint();const e=r.representation();return[exports.TokenType.Delim,r.representationString(),e[0],e[1],{value:"-"}]}case x:{if(checkIfFourCodePointsWouldStartCDO(0,r)){r.readCodePoint(),r.readCodePoint(),r.readCodePoint(),r.readCodePoint();const e=r.representation();return[exports.TokenType.CDO,r.representationString(),e[0],e[1],void 0]}r.readCodePoint();const e=r.representation();return[exports.TokenType.Delim,r.representationString(),e[0],e[1],{value:"<"}]}case u:{if(r.readCodePoint(),checkIfThreeCodePointsWouldStartAnIdentSequence(0,r)){const e=consumeIdentSequence(i,r),t=r.representation();return[exports.TokenType.AtKeyword,r.representationString(),t[0],t[1],{value:codePointsToString(e)}]}const e=r.representation();return[exports.TokenType.Delim,r.representationString(),e[0],e[1],{value:"@"}]}case L:{if(checkIfTwoCodePointsAreAValidEscape(0,r))return consumeIdentLikeToken(i,r);r.readCodePoint();const e=r.representation();return i.onParseError({message:'Invalid escape sequence after "\\"',start:e[0],end:e[1],state:["4.3.1. Consume a token","U+005C REVERSE SOLIDUS (\\)","The input stream does not start with a valid escape sequence"]}),[exports.TokenType.Delim,r.representationString(),e[0],e[1],{value:"\\"}]}}if(isWhitespace(e))return consumeWhiteSpace(0,r);if(isDigitCodePoint(e))return consumeNumericToken(i,r);if(isIdentStartCodePoint(e))return consumeIdentLikeToken(i,r);r.readCodePoint();const n=r.representation();return[exports.TokenType.Delim,r.representationString(),n[0],n[1],{value:String.fromCharCode(e)}]},endOfFile:function endOfFile(){return!1===r.peekOneCodePoint()}}}; +"use strict";class ParseError extends Error{sourceStart;sourceEnd;parserState;constructor(e,o,r,n){super(e),this.name="ParseError",this.sourceStart=o,this.sourceEnd=r,this.parserState=n}}class Reader{cursor;source="";codePointSource=[];length=0;representationStart=0;representationEnd=-1;constructor(e){this.cursor=0,this.source=e,this.length=e.length,this.codePointSource=new Array(this.length);for(let e=0;e=48&&e<=57}function isUppercaseLetterCodePoint(e){return e>=65&&e<=90}function isLowercaseLetterCodePoint(e){return e>=97&&e<=122}function isHexDigitCodePoint(e){return isDigitCodePoint(e)||e>=97&&e<=102||e>=65&&e<=70}function isLetterCodePoint(e){return isLowercaseLetterCodePoint(e)||isUppercaseLetterCodePoint(e)}function isNonASCIICodePoint(e){return e>=128}function isIdentStartCodePoint(e){return isLetterCodePoint(e)||isNonASCIICodePoint(e)||95===e}function isIdentCodePoint(e){return isIdentStartCodePoint(e)||isDigitCodePoint(e)||e===n}function isNewLine(e){return 10===e||13===e||12===e}function isWhitespace(e){return 32===e||10===e||9===e||13===e||12===e}function checkIfTwoCodePointsAreAValidEscape(e,o){return 92===o.codePointSource[o.cursor]&&!isNewLine(o.codePointSource[o.cursor+1])}function checkIfThreeCodePointsWouldStartAnIdentSequence(e,o){return o.codePointSource[o.cursor]===n?o.codePointSource[o.cursor+1]===n||(!!isIdentStartCodePoint(o.codePointSource[o.cursor+1])||92===o.codePointSource[o.cursor+1]&&10!==o.codePointSource[o.cursor+2]):!!isIdentStartCodePoint(o.codePointSource[o.cursor])||checkIfTwoCodePointsAreAValidEscape(0,o)}function checkIfThreeCodePointsWouldStartANumber(e,o){return 43===o.codePointSource[o.cursor]||o.codePointSource[o.cursor]===n?!!isDigitCodePoint(o.codePointSource[o.cursor+1])||46===o.codePointSource[o.cursor+1]&&isDigitCodePoint(o.codePointSource[o.cursor+2]):46===o.codePointSource[o.cursor]?isDigitCodePoint(o.codePointSource[o.cursor+1]):!!isDigitCodePoint(o.codePointSource[o.cursor])}function checkIfTwoCodePointsStartAComment(e,o){return 47===o.codePointSource[o.cursor]&&42===o.codePointSource[o.cursor+1]}function checkIfThreeCodePointsWouldStartCDC(e,o){return o.codePointSource[o.cursor]===n&&o.codePointSource[o.cursor+1]===n&&62===o.codePointSource[o.cursor+2]}function consumeComment(e,o){for(o.advanceCodePoint(2);;){const r=o.readCodePoint();if(!1===r){e.onParseError(new ParseError("Unexpected EOF while consuming a comment.",o.representationStart,o.representationEnd,["4.3.2. Consume comments","Unexpected EOF"]));break}if(42===r&&(void 0!==o.codePointSource[o.cursor]&&47===o.codePointSource[o.cursor])){o.advanceCodePoint();break}}return[exports.TokenType.Comment,o.source.slice(o.representationStart,o.representationEnd+1),o.representationStart,o.representationEnd,void 0]}function consumeEscapedCodePoint(e,o){const r=o.readCodePoint();if(!1===r)return e.onParseError(new ParseError("Unexpected EOF while consuming an escaped code point.",o.representationStart,o.representationEnd,["4.3.7. Consume an escaped code point","Unexpected EOF"])),t;if(isHexDigitCodePoint(r)){const e=[r];for(;void 0!==o.codePointSource[o.cursor]&&isHexDigitCodePoint(o.codePointSource[o.cursor])&&e.length<6;)e.push(o.codePointSource[o.cursor]),o.advanceCodePoint();isWhitespace(o.codePointSource[o.cursor])&&o.advanceCodePoint();const i=parseInt(String.fromCharCode(...e),16);return 0===i?t:(n=i)>=55296&&n<=57343||i>1114111?t:i}var n;return r}function consumeIdentSequence(e,o){const r=[];for(;;)if(isIdentCodePoint(o.codePointSource[o.cursor]))r.push(o.codePointSource[o.cursor]),o.advanceCodePoint();else{if(!checkIfTwoCodePointsAreAValidEscape(0,o))return r;o.advanceCodePoint(),r.push(consumeEscapedCodePoint(e,o))}}function consumeHashToken(e,o){if(o.advanceCodePoint(),void 0!==o.codePointSource[o.cursor]&&isIdentCodePoint(o.codePointSource[o.cursor])||checkIfTwoCodePointsAreAValidEscape(0,o)){let n=r.Unrestricted;checkIfThreeCodePointsWouldStartAnIdentSequence(0,o)&&(n=r.ID);const t=consumeIdentSequence(e,o);return[exports.TokenType.Hash,o.source.slice(o.representationStart,o.representationEnd+1),o.representationStart,o.representationEnd,{value:String.fromCharCode(...t),type:n}]}return[exports.TokenType.Delim,"#",o.representationStart,o.representationEnd,{value:"#"}]}function consumeNumber(e,o){let r=exports.NumberType.Integer;for(43!==o.codePointSource[o.cursor]&&o.codePointSource[o.cursor]!==n||o.advanceCodePoint();isDigitCodePoint(o.codePointSource[o.cursor]);)o.advanceCodePoint();if(46===o.codePointSource[o.cursor]&&isDigitCodePoint(o.codePointSource[o.cursor+1]))for(o.advanceCodePoint(2),r=exports.NumberType.Number;isDigitCodePoint(o.codePointSource[o.cursor]);)o.advanceCodePoint();if((101===o.codePointSource[o.cursor]||69===o.codePointSource[o.cursor])&&isDigitCodePoint(o.codePointSource[o.cursor+1]))for(o.advanceCodePoint(2),r=exports.NumberType.Number;isDigitCodePoint(o.codePointSource[o.cursor]);)o.advanceCodePoint();if((101===o.codePointSource[o.cursor]||69===o.codePointSource[o.cursor])&&(o.codePointSource[o.cursor+1]===n||43===o.codePointSource[o.cursor+1])&&isDigitCodePoint(o.codePointSource[o.cursor+2]))for(o.advanceCodePoint(3),r=exports.NumberType.Number;isDigitCodePoint(o.codePointSource[o.cursor]);)o.advanceCodePoint();return[parseFloat(o.source.slice(o.representationStart,o.representationEnd+1)),r]}function consumeNumericToken(e,o){const r=consumeNumber(0,o);if(checkIfThreeCodePointsWouldStartAnIdentSequence(0,o)){const n=consumeIdentSequence(e,o);return[exports.TokenType.Dimension,o.source.slice(o.representationStart,o.representationEnd+1),o.representationStart,o.representationEnd,{value:r[0],type:r[1],unit:String.fromCharCode(...n)}]}return 37===o.codePointSource[o.cursor]?(o.advanceCodePoint(),[exports.TokenType.Percentage,o.source.slice(o.representationStart,o.representationEnd+1),o.representationStart,o.representationEnd,{value:r[0]}]):[exports.TokenType.Number,o.source.slice(o.representationStart,o.representationEnd+1),o.representationStart,o.representationEnd,{value:r[0],type:r[1]}]}function consumeWhiteSpace(e,o){for(;isWhitespace(o.codePointSource[o.cursor]);)o.advanceCodePoint();return[exports.TokenType.Whitespace,o.source.slice(o.representationStart,o.representationEnd+1),o.representationStart,o.representationEnd,void 0]}function consumeStringToken(e,o){let r="";const n=o.readCodePoint();for(;;){const t=o.readCodePoint();if(!1===t)return e.onParseError(new ParseError("Unexpected EOF while consuming a string token.",o.representationStart,o.representationEnd,["4.3.5. Consume a string token","Unexpected EOF"])),[exports.TokenType.String,o.source.slice(o.representationStart,o.representationEnd+1),o.representationStart,o.representationEnd,{value:r}];if(isNewLine(t))return e.onParseError(new ParseError("Unexpected newline while consuming a string token.",o.representationStart,o.representationEnd,["4.3.5. Consume a string token","Unexpected newline"])),o.unreadCodePoint(),[exports.TokenType.BadString,o.source.slice(o.representationStart,o.representationEnd+1),o.representationStart,o.representationEnd,void 0];if(t===n)return[exports.TokenType.String,o.source.slice(o.representationStart,o.representationEnd+1),o.representationStart,o.representationEnd,{value:r}];if(92!==t)r+=String.fromCharCode(t);else{if(void 0===o.codePointSource[o.cursor])continue;if(isNewLine(o.codePointSource[o.cursor])){o.advanceCodePoint();continue}r+=String.fromCharCode(consumeEscapedCodePoint(e,o))}}}const i="u".charCodeAt(0),s="U".charCodeAt(0),c="r".charCodeAt(0),a="R".charCodeAt(0),u="l".charCodeAt(0),d="L".charCodeAt(0);function checkIfCodePointsMatchURLIdent(e,o){return 3===o.length&&((o[0]===i||o[0]===s)&&((o[1]===c||o[1]===a)&&(o[2]===u||o[2]===d)))}function consumeBadURL(e,o){for(;;){if(void 0===o.codePointSource[o.cursor])return;if(41===o.codePointSource[o.cursor])return void o.advanceCodePoint();checkIfTwoCodePointsAreAValidEscape(0,o)?(o.advanceCodePoint(),consumeEscapedCodePoint(e,o)):o.advanceCodePoint()}}function consumeUrlToken(e,o){consumeWhiteSpace(0,o);let r="";for(;;){if(void 0===o.codePointSource[o.cursor])return e.onParseError(new ParseError("Unexpected EOF while consuming a url token.",o.representationStart,o.representationEnd,["4.3.6. Consume a url token","Unexpected EOF"])),[exports.TokenType.URL,o.source.slice(o.representationStart,o.representationEnd+1),o.representationStart,o.representationEnd,{value:r}];if(41===o.codePointSource[o.cursor])return o.advanceCodePoint(),[exports.TokenType.URL,o.source.slice(o.representationStart,o.representationEnd+1),o.representationStart,o.representationEnd,{value:r}];if(isWhitespace(o.codePointSource[o.cursor]))return consumeWhiteSpace(0,o),void 0===o.codePointSource[o.cursor]?(e.onParseError(new ParseError("Unexpected EOF while consuming a url token.",o.representationStart,o.representationEnd,["4.3.6. Consume a url token","Consume as much whitespace as possible","Unexpected EOF"])),[exports.TokenType.URL,o.source.slice(o.representationStart,o.representationEnd+1),o.representationStart,o.representationEnd,{value:r}]):41===o.codePointSource[o.cursor]?(o.advanceCodePoint(),[exports.TokenType.URL,o.source.slice(o.representationStart,o.representationEnd+1),o.representationStart,o.representationEnd,{value:r}]):(consumeBadURL(e,o),[exports.TokenType.BadURL,o.source.slice(o.representationStart,o.representationEnd+1),o.representationStart,o.representationEnd,void 0]);if(34===o.codePointSource[o.cursor]||39===o.codePointSource[o.cursor]||40===o.codePointSource[o.cursor]||(11===(n=o.codePointSource[o.cursor])||127===n||0<=n&&n<=8||14<=n&&n<=31))return consumeBadURL(e,o),e.onParseError(new ParseError("Unexpected character while consuming a url token.",o.representationStart,o.representationEnd,["4.3.6. Consume a url token","Unexpected U+0022 QUOTATION MARK (\"), U+0027 APOSTROPHE ('), U+0028 LEFT PARENTHESIS (() or non-printable code point"])),[exports.TokenType.BadURL,o.source.slice(o.representationStart,o.representationEnd+1),o.representationStart,o.representationEnd,void 0];if(92===o.codePointSource[o.cursor]){if(checkIfTwoCodePointsAreAValidEscape(0,o)){r+=String.fromCharCode(consumeEscapedCodePoint(e,o));continue}return consumeBadURL(e,o),e.onParseError(new ParseError("Invalid escape sequence while consuming a url token.",o.representationStart,o.representationEnd,["4.3.6. Consume a url token","U+005C REVERSE SOLIDUS (\\)","The input stream does not start with a valid escape sequence"])),[exports.TokenType.BadURL,o.source.slice(o.representationStart,o.representationEnd+1),o.representationStart,o.representationEnd,void 0]}r+=String.fromCharCode(o.codePointSource[o.cursor]),o.advanceCodePoint()}var n}function consumeIdentLikeToken(e,o){const r=consumeIdentSequence(e,o);if(40!==o.codePointSource[o.cursor])return[exports.TokenType.Ident,o.source.slice(o.representationStart,o.representationEnd+1),o.representationStart,o.representationEnd,{value:String.fromCharCode(...r)}];if(checkIfCodePointsMatchURLIdent(0,r)){o.advanceCodePoint();let n=0;for(;;){const e=isWhitespace(o.codePointSource[o.cursor]),t=isWhitespace(o.codePointSource[o.cursor+1]);if(e&&t){n+=1,o.advanceCodePoint(1);continue}const i=e?o.codePointSource[o.cursor+1]:o.codePointSource[o.cursor];if(34===i||39===i)return n>0&&o.unreadCodePoint(n),[exports.TokenType.Function,o.source.slice(o.representationStart,o.representationEnd+1),o.representationStart,o.representationEnd,{value:String.fromCharCode(...r)}];break}return consumeUrlToken(e,o)}return o.advanceCodePoint(),[exports.TokenType.Function,o.source.slice(o.representationStart,o.representationEnd+1),o.representationStart,o.representationEnd,{value:String.fromCharCode(...r)}]}exports.ParseError=ParseError,exports.Reader=Reader,exports.cloneTokens=function cloneTokens(e){return"undefined"!=typeof globalThis&&"structuredClone"in globalThis?structuredClone(e):JSON.parse(JSON.stringify(e))},exports.isToken=function isToken(e){return!!Array.isArray(e)&&(!(e.length<4)&&(e[0]in exports.TokenType&&("string"==typeof e[1]&&("number"==typeof e[2]&&"number"==typeof e[3]))))},exports.mirrorVariant=function mirrorVariant(e){switch(e[0]){case exports.TokenType.OpenParen:return[exports.TokenType.CloseParen,")",-1,-1,void 0];case exports.TokenType.CloseParen:return[exports.TokenType.OpenParen,"(",-1,-1,void 0];case exports.TokenType.OpenCurly:return[exports.TokenType.CloseCurly,"}",-1,-1,void 0];case exports.TokenType.CloseCurly:return[exports.TokenType.OpenCurly,"{",-1,-1,void 0];case exports.TokenType.OpenSquare:return[exports.TokenType.CloseSquare,"]",-1,-1,void 0];case exports.TokenType.CloseSquare:return[exports.TokenType.OpenSquare,"[",-1,-1,void 0];default:return null}},exports.mirrorVariantType=function mirrorVariantType(e){switch(e){case exports.TokenType.OpenParen:return exports.TokenType.CloseParen;case exports.TokenType.CloseParen:return exports.TokenType.OpenParen;case exports.TokenType.OpenCurly:return exports.TokenType.CloseCurly;case exports.TokenType.CloseCurly:return exports.TokenType.OpenCurly;case exports.TokenType.OpenSquare:return exports.TokenType.CloseSquare;case exports.TokenType.CloseSquare:return exports.TokenType.OpenSquare;default:return null}},exports.mutateIdent=function mutateIdent(e,o){let r="";const t=new Array(o.length);for(let e=0;e{})};return{nextToken:function nextToken(){if(t.representationStart=t.cursor,t.representationEnd=-1,checkIfTwoCodePointsStartAComment(0,t)){if(null!=o&&o.commentsAreTokens)return consumeComment(i,t);consumeComment(i,t),t.representationStart=t.cursor,t.representationEnd=-1}const e=t.codePointSource[t.cursor];if(void 0===e)return[exports.TokenType.EOF,"",-1,-1,void 0];if(isIdentStartCodePoint(e))return consumeIdentLikeToken(i,t);if(isDigitCodePoint(e))return consumeNumericToken(i,t);switch(e){case 44:return t.advanceCodePoint(),[exports.TokenType.Comma,",",t.representationStart,t.representationEnd,void 0];case 58:return t.advanceCodePoint(),[exports.TokenType.Colon,":",t.representationStart,t.representationEnd,void 0];case 59:return t.advanceCodePoint(),[exports.TokenType.Semicolon,";",t.representationStart,t.representationEnd,void 0];case 40:return t.advanceCodePoint(),[exports.TokenType.OpenParen,"(",t.representationStart,t.representationEnd,void 0];case 41:return t.advanceCodePoint(),[exports.TokenType.CloseParen,")",t.representationStart,t.representationEnd,void 0];case 91:return t.advanceCodePoint(),[exports.TokenType.OpenSquare,"[",t.representationStart,t.representationEnd,void 0];case 93:return t.advanceCodePoint(),[exports.TokenType.CloseSquare,"]",t.representationStart,t.representationEnd,void 0];case 123:return t.advanceCodePoint(),[exports.TokenType.OpenCurly,"{",t.representationStart,t.representationEnd,void 0];case 125:return t.advanceCodePoint(),[exports.TokenType.CloseCurly,"}",t.representationStart,t.representationEnd,void 0];case 39:case 34:return consumeStringToken(i,t);case 35:return consumeHashToken(i,t);case 43:case 46:return checkIfThreeCodePointsWouldStartANumber(0,t)?consumeNumericToken(i,t):(t.advanceCodePoint(),[exports.TokenType.Delim,t.source[t.representationStart],t.representationStart,t.representationEnd,{value:t.source[t.representationStart]}]);case 10:case 13:case 12:case 9:case 32:return consumeWhiteSpace(0,t);case n:return checkIfThreeCodePointsWouldStartANumber(0,t)?consumeNumericToken(i,t):checkIfThreeCodePointsWouldStartCDC(0,t)?(t.advanceCodePoint(3),[exports.TokenType.CDC,"--\x3e",t.representationStart,t.representationEnd,void 0]):checkIfThreeCodePointsWouldStartAnIdentSequence(0,t)?consumeIdentLikeToken(i,t):(t.advanceCodePoint(),[exports.TokenType.Delim,"-",t.representationStart,t.representationEnd,{value:"-"}]);case 60:return checkIfFourCodePointsWouldStartCDO(0,t)?(t.advanceCodePoint(4),[exports.TokenType.CDO,"\x3c!--",t.representationStart,t.representationEnd,void 0]):(t.advanceCodePoint(),[exports.TokenType.Delim,"<",t.representationStart,t.representationEnd,{value:"<"}]);case 64:if(t.advanceCodePoint(),checkIfThreeCodePointsWouldStartAnIdentSequence(0,t)){const e=consumeIdentSequence(i,t);return[exports.TokenType.AtKeyword,t.source.slice(t.representationStart,t.representationEnd+1),t.representationStart,t.representationEnd,{value:String.fromCharCode(...e)}]}return[exports.TokenType.Delim,"@",t.representationStart,t.representationEnd,{value:"@"}];case 92:return checkIfTwoCodePointsAreAValidEscape(0,t)?consumeIdentLikeToken(i,t):(t.advanceCodePoint(),i.onParseError(new ParseError('Invalid escape sequence after "\\"',t.representationStart,t.representationEnd,["4.3.1. Consume a token","U+005C REVERSE SOLIDUS (\\)","The input stream does not start with a valid escape sequence"])),[exports.TokenType.Delim,"\\",t.representationStart,t.representationEnd,{value:"\\"}])}return t.advanceCodePoint(),[exports.TokenType.Delim,t.source[t.representationStart],t.representationStart,t.representationEnd,{value:t.source[t.representationStart]}]},endOfFile:function endOfFile(){return void 0===t.codePointSource[t.cursor]}}}; diff --git a/packages/css-tokenizer/dist/index.d.ts b/packages/css-tokenizer/dist/index.d.ts index dab5f2866..be3600ad9 100644 --- a/packages/css-tokenizer/dist/index.d.ts +++ b/packages/css-tokenizer/dist/index.d.ts @@ -1,7 +1,9 @@ export type { CSSToken } from './interfaces/token'; +export { ParseError } from './interfaces/error'; export { Reader } from './reader'; -export { TokenType, NumberType, mirrorVariantType, isToken } from './interfaces/token'; +export { TokenType, NumberType, mirrorVariantType, mirrorVariant, isToken } from './interfaces/token'; export { stringify } from './stringify'; export { tokenizer } from './tokenizer'; export { cloneTokens } from './util/clone-tokens'; export type { 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, } from './interfaces/token'; +export { mutateIdent, } from './util/mutations'; diff --git a/packages/css-tokenizer/dist/index.mjs b/packages/css-tokenizer/dist/index.mjs index a9c73e6e9..4682a441e 100644 --- a/packages/css-tokenizer/dist/index.mjs +++ b/packages/css-tokenizer/dist/index.mjs @@ -1 +1 @@ -class Reader{#e;#t="";#n=[];#o=0;#r=0;#i=-1;constructor(e){this.#e=0,this.#t=e,this.#o=e.length;for(let e=0;e=j}function isIdentStartCodePoint(e){return!!isLetterCodePoint(e)||(!!isNonASCIICodePoint(e)||e===E)}function isIdentCodePoint(e){return!!isIdentStartCodePoint(e)||(!!isDigitCodePoint(e)||e===l)}function isNewLine(e){switch(e){case v:case s:case h:return!0;default:return!1}}function isWhitespace(e){switch(e){case v:case s:case h:case c:case H:return!0;default:return!1}}const G="\ud800".charCodeAt(0),X="\udfff".charCodeAt(0);function checkIfTwoCodePointsAreAValidEscape(e,t){const n=t.peekTwoCodePoints();return n[0]===W&&n[1]!==v}function checkIfThreeCodePointsWouldStartAnIdentSequence(e,t){const n=t.peekThreeCodePoints(),[o,r,i]=n;return o===l?r===l||(!!isIdentStartCodePoint(r)||r===W&&i!==v):!!isIdentStartCodePoint(o)||o===W&&checkIfTwoCodePointsAreAValidEscape(0,t)}function checkIfThreeCodePointsWouldStartANumber(e,t){const n=t.peekThreeCodePoints(),[o,r,i]=n;return o===N||o===l?!!isDigitCodePoint(r)||r===P&&isDigitCodePoint(i):o===P?isDigitCodePoint(r):!!isDigitCodePoint(o)}function checkIfTwoCodePointsStartAComment(e,t){const n=t.peekTwoCodePoints();return n[0]===B&&n[1]===r}function checkIfThreeCodePointsWouldStartCDC(e,t){const n=t.peekThreeCodePoints(),[o,r,i]=n;return o===l&&r===l&&i===f}function consumeComment(t,n){for(n.readCodePoint(),n.readCodePoint();;){const e=n.readCodePoint();if(!1===e){const e=n.representation();t.onParseError({message:"Unexpected EOF while consuming a comment.",start:e[0],end:e[1],state:["4.3.2. Consume comments","Unexpected EOF"]});break}if(e!==r)continue;const o=n.peekOneCodePoint();if(!1!==o&&o===B){n.readCodePoint();break}}const o=n.representation();return[e.Comment,n.representationString(),o[0],o[1],void 0]}function codePointsToString(e){let t="";for(let n=0;nw?R:i}var o;return n}function consumeIdentSequence(e,t){const n=[];for(;;){const o=t.peekOneCodePoint();if(!1===o)return n;if(isIdentCodePoint(o))t.readCodePoint(),n.push(o);else{if(o!==W||!checkIfTwoCodePointsAreAValidEscape(0,t))return n;t.readCodePoint(),n.push(consumeEscapedCodePoint(e,t))}}}function consumeHashToken(t,o){o.readCodePoint();const r=o.peekOneCodePoint();if(!1!==r&&isIdentCodePoint(r)||checkIfTwoCodePointsAreAValidEscape(0,o)){let r=n.Unrestricted;checkIfThreeCodePointsWouldStartAnIdentSequence(0,o)&&(r=n.ID);const i=consumeIdentSequence(t,o),s=o.representation();return[e.Hash,o.representationString(),s[0],s[1],{value:codePointsToString(i),type:r}]}const i=o.representation();return[e.Delim,o.representationString(),i[0],i[1],{value:"#"}]}function consumeNumber(e,n){let o=t.Integer;const r=[];{const e=n.peekOneCodePoint();e!==N&&e!==l||(n.readCodePoint(),r.push(e));const t=consumeDigits(n);for(let e=0;e{})};return{nextToken:function nextToken(){if(i.resetRepresentation(),checkIfTwoCodePointsStartAComment(0,i)){if(null!=n&&n.commentsAreTokens)return consumeComment(s,i);consumeComment(s,i)}i.resetRepresentation();const t=i.peekOneCodePoint();if(!1===t)return[e.EOF,"",-1,-1,void 0];switch(t){case d:{i.readCodePoint();const t=i.representation();return[e.Comma,i.representationString(),t[0],t[1],void 0]}case a:{i.readCodePoint();const t=i.representation();return[e.Colon,i.representationString(),t[0],t[1],void 0]}case x:{i.readCodePoint();const t=i.representation();return[e.Semicolon,i.representationString(),t[0],t[1],void 0]}case A:{i.readCodePoint();const t=i.representation();return[e.OpenParen,i.representationString(),t[0],t[1],void 0]}case F:{i.readCodePoint();const t=i.representation();return[e.CloseParen,i.representationString(),t[0],t[1],void 0]}case T:{i.readCodePoint();const t=i.representation();return[e.OpenSquare,i.representationString(),t[0],t[1],void 0]}case q:{i.readCodePoint();const t=i.representation();return[e.CloseSquare,i.representationString(),t[0],t[1],void 0]}case k:{i.readCodePoint();const t=i.representation();return[e.OpenCurly,i.representationString(),t[0],t[1],void 0]}case y:{i.readCodePoint();const t=i.representation();return[e.CloseCurly,i.representationString(),t[0],t[1],void 0]}}switch(t){case o:case b:return consumeStringToken(s,i);case U:return consumeHashToken(s,i);case N:case P:{if(checkIfThreeCodePointsWouldStartANumber(0,i))return consumeNumericToken(s,i);i.readCodePoint();const n=i.representation();return[e.Delim,i.representationString(),n[0],n[1],{value:String.fromCharCode(t)}]}case l:{if(checkIfThreeCodePointsWouldStartANumber(0,i))return consumeNumericToken(s,i);if(checkIfThreeCodePointsWouldStartCDC(0,i)){i.readCodePoint(),i.readCodePoint(),i.readCodePoint();const t=i.representation();return[e.CDC,i.representationString(),t[0],t[1],void 0]}if(checkIfThreeCodePointsWouldStartAnIdentSequence(0,i))return consumeIdentLikeToken(s,i);i.readCodePoint();const t=i.representation();return[e.Delim,i.representationString(),t[0],t[1],{value:"-"}]}case I:{if(checkIfFourCodePointsWouldStartCDO(0,i)){i.readCodePoint(),i.readCodePoint(),i.readCodePoint(),i.readCodePoint();const t=i.representation();return[e.CDO,i.representationString(),t[0],t[1],void 0]}i.readCodePoint();const t=i.representation();return[e.Delim,i.representationString(),t[0],t[1],{value:"<"}]}case u:{if(i.readCodePoint(),checkIfThreeCodePointsWouldStartAnIdentSequence(0,i)){const t=consumeIdentSequence(s,i),n=i.representation();return[e.AtKeyword,i.representationString(),n[0],n[1],{value:codePointsToString(t)}]}const t=i.representation();return[e.Delim,i.representationString(),t[0],t[1],{value:"@"}]}case W:{if(checkIfTwoCodePointsAreAValidEscape(0,i))return consumeIdentLikeToken(s,i);i.readCodePoint();const t=i.representation();return s.onParseError({message:'Invalid escape sequence after "\\"',start:t[0],end:t[1],state:["4.3.1. Consume a token","U+005C REVERSE SOLIDUS (\\)","The input stream does not start with a valid escape sequence"]}),[e.Delim,i.representationString(),t[0],t[1],{value:"\\"}]}}if(isWhitespace(t))return consumeWhiteSpace(0,i);if(isDigitCodePoint(t))return consumeNumericToken(s,i);if(isIdentStartCodePoint(t))return consumeIdentLikeToken(s,i);i.readCodePoint();const r=i.representation();return[e.Delim,i.representationString(),r[0],r[1],{value:String.fromCharCode(t)}]},endOfFile:function endOfFile(){return!1===i.peekOneCodePoint()}}}function cloneTokens(e){return"undefined"!=typeof globalThis&&"structuredClone"in globalThis?structuredClone(e):JSON.parse(JSON.stringify(e))}export{t as NumberType,Reader,e as TokenType,cloneTokens,isToken,mirrorVariantType,stringify,tokenizer}; +class ParseError extends Error{sourceStart;sourceEnd;parserState;constructor(e,r,n,o){super(e),this.name="ParseError",this.sourceStart=r,this.sourceEnd=n,this.parserState=o}}class Reader{cursor;source="";codePointSource=[];length=0;representationStart=0;representationEnd=-1;constructor(e){this.cursor=0,this.source=e,this.length=e.length,this.codePointSource=new Array(this.length);for(let e=0;e=48&&e<=57}function isUppercaseLetterCodePoint(e){return e>=65&&e<=90}function isLowercaseLetterCodePoint(e){return e>=97&&e<=122}function isHexDigitCodePoint(e){return isDigitCodePoint(e)||e>=97&&e<=102||e>=65&&e<=70}function isLetterCodePoint(e){return isLowercaseLetterCodePoint(e)||isUppercaseLetterCodePoint(e)}function isNonASCIICodePoint(e){return e>=128}function isIdentStartCodePoint(e){return isLetterCodePoint(e)||isNonASCIICodePoint(e)||95===e}function isIdentCodePoint(e){return isIdentStartCodePoint(e)||isDigitCodePoint(e)||e===o}function isNewLine(e){return 10===e||13===e||12===e}function isWhitespace(e){return 32===e||10===e||9===e||13===e||12===e}function checkIfTwoCodePointsAreAValidEscape(e,r){return 92===r.codePointSource[r.cursor]&&!isNewLine(r.codePointSource[r.cursor+1])}function checkIfThreeCodePointsWouldStartAnIdentSequence(e,r){return r.codePointSource[r.cursor]===o?r.codePointSource[r.cursor+1]===o||(!!isIdentStartCodePoint(r.codePointSource[r.cursor+1])||92===r.codePointSource[r.cursor+1]&&10!==r.codePointSource[r.cursor+2]):!!isIdentStartCodePoint(r.codePointSource[r.cursor])||checkIfTwoCodePointsAreAValidEscape(0,r)}function checkIfThreeCodePointsWouldStartANumber(e,r){return 43===r.codePointSource[r.cursor]||r.codePointSource[r.cursor]===o?!!isDigitCodePoint(r.codePointSource[r.cursor+1])||46===r.codePointSource[r.cursor+1]&&isDigitCodePoint(r.codePointSource[r.cursor+2]):46===r.codePointSource[r.cursor]?isDigitCodePoint(r.codePointSource[r.cursor+1]):!!isDigitCodePoint(r.codePointSource[r.cursor])}function checkIfTwoCodePointsStartAComment(e,r){return 47===r.codePointSource[r.cursor]&&42===r.codePointSource[r.cursor+1]}function checkIfThreeCodePointsWouldStartCDC(e,r){return r.codePointSource[r.cursor]===o&&r.codePointSource[r.cursor+1]===o&&62===r.codePointSource[r.cursor+2]}function consumeComment(r,n){for(n.advanceCodePoint(2);;){const e=n.readCodePoint();if(!1===e){r.onParseError(new ParseError("Unexpected EOF while consuming a comment.",n.representationStart,n.representationEnd,["4.3.2. Consume comments","Unexpected EOF"]));break}if(42===e&&(void 0!==n.codePointSource[n.cursor]&&47===n.codePointSource[n.cursor])){n.advanceCodePoint();break}}return[e.Comment,n.source.slice(n.representationStart,n.representationEnd+1),n.representationStart,n.representationEnd,void 0]}function consumeEscapedCodePoint(e,r){const n=r.readCodePoint();if(!1===n)return e.onParseError(new ParseError("Unexpected EOF while consuming an escaped code point.",r.representationStart,r.representationEnd,["4.3.7. Consume an escaped code point","Unexpected EOF"])),t;if(isHexDigitCodePoint(n)){const e=[n];for(;void 0!==r.codePointSource[r.cursor]&&isHexDigitCodePoint(r.codePointSource[r.cursor])&&e.length<6;)e.push(r.codePointSource[r.cursor]),r.advanceCodePoint();isWhitespace(r.codePointSource[r.cursor])&&r.advanceCodePoint();const i=parseInt(String.fromCharCode(...e),16);return 0===i?t:(o=i)>=55296&&o<=57343||i>1114111?t:i}var o;return n}function consumeIdentSequence(e,r){const n=[];for(;;)if(isIdentCodePoint(r.codePointSource[r.cursor]))n.push(r.codePointSource[r.cursor]),r.advanceCodePoint();else{if(!checkIfTwoCodePointsAreAValidEscape(0,r))return n;r.advanceCodePoint(),n.push(consumeEscapedCodePoint(e,r))}}function consumeHashToken(r,o){if(o.advanceCodePoint(),void 0!==o.codePointSource[o.cursor]&&isIdentCodePoint(o.codePointSource[o.cursor])||checkIfTwoCodePointsAreAValidEscape(0,o)){let t=n.Unrestricted;checkIfThreeCodePointsWouldStartAnIdentSequence(0,o)&&(t=n.ID);const i=consumeIdentSequence(r,o);return[e.Hash,o.source.slice(o.representationStart,o.representationEnd+1),o.representationStart,o.representationEnd,{value:String.fromCharCode(...i),type:t}]}return[e.Delim,"#",o.representationStart,o.representationEnd,{value:"#"}]}function consumeNumber(e,n){let t=r.Integer;for(43!==n.codePointSource[n.cursor]&&n.codePointSource[n.cursor]!==o||n.advanceCodePoint();isDigitCodePoint(n.codePointSource[n.cursor]);)n.advanceCodePoint();if(46===n.codePointSource[n.cursor]&&isDigitCodePoint(n.codePointSource[n.cursor+1]))for(n.advanceCodePoint(2),t=r.Number;isDigitCodePoint(n.codePointSource[n.cursor]);)n.advanceCodePoint();if((101===n.codePointSource[n.cursor]||69===n.codePointSource[n.cursor])&&isDigitCodePoint(n.codePointSource[n.cursor+1]))for(n.advanceCodePoint(2),t=r.Number;isDigitCodePoint(n.codePointSource[n.cursor]);)n.advanceCodePoint();if((101===n.codePointSource[n.cursor]||69===n.codePointSource[n.cursor])&&(n.codePointSource[n.cursor+1]===o||43===n.codePointSource[n.cursor+1])&&isDigitCodePoint(n.codePointSource[n.cursor+2]))for(n.advanceCodePoint(3),t=r.Number;isDigitCodePoint(n.codePointSource[n.cursor]);)n.advanceCodePoint();return[parseFloat(n.source.slice(n.representationStart,n.representationEnd+1)),t]}function consumeNumericToken(r,n){const o=consumeNumber(0,n);if(checkIfThreeCodePointsWouldStartAnIdentSequence(0,n)){const t=consumeIdentSequence(r,n);return[e.Dimension,n.source.slice(n.representationStart,n.representationEnd+1),n.representationStart,n.representationEnd,{value:o[0],type:o[1],unit:String.fromCharCode(...t)}]}return 37===n.codePointSource[n.cursor]?(n.advanceCodePoint(),[e.Percentage,n.source.slice(n.representationStart,n.representationEnd+1),n.representationStart,n.representationEnd,{value:o[0]}]):[e.Number,n.source.slice(n.representationStart,n.representationEnd+1),n.representationStart,n.representationEnd,{value:o[0],type:o[1]}]}function consumeWhiteSpace(r,n){for(;isWhitespace(n.codePointSource[n.cursor]);)n.advanceCodePoint();return[e.Whitespace,n.source.slice(n.representationStart,n.representationEnd+1),n.representationStart,n.representationEnd,void 0]}function consumeStringToken(r,n){let o="";const t=n.readCodePoint();for(;;){const i=n.readCodePoint();if(!1===i)return r.onParseError(new ParseError("Unexpected EOF while consuming a string token.",n.representationStart,n.representationEnd,["4.3.5. Consume a string token","Unexpected EOF"])),[e.String,n.source.slice(n.representationStart,n.representationEnd+1),n.representationStart,n.representationEnd,{value:o}];if(isNewLine(i))return r.onParseError(new ParseError("Unexpected newline while consuming a string token.",n.representationStart,n.representationEnd,["4.3.5. Consume a string token","Unexpected newline"])),n.unreadCodePoint(),[e.BadString,n.source.slice(n.representationStart,n.representationEnd+1),n.representationStart,n.representationEnd,void 0];if(i===t)return[e.String,n.source.slice(n.representationStart,n.representationEnd+1),n.representationStart,n.representationEnd,{value:o}];if(92!==i)o+=String.fromCharCode(i);else{if(void 0===n.codePointSource[n.cursor])continue;if(isNewLine(n.codePointSource[n.cursor])){n.advanceCodePoint();continue}o+=String.fromCharCode(consumeEscapedCodePoint(r,n))}}}const i="u".charCodeAt(0),c="U".charCodeAt(0),s="r".charCodeAt(0),a="R".charCodeAt(0),u="l".charCodeAt(0),d="L".charCodeAt(0);function checkIfCodePointsMatchURLIdent(e,r){return 3===r.length&&((r[0]===i||r[0]===c)&&((r[1]===s||r[1]===a)&&(r[2]===u||r[2]===d)))}function consumeBadURL(e,r){for(;;){if(void 0===r.codePointSource[r.cursor])return;if(41===r.codePointSource[r.cursor])return void r.advanceCodePoint();checkIfTwoCodePointsAreAValidEscape(0,r)?(r.advanceCodePoint(),consumeEscapedCodePoint(e,r)):r.advanceCodePoint()}}function consumeUrlToken(r,n){consumeWhiteSpace(0,n);let o="";for(;;){if(void 0===n.codePointSource[n.cursor])return r.onParseError(new ParseError("Unexpected EOF while consuming a url token.",n.representationStart,n.representationEnd,["4.3.6. Consume a url token","Unexpected EOF"])),[e.URL,n.source.slice(n.representationStart,n.representationEnd+1),n.representationStart,n.representationEnd,{value:o}];if(41===n.codePointSource[n.cursor])return n.advanceCodePoint(),[e.URL,n.source.slice(n.representationStart,n.representationEnd+1),n.representationStart,n.representationEnd,{value:o}];if(isWhitespace(n.codePointSource[n.cursor]))return consumeWhiteSpace(0,n),void 0===n.codePointSource[n.cursor]?(r.onParseError(new ParseError("Unexpected EOF while consuming a url token.",n.representationStart,n.representationEnd,["4.3.6. Consume a url token","Consume as much whitespace as possible","Unexpected EOF"])),[e.URL,n.source.slice(n.representationStart,n.representationEnd+1),n.representationStart,n.representationEnd,{value:o}]):41===n.codePointSource[n.cursor]?(n.advanceCodePoint(),[e.URL,n.source.slice(n.representationStart,n.representationEnd+1),n.representationStart,n.representationEnd,{value:o}]):(consumeBadURL(r,n),[e.BadURL,n.source.slice(n.representationStart,n.representationEnd+1),n.representationStart,n.representationEnd,void 0]);if(34===n.codePointSource[n.cursor]||39===n.codePointSource[n.cursor]||40===n.codePointSource[n.cursor]||(11===(t=n.codePointSource[n.cursor])||127===t||0<=t&&t<=8||14<=t&&t<=31))return consumeBadURL(r,n),r.onParseError(new ParseError("Unexpected character while consuming a url token.",n.representationStart,n.representationEnd,["4.3.6. Consume a url token","Unexpected U+0022 QUOTATION MARK (\"), U+0027 APOSTROPHE ('), U+0028 LEFT PARENTHESIS (() or non-printable code point"])),[e.BadURL,n.source.slice(n.representationStart,n.representationEnd+1),n.representationStart,n.representationEnd,void 0];if(92===n.codePointSource[n.cursor]){if(checkIfTwoCodePointsAreAValidEscape(0,n)){o+=String.fromCharCode(consumeEscapedCodePoint(r,n));continue}return consumeBadURL(r,n),r.onParseError(new ParseError("Invalid escape sequence while consuming a url token.",n.representationStart,n.representationEnd,["4.3.6. Consume a url token","U+005C REVERSE SOLIDUS (\\)","The input stream does not start with a valid escape sequence"])),[e.BadURL,n.source.slice(n.representationStart,n.representationEnd+1),n.representationStart,n.representationEnd,void 0]}o+=String.fromCharCode(n.codePointSource[n.cursor]),n.advanceCodePoint()}var t}function consumeIdentLikeToken(r,n){const o=consumeIdentSequence(r,n);if(40!==n.codePointSource[n.cursor])return[e.Ident,n.source.slice(n.representationStart,n.representationEnd+1),n.representationStart,n.representationEnd,{value:String.fromCharCode(...o)}];if(checkIfCodePointsMatchURLIdent(0,o)){n.advanceCodePoint();let t=0;for(;;){const r=isWhitespace(n.codePointSource[n.cursor]),i=isWhitespace(n.codePointSource[n.cursor+1]);if(r&&i){t+=1,n.advanceCodePoint(1);continue}const c=r?n.codePointSource[n.cursor+1]:n.codePointSource[n.cursor];if(34===c||39===c)return t>0&&n.unreadCodePoint(t),[e.Function,n.source.slice(n.representationStart,n.representationEnd+1),n.representationStart,n.representationEnd,{value:String.fromCharCode(...o)}];break}return consumeUrlToken(r,n)}return n.advanceCodePoint(),[e.Function,n.source.slice(n.representationStart,n.representationEnd+1),n.representationStart,n.representationEnd,{value:String.fromCharCode(...o)}]}function tokenizer(r,n){const t=r.css.valueOf(),i=new Reader(t),c={onParseError:(null==n?void 0:n.onParseError)??(()=>{})};return{nextToken:function nextToken(){if(i.representationStart=i.cursor,i.representationEnd=-1,checkIfTwoCodePointsStartAComment(0,i)){if(null!=n&&n.commentsAreTokens)return consumeComment(c,i);consumeComment(c,i),i.representationStart=i.cursor,i.representationEnd=-1}const r=i.codePointSource[i.cursor];if(void 0===r)return[e.EOF,"",-1,-1,void 0];if(isIdentStartCodePoint(r))return consumeIdentLikeToken(c,i);if(isDigitCodePoint(r))return consumeNumericToken(c,i);switch(r){case 44:return i.advanceCodePoint(),[e.Comma,",",i.representationStart,i.representationEnd,void 0];case 58:return i.advanceCodePoint(),[e.Colon,":",i.representationStart,i.representationEnd,void 0];case 59:return i.advanceCodePoint(),[e.Semicolon,";",i.representationStart,i.representationEnd,void 0];case 40:return i.advanceCodePoint(),[e.OpenParen,"(",i.representationStart,i.representationEnd,void 0];case 41:return i.advanceCodePoint(),[e.CloseParen,")",i.representationStart,i.representationEnd,void 0];case 91:return i.advanceCodePoint(),[e.OpenSquare,"[",i.representationStart,i.representationEnd,void 0];case 93:return i.advanceCodePoint(),[e.CloseSquare,"]",i.representationStart,i.representationEnd,void 0];case 123:return i.advanceCodePoint(),[e.OpenCurly,"{",i.representationStart,i.representationEnd,void 0];case 125:return i.advanceCodePoint(),[e.CloseCurly,"}",i.representationStart,i.representationEnd,void 0];case 39:case 34:return consumeStringToken(c,i);case 35:return consumeHashToken(c,i);case 43:case 46:return checkIfThreeCodePointsWouldStartANumber(0,i)?consumeNumericToken(c,i):(i.advanceCodePoint(),[e.Delim,i.source[i.representationStart],i.representationStart,i.representationEnd,{value:i.source[i.representationStart]}]);case 10:case 13:case 12:case 9:case 32:return consumeWhiteSpace(0,i);case o:return checkIfThreeCodePointsWouldStartANumber(0,i)?consumeNumericToken(c,i):checkIfThreeCodePointsWouldStartCDC(0,i)?(i.advanceCodePoint(3),[e.CDC,"--\x3e",i.representationStart,i.representationEnd,void 0]):checkIfThreeCodePointsWouldStartAnIdentSequence(0,i)?consumeIdentLikeToken(c,i):(i.advanceCodePoint(),[e.Delim,"-",i.representationStart,i.representationEnd,{value:"-"}]);case 60:return checkIfFourCodePointsWouldStartCDO(0,i)?(i.advanceCodePoint(4),[e.CDO,"\x3c!--",i.representationStart,i.representationEnd,void 0]):(i.advanceCodePoint(),[e.Delim,"<",i.representationStart,i.representationEnd,{value:"<"}]);case 64:if(i.advanceCodePoint(),checkIfThreeCodePointsWouldStartAnIdentSequence(0,i)){const r=consumeIdentSequence(c,i);return[e.AtKeyword,i.source.slice(i.representationStart,i.representationEnd+1),i.representationStart,i.representationEnd,{value:String.fromCharCode(...r)}]}return[e.Delim,"@",i.representationStart,i.representationEnd,{value:"@"}];case 92:return checkIfTwoCodePointsAreAValidEscape(0,i)?consumeIdentLikeToken(c,i):(i.advanceCodePoint(),c.onParseError(new ParseError('Invalid escape sequence after "\\"',i.representationStart,i.representationEnd,["4.3.1. Consume a token","U+005C REVERSE SOLIDUS (\\)","The input stream does not start with a valid escape sequence"])),[e.Delim,"\\",i.representationStart,i.representationEnd,{value:"\\"}])}return i.advanceCodePoint(),[e.Delim,i.source[i.representationStart],i.representationStart,i.representationEnd,{value:i.source[i.representationStart]}]},endOfFile:function endOfFile(){return void 0===i.codePointSource[i.cursor]}}}function cloneTokens(e){return"undefined"!=typeof globalThis&&"structuredClone"in globalThis?structuredClone(e):JSON.parse(JSON.stringify(e))}function mutateIdent(e,r){let n="";const t=new Array(r.length);for(let e=0;e; + source: string; cursorPositionOfLastReadCodePoint(): number; - peekOneCodePoint(): number | false; - peekTwoCodePoints(): [number, number] | [number] | []; - peekThreeCodePoints(): [number, number, number] | [number, number] | [number] | []; - peekFourCodePoints(): [number, number, number, number] | [number, number, number] | [number, number] | [number] | []; - readCodePoint(): number | false; - unreadCodePoint(): boolean; - representation(): [number, number]; - representationString(): string; - resetRepresentation(): any; - slice(start: number, end: number): string; + advanceCodePoint(n?: number): void; + readCodePoint(n?: number): number | false; + unreadCodePoint(n?: number): boolean; }; diff --git a/packages/css-tokenizer/dist/interfaces/context.d.ts b/packages/css-tokenizer/dist/interfaces/context.d.ts index e73055642..339bfe4e4 100644 --- a/packages/css-tokenizer/dist/interfaces/context.d.ts +++ b/packages/css-tokenizer/dist/interfaces/context.d.ts @@ -1,4 +1,4 @@ -import { ParserError } from './error'; +import { ParseError } from './error'; export type Context = { - onParseError: (error: ParserError) => void; + onParseError: (error: ParseError) => void; }; diff --git a/packages/css-tokenizer/dist/interfaces/error.d.ts b/packages/css-tokenizer/dist/interfaces/error.d.ts index feff4c468..4803e57eb 100644 --- a/packages/css-tokenizer/dist/interfaces/error.d.ts +++ b/packages/css-tokenizer/dist/interfaces/error.d.ts @@ -1,6 +1,9 @@ -export type ParserError = { - message: string; - start: number; - end: number; - state: Array; -}; +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; + constructor(message: any, sourceStart: number, sourceEnd: number, parserState: Array); +} diff --git a/packages/css-tokenizer/dist/interfaces/token.d.ts b/packages/css-tokenizer/dist/interfaces/token.d.ts index 4421cf015..ef353850b 100644 --- a/packages/css-tokenizer/dist/interfaces/token.d.ts +++ b/packages/css-tokenizer/dist/interfaces/token.d.ts @@ -124,4 +124,5 @@ export type Token = [ U ]; export declare function mirrorVariantType(type: TokenType): TokenType | null; +export declare function mirrorVariant(token: CSSToken): CSSToken | null; export declare function isToken(x: any): x is CSSToken; diff --git a/packages/css-tokenizer/dist/reader.d.ts b/packages/css-tokenizer/dist/reader.d.ts index 3647d18b4..f92b678fd 100644 --- a/packages/css-tokenizer/dist/reader.d.ts +++ b/packages/css-tokenizer/dist/reader.d.ts @@ -1,16 +1,14 @@ import { CodePointReader } from './interfaces/code-point-reader'; export declare class Reader implements CodePointReader { - #private; + cursor: number; + source: string; + codePointSource: Array; + length: number; + representationStart: number; + representationEnd: number; constructor(source: string); cursorPositionOfLastReadCodePoint(): number; - peekOneCodePoint(): number | false; - peekTwoCodePoints(): [number, number] | [number] | []; - peekThreeCodePoints(): [number, number, number] | [number, number] | [number] | []; - peekFourCodePoints(): [number, number, number, number] | [number, number, number] | [number, number] | [number] | []; - readCodePoint(): number | false; - unreadCodePoint(): boolean; - representation(): [number, number]; - representationString(): string; - resetRepresentation(): void; - slice(start: number, end: number): string; + advanceCodePoint(n?: number): void; + readCodePoint(n?: number): number | false; + unreadCodePoint(n?: number): boolean; } diff --git a/packages/css-tokenizer/dist/tokenizer.d.ts b/packages/css-tokenizer/dist/tokenizer.d.ts index 0cac7a302..cc3a50996 100644 --- a/packages/css-tokenizer/dist/tokenizer.d.ts +++ b/packages/css-tokenizer/dist/tokenizer.d.ts @@ -1,5 +1,5 @@ import { CSSToken } from './interfaces/token'; -import { ParserError } from './interfaces/error'; +import { ParseError } from './interfaces/error'; interface Stringer { valueOf(): string; } @@ -7,7 +7,7 @@ export declare function tokenizer(input: { css: Stringer; }, options?: { commentsAreTokens?: boolean; - onParseError?: (error: ParserError) => void; + onParseError?: (error: ParseError) => void; }): { nextToken: () => CSSToken | undefined; endOfFile: () => boolean; diff --git a/packages/css-tokenizer/dist/util/mutations.d.ts b/packages/css-tokenizer/dist/util/mutations.d.ts new file mode 100644 index 000000000..37f23cd29 --- /dev/null +++ b/packages/css-tokenizer/dist/util/mutations.d.ts @@ -0,0 +1,2 @@ +import { TokenIdent } from '../interfaces/token'; +export declare function mutateIdent(ident: TokenIdent, newValue: string): void; diff --git a/packages/css-tokenizer/package.json b/packages/css-tokenizer/package.json index 17f9d1d13..28845024e 100644 --- a/packages/css-tokenizer/package.json +++ b/packages/css-tokenizer/package.json @@ -38,6 +38,7 @@ "dist" ], "devDependencies": { + "@rmenke/css-tokenizer-tests": "^1.0.1", "postcss-parser-tests": "^8.5.1" }, "scripts": { diff --git a/packages/css-tokenizer/src/checks/four-code-points-would-start-cdo.ts b/packages/css-tokenizer/src/checks/four-code-points-would-start-cdo.ts index 722a0e6ec..5391ccf99 100644 --- a/packages/css-tokenizer/src/checks/four-code-points-would-start-cdo.ts +++ b/packages/css-tokenizer/src/checks/four-code-points-would-start-cdo.ts @@ -4,8 +4,5 @@ import { Context } from '../interfaces/context'; // https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#consume-token export function checkIfFourCodePointsWouldStartCDO(ctx: Context, reader: CodePointReader): boolean { - const peeked = reader.peekFourCodePoints(); - const [first, second, third, fourth] = peeked; - - return first === LESS_THAN_SIGN && second === EXCLAMATION_MARK && third === HYPHEN_MINUS && fourth === HYPHEN_MINUS; + return reader.codePointSource[reader.cursor] === LESS_THAN_SIGN && reader.codePointSource[reader.cursor+1] === EXCLAMATION_MARK && reader.codePointSource[reader.cursor + 2] === HYPHEN_MINUS && reader.codePointSource[reader.cursor+3] === HYPHEN_MINUS; } diff --git a/packages/css-tokenizer/src/checks/next-is-eof.ts b/packages/css-tokenizer/src/checks/next-is-eof.ts index 18ee497a7..cc57009aa 100644 --- a/packages/css-tokenizer/src/checks/next-is-eof.ts +++ b/packages/css-tokenizer/src/checks/next-is-eof.ts @@ -2,8 +2,7 @@ import { CodePointReader } from '../interfaces/code-point-reader'; import { Context } from '../interfaces/context'; export function checkIfNextIsEOF(ctx: Context, reader: CodePointReader): boolean { - const peeked = reader.peekOneCodePoint(); - if (peeked === false) { + if (reader.codePointSource[reader.cursor] === undefined) { return true; } diff --git a/packages/css-tokenizer/src/checks/three-code-points-would-start-cdc.ts b/packages/css-tokenizer/src/checks/three-code-points-would-start-cdc.ts index 38cdf70dc..220a78c62 100644 --- a/packages/css-tokenizer/src/checks/three-code-points-would-start-cdc.ts +++ b/packages/css-tokenizer/src/checks/three-code-points-would-start-cdc.ts @@ -4,8 +4,5 @@ import { Context } from '../interfaces/context'; // https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#consume-token export function checkIfThreeCodePointsWouldStartCDC(ctx: Context, reader: CodePointReader): boolean { - const peeked = reader.peekThreeCodePoints(); - const [first, second, third] = peeked; - - return first === HYPHEN_MINUS && second === HYPHEN_MINUS && third === GREATER_THAN_SIGN; + return reader.codePointSource[reader.cursor] === HYPHEN_MINUS && reader.codePointSource[reader.cursor+1] === HYPHEN_MINUS && reader.codePointSource[reader.cursor + 2] === GREATER_THAN_SIGN; } diff --git a/packages/css-tokenizer/src/checks/three-code-points-would-start-ident-sequence.ts b/packages/css-tokenizer/src/checks/three-code-points-would-start-ident-sequence.ts index 28ffc8fc6..16c31196f 100644 --- a/packages/css-tokenizer/src/checks/three-code-points-would-start-ident-sequence.ts +++ b/packages/css-tokenizer/src/checks/three-code-points-would-start-ident-sequence.ts @@ -6,23 +6,20 @@ import { checkIfTwoCodePointsAreAValidEscape } from './two-code-points-are-valid // https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#would-start-an-identifier export function checkIfThreeCodePointsWouldStartAnIdentSequence(ctx: Context, reader: CodePointReader): boolean { - const peeked = reader.peekThreeCodePoints(); - const [first, second, third] = peeked; - // // U+002D HYPHEN-MINUS - if (first === HYPHEN_MINUS) { + if (reader.codePointSource[reader.cursor] === HYPHEN_MINUS) { // If the second code point is a U+002D HYPHEN-MINUS return true - if (second === HYPHEN_MINUS) { + if (reader.codePointSource[reader.cursor+1] === HYPHEN_MINUS) { return true; } // If the second code point is an ident-start code point return true - if (isIdentStartCodePoint(second)) { + if (isIdentStartCodePoint(reader.codePointSource[reader.cursor+1])) { return true; } // If the second and third code points are a valid escape return true - if (second === REVERSE_SOLIDUS && third !== LINE_FEED) { + if (reader.codePointSource[reader.cursor+1] === REVERSE_SOLIDUS && reader.codePointSource[reader.cursor + 2] !== LINE_FEED) { return true; } @@ -31,18 +28,10 @@ export function checkIfThreeCodePointsWouldStartAnIdentSequence(ctx: Context, re // ident-start code point // Return true. - if (isIdentStartCodePoint(first)) { + if (isIdentStartCodePoint(reader.codePointSource[reader.cursor])) { return true; } // U+005C REVERSE SOLIDUS (\) - if (first === REVERSE_SOLIDUS) { - // If the first and second code points are a valid escape, return true. - // Otherwise, return false. - return checkIfTwoCodePointsAreAValidEscape(ctx, reader); - } - - // anything else - // Return false. - return false; + return checkIfTwoCodePointsAreAValidEscape(ctx, reader); } diff --git a/packages/css-tokenizer/src/checks/three-code-points-would-start-number.ts b/packages/css-tokenizer/src/checks/three-code-points-would-start-number.ts index e4b964e9c..3b26474c9 100644 --- a/packages/css-tokenizer/src/checks/three-code-points-would-start-number.ts +++ b/packages/css-tokenizer/src/checks/three-code-points-would-start-number.ts @@ -5,30 +5,27 @@ import { Context } from '../interfaces/context'; // https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#starts-with-a-number export function checkIfThreeCodePointsWouldStartANumber(ctx: Context, reader: CodePointReader): boolean { - const peeked = reader.peekThreeCodePoints(); - const [first, second, third] = peeked; - - if (first === PLUS_SIGN || first === HYPHEN_MINUS) { // U+002B PLUS SIGN (+) or U+002D HYPHEN-MINUS (-) + if (reader.codePointSource[reader.cursor] === PLUS_SIGN || reader.codePointSource[reader.cursor] === HYPHEN_MINUS) { // U+002B PLUS SIGN (+) or U+002D HYPHEN-MINUS (-) // If the second code point is a digit, return true. - if (isDigitCodePoint(second)) { + if (isDigitCodePoint(reader.codePointSource[reader.cursor+1])) { return true; } // Otherwise, if the second code point is a U+002E FULL STOP (.) - if (second === FULL_STOP) { + if (reader.codePointSource[reader.cursor+1] === FULL_STOP) { // and the third code point is a digit, return true. - return isDigitCodePoint(third); + return isDigitCodePoint(reader.codePointSource[reader.cursor+2]); } // Otherwise, return false. return false; - } else if (first === FULL_STOP) { // U+002E FULL STOP (.) + } else if (reader.codePointSource[reader.cursor] === FULL_STOP) { // U+002E FULL STOP (.) // If the second code point is a digit, return true. // Otherwise, return false. - return isDigitCodePoint(second); + return isDigitCodePoint(reader.codePointSource[reader.cursor+1]); - } else if (isDigitCodePoint(first)) { // digit + } else if (isDigitCodePoint(reader.codePointSource[reader.cursor])) { // digit // Return true. return true; } diff --git a/packages/css-tokenizer/src/checks/two-code-points-are-valid-escape.ts b/packages/css-tokenizer/src/checks/two-code-points-are-valid-escape.ts index 4e30730fb..6d66d6d6d 100644 --- a/packages/css-tokenizer/src/checks/two-code-points-are-valid-escape.ts +++ b/packages/css-tokenizer/src/checks/two-code-points-are-valid-escape.ts @@ -1,17 +1,17 @@ -import { LINE_FEED, REVERSE_SOLIDUS } from '../code-points/code-points'; +import { REVERSE_SOLIDUS } from '../code-points/code-points'; +import { isNewLine } from '../code-points/ranges'; import { CodePointReader } from '../interfaces/code-point-reader'; import { Context } from '../interfaces/context'; // https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#starts-with-a-valid-escape export function checkIfTwoCodePointsAreAValidEscape(ctx: Context, reader: CodePointReader): boolean { - const peeked = reader.peekTwoCodePoints(); // If the first code point is not U+005C REVERSE SOLIDUS (\), return false. - if (peeked[0] !== REVERSE_SOLIDUS) { // "\" + if (reader.codePointSource[reader.cursor] !== REVERSE_SOLIDUS) { // "\" return false; } // Otherwise, if the second code point is a newline, return false. - if (peeked[1] === LINE_FEED) { + if (isNewLine(reader.codePointSource[reader.cursor+1])) { return false; } diff --git a/packages/css-tokenizer/src/checks/two-code-points-start-comment.ts b/packages/css-tokenizer/src/checks/two-code-points-start-comment.ts index f24fabf56..91a1a05aa 100644 --- a/packages/css-tokenizer/src/checks/two-code-points-start-comment.ts +++ b/packages/css-tokenizer/src/checks/two-code-points-start-comment.ts @@ -4,12 +4,11 @@ import { Context } from '../interfaces/context'; // https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#consume-comments export function checkIfTwoCodePointsStartAComment(ctx: Context, reader: CodePointReader): boolean { - const peeked = reader.peekTwoCodePoints(); - if (peeked[0] !== SOLIDUS) { + if (reader.codePointSource[reader.cursor] !== SOLIDUS) { return false; } - if (peeked[1] !== ASTERISK) { + if (reader.codePointSource[reader.cursor+1] !== ASTERISK) { return false; } diff --git a/packages/css-tokenizer/src/code-points/code-points-to-string.ts b/packages/css-tokenizer/src/code-points/code-points-to-string.ts deleted file mode 100644 index 33b808d06..000000000 --- a/packages/css-tokenizer/src/code-points/code-points-to-string.ts +++ /dev/null @@ -1,9 +0,0 @@ -export function codePointsToString(codePoints: Array): string { - let result = ''; - - for (let i = 0; i < codePoints.length; i++) { - result += String.fromCharCode(codePoints[i]); - } - - return result; -} diff --git a/packages/css-tokenizer/src/code-points/code-points.ts b/packages/css-tokenizer/src/code-points/code-points.ts index fca7ea361..ad9ae5286 100644 --- a/packages/css-tokenizer/src/code-points/code-points.ts +++ b/packages/css-tokenizer/src/code-points/code-points.ts @@ -1,78 +1,78 @@ /** ' */ -export const APOSTROPHE = '\u{27}'.charCodeAt(0); +export const APOSTROPHE = 0x0027; /** * */ -export const ASTERISK = '\u{2a}'.charCodeAt(0); +export const ASTERISK = 0x002a; /** \b */ -export const BACKSPACE = '\u{8}'.charCodeAt(0); +export const BACKSPACE = 0x008; /** \r */ -export const CARRIAGE_RETURN = '\u{d}'.charCodeAt(0); +export const CARRIAGE_RETURN = 0x00d; /** \t */ -export const CHARACTER_TABULATION = '\u{9}'.charCodeAt(0); +export const CHARACTER_TABULATION = 0x009; /** : */ -export const COLON = '\u{3a}'.charCodeAt(0); +export const COLON = 0x003a; /** , */ -export const COMMA = '\u{2c}'.charCodeAt(0); +export const COMMA = 0x002c; /** @ */ -export const COMMERCIAL_AT = '\u{40}'.charCodeAt(0); +export const COMMERCIAL_AT = 0x0040; /** \x7F */ -export const DELETE = '\u{7f}'.charCodeAt(0); +export const DELETE = 0x007f; /** ! */ -export const EXCLAMATION_MARK = '\u{21}'.charCodeAt(0); +export const EXCLAMATION_MARK = 0x0021; /** \f */ -export const FORM_FEED = '\u{c}'.charCodeAt(0); +export const FORM_FEED = 0x000c; /** . */ -export const FULL_STOP = '\u{2e}'.charCodeAt(0); +export const FULL_STOP = 0x002e; /** > */ -export const GREATER_THAN_SIGN = '\u{3e}'.charCodeAt(0); +export const GREATER_THAN_SIGN = 0x003e; /** - */ -export const HYPHEN_MINUS = '\u{2d}'.charCodeAt(0); +export const HYPHEN_MINUS = 0x002d; /** \x1F */ -export const INFORMATION_SEPARATOR_ONE = '\u{1f}'.charCodeAt(0); +export const INFORMATION_SEPARATOR_ONE = 0x001f; /** E */ -export const LATIN_CAPITAL_LETTER_E = '\u{45}'.charCodeAt(0); +export const LATIN_CAPITAL_LETTER_E = 0x0045; /** e */ -export const LATIN_SMALL_LETTER_E = '\u{65}'.charCodeAt(0); +export const LATIN_SMALL_LETTER_E = 0x0065; /** { */ -export const LEFT_CURLY_BRACKET = '\u{7b}'.charCodeAt(0); +export const LEFT_CURLY_BRACKET = 0x007b; /** ( */ -export const LEFT_PARENTHESIS = '\u{28}'.charCodeAt(0); +export const LEFT_PARENTHESIS = 0x0028; /** [ */ -export const LEFT_SQUARE_BRACKET = '\u{5b}'.charCodeAt(0); +export const LEFT_SQUARE_BRACKET = 0x005b; /** < */ -export const LESS_THAN_SIGN = '\u{3c}'.charCodeAt(0); +export const LESS_THAN_SIGN = 0x003c; /** \n */ -export const LINE_FEED = '\u{a}'.charCodeAt(0); +export const LINE_FEED = 0x00a; /** \v */ -export const LINE_TABULATION = '\u{b}'.charCodeAt(0); +export const LINE_TABULATION = 0x00b; /** _ */ -export const LOW_LINE = '\u{5f}'.charCodeAt(0); +export const LOW_LINE = 0x005f; /** \x10FFFF */ -export const MAXIMUM_ALLOWED_CODEPOINT = '\u{10FFFF}'.charCodeAt(0); +export const MAXIMUM_ALLOWED_CODEPOINT = 0x10FFFF; /** \x00 */ -export const NULL = '\u{0}'.charCodeAt(0); +export const NULL = 0x000; /** # */ -export const NUMBER_SIGN = '\u{23}'.charCodeAt(0); +export const NUMBER_SIGN = 0x0023; /** % */ -export const PERCENTAGE_SIGN = '\u{25}'.charCodeAt(0); +export const PERCENTAGE_SIGN = 0x0025; /** + */ -export const PLUS_SIGN = '\u{2b}'.charCodeAt(0); +export const PLUS_SIGN = 0x002b; /** " */ -export const QUOTATION_MARK = '\u{22}'.charCodeAt(0); +export const QUOTATION_MARK = 0x0022; /** � */ -export const REPLACEMENT_CHARACTER = '\u{0FFFD}'.charCodeAt(0); +export const REPLACEMENT_CHARACTER = 0xFFFD; /** \ */ -export const REVERSE_SOLIDUS = '\u{5c}'.charCodeAt(0); +export const REVERSE_SOLIDUS = 0x005c; /** } */ -export const RIGHT_CURLY_BRACKET = '\u{7d}'.charCodeAt(0); +export const RIGHT_CURLY_BRACKET = 0x007d; /** ) */ -export const RIGHT_PARENTHESIS = '\u{29}'.charCodeAt(0); +export const RIGHT_PARENTHESIS = 0x0029; /** ] */ -export const RIGHT_SQUARE_BRACKET = '\u{5d}'.charCodeAt(0); +export const RIGHT_SQUARE_BRACKET = 0x005d; /** ; */ -export const SEMICOLON = '\u{3b}'.charCodeAt(0); +export const SEMICOLON = 0x003b; /** \u0E */ -export const SHIFT_OUT = '\u{e}'.charCodeAt(0); +export const SHIFT_OUT = 0x00e; /** / */ -export const SOLIDUS = '\u{2f}'.charCodeAt(0); +export const SOLIDUS = 0x002f; /** \u20 */ -export const SPACE = '\u{20}'.charCodeAt(0); +export const SPACE = 0x0020; diff --git a/packages/css-tokenizer/src/code-points/ranges.ts b/packages/css-tokenizer/src/code-points/ranges.ts index 9aa4628bd..43d494624 100644 --- a/packages/css-tokenizer/src/code-points/ranges.ts +++ b/packages/css-tokenizer/src/code-points/ranges.ts @@ -1,170 +1,71 @@ -import { BACKSPACE, DELETE, INFORMATION_SEPARATOR_ONE, LINE_TABULATION, LOW_LINE, HYPHEN_MINUS, NULL, SHIFT_OUT, LINE_FEED, CARRIAGE_RETURN, FORM_FEED, CHARACTER_TABULATION, SPACE } from './code-points'; +import { BACKSPACE, DELETE, INFORMATION_SEPARATOR_ONE, LINE_TABULATION, LOW_LINE, HYPHEN_MINUS, NULL, SHIFT_OUT } from './code-points'; // https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#tokenizer-definitions -const digitsLow = '\u{30}'.charCodeAt(0); -const digitsHigh = '\u{39}'.charCodeAt(0); - // https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#digit export function isDigitCodePoint(search: number): boolean { - if (digitsLow <= search && search <= digitsHigh) { - return true; - } - - return false; + return search >= 0x0030 && search <= 0x0039; } -const letterUppercaseLow = '\u{41}'.charCodeAt(0); -const letterUppercaseHigh = '\u{5a}'.charCodeAt(0); - // https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#uppercase-letter export function isUppercaseLetterCodePoint(search: number): boolean { - if (letterUppercaseLow <= search && search <= letterUppercaseHigh) { - return true; - } - - return false; + return search >= 0x0041 && search <= 0x005a; } -const letterLowercaseLow = '\u{61}'.charCodeAt(0); -const letterLowercaseHigh = '\u{7a}'.charCodeAt(0); - // https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#lowercase-letter export function isLowercaseLetterCodePoint(search: number): boolean { - if (letterLowercaseLow <= search && search <= letterLowercaseHigh) { - return true; - } - - return false; + return search >= 0x0061 && search <= 0x007a; } -const afUppercaseHigh = '\u{46}'.charCodeAt(0); -const afLowercaseHigh = '\u{66}'.charCodeAt(0); - // https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#hex-digit export function isHexDigitCodePoint(search: number): boolean { - if (isDigitCodePoint(search)) { - return true; - } - - if (letterUppercaseLow <= search && search <= afUppercaseHigh) { - return true; - } - - if (letterLowercaseLow <= search && search <= afLowercaseHigh) { - return true; - } - - return false; + return ( + isDigitCodePoint(search) || // 0 .. 9 + (search >= 0x0061 && search <= 0x0066) || // a .. f + (search >= 0x0041 && search <= 0x0046) // A .. F + ); } // https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#letter export function isLetterCodePoint(search: number): boolean { - if (isUppercaseLetterCodePoint(search) || isLowercaseLetterCodePoint(search)) { - return true; - } - - return false; + return isLowercaseLetterCodePoint(search) || isUppercaseLetterCodePoint(search); } -const nonASCIILow = '\u{80}'.charCodeAt(0); - // https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#non-ascii-code-point export function isNonASCIICodePoint(search: number): boolean { - return search >= nonASCIILow; + return search >= 0x0080; } // https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#ident-start-code-point export function isIdentStartCodePoint(search: number): boolean { - if (isLetterCodePoint(search)) { - return true; - } - - if (isNonASCIICodePoint(search)) { - return true; - } - - return search === LOW_LINE; + return isLetterCodePoint(search) || isNonASCIICodePoint(search) || search === LOW_LINE; } // https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#ident-code-point export function isIdentCodePoint(search: number): boolean { - if (isIdentStartCodePoint(search)) { - return true; - } - - if (isDigitCodePoint(search)) { - return true; - } - - return search === HYPHEN_MINUS; + return isIdentStartCodePoint(search) || isDigitCodePoint(search) || search === HYPHEN_MINUS; } // https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#non-printable-code-point export function isNonPrintableCodePoint(search: number): boolean { - if (search === LINE_TABULATION) { - return true; - } - - if (search === DELETE) { - return true; - } - - if (NULL <= search && search <= BACKSPACE) { - return true; - } - - if (SHIFT_OUT <= search && search <= INFORMATION_SEPARATOR_ONE) { - return true; - } - - return false; + return ( + (search === LINE_TABULATION) || + (search === DELETE) || + (NULL <= search && search <= BACKSPACE) || + (SHIFT_OUT <= search && search <= INFORMATION_SEPARATOR_ONE) + ); } // https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#whitespace export function isNewLine(search: number): boolean { - switch (search) { - case LINE_FEED: - case CARRIAGE_RETURN: - case FORM_FEED: - // https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#input-preprocessing - // We can not follow the preprocessing rules because our output is text and must be minimally different from the input. - // Applying the preprocessing rules would make it impossible to match the input. - // A side effect of this is that our definition of whitespace is broader. - return true; - default: - return false; - } + return search === 0x000a || search === 0x000d || search === 0x000c; } // https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#whitespace export function isWhitespace(search: number): boolean { - switch (search) { - case LINE_FEED: - case CARRIAGE_RETURN: - case FORM_FEED: - // https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#input-preprocessing - // We can not follow the preprocessing rules because our output is text and must be minimally different from the input. - // Applying the preprocessing rules would make it impossible to match the input. - // A side effect of this is that our definition of whitespace is broader. - return true; - case CHARACTER_TABULATION: - return true; - case SPACE: - return true; - - default: - return false; - } + return search === 0x0020 || search === 0x000a || search === 0x0009 || search === 0x000d || search === 0x000c; } -const surrogateLow = '\u{d800}'.charCodeAt(0); -const surrogateHigh = '\u{dfff}'.charCodeAt(0); - // https://infra.spec.whatwg.org/#surrogate export function isSurrogate(search: number): boolean { - if (surrogateLow <= search && search <= surrogateHigh) { - return true; - } - - return false; + return search >= 0xd800 && search <= 0xdfff; } diff --git a/packages/css-tokenizer/src/consume/bad-url.ts b/packages/css-tokenizer/src/consume/bad-url.ts index 40a44f9ab..bdd8eaa73 100644 --- a/packages/css-tokenizer/src/consume/bad-url.ts +++ b/packages/css-tokenizer/src/consume/bad-url.ts @@ -8,23 +8,22 @@ import { consumeEscapedCodePoint } from './escaped-code-point'; export function consumeBadURL(ctx: Context, reader: CodePointReader) { // eslint-disable-next-line no-constant-condition while (true) { - const peeked = reader.peekOneCodePoint(); - if (peeked === false) { + if (reader.codePointSource[reader.cursor] === undefined) { return; } - if (peeked === RIGHT_PARENTHESIS) { - reader.readCodePoint(); + if (reader.codePointSource[reader.cursor] === RIGHT_PARENTHESIS) { + reader.advanceCodePoint(); return; } if (checkIfTwoCodePointsAreAValidEscape(ctx, reader)) { - reader.readCodePoint(); + reader.advanceCodePoint(); consumeEscapedCodePoint(ctx, reader); continue; } - reader.readCodePoint(); + reader.advanceCodePoint(); continue; } } diff --git a/packages/css-tokenizer/src/consume/comment.ts b/packages/css-tokenizer/src/consume/comment.ts index c6c6191da..817a2d228 100644 --- a/packages/css-tokenizer/src/consume/comment.ts +++ b/packages/css-tokenizer/src/consume/comment.ts @@ -1,27 +1,26 @@ import { ASTERISK, SOLIDUS } from '../code-points/code-points'; import { CodePointReader } from '../interfaces/code-point-reader'; import { Context } from '../interfaces/context'; +import { ParseError } from '../interfaces/error'; import { TokenComment, TokenType } from '../interfaces/token'; // https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#consume-comment export function consumeComment(ctx: Context, reader: CodePointReader): TokenComment { - reader.readCodePoint(); - reader.readCodePoint(); + reader.advanceCodePoint(2); // eslint-disable-next-line no-constant-condition while (true) { const codePoint = reader.readCodePoint(); if (codePoint === false) { - const representation = reader.representation(); - ctx.onParseError({ - message: 'Unexpected EOF while consuming a comment.', - start: representation[0], - end: representation[1], - state: [ + ctx.onParseError(new ParseError( + 'Unexpected EOF while consuming a comment.', + reader.representationStart, + reader.representationEnd, + [ '4.3.2. Consume comments', 'Unexpected EOF', ], - }); + )); break; } @@ -30,23 +29,21 @@ export function consumeComment(ctx: Context, reader: CodePointReader): TokenComm continue; } - const close = reader.peekOneCodePoint(); - if (close === false) { + if (reader.codePointSource[reader.cursor] === undefined) { continue; } - if (close === SOLIDUS) { - reader.readCodePoint(); + if (reader.codePointSource[reader.cursor] === SOLIDUS) { + reader.advanceCodePoint(); break; } } - const representation = reader.representation(); return [ TokenType.Comment, - reader.representationString(), - representation[0], - representation[1], + reader.source.slice(reader.representationStart, reader.representationEnd + 1), + reader.representationStart, + reader.representationEnd, undefined, ]; } diff --git a/packages/css-tokenizer/src/consume/escaped-code-point.ts b/packages/css-tokenizer/src/consume/escaped-code-point.ts index 0f2f6d3a5..6105310b4 100644 --- a/packages/css-tokenizer/src/consume/escaped-code-point.ts +++ b/packages/css-tokenizer/src/consume/escaped-code-point.ts @@ -1,23 +1,22 @@ import { MAXIMUM_ALLOWED_CODEPOINT, REPLACEMENT_CHARACTER } from '../code-points/code-points'; -import { codePointsToString } from '../code-points/code-points-to-string'; import { isHexDigitCodePoint, isSurrogate, isWhitespace } from '../code-points/ranges'; import { CodePointReader } from '../interfaces/code-point-reader'; import { Context } from '../interfaces/context'; +import { ParseError } from '../interfaces/error'; // https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#consume-escaped-code-point export function consumeEscapedCodePoint(ctx: Context, reader: CodePointReader): number { const codePoint = reader.readCodePoint(); if (codePoint === false) { - const representation = reader.representation(); - ctx.onParseError({ - message: 'Unexpected EOF while consuming an escaped code point.', - start: representation[0], - end: representation[1], - state: [ + ctx.onParseError(new ParseError( + 'Unexpected EOF while consuming an escaped code point.', + reader.representationStart, + reader.representationEnd, + [ '4.3.7. Consume an escaped code point', 'Unexpected EOF', ], - }); + )); return REPLACEMENT_CHARACTER; } @@ -25,18 +24,16 @@ export function consumeEscapedCodePoint(ctx: Context, reader: CodePointReader): if (isHexDigitCodePoint(codePoint)) { const hexSequence: Array = [codePoint]; - let peeked = reader.peekOneCodePoint(); - while (peeked !== false && isHexDigitCodePoint(peeked) && hexSequence.length < 6) { - reader.readCodePoint(); - hexSequence.push(peeked); - peeked = reader.peekOneCodePoint(); + while ((reader.codePointSource[reader.cursor] !== undefined) && isHexDigitCodePoint(reader.codePointSource[reader.cursor]) && hexSequence.length < 6) { + hexSequence.push(reader.codePointSource[reader.cursor]); + reader.advanceCodePoint(); } - if (peeked !== false && isWhitespace(peeked)) { - reader.readCodePoint(); + if (isWhitespace(reader.codePointSource[reader.cursor])) { + reader.advanceCodePoint(); } - const codePointLiteral = parseInt(codePointsToString(hexSequence), 16); + const codePointLiteral = parseInt(String.fromCharCode(...hexSequence), 16); if (codePointLiteral === 0) { return REPLACEMENT_CHARACTER; } diff --git a/packages/css-tokenizer/src/consume/hash-token.ts b/packages/css-tokenizer/src/consume/hash-token.ts index 96bb2eb70..11723e54f 100644 --- a/packages/css-tokenizer/src/consume/hash-token.ts +++ b/packages/css-tokenizer/src/consume/hash-token.ts @@ -1,6 +1,5 @@ import { checkIfThreeCodePointsWouldStartAnIdentSequence } from '../checks/three-code-points-would-start-ident-sequence'; import { checkIfTwoCodePointsAreAValidEscape } from '../checks/two-code-points-are-valid-escape'; -import { codePointsToString } from '../code-points/code-points-to-string'; import { isIdentCodePoint } from '../code-points/ranges'; import { CodePointReader } from '../interfaces/code-point-reader'; import { Context } from '../interfaces/context'; @@ -9,12 +8,11 @@ import { consumeIdentSequence } from './ident-sequence'; // https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#consume-token export function consumeHashToken(ctx: Context, reader: CodePointReader): TokenDelim|TokenHash { - reader.readCodePoint(); + reader.advanceCodePoint(); - const peeked = reader.peekOneCodePoint(); if ( - peeked !== false && - isIdentCodePoint(peeked) || + (reader.codePointSource[reader.cursor] !== undefined) && + isIdentCodePoint(reader.codePointSource[reader.cursor]) || checkIfTwoCodePointsAreAValidEscape(ctx, reader) ) { let hashType = HashType.Unrestricted; @@ -24,25 +22,23 @@ export function consumeHashToken(ctx: Context, reader: CodePointReader): TokenDe } const identSequence = consumeIdentSequence(ctx, reader); - const representation = reader.representation(); return [ TokenType.Hash, - reader.representationString(), - representation[0], - representation[1], + reader.source.slice(reader.representationStart, reader.representationEnd + 1), + reader.representationStart, + reader.representationEnd, { - value: codePointsToString(identSequence), + value: String.fromCharCode(...identSequence), type: hashType, }, ]; } - const representation = reader.representation(); return [ TokenType.Delim, - reader.representationString(), - representation[0], - representation[1], + '#', + reader.representationStart, + reader.representationEnd, { value: '#', }, diff --git a/packages/css-tokenizer/src/consume/ident-like-token.ts b/packages/css-tokenizer/src/consume/ident-like-token.ts index bca0d4a30..3782a9256 100644 --- a/packages/css-tokenizer/src/consume/ident-like-token.ts +++ b/packages/css-tokenizer/src/consume/ident-like-token.ts @@ -1,6 +1,5 @@ import { checkIfCodePointsMatchURLIdent } from '../checks/matches-url-ident'; import { APOSTROPHE, LEFT_PARENTHESIS, QUOTATION_MARK } from '../code-points/code-points'; -import { codePointsToString } from '../code-points/code-points-to-string'; import { isWhitespace } from '../code-points/ranges'; import { CodePointReader } from '../interfaces/code-point-reader'; import { Context } from '../interfaces/context'; @@ -11,74 +10,65 @@ import { consumeUrlToken } from './url-token'; // https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#consume-ident-like-token export function consumeIdentLikeToken(ctx: Context, reader: CodePointReader): TokenIdent | TokenFunction | TokenURL | TokenBadURL { const codePoints = consumeIdentSequence(ctx, reader); - const peeked = reader.peekOneCodePoint(); - if (peeked === LEFT_PARENTHESIS) { - if (checkIfCodePointsMatchURLIdent(ctx, codePoints)) { - reader.readCodePoint(); + if (reader.codePointSource[reader.cursor] !== LEFT_PARENTHESIS) { + return [ + TokenType.Ident, + reader.source.slice(reader.representationStart, reader.representationEnd + 1), + reader.representationStart, + reader.representationEnd, + { + value: String.fromCharCode(...codePoints), + }, + ]; + } - let read = 0; - // eslint-disable-next-line no-constant-condition - while (true) { - const peeked2 = reader.peekTwoCodePoints(); - const firstIsWhitespace = isWhitespace(peeked2[0]); - const secondIsWhitespace = isWhitespace(peeked2[1]); - if (firstIsWhitespace && secondIsWhitespace) { - read += 2; - reader.readCodePoint(); - reader.readCodePoint(); - continue; - } + if (checkIfCodePointsMatchURLIdent(ctx, codePoints)) { + reader.advanceCodePoint(); - const firstNonWhitespace = firstIsWhitespace ? peeked2[1] : peeked2[0]; - if (firstNonWhitespace === QUOTATION_MARK || firstNonWhitespace === APOSTROPHE) { - for (let i = 0; i < read; i++) { - reader.unreadCodePoint(); - } + let read = 0; + // eslint-disable-next-line no-constant-condition + while (true) { + const firstIsWhitespace = isWhitespace(reader.codePointSource[reader.cursor]); + const secondIsWhitespace = isWhitespace(reader.codePointSource[reader.cursor+1]); + if (firstIsWhitespace && secondIsWhitespace) { + read += 1; + reader.advanceCodePoint(1); + continue; + } - const representation = reader.representation(); - return [ - TokenType.Function, - reader.representationString(), - representation[0], - representation[1], - { - value: codePointsToString(codePoints), - }, - ]; + const firstNonWhitespace = firstIsWhitespace ? reader.codePointSource[reader.cursor+1] : reader.codePointSource[reader.cursor]; + if (firstNonWhitespace === QUOTATION_MARK || firstNonWhitespace === APOSTROPHE) { + if (read > 0) { + // https://github.com/w3c/csswg-drafts/issues/8280#issuecomment-1370566921 + reader.unreadCodePoint(read); } - break; + return [ + TokenType.Function, + reader.source.slice(reader.representationStart, reader.representationEnd + 1), + reader.representationStart, + reader.representationEnd, + { + value: String.fromCharCode(...codePoints), + }, + ]; } - for (let i = 0; i < read; i++) { - reader.unreadCodePoint(); - } - - return consumeUrlToken(ctx, reader); + break; } - reader.readCodePoint(); - const representation = reader.representation(); - return [ - TokenType.Function, - reader.representationString(), - representation[0], - representation[1], - { - value: codePointsToString(codePoints), - }, - ]; + return consumeUrlToken(ctx, reader); } - const representation = reader.representation(); + reader.advanceCodePoint(); return [ - TokenType.Ident, - reader.representationString(), - representation[0], - representation[1], + TokenType.Function, + reader.source.slice(reader.representationStart, reader.representationEnd + 1), + reader.representationStart, + reader.representationEnd, { - value: codePointsToString(codePoints), + value: String.fromCharCode(...codePoints), }, ]; } diff --git a/packages/css-tokenizer/src/consume/ident-sequence.ts b/packages/css-tokenizer/src/consume/ident-sequence.ts index ee4dfc696..42648f544 100644 --- a/packages/css-tokenizer/src/consume/ident-sequence.ts +++ b/packages/css-tokenizer/src/consume/ident-sequence.ts @@ -1,5 +1,4 @@ import { checkIfTwoCodePointsAreAValidEscape } from '../checks/two-code-points-are-valid-escape'; -import { REVERSE_SOLIDUS } from '../code-points/code-points'; import { isIdentCodePoint } from '../code-points/ranges'; import { CodePointReader } from '../interfaces/code-point-reader'; import { Context } from '../interfaces/context'; @@ -7,23 +6,18 @@ import { consumeEscapedCodePoint } from './escaped-code-point'; // https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#consume-name export function consumeIdentSequence(ctx: Context, reader: CodePointReader): Array { - const result = []; + const result: Array = []; // eslint-disable-next-line no-constant-condition while (true) { - const peeked = reader.peekOneCodePoint(); - if (peeked === false) { - return result; - } - - if (isIdentCodePoint(peeked)) { - reader.readCodePoint(); - result.push(peeked); + if (isIdentCodePoint(reader.codePointSource[reader.cursor])) { + result.push(reader.codePointSource[reader.cursor]); + reader.advanceCodePoint(); continue; } - if (peeked === REVERSE_SOLIDUS && checkIfTwoCodePointsAreAValidEscape(ctx, reader)) { - reader.readCodePoint(); + if (checkIfTwoCodePointsAreAValidEscape(ctx, reader)) { + reader.advanceCodePoint(); result.push(consumeEscapedCodePoint(ctx, reader)); continue; } diff --git a/packages/css-tokenizer/src/consume/number.ts b/packages/css-tokenizer/src/consume/number.ts index 00bd0659f..340b435d8 100644 --- a/packages/css-tokenizer/src/consume/number.ts +++ b/packages/css-tokenizer/src/consume/number.ts @@ -1,5 +1,4 @@ import { FULL_STOP, HYPHEN_MINUS, LATIN_CAPITAL_LETTER_E, LATIN_SMALL_LETTER_E, PLUS_SIGN } from '../code-points/code-points'; -import { codePointsToString } from '../code-points/code-points-to-string'; import { isDigitCodePoint } from '../code-points/ranges'; import { CodePointReader } from '../interfaces/code-point-reader'; import { Context } from '../interfaces/context'; @@ -8,225 +7,73 @@ import { NumberType } from '../interfaces/token'; // https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#consume-number export function consumeNumber(ctx: Context, reader: CodePointReader): [number, NumberType] { // 1. Initially set type to "integer". - // Let repr be the empty string. let type = NumberType.Integer; - const repr: Array = []; - { - // 2. If the next input code point is U+002B PLUS SIGN (+) or U+002D HYPHEN-MINUS (-), consume it and append it to repr. - const peeked = reader.peekOneCodePoint(); - if (peeked === PLUS_SIGN || peeked === HYPHEN_MINUS) { - reader.readCodePoint(); - repr.push(peeked); - } - - // 3. While the next input code point is a digit, consume it and append it to repr. - const newPart = consumeDigits(reader); - for (let i = 0; i < newPart.length; i++) { - repr.push(newPart[i]); - } + // 2. If the next input code point is U+002B PLUS SIGN (+) or U+002D HYPHEN-MINUS (-), consume it and append it to repr. + if (reader.codePointSource[reader.cursor] === PLUS_SIGN || reader.codePointSource[reader.cursor] === HYPHEN_MINUS) { + reader.advanceCodePoint(); } - { - // 4. If the next 2 input code points are U+002E FULL STOP (.) followed by a digit, then: - const peeked = reader.peekTwoCodePoints(); - if (peeked[0] === FULL_STOP && isDigitCodePoint(peeked[1])) { - // 4.1. Consume them. - reader.readCodePoint(); - reader.readCodePoint(); + // 3. While the next input code point is a digit, consume it and append it to repr. + while (isDigitCodePoint(reader.codePointSource[reader.cursor])) { + reader.advanceCodePoint(); + } - // 4.2. Append them to repr. - repr.push(peeked[0]); - repr.push(peeked[1]); + // 4. If the next 2 input code points are U+002E FULL STOP (.) followed by a digit, then: + if (reader.codePointSource[reader.cursor] === FULL_STOP && isDigitCodePoint(reader.codePointSource[reader.cursor+1])) { + // 4.1. Consume them. + reader.advanceCodePoint(2); - // 4.3. Set type to "number". - type = NumberType.Number; + // 4.3. Set type to "number". + type = NumberType.Number; - // 4.4. While the next input code point is a digit, consume it and append it to repr. - const newPart = consumeDigits(reader); - for (let i = 0; i < newPart.length; i++) { - repr.push(newPart[i]); - } + // 4.4. While the next input code point is a digit, consume it and append it to repr. + while (isDigitCodePoint(reader.codePointSource[reader.cursor])) { + reader.advanceCodePoint(); } } - { - // 5. If the next 2 or 3 input code points are U+0045 LATIN CAPITAL LETTER E (E) or U+0065 LATIN SMALL LETTER E (e), - // optionally followed by U+002D HYPHEN-MINUS (-) or U+002B PLUS SIGN (+), - // followed by a digit, then: - const peeked = reader.peekThreeCodePoints(); - if ( - (peeked[0] === LATIN_SMALL_LETTER_E || peeked[0] === LATIN_CAPITAL_LETTER_E) && - isDigitCodePoint(peeked[1]) - ) { - // 5.1. Consume them. - reader.readCodePoint(); - reader.readCodePoint(); - - // 5.2. Append them to repr. - repr.push(peeked[0]); - repr.push(peeked[1]); - - // 5.3. Set type to "number". - type = NumberType.Number; - - // 5.4. While the next input code point is a digit, consume it and append it to repr. - const newPart = consumeDigits(reader); - for (let i = 0; i < newPart.length; i++) { - repr.push(newPart[i]); - } + // 5. If the next 2 or 3 input code points are U+0045 LATIN CAPITAL LETTER E (E) or U+0065 LATIN SMALL LETTER E (e), + // optionally followed by U+002D HYPHEN-MINUS (-) or U+002B PLUS SIGN (+), + // followed by a digit, then: + if ( + (reader.codePointSource[reader.cursor] === LATIN_SMALL_LETTER_E || reader.codePointSource[reader.cursor] === LATIN_CAPITAL_LETTER_E) && + isDigitCodePoint(reader.codePointSource[reader.cursor+1]) + ) { + // 5.1. Consume them. + reader.advanceCodePoint(2); + + // 5.3. Set type to "number". + type = NumberType.Number; + + // 5.4. While the next input code point is a digit, consume it and append it to repr. + while (isDigitCodePoint(reader.codePointSource[reader.cursor])) { + reader.advanceCodePoint(); } + } - if ( - (peeked[0] === LATIN_SMALL_LETTER_E || peeked[0] === LATIN_CAPITAL_LETTER_E) && + if ( + (reader.codePointSource[reader.cursor] === LATIN_SMALL_LETTER_E || reader.codePointSource[reader.cursor] === LATIN_CAPITAL_LETTER_E) && ( - (peeked[1] === HYPHEN_MINUS || peeked[1] === PLUS_SIGN) && - isDigitCodePoint(peeked[2]) + (reader.codePointSource[reader.cursor+1] === HYPHEN_MINUS || reader.codePointSource[reader.cursor+1] === PLUS_SIGN) && + isDigitCodePoint(reader.codePointSource[reader.cursor+2]) ) - ) { - // 5.1. Consume them. - reader.readCodePoint(); - reader.readCodePoint(); - reader.readCodePoint(); - - // 5.2. Append them to repr. - repr.push(peeked[0]); - repr.push(peeked[1]); - repr.push(peeked[2]); + ) { + // 5.1. Consume them. + reader.advanceCodePoint(3); - // 5.3. Set type to "number". - type = NumberType.Number; + // 5.3. Set type to "number". + type = NumberType.Number; - // 5.4. While the next input code point is a digit, consume it and append it to repr. - const newPart = consumeDigits(reader); - for (let i = 0; i < newPart.length; i++) { - repr.push(newPart[i]); - } + // 5.4. While the next input code point is a digit, consume it and append it to repr. + while (isDigitCodePoint(reader.codePointSource[reader.cursor])) { + reader.advanceCodePoint(); } } // 6. Convert repr to a number, and set the value to the returned value. - const value = convertCodePointsToNumber(repr); + const value = parseFloat(reader.source.slice(reader.representationStart, reader.representationEnd+1)); // 7. Return value and type. return [value, type]; } - -function consumeDigits(reader: CodePointReader): Array { - const value: Array = []; - - // eslint-disable-next-line no-constant-condition - while (true) { - const peeked = reader.peekOneCodePoint(); - if (peeked === false) { - return value; - } - - if (isDigitCodePoint(peeked)) { - value.push(peeked); - reader.readCodePoint(); - } else { - return value; - } - } -} - -// https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#convert-string-to-number -function convertCodePointsToNumber(codePoints: Array): number { - let s = 1; - const iCodePoints: Array = []; - let i = 0; - - let d = 0; - const fCodePoints: Array = []; - let f = 0; - - let t = 1; - - const eCodePoints: Array = []; - let e = 0; - - let cursor = 0; - - // 1. A sign: a single U+002B PLUS SIGN (+) or U+002D HYPHEN-MINUS (-), or the empty string. - // Let s be the number -1 if the sign is U+002D HYPHEN-MINUS (-); - // otherwise, let s be the number 1. - if (codePoints[cursor] === HYPHEN_MINUS) { - cursor++; - s = -1; - } else if (codePoints[cursor] === PLUS_SIGN) { - cursor++; - } - - // 2. An integer part: zero or more digits. - // If there is at least one digit, - // let i be the number formed by interpreting the digits as a base-10 integer; - // otherwise, let i be the number 0. - while (cursor < codePoints.length && isDigitCodePoint(codePoints[cursor])) { - iCodePoints.push(codePoints[cursor]); - cursor++; - } - - i = digitCodePointsToInteger(iCodePoints); - - // 3. A decimal point: a single U+002E FULL STOP (.), or the empty string. - if (codePoints[cursor] === FULL_STOP) { - cursor++; - } - - // 4. A fractional part: zero or more digits. - // If there is at least one digit, - // let f be the number formed by interpreting the digits as a base-10 integer and d be the number of digits; - // otherwise, let f and d be the number 0. - while (cursor < codePoints.length && isDigitCodePoint(codePoints[cursor])) { - fCodePoints.push(codePoints[cursor]); - cursor++; - } - - d = fCodePoints.length; - f = (digitCodePointsToInteger(fCodePoints) / Math.pow(10, d)); - - // 5. An exponent indicator: a single U+0045 LATIN CAPITAL LETTER E (E) or U+0065 LATIN SMALL LETTER E (e), or the empty string. - if (codePoints[cursor] === LATIN_SMALL_LETTER_E || codePoints[cursor] === LATIN_CAPITAL_LETTER_E) { - cursor++; - } - - // 6. An exponent sign: a single U+002B PLUS SIGN (+) or U+002D HYPHEN-MINUS (-), or the empty string. - // Let t be the number -1 if the sign is U+002D HYPHEN-MINUS (-); - // otherwise, let t be the number 1. - if (codePoints[cursor] === HYPHEN_MINUS) { - cursor++; - t = -1; - } else if (codePoints[cursor] === PLUS_SIGN) { - cursor++; - } - - // 7. An exponent: zero or more digits. - // If there is at least one digit, - // let e be the number formed by interpreting the digits as a base-10 integer; - // otherwise, let e be the number 0. - while (cursor < codePoints.length && isDigitCodePoint(codePoints[cursor])) { - eCodePoints.push(codePoints[cursor]); - cursor++; - } - - e = digitCodePointsToInteger(eCodePoints); - - // Return the number s·(i + f·10-d)·10te. - return s * (i + f) * Math.pow(10, t * e); -} - -function digitCodePointsToInteger(codePoints: Array): number { - if (codePoints.length === 0) { - return 0; - } - - const stringValue = codePointsToString(codePoints); - - const integerValue = Number.parseInt(stringValue, 10); - if (Number.isNaN(integerValue)) { - throw new Error(`Unexpected "NaN" result when parsing a number from digit code points: "${stringValue}"`); - } - - return integerValue; -} diff --git a/packages/css-tokenizer/src/consume/numeric-token.ts b/packages/css-tokenizer/src/consume/numeric-token.ts index f039c1795..2e7a428eb 100644 --- a/packages/css-tokenizer/src/consume/numeric-token.ts +++ b/packages/css-tokenizer/src/consume/numeric-token.ts @@ -1,6 +1,5 @@ import { checkIfThreeCodePointsWouldStartAnIdentSequence } from '../checks/three-code-points-would-start-ident-sequence'; import { PERCENTAGE_SIGN } from '../code-points/code-points'; -import { codePointsToString } from '../code-points/code-points-to-string'; import { CodePointReader } from '../interfaces/code-point-reader'; import { Context } from '../interfaces/context'; import { TokenDimension, TokenNumber, TokenPercentage, TokenType } from '../interfaces/token'; @@ -13,45 +12,38 @@ export function consumeNumericToken(ctx: Context, reader: CodePointReader): Toke if (checkIfThreeCodePointsWouldStartAnIdentSequence(ctx, reader)) { const unit = consumeIdentSequence(ctx, reader); - - const representation = reader.representation(); return [ TokenType.Dimension, - reader.representationString(), - representation[0], - representation[1], + reader.source.slice(reader.representationStart, reader.representationEnd + 1), + reader.representationStart, + reader.representationEnd, { value: numberValue[0], type: numberValue[1], - unit: codePointsToString(unit), + unit: String.fromCharCode(...unit), }, ]; } - { - const peeked = reader.peekOneCodePoint(); - if (peeked === PERCENTAGE_SIGN) { - reader.readCodePoint(); + if (reader.codePointSource[reader.cursor] === PERCENTAGE_SIGN) { + reader.advanceCodePoint(); - const representation = reader.representation(); - return [ - TokenType.Percentage, - reader.representationString(), - representation[0], - representation[1], - { - value: numberValue[0], - }, - ]; - } + return [ + TokenType.Percentage, + reader.source.slice(reader.representationStart, reader.representationEnd + 1), + reader.representationStart, + reader.representationEnd, + { + value: numberValue[0], + }, + ]; } - const representation = reader.representation(); return [ TokenType.Number, - reader.representationString(), - representation[0], - representation[1], + reader.source.slice(reader.representationStart, reader.representationEnd + 1), + reader.representationStart, + reader.representationEnd, { value: numberValue[0], type: numberValue[1], diff --git a/packages/css-tokenizer/src/consume/string-token.ts b/packages/css-tokenizer/src/consume/string-token.ts index fcadd2fc4..5b9094caf 100644 --- a/packages/css-tokenizer/src/consume/string-token.ts +++ b/packages/css-tokenizer/src/consume/string-token.ts @@ -2,6 +2,7 @@ import { REVERSE_SOLIDUS } from '../code-points/code-points'; import { isNewLine } from '../code-points/ranges'; import { CodePointReader } from '../interfaces/code-point-reader'; import { Context } from '../interfaces/context'; +import { ParseError } from '../interfaces/error'; import { TokenBadString, TokenString, TokenType } from '../interfaces/token'; import { consumeEscapedCodePoint } from './escaped-code-point'; @@ -10,59 +11,52 @@ export function consumeStringToken(ctx: Context, reader: CodePointReader): Token let result = ''; const first = reader.readCodePoint(); - if (first === false) { - throw new Error('Unexpected EOF'); - } // eslint-disable-next-line no-constant-condition while (true) { const next = reader.readCodePoint(); if (next === false) { - const representation = reader.representation(); - ctx.onParseError({ - message: 'Unexpected EOF while consuming a string token.', - start: representation[0], - end: representation[1], - state: [ + ctx.onParseError(new ParseError( + 'Unexpected EOF while consuming a string token.', + reader.representationStart, + reader.representationEnd, + [ '4.3.5. Consume a string token', 'Unexpected EOF', ], - }); + )); - return [TokenType.String, reader.representationString(), representation[0], representation[1], { value: result }]; + return [TokenType.String, reader.source.slice(reader.representationStart, reader.representationEnd + 1), reader.representationStart, reader.representationEnd, { value: result }]; } if (isNewLine(next)) { - const representation = reader.representation(); { - ctx.onParseError({ - message: 'Unexpected newline while consuming a string token.', - start: representation[0], - end: representation[1], - state: [ + ctx.onParseError(new ParseError( + 'Unexpected newline while consuming a string token.', + reader.representationStart, + reader.representationEnd, + [ '4.3.5. Consume a string token', 'Unexpected newline', ], - }); + )); } reader.unreadCodePoint(); - return [TokenType.BadString, reader.representationString(), representation[0], representation[1], undefined]; + return [TokenType.BadString, reader.source.slice(reader.representationStart, reader.representationEnd + 1), reader.representationStart, reader.representationEnd, undefined]; } if (next === first) { - const representation = reader.representation(); - return [TokenType.String, reader.representationString(), representation[0], representation[1], { value: result }]; + return [TokenType.String, reader.source.slice(reader.representationStart, reader.representationEnd + 1), reader.representationStart, reader.representationEnd, { value: result }]; } if (next === REVERSE_SOLIDUS) { - const peeked = reader.peekOneCodePoint(); - if (peeked === false) { + if (reader.codePointSource[reader.cursor] === undefined) { continue; } - if (isNewLine(peeked)) { - reader.readCodePoint(); + if (isNewLine(reader.codePointSource[reader.cursor])) { + reader.advanceCodePoint(); continue; } diff --git a/packages/css-tokenizer/src/consume/url-token.ts b/packages/css-tokenizer/src/consume/url-token.ts index 961e3f953..d7f295ee9 100644 --- a/packages/css-tokenizer/src/consume/url-token.ts +++ b/packages/css-tokenizer/src/consume/url-token.ts @@ -3,6 +3,7 @@ import { APOSTROPHE, LEFT_PARENTHESIS, QUOTATION_MARK, REVERSE_SOLIDUS, RIGHT_PA import { isNonPrintableCodePoint, isWhitespace } from '../code-points/ranges'; import { CodePointReader } from '../interfaces/code-point-reader'; import { Context } from '../interfaces/context'; +import { ParseError } from '../interfaces/error'; import { TokenBadURL, TokenType, TokenURL } from '../interfaces/token'; import { consumeBadURL } from './bad-url'; import { consumeEscapedCodePoint } from './escaped-code-point'; @@ -15,79 +16,73 @@ export function consumeUrlToken(ctx: Context, reader: CodePointReader): TokenURL // eslint-disable-next-line no-constant-condition while (true) { - const peeked = reader.peekOneCodePoint(); - if (peeked === false) { - const representation = reader.representation(); - ctx.onParseError({ - message: 'Unexpected EOF while consuming a url token.', - start: representation[0], - end: representation[1], - state: [ + if (reader.codePointSource[reader.cursor] === undefined) { + ctx.onParseError(new ParseError( + 'Unexpected EOF while consuming a url token.', + reader.representationStart, + reader.representationEnd, + [ '4.3.6. Consume a url token', 'Unexpected EOF', ], - }); + )); return [ TokenType.URL, - reader.representationString(), - representation[0], - representation[1], + reader.source.slice(reader.representationStart, reader.representationEnd + 1), + reader.representationStart, + reader.representationEnd, { value: string, }, ]; } - if (peeked === RIGHT_PARENTHESIS) { - reader.readCodePoint(); - const representation = reader.representation(); + if (reader.codePointSource[reader.cursor] === RIGHT_PARENTHESIS) { + reader.advanceCodePoint(); return [ TokenType.URL, - reader.representationString(), - representation[0], - representation[1], + reader.source.slice(reader.representationStart, reader.representationEnd + 1), + reader.representationStart, + reader.representationEnd, { value: string, }, ]; } - if (isWhitespace(peeked)) { + if (isWhitespace(reader.codePointSource[reader.cursor])) { consumeWhiteSpace(ctx, reader); - const peeked2 = reader.peekOneCodePoint(); - if (peeked2 === false) { - const representation = reader.representation(); - ctx.onParseError({ - message: 'Unexpected EOF while consuming a url token.', - start: representation[0], - end: representation[1], - state: [ + if (reader.codePointSource[reader.cursor] === undefined) { + ctx.onParseError(new ParseError( + 'Unexpected EOF while consuming a url token.', + reader.representationStart, + reader.representationEnd, + [ '4.3.6. Consume a url token', 'Consume as much whitespace as possible', 'Unexpected EOF', ], - }); + )); return [ TokenType.URL, - reader.representationString(), - representation[0], - representation[1], + reader.source.slice(reader.representationStart, reader.representationEnd + 1), + reader.representationStart, + reader.representationEnd, { value: string, }, ]; } - if (peeked2 === RIGHT_PARENTHESIS) { - reader.readCodePoint(); - const representation = reader.representation(); + if (reader.codePointSource[reader.cursor] === RIGHT_PARENTHESIS) { + reader.advanceCodePoint(); return [ TokenType.URL, - reader.representationString(), - representation[0], - representation[1], + reader.source.slice(reader.representationStart, reader.representationEnd + 1), + reader.representationStart, + reader.representationEnd, { value: string, }, @@ -95,40 +90,38 @@ export function consumeUrlToken(ctx: Context, reader: CodePointReader): TokenURL } consumeBadURL(ctx, reader); - const representation = reader.representation(); return [ TokenType.BadURL, - reader.representationString(), - representation[0], - representation[1], + reader.source.slice(reader.representationStart, reader.representationEnd + 1), + reader.representationStart, + reader.representationEnd, undefined, ]; } - if (peeked === QUOTATION_MARK || peeked === APOSTROPHE || peeked === LEFT_PARENTHESIS || isNonPrintableCodePoint(peeked)) { + if (reader.codePointSource[reader.cursor] === QUOTATION_MARK || reader.codePointSource[reader.cursor] === APOSTROPHE || reader.codePointSource[reader.cursor] === LEFT_PARENTHESIS || isNonPrintableCodePoint(reader.codePointSource[reader.cursor])) { consumeBadURL(ctx, reader); - const representation = reader.representation(); - ctx.onParseError({ - message: 'Unexpected character while consuming a url token.', - start: representation[0], - end: representation[1], - state: [ + ctx.onParseError(new ParseError( + 'Unexpected character while consuming a url token.', + reader.representationStart, + reader.representationEnd, + [ '4.3.6. Consume a url token', 'Unexpected U+0022 QUOTATION MARK ("), U+0027 APOSTROPHE (\'), U+0028 LEFT PARENTHESIS (() or non-printable code point', ], - }); + )); return [ TokenType.BadURL, - reader.representationString(), - representation[0], - representation[1], + reader.source.slice(reader.representationStart, reader.representationEnd + 1), + reader.representationStart, + reader.representationEnd, undefined, ]; } - if (peeked === REVERSE_SOLIDUS) { + if (reader.codePointSource[reader.cursor] === REVERSE_SOLIDUS) { if (checkIfTwoCodePointsAreAValidEscape(ctx, reader)) { string += String.fromCharCode(consumeEscapedCodePoint(ctx, reader)); continue; @@ -136,28 +129,27 @@ export function consumeUrlToken(ctx: Context, reader: CodePointReader): TokenURL consumeBadURL(ctx, reader); - const representation = reader.representation(); - ctx.onParseError({ - message: 'Invalid escape sequence while consuming a url token.', - start: representation[0], - end: representation[1], - state: [ + ctx.onParseError(new ParseError( + 'Invalid escape sequence while consuming a url token.', + reader.representationStart, + reader.representationEnd, + [ '4.3.6. Consume a url token', 'U+005C REVERSE SOLIDUS (\\)', 'The input stream does not start with a valid escape sequence', ], - }); + )); return [ TokenType.BadURL, - reader.representationString(), - representation[0], - representation[1], + reader.source.slice(reader.representationStart, reader.representationEnd + 1), + reader.representationStart, + reader.representationEnd, undefined, ]; } - reader.readCodePoint(); - string += String.fromCharCode(peeked); + string += String.fromCharCode(reader.codePointSource[reader.cursor]); + reader.advanceCodePoint(); } } diff --git a/packages/css-tokenizer/src/consume/whitespace-token.ts b/packages/css-tokenizer/src/consume/whitespace-token.ts index e6febd985..e2ac9d6e2 100644 --- a/packages/css-tokenizer/src/consume/whitespace-token.ts +++ b/packages/css-tokenizer/src/consume/whitespace-token.ts @@ -3,48 +3,21 @@ import { CodePointReader } from '../interfaces/code-point-reader'; import { Context } from '../interfaces/context'; import { TokenType, TokenWhitespace } from '../interfaces/token'; -export function consumeWhiteSpace(ctx: Context, reader: CodePointReader, max = -1): TokenWhitespace { - let current = 0; - +export function consumeWhiteSpace(ctx: Context, reader: CodePointReader): TokenWhitespace { // eslint-disable-next-line no-constant-condition while (true) { - if (max !== -1 && current === max) { - const representation = reader.representation(); - return [ - TokenType.Whitespace, - reader.representationString(), - representation[0], - representation[1], - undefined, - ]; - } - - current++; - const peeked = reader.peekOneCodePoint(); - if (peeked === false) { - const representation = reader.representation(); - return [ - TokenType.Whitespace, - reader.representationString(), - representation[0], - representation[1], - undefined, - ]; - } - - if (!isWhitespace(peeked)) { + if (!isWhitespace(reader.codePointSource[reader.cursor])) { break; } - reader.readCodePoint(); + reader.advanceCodePoint(); } - const representation = reader.representation(); return [ TokenType.Whitespace, - reader.representationString(), - representation[0], - representation[1], + reader.source.slice(reader.representationStart, reader.representationEnd + 1), + reader.representationStart, + reader.representationEnd, undefined, ]; } diff --git a/packages/css-tokenizer/src/index.ts b/packages/css-tokenizer/src/index.ts index 015d99029..621d6b5e9 100644 --- a/packages/css-tokenizer/src/index.ts +++ b/packages/css-tokenizer/src/index.ts @@ -1,6 +1,7 @@ export type { CSSToken } from './interfaces/token'; +export { ParseError } from './interfaces/error'; export { Reader } from './reader'; -export { TokenType, NumberType, mirrorVariantType, isToken } from './interfaces/token'; +export { TokenType, NumberType, mirrorVariantType, mirrorVariant, isToken } from './interfaces/token'; export { stringify } from './stringify'; export { tokenizer } from './tokenizer'; export { cloneTokens } from './util/clone-tokens'; @@ -33,3 +34,7 @@ export type { TokenOpenCurly, TokenCloseCurly, } from './interfaces/token'; + +export { + mutateIdent, +} from './util/mutations'; diff --git a/packages/css-tokenizer/src/interfaces/code-point-reader.ts b/packages/css-tokenizer/src/interfaces/code-point-reader.ts index 6f1c8b216..efc3647ae 100644 --- a/packages/css-tokenizer/src/interfaces/code-point-reader.ts +++ b/packages/css-tokenizer/src/interfaces/code-point-reader.ts @@ -5,19 +5,16 @@ // A char code in JS is equivalent to a code point from the CSS Specification. export type CodePointReader = { - cursorPositionOfLastReadCodePoint(): number; - - peekOneCodePoint(): number | false - peekTwoCodePoints(): [number, number] | [number] | [] - peekThreeCodePoints(): [number, number, number] | [number, number] | [number] | [] - peekFourCodePoints(): [number, number, number, number] | [number, number, number] | [number, number] | [number] | [] + representationStart: number; + representationEnd: number; - readCodePoint(): number | false - unreadCodePoint(): boolean + cursor: number; + codePointSource: Array; + source: string; - representation(): [number, number] - representationString(): string - resetRepresentation() + cursorPositionOfLastReadCodePoint(): number; - slice(start: number, end: number): string + advanceCodePoint(n?: number): void + readCodePoint(n?: number): number | false + unreadCodePoint(n?: number): boolean } diff --git a/packages/css-tokenizer/src/interfaces/context.ts b/packages/css-tokenizer/src/interfaces/context.ts index 27eb77de6..6a2b523b1 100644 --- a/packages/css-tokenizer/src/interfaces/context.ts +++ b/packages/css-tokenizer/src/interfaces/context.ts @@ -1,5 +1,5 @@ -import { ParserError } from './error'; +import { ParseError } from './error'; export type Context = { - onParseError: (error: ParserError) => void + onParseError: (error: ParseError) => void } diff --git a/packages/css-tokenizer/src/interfaces/error.ts b/packages/css-tokenizer/src/interfaces/error.ts index e8c677745..a22dbc830 100644 --- a/packages/css-tokenizer/src/interfaces/error.ts +++ b/packages/css-tokenizer/src/interfaces/error.ts @@ -1,6 +1,17 @@ -export type ParserError = { - message: string, - start: number, - end: number, - state: Array +export 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; + + constructor(message, sourceStart: number, sourceEnd: number, parserState: Array) { + super(message); + this.name = 'ParseError'; + + this.sourceStart = sourceStart; + this.sourceEnd = sourceEnd; + this.parserState = parserState; + } } diff --git a/packages/css-tokenizer/src/interfaces/token.ts b/packages/css-tokenizer/src/interfaces/token.ts index 81a916036..c0be658ed 100644 --- a/packages/css-tokenizer/src/interfaces/token.ts +++ b/packages/css-tokenizer/src/interfaces/token.ts @@ -133,7 +133,7 @@ export type Token = [ U, ] -export function mirrorVariantType(type: TokenType): TokenType|null { +export function mirrorVariantType(type: TokenType): TokenType | null { switch (type) { case TokenType.OpenParen: return TokenType.CloseParen; @@ -155,6 +155,28 @@ export function mirrorVariantType(type: TokenType): TokenType|null { } } +export function mirrorVariant(token: CSSToken): CSSToken | null { + switch (token[0]) { + case TokenType.OpenParen: + return [TokenType.CloseParen, ')', -1, -1, undefined]; + case TokenType.CloseParen: + return [TokenType.OpenParen, '(', -1, -1, undefined]; + + case TokenType.OpenCurly: + return [TokenType.CloseCurly, '}', -1, -1, undefined]; + case TokenType.CloseCurly: + return [TokenType.OpenCurly, '{', -1, -1, undefined]; + + case TokenType.OpenSquare: + return [TokenType.CloseSquare, ']', -1, -1, undefined]; + case TokenType.CloseSquare: + return [TokenType.OpenSquare, '[', -1, -1, undefined]; + + default: + return null; + } +} + // eslint-disable-next-line @typescript-eslint/no-explicit-any export function isToken(x: any): x is CSSToken { if (!Array.isArray(x)) { diff --git a/packages/css-tokenizer/src/reader.ts b/packages/css-tokenizer/src/reader.ts index 287b5627d..73d1b3223 100644 --- a/packages/css-tokenizer/src/reader.ts +++ b/packages/css-tokenizer/src/reader.ts @@ -1,139 +1,54 @@ import { CodePointReader } from './interfaces/code-point-reader'; export class Reader implements CodePointReader { - #cursor: number; - #stringSource = ''; - #codePointSource: Array = []; - #length = 0; + cursor: number; + source = ''; + codePointSource: Array = []; + length = 0; - #representationStart = 0; - #representationEnd = -1; + representationStart = 0; + representationEnd = -1; constructor(source: string) { - this.#cursor = 0; - this.#stringSource = source; - this.#length = source.length; + this.cursor = 0; + this.source = source; + this.length = source.length; - for (let i = 0; i < this.#length; i++) { - this.#codePointSource.push(this.#stringSource.charCodeAt(i)); + this.codePointSource = new Array(this.length); + for (let i = 0; i < this.length; i++) { + this.codePointSource[i] = this.source.charCodeAt(i); } } cursorPositionOfLastReadCodePoint(): number { - return this.#cursor - 1; + return this.cursor - 1; } - peekOneCodePoint(): number | false { - const first = this.#codePointSource[this.#cursor]; - if (typeof first === 'undefined') { - return false; - } - - return first; - } - - peekTwoCodePoints(): [number, number] | [number] | [] { - const first = this.#codePointSource[this.#cursor]; - if (typeof first === 'undefined') { - return []; - } - - const second = this.#codePointSource[this.#cursor + 1]; - if (typeof second === 'undefined') { - return [first]; - } - - return [first, second]; + advanceCodePoint(n = 1): void { + this.cursor += n; + this.representationEnd = this.cursor - 1; } - peekThreeCodePoints(): [number, number, number] | [number, number] | [number] | [] { - const first = this.#codePointSource[this.#cursor]; - if (typeof first === 'undefined') { - return []; - } - - const second = this.#codePointSource[this.#cursor + 1]; - if (typeof second === 'undefined') { - return [first]; - } - - const third = this.#codePointSource[this.#cursor + 2]; - if (typeof third === 'undefined') { - return [first, second]; - } - - return [first, second, third]; - } - - peekFourCodePoints(): [number, number, number, number] | [number, number, number] | [number, number] | [number] | [] { - const first = this.#codePointSource[this.#cursor]; - if (typeof first === 'undefined') { - return []; - } - - const second = this.#codePointSource[this.#cursor + 1]; - if (typeof second === 'undefined') { - return [first]; - } - - const third = this.#codePointSource[this.#cursor + 2]; - if (typeof third === 'undefined') { - return [first, second]; - } - - const fourth = this.#codePointSource[this.#cursor + 2]; - if (typeof fourth === 'undefined') { - return [first, second, third]; - } - - return [first, second, third, fourth]; - } - - readCodePoint(): number | false { - const codePoint = this.#codePointSource[this.#cursor]; - if (typeof codePoint === 'undefined') { + readCodePoint(n = 1): number | false { + const codePoint = this.codePointSource[this.cursor]; + if (codePoint === undefined) { return false; } - this.#representationEnd = this.#cursor; - this.#cursor += 1; + this.cursor += n; + this.representationEnd = this.cursor - 1; return codePoint; } - unreadCodePoint(): boolean { - if (this.#cursor === 0) { + unreadCodePoint(n = 1): boolean { + if (this.cursor === 0) { return false; } - this.#representationEnd = this.#cursor - 1; - this.#cursor -= 1; + this.cursor -= n; + this.representationEnd = this.cursor - 1; return true; } - - representation(): [number, number] { - return [ - this.#representationStart, - this.#representationEnd, - ]; - } - - representationString(): string { - const representation = this.representation(); - if (representation[1] === -1) { - return ''; - } - - return this.slice(representation[0], representation[1] + 1); - } - - resetRepresentation() { - this.#representationStart = this.#cursor; - this.#representationEnd = -1; - } - - slice(start: number, end: number): string { - return this.#stringSource.slice(start, end); - } } diff --git a/packages/css-tokenizer/src/stringify.ts b/packages/css-tokenizer/src/stringify.ts index 90d190871..39f797894 100644 --- a/packages/css-tokenizer/src/stringify.ts +++ b/packages/css-tokenizer/src/stringify.ts @@ -3,7 +3,7 @@ import type { CSSToken } from './interfaces/token'; export function stringify(...tokens: Array): string { let buffer = ''; for (let i = 0; i < tokens.length; i++) { - buffer = buffer + tokens[i][1]; + buffer += tokens[i][1]; } return buffer; diff --git a/packages/css-tokenizer/src/tokenizer.ts b/packages/css-tokenizer/src/tokenizer.ts index 6a4d9ee7b..e45d666c5 100644 --- a/packages/css-tokenizer/src/tokenizer.ts +++ b/packages/css-tokenizer/src/tokenizer.ts @@ -3,8 +3,8 @@ import { checkIfThreeCodePointsWouldStartAnIdentSequence } from './checks/three- import { checkIfThreeCodePointsWouldStartANumber } from './checks/three-code-points-would-start-number'; import { checkIfTwoCodePointsStartAComment } from './checks/two-code-points-start-comment'; import { checkIfThreeCodePointsWouldStartCDC } from './checks/three-code-points-would-start-cdc'; -import { APOSTROPHE, COLON, COMMA, COMMERCIAL_AT, FULL_STOP, HYPHEN_MINUS, LEFT_CURLY_BRACKET, LEFT_PARENTHESIS, LEFT_SQUARE_BRACKET, LESS_THAN_SIGN, NUMBER_SIGN, PLUS_SIGN, QUOTATION_MARK, REVERSE_SOLIDUS, RIGHT_CURLY_BRACKET, RIGHT_PARENTHESIS, RIGHT_SQUARE_BRACKET, SEMICOLON } from './code-points/code-points'; -import { isDigitCodePoint, isIdentStartCodePoint, isWhitespace } from './code-points/ranges'; +import { APOSTROPHE, CARRIAGE_RETURN, CHARACTER_TABULATION, COLON, COMMA, COMMERCIAL_AT, FORM_FEED, FULL_STOP, HYPHEN_MINUS, LEFT_CURLY_BRACKET, LEFT_PARENTHESIS, LEFT_SQUARE_BRACKET, LESS_THAN_SIGN, LINE_FEED, NUMBER_SIGN, PLUS_SIGN, QUOTATION_MARK, REVERSE_SOLIDUS, RIGHT_CURLY_BRACKET, RIGHT_PARENTHESIS, RIGHT_SQUARE_BRACKET, SEMICOLON, SPACE } from './code-points/code-points'; +import { isDigitCodePoint, isIdentStartCodePoint } from './code-points/ranges'; import { consumeComment } from './consume/comment'; import { consumeHashToken } from './consume/hash-token'; import { consumeIdentSequence } from './consume/ident-sequence'; @@ -15,14 +15,13 @@ import { Reader } from './reader'; import { consumeStringToken } from './consume/string-token'; import { consumeIdentLikeToken } from './consume/ident-like-token'; import { checkIfTwoCodePointsAreAValidEscape } from './checks/two-code-points-are-valid-escape'; -import { ParserError } from './interfaces/error'; -import { codePointsToString } from './code-points/code-points-to-string'; +import { ParseError } from './interfaces/error'; interface Stringer { valueOf(): string } -export function tokenizer(input: { css: Stringer }, options?: { commentsAreTokens?: boolean, onParseError?: (error: ParserError) => void }) { +export function tokenizer(input: { css: Stringer }, options?: { commentsAreTokens?: boolean, onParseError?: (error: ParseError) => void }) { const css = input.css.valueOf(); const reader = new Reader(css); @@ -32,197 +31,171 @@ export function tokenizer(input: { css: Stringer }, options?: { commentsAreToken }; function endOfFile() { - return reader.peekOneCodePoint() === false; + return reader.codePointSource[reader.cursor] === undefined; } function nextToken(): CSSToken | undefined { - reader.resetRepresentation(); + reader.representationStart = reader.cursor; + reader.representationEnd = -1; if (checkIfTwoCodePointsStartAComment(ctx, reader)) { if (options?.commentsAreTokens) { return consumeComment(ctx, reader); } else { consumeComment(ctx, reader); + reader.representationStart = reader.cursor; + reader.representationEnd = -1; } } - reader.resetRepresentation(); - - const peeked = reader.peekOneCodePoint(); - if (peeked === false) { + const peeked = reader.codePointSource[reader.cursor]; + if (peeked === undefined) { return [TokenType.EOF, '', -1, -1, undefined]; } - // Simple, one character tokens: - switch (peeked) { - case COMMA: { - reader.readCodePoint(); - const representation = reader.representation(); - return [TokenType.Comma, reader.representationString(), representation[0], representation[1], undefined]; - } - case COLON: { - reader.readCodePoint(); - const representation = reader.representation(); - return [TokenType.Colon, reader.representationString(), representation[0], representation[1], undefined]; - } - case SEMICOLON: { - reader.readCodePoint(); - const representation = reader.representation(); - return [TokenType.Semicolon, reader.representationString(), representation[0], representation[1], undefined]; - } - case LEFT_PARENTHESIS: { - reader.readCodePoint(); - const representation = reader.representation(); - return [TokenType.OpenParen, reader.representationString(), representation[0], representation[1], undefined]; - } - case RIGHT_PARENTHESIS: { - reader.readCodePoint(); - const representation = reader.representation(); - return [TokenType.CloseParen, reader.representationString(), representation[0], representation[1], undefined]; - } - case LEFT_SQUARE_BRACKET: { - reader.readCodePoint(); - const representation = reader.representation(); - return [TokenType.OpenSquare, reader.representationString(), representation[0], representation[1], undefined]; - } - case RIGHT_SQUARE_BRACKET: { - reader.readCodePoint(); - const representation = reader.representation(); - return [TokenType.CloseSquare, reader.representationString(), representation[0], representation[1], undefined]; - } - case LEFT_CURLY_BRACKET: { - reader.readCodePoint(); - const representation = reader.representation(); - return [TokenType.OpenCurly, reader.representationString(), representation[0], representation[1], undefined]; - } - case RIGHT_CURLY_BRACKET: { - reader.readCodePoint(); - const representation = reader.representation(); - return [TokenType.CloseCurly, reader.representationString(), representation[0], representation[1], undefined]; - } + if (isIdentStartCodePoint(peeked)) { + return consumeIdentLikeToken(ctx, reader); + } + + if (isDigitCodePoint(peeked)) { + return consumeNumericToken(ctx, reader); } + // Simple, one character tokens: switch (peeked) { + case COMMA: + reader.advanceCodePoint(); + return [TokenType.Comma, ',', reader.representationStart, reader.representationEnd, undefined]; + + case COLON: + reader.advanceCodePoint(); + return [TokenType.Colon, ':', reader.representationStart, reader.representationEnd, undefined]; + + case SEMICOLON: + reader.advanceCodePoint(); + return [TokenType.Semicolon, ';', reader.representationStart, reader.representationEnd, undefined]; + + case LEFT_PARENTHESIS: + reader.advanceCodePoint(); + return [TokenType.OpenParen, '(', reader.representationStart, reader.representationEnd, undefined]; + + case RIGHT_PARENTHESIS: + reader.advanceCodePoint(); + return [TokenType.CloseParen, ')', reader.representationStart, reader.representationEnd, undefined]; + + case LEFT_SQUARE_BRACKET: + reader.advanceCodePoint(); + return [TokenType.OpenSquare, '[', reader.representationStart, reader.representationEnd, undefined]; + + case RIGHT_SQUARE_BRACKET: + reader.advanceCodePoint(); + return [TokenType.CloseSquare, ']', reader.representationStart, reader.representationEnd, undefined]; + + case LEFT_CURLY_BRACKET: + reader.advanceCodePoint(); + return [TokenType.OpenCurly, '{', reader.representationStart, reader.representationEnd, undefined]; + + case RIGHT_CURLY_BRACKET: + reader.advanceCodePoint(); + return [TokenType.CloseCurly, '}', reader.representationStart, reader.representationEnd, undefined]; + case APOSTROPHE: case QUOTATION_MARK: return consumeStringToken(ctx, reader); + case NUMBER_SIGN: return consumeHashToken(ctx, reader); case PLUS_SIGN: - case FULL_STOP: { + case FULL_STOP: if (checkIfThreeCodePointsWouldStartANumber(ctx, reader)) { return consumeNumericToken(ctx, reader); } - reader.readCodePoint(); - const representation = reader.representation(); - return [TokenType.Delim, reader.representationString(), representation[0], representation[1], { - value: String.fromCharCode(peeked), + reader.advanceCodePoint(); + return [TokenType.Delim, reader.source[reader.representationStart], reader.representationStart, reader.representationEnd, { + value: reader.source[reader.representationStart], }]; - } - case HYPHEN_MINUS: { + case LINE_FEED: + case CARRIAGE_RETURN: + case FORM_FEED: + case CHARACTER_TABULATION: + case SPACE: + return consumeWhiteSpace(ctx, reader); + + case HYPHEN_MINUS: if (checkIfThreeCodePointsWouldStartANumber(ctx, reader)) { return consumeNumericToken(ctx, reader); } if (checkIfThreeCodePointsWouldStartCDC(ctx, reader)) { - reader.readCodePoint(); - reader.readCodePoint(); - reader.readCodePoint(); + reader.advanceCodePoint(3); - const representation = reader.representation(); - return [TokenType.CDC, reader.representationString(), representation[0], representation[1], undefined]; + return [TokenType.CDC, '-->', reader.representationStart, reader.representationEnd, undefined]; } if (checkIfThreeCodePointsWouldStartAnIdentSequence(ctx, reader)) { return consumeIdentLikeToken(ctx, reader); } - reader.readCodePoint(); - const representation = reader.representation(); - return [TokenType.Delim, reader.representationString(), representation[0], representation[1], { + reader.advanceCodePoint(); + return [TokenType.Delim, '-', reader.representationStart, reader.representationEnd, { value: '-', }]; - } - case LESS_THAN_SIGN: { + case LESS_THAN_SIGN: if (checkIfFourCodePointsWouldStartCDO(ctx, reader)) { - reader.readCodePoint(); - reader.readCodePoint(); - reader.readCodePoint(); - reader.readCodePoint(); + reader.advanceCodePoint(4); - const representation = reader.representation(); - return [TokenType.CDO, reader.representationString(), representation[0], representation[1], undefined]; + return [TokenType.CDO, ' + +--> +/* more comments */ + +.foo { + image: url(https://example.com/foo.jpg); + image: url("https://example.com/foo.jpg"); +} + +.foo { + image: url((); +} + +.foo { + content: "foo +bar"; +} + +#1 {} + +#foo { + width: c\\61 lc(10% * 10px); +} + +.fooz\\{\\} {} + +.foo { + margin: 0; + margin: 1px; + line-height: 1%; + line-height: 1.2; +} + +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm { + display: block !important; + } + table.visible-sm { + display: table !important; + } + + tr.visible-sm { + display: table-row !important; + } + + th.visible-sm, + td.visible-sm { + display: table-cell !important; + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-block { + display: block !important; + } +} +`; + +const largeSource = ` +${smallSource} +${bootstrapSource} +${openPropsSource} +`; + +function csstoolsLargeSource() { const results = []; let tokenStreamLength = 0; @@ -12,19 +106,19 @@ function csstoolsLargeSource() { tokenStreamLength = 0; { + const start = performance.now(); + const t = tokenizer( { - css: source, + css: largeSource, }, { - onParseError: (err) => { - throw new Error(JSON.stringify(err)); + onParseError: () => { + // noop }, }, ); - const start = performance.now(); - // eslint-disable-next-line no-constant-condition while (true) { const token = t.nextToken(); @@ -44,63 +138,75 @@ function csstoolsLargeSource() { return a - b; }); - console.log('-------------- csstools tokenizer -------------'); - console.log('tokens', tokenStreamLength); - console.log('tokens/μs @ 95th', (tokenStreamLength / results[949]) / 1000); - console.log('tokens/μs @ 50th', (tokenStreamLength / results[499]) / 1000); - console.log('-----------------------------------------------'); - console.log('95th', results[949]); - console.log('90th', results[899]); - console.log('50th', results[499]); - console.log('deviation', results[949] - results[49]); + logResults('csstools tokenizer', tokenStreamLength, results); } function csstoolsSmallSource() { - const source = `@media (min-width: 768px) and (max-width: 991px) { - .visible-sm { - display: block !important; - } + const results = []; + let tokenStreamLength = 0; - table.visible-sm { - display: table !important; - } + for (let i = 0; i < 1000; i++) { + tokenStreamLength = 0; - tr.visible-sm { - display: table-row !important; - } + { + const start = performance.now(); - th.visible-sm, - td.visible-sm { - display: table-cell !important; - } -} + const t = tokenizer( + { + css: smallSource, + }, + { + onParseError: () => { + // noop + }, + }, + ); -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm-block { - display: block !important; + // eslint-disable-next-line no-constant-condition + while (true) { + const token = t.nextToken(); + if (token[0] === TokenType.EOF) { + break; + } + + tokenStreamLength++; + } + + const end = performance.now(); + results.push(end - start); + } } -}`; + results.sort((a, b) => { + return a - b; + }); + + logResults('csstools tokenizer', tokenStreamLength, results); +} + +function csstoolsTinySource() { const results = []; let tokenStreamLength = 0; for (let i = 0; i < 1000; i++) { tokenStreamLength = 0; - { + for (let j = 0; j < tinySources.length; j++) { + const source = tinySources[j]; + + const start = performance.now(); + const t = tokenizer( { css: source, }, { - onParseError: (err) => { - throw new Error(JSON.stringify(err)); + onParseError: () => { + // noop }, }, ); - const start = performance.now(); - // eslint-disable-next-line no-constant-condition while (true) { const token = t.nextToken(); @@ -120,34 +226,24 @@ function csstoolsSmallSource() { return a - b; }); - console.log('-------------- csstools tokenizer -------------'); - console.log('tokens', tokenStreamLength); - console.log('tokens/μs @ 95th', (tokenStreamLength / results[949]) / 1000); - console.log('tokens/μs @ 50th', (tokenStreamLength / results[499]) / 1000); - console.log('-----------------------------------------------'); - console.log('95th', results[949]); - console.log('90th', results[899]); - console.log('50th', results[499]); - console.log('deviation', results[949] - results[49]); + logResults('csstools tokenizer', tokenStreamLength, results); } function postcssLargeSource() { - const source = fs.readFileSync('./test/community/bootstrap.css').toString(); - const results = []; let tokenStreamLength = 0; for (let i = 0; i < 1000; i++) { tokenStreamLength = 0; { + const start = performance.now(); + const t = postcssTokenizer( { - css: source, + css: largeSource, }, ); - const start = performance.now(); - // eslint-disable-next-line no-constant-condition while (true) { const token = t.nextToken(); @@ -167,57 +263,64 @@ function postcssLargeSource() { return a - b; }); - console.log('-------------- postcss tokenizer -------------'); - console.log('tokens', tokenStreamLength); - console.log('tokens/μs @ 95th', (tokenStreamLength / results[949]) / 1000); - console.log('tokens/μs @ 50th', (tokenStreamLength / results[499]) / 1000); - console.log('----------------------------------------------'); - console.log('95th', results[949]); - console.log('90th', results[899]); - console.log('50th', results[499]); - console.log('deviation', results[949] - results[49]); + logResults('postcss tokenizer', tokenStreamLength, results); } function postcssSmallSource() { - const source = `@media (min-width: 768px) and (max-width: 991px) { - .visible-sm { - display: block !important; - } + const results = []; + let tokenStreamLength = 0; - table.visible-sm { - display: table !important; - } + for (let i = 0; i < 1000; i++) { + tokenStreamLength = 0; + { + const start = performance.now(); - tr.visible-sm { - display: table-row !important; - } + const t = postcssTokenizer( + { + css: smallSource, + }, + ); - th.visible-sm, - td.visible-sm { - display: table-cell !important; - } -} + // eslint-disable-next-line no-constant-condition + while (true) { + const token = t.nextToken(); + if (!token) { + break; + } -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm-block { - display: block !important; + tokenStreamLength++; + } + + const end = performance.now(); + results.push(end - start); + } } -}`; + results.sort((a, b) => { + return a - b; + }); + + logResults('postcss tokenizer', tokenStreamLength, results); +} + +function postcssTinySource() { const results = []; let tokenStreamLength = 0; for (let i = 0; i < 1000; i++) { tokenStreamLength = 0; - { + + for (let j = 0; j < tinySources.length; j++) { + const source = tinySources[j]; + + const start = performance.now(); + const t = postcssTokenizer( { css: source, }, ); - const start = performance.now(); - // eslint-disable-next-line no-constant-condition while (true) { const token = t.nextToken(); @@ -237,38 +340,80 @@ function postcssSmallSource() { return a - b; }); - console.log('-------------- postcss tokenizer -------------'); - console.log('tokens', tokenStreamLength); - console.log('tokens/μs @ 95th', (tokenStreamLength / results[949]) / 1000); - console.log('tokens/μs @ 50th', (tokenStreamLength / results[499]) / 1000); - console.log('----------------------------------------------'); - console.log('95th', results[949]); - console.log('90th', results[899]); - console.log('50th', results[499]); - console.log('deviation', results[949] - results[49]); + logResults('postcss tokenizer', tokenStreamLength, results); } -csstoolsLargeSource(); +await new Promise((resolve) => setTimeout(resolve(), 100)); +csstoolsTinySource(); +await new Promise((resolve) => setTimeout(resolve(), 1000)); +postcssTinySource(); +await new Promise((resolve) => setTimeout(resolve(), 1000)); csstoolsSmallSource(); -postcssLargeSource(); +await new Promise((resolve) => setTimeout(resolve(), 1000)); postcssSmallSource(); +await new Promise((resolve) => setTimeout(resolve(), 1000)); +csstoolsLargeSource(); +await new Promise((resolve) => setTimeout(resolve(), 1000)); +postcssLargeSource(); // Last result: // -------------- csstools tokenizer ------------- -// tokens 37088 -// tokens/μs @ 95th 10.38213287435934 -// tokens/μs @ 50th 11.020875148827852 +// tokens 43 +// tokens/μs @ 95th 129.1941776504298 +// tokens/μs @ 50th 172.09453435114503 +// tokens/μs @ 5th 257.6501028571429 +// ----------------------------------------------- +// 95th 0.00033283233642578125 +// 50th 0.0002498626708984375 +// 5th 0.00016689300537109375 +// deviation 0.0001659393310546875 +// -------------- postcss tokenizer ------------- +// tokens 24 +// tokens/μs @ 95th 192.10552671755724 +// tokens/μs @ 50th 192.10552671755724 +// tokens/μs @ 5th 289.2623448275862 +// ----------------------------------------------- +// 95th 0.00012493133544921875 +// 50th 0.00012493133544921875 +// 5th 0.00008296966552734375 +// deviation 0.000041961669921875 +// -------------- csstools tokenizer ------------- +// tokens 252 +// tokens/μs @ 95th 2.715778269963052 +// tokens/μs @ 50th 14.788099280857375 +// tokens/μs @ 5th 17.58149985029442 +// ----------------------------------------------- +// 95th 0.09279108047485352 +// 50th 0.017040729522705078 +// 5th 0.014333248138427734 +// deviation 0.07845783233642578 +// -------------- postcss tokenizer ------------- +// tokens 214 +// tokens/μs @ 95th 6.8027424968168315 +// tokens/μs @ 50th 7.901659031286864 +// tokens/μs @ 5th 36.16653461197518 +// ----------------------------------------------- +// 95th 0.03145790100097656 +// 50th 0.02708292007446289 +// 5th 0.005917072296142578 +// deviation 0.025540828704833984 +// -------------- csstools tokenizer ------------- +// tokens 87701 +// tokens/μs @ 95th 10.659602521712898 +// tokens/μs @ 50th 13.135529246644223 +// tokens/μs @ 5th 13.474753513157573 // ----------------------------------------------- -// 95th 3.5722910165786743 -// 90th 3.4610829949378967 -// 50th 3.365249991416931 -// deviation 0.24970799684524536 +// 95th 8.227417469024658 +// 50th 6.676624774932861 +// 5th 6.508542060852051 +// deviation 1.7188754081726074 // -------------- postcss tokenizer ------------- -// tokens 27045 -// tokens/μs @ 95th 18.530318414584258 -// tokens/μs @ 50th 26.92161819321654 -// ---------------------------------------------- -// 95th 1.459500014781952 -// 90th 1.2470839619636536 -// 50th 1.0045830011367798 -// deviation 0.4862080216407776 +// tokens 66238 +// tokens/μs @ 95th 18.231267325170737 +// tokens/μs @ 50th 25.111551990929474 +// tokens/μs @ 5th 26.735369993583255 +// ----------------------------------------------- +// 95th 3.633208751678467 +// 50th 2.6377501487731934 +// 5th 2.477541923522949 +// deviation 1.1556668281555176 diff --git a/packages/css-tokenizer/test/community/open-props.css b/packages/css-tokenizer/test/community/open-props.css new file mode 100644 index 000000000..ebc039155 --- /dev/null +++ b/packages/css-tokenizer/test/community/open-props.css @@ -0,0 +1,693 @@ +:where(html) { + --font-sans: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif; + --font-serif: ui-serif, serif; + --font-mono: Dank Mono, Operator Mono, Inconsolata, Fira Mono, ui-monospace, SF Mono, Monaco, Droid Sans Mono, Source Code Pro, monospace; + --font-weight-1: 100; + --font-weight-2: 200; + --font-weight-3: 300; + --font-weight-4: 400; + --font-weight-5: 500; + --font-weight-6: 600; + --font-weight-7: 700; + --font-weight-8: 800; + --font-weight-9: 900; + --font-lineheight-00: .95; + --font-lineheight-0: 1.1; + --font-lineheight-1: 1.25; + --font-lineheight-2: 1.375; + --font-lineheight-3: 1.5; + --font-lineheight-4: 1.75; + --font-lineheight-5: 2; + --font-letterspacing-0: -.05em; + --font-letterspacing-1: .025em; + --font-letterspacing-2: .050em; + --font-letterspacing-3: .075em; + --font-letterspacing-4: .150em; + --font-letterspacing-5: .500em; + --font-letterspacing-6: .750em; + --font-letterspacing-7: 1em; + --font-size-00: .5rem; + --font-size-0: .75rem; + --font-size-1: 1rem; + --font-size-2: 1.1rem; + --font-size-3: 1.25rem; + --font-size-4: 1.5rem; + --font-size-5: 2rem; + --font-size-6: 2.5rem; + --font-size-7: 3rem; + --font-size-8: 3.5rem; + --font-size-fluid-0: clamp(.75rem, 2vw, 1rem); + --font-size-fluid-1: clamp(1rem, 4vw, 1.5rem); + --font-size-fluid-2: clamp(1.5rem, 6vw, 2.5rem); + --font-size-fluid-3: clamp(2rem, 9vw, 3.5rem); + --size-000: -.5rem; + --size-00: -.25rem; + --size-1: .25rem; + --size-2: .5rem; + --size-3: 1rem; + --size-4: 1.25rem; + --size-5: 1.5rem; + --size-6: 1.75rem; + --size-7: 2rem; + --size-8: 3rem; + --size-9: 4rem; + --size-10: 5rem; + --size-11: 7.5rem; + --size-12: 10rem; + --size-13: 15rem; + --size-14: 20rem; + --size-15: 30rem; + --size-fluid-1: clamp(.5rem, 1vw, 1rem); + --size-fluid-2: clamp(1rem, 2vw, 1.5rem); + --size-fluid-3: clamp(1.5rem, 3vw, 2rem); + --size-fluid-4: clamp(2rem, 4vw, 3rem); + --size-fluid-5: clamp(4rem, 5vw, 5rem); + --size-fluid-6: clamp(5rem, 7vw, 7.5rem); + --size-fluid-7: clamp(7.5rem, 10vw, 10rem); + --size-fluid-8: clamp(10rem, 20vw, 15rem); + --size-fluid-9: clamp(15rem, 30vw, 20rem); + --size-fluid-10: clamp(20rem, 40vw, 30rem); + --size-content-1: 20ch; + --size-content-2: 45ch; + --size-content-3: 60ch; + --size-header-1: 20ch; + --size-header-2: 25ch; + --size-header-3: 35ch; + --size-xxs: 240px; + --size-xs: 360px; + --size-sm: 480px; + --size-md: 768px; + --size-lg: 1024px; + --size-xl: 1440px; + --size-xxl: 1920px; + --ease-1: cubic-bezier(.25, 0, .5, 1); + --ease-2: cubic-bezier(.25, 0, .4, 1); + --ease-3: cubic-bezier(.25, 0, .3, 1); + --ease-4: cubic-bezier(.25, 0, .2, 1); + --ease-5: cubic-bezier(.25, 0, .1, 1); + --ease-in-1: cubic-bezier(.25, 0, 1, 1); + --ease-in-2: cubic-bezier(.50, 0, 1, 1); + --ease-in-3: cubic-bezier(.70, 0, 1, 1); + --ease-in-4: cubic-bezier(.90, 0, 1, 1); + --ease-in-5: cubic-bezier(1, 0, 1, 1); + --ease-out-1: cubic-bezier(0, 0, .75, 1); + --ease-out-2: cubic-bezier(0, 0, .50, 1); + --ease-out-3: cubic-bezier(0, 0, .3, 1); + --ease-out-4: cubic-bezier(0, 0, .1, 1); + --ease-out-5: cubic-bezier(0, 0, 0, 1); + --ease-in-out-1: cubic-bezier(.1, 0, .9, 1); + --ease-in-out-2: cubic-bezier(.3, 0, .7, 1); + --ease-in-out-3: cubic-bezier(.5, 0, .5, 1); + --ease-in-out-4: cubic-bezier(.7, 0, .3, 1); + --ease-in-out-5: cubic-bezier(.9, 0, .1, 1); + --ease-elastic-1: cubic-bezier(.5, .75, .75, 1.25); + --ease-elastic-2: cubic-bezier(.5, 1, .75, 1.25); + --ease-elastic-3: cubic-bezier(.5, 1.25, .75, 1.25); + --ease-elastic-4: cubic-bezier(.5, 1.5, .75, 1.25); + --ease-elastic-5: cubic-bezier(.5, 1.75, .75, 1.25); + --ease-squish-1: cubic-bezier(.5, -.1, .1, 1.5); + --ease-squish-2: cubic-bezier(.5, -.3, .1, 1.5); + --ease-squish-3: cubic-bezier(.5, -.5, .1, 1.5); + --ease-squish-4: cubic-bezier(.5, -.7, .1, 1.5); + --ease-squish-5: cubic-bezier(.5, -.9, .1, 1.5); + --ease-step-1: steps(2); + --ease-step-2: steps(3); + --ease-step-3: steps(4); + --ease-step-4: steps(7); + --ease-step-5: steps(10); + --layer-1: 1; + --layer-2: 2; + --layer-3: 3; + --layer-4: 4; + --layer-5: 5; + --layer-important: 2147483647; + --shadow-color: 220 3% 15%; + --shadow-strength: 1%; + --shadow-1: 0 1px 2px -1px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 9%)); + --shadow-2: 0 3px 5px -2px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 3%)), 0 7px 14px -5px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 5%)); + --shadow-3: 0 -1px 3px 0 hsl(var(--shadow-color)/calc(var(--shadow-strength) + 2%)), 0 1px 2px -5px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 2%)), 0 2px 5px -5px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 4%)), 0 4px 12px -5px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 5%)), 0 12px 15px -5px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 7%)); + --shadow-4: 0 -2px 5px 0 hsl(var(--shadow-color)/calc(var(--shadow-strength) + 2%)), 0 1px 1px -2px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 3%)), 0 2px 2px -2px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 3%)), 0 5px 5px -2px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 4%)), 0 9px 9px -2px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 5%)), 0 16px 16px -2px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 6%)); + --shadow-5: 0 -1px 2px 0 hsl(var(--shadow-color)/calc(var(--shadow-strength) + 2%)), 0 2px 1px -2px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 3%)), 0 5px 5px -2px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 3%)), 0 10px 10px -2px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 4%)), 0 20px 20px -2px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 5%)), 0 40px 40px -2px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 7%)); + --shadow-6: 0 -1px 2px 0 hsl(var(--shadow-color)/calc(var(--shadow-strength) + 2%)), 0 3px 2px -2px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 3%)), 0 7px 5px -2px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 3%)), 0 12px 10px -2px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 4%)), 0 22px 18px -2px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 5%)), 0 41px 33px -2px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 6%)), 0 100px 80px -2px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 7%)); + --inner-shadow-0: inset 0 0 0 1px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 9%)); + --inner-shadow-1: inset 0 1px 2px 0 hsl(var(--shadow-color)/calc(var(--shadow-strength) + 9%)); + --inner-shadow-2: inset 0 1px 4px 0 hsl(var(--shadow-color)/calc(var(--shadow-strength) + 9%)); + --inner-shadow-3: inset 0 2px 8px 0 hsl(var(--shadow-color)/calc(var(--shadow-strength) + 9%)); + --inner-shadow-4: inset 0 2px 14px 0 hsl(var(--shadow-color)/calc(var(--shadow-strength) + 9%)); + --ratio-square: 1; + --ratio-landscape: 4/3; + --ratio-portrait: 3/4; + --ratio-widescreen: 16/9; + --ratio-ultrawide: 18/5; + --ratio-golden: 1.6180/1; + --gray-0: #f8f9fa; + --gray-1: #f1f3f5; + --gray-2: #e9ecef; + --gray-3: #dee2e6; + --gray-4: #ced4da; + --gray-5: #adb5bd; + --gray-6: #868e96; + --gray-7: #495057; + --gray-8: #343a40; + --gray-9: #212529; + --gray-10: #16191d; + --gray-11: #0d0f12; + --gray-12: #030507; + --stone-0: #f8fafb; + --stone-1: #f2f4f6; + --stone-2: #ebedef; + --stone-3: #e0e4e5; + --stone-4: #d1d6d8; + --stone-5: #b1b6b9; + --stone-6: #979b9d; + --stone-7: #7e8282; + --stone-8: #666968; + --stone-9: #50514f; + --stone-10: #3a3a37; + --stone-11: #252521; + --stone-12: #121210; + --red-0: #fff5f5; + --red-1: #ffe3e3; + --red-2: #ffc9c9; + --red-3: #ffa8a8; + --red-4: #ff8787; + --red-5: #ff6b6b; + --red-6: #fa5252; + --red-7: #f03e3e; + --red-8: #e03131; + --red-9: #c92a2a; + --red-10: #b02525; + --red-11: #962020; + --red-12: #7d1a1a; + --pink-0: #fff0f6; + --pink-1: #ffdeeb; + --pink-2: #fcc2d7; + --pink-3: #faa2c1; + --pink-4: #f783ac; + --pink-5: #f06595; + --pink-6: #e64980; + --pink-7: #d6336c; + --pink-8: #c2255c; + --pink-9: #a61e4d; + --pink-10: #8c1941; + --pink-11: #731536; + --pink-12: #59102a; + --purple-0: #f8f0fc; + --purple-1: #f3d9fa; + --purple-2: #eebefa; + --purple-3: #e599f7; + --purple-4: #da77f2; + --purple-5: #cc5de8; + --purple-6: #be4bdb; + --purple-7: #ae3ec9; + --purple-8: #9c36b5; + --purple-9: #862e9c; + --purple-10: #702682; + --purple-11: #5a1e69; + --purple-12: #44174f; + --violet-0: #f3f0ff; + --violet-1: #e5dbff; + --violet-2: #d0bfff; + --violet-3: #b197fc; + --violet-4: #9775fa; + --violet-5: #845ef7; + --violet-6: #7950f2; + --violet-7: #7048e8; + --violet-8: #6741d9; + --violet-9: #5f3dc4; + --violet-10: #5235ab; + --violet-11: #462d91; + --violet-12: #3a2578; + --indigo-0: #edf2ff; + --indigo-1: #dbe4ff; + --indigo-2: #bac8ff; + --indigo-3: #91a7ff; + --indigo-4: #748ffc; + --indigo-5: #5c7cfa; + --indigo-6: #4c6ef5; + --indigo-7: #4263eb; + --indigo-8: #3b5bdb; + --indigo-9: #364fc7; + --indigo-10: #2f44ad; + --indigo-11: #283a94; + --indigo-12: #21307a; + --blue-0: #e7f5ff; + --blue-1: #d0ebff; + --blue-2: #a5d8ff; + --blue-3: #74c0fc; + --blue-4: #4dabf7; + --blue-5: #339af0; + --blue-6: #228be6; + --blue-7: #1c7ed6; + --blue-8: #1971c2; + --blue-9: #1864ab; + --blue-10: #145591; + --blue-11: #114678; + --blue-12: #0d375e; + --cyan-0: #e3fafc; + --cyan-1: #c5f6fa; + --cyan-2: #99e9f2; + --cyan-3: #66d9e8; + --cyan-4: #3bc9db; + --cyan-5: #22b8cf; + --cyan-6: #15aabf; + --cyan-7: #1098ad; + --cyan-8: #0c8599; + --cyan-9: #0b7285; + --cyan-10: #095c6b; + --cyan-11: #074652; + --cyan-12: #053038; + --teal-0: #e6fcf5; + --teal-1: #c3fae8; + --teal-2: #96f2d7; + --teal-3: #63e6be; + --teal-4: #38d9a9; + --teal-5: #20c997; + --teal-6: #12b886; + --teal-7: #0ca678; + --teal-8: #099268; + --teal-9: #087f5b; + --teal-10: #066649; + --teal-11: #054d37; + --teal-12: #033325; + --green-0: #ebfbee; + --green-1: #d3f9d8; + --green-2: #b2f2bb; + --green-3: #8ce99a; + --green-4: #69db7c; + --green-5: #51cf66; + --green-6: #40c057; + --green-7: #37b24d; + --green-8: #2f9e44; + --green-9: #2b8a3e; + --green-10: #237032; + --green-11: #1b5727; + --green-12: #133d1b; + --lime-0: #f4fce3; + --lime-1: #e9fac8; + --lime-2: #d8f5a2; + --lime-3: #c0eb75; + --lime-4: #a9e34b; + --lime-5: #94d82d; + --lime-6: #82c91e; + --lime-7: #74b816; + --lime-8: #66a80f; + --lime-9: #5c940d; + --lime-10: #4c7a0b; + --lime-11: #3c6109; + --lime-12: #2c4706; + --yellow-0: #fff9db; + --yellow-1: #fff3bf; + --yellow-2: #ffec99; + --yellow-3: #ffe066; + --yellow-4: #ffd43b; + --yellow-5: #fcc419; + --yellow-6: #fab005; + --yellow-7: #f59f00; + --yellow-8: #f08c00; + --yellow-9: #e67700; + --yellow-10: #b35c00; + --yellow-11: #804200; + --yellow-12: #663500; + --orange-0: #fff4e6; + --orange-1: #ffe8cc; + --orange-2: #ffd8a8; + --orange-3: #ffc078; + --orange-4: #ffa94d; + --orange-5: #ff922b; + --orange-6: #fd7e14; + --orange-7: #f76707; + --orange-8: #e8590c; + --orange-9: #d9480f; + --orange-10: #bf400d; + --orange-11: #99330b; + --orange-12: #802b09; + --choco-0: #fff8dc; + --choco-1: #fce1bc; + --choco-2: #f7ca9e; + --choco-3: #f1b280; + --choco-4: #e99b62; + --choco-5: #df8545; + --choco-6: #d46e25; + --choco-7: #bd5f1b; + --choco-8: #a45117; + --choco-9: #8a4513; + --choco-10: #703a13; + --choco-11: #572f12; + --choco-12: #3d210d; + --brown-0: #faf4eb; + --brown-1: #ede0d1; + --brown-2: #e0cab7; + --brown-3: #d3b79e; + --brown-4: #c5a285; + --brown-5: #b78f6d; + --brown-6: #a87c56; + --brown-7: #956b47; + --brown-8: #825b3a; + --brown-9: #6f4b2d; + --brown-10: #5e3a21; + --brown-11: #4e2b15; + --brown-12: #422412; + --sand-0: #f8fafb; + --sand-1: #e6e4dc; + --sand-2: #d5cfbd; + --sand-3: #c2b9a0; + --sand-4: #aea58c; + --sand-5: #9a9178; + --sand-6: #867c65; + --sand-7: #736a53; + --sand-8: #5f5746; + --sand-9: #4b4639; + --sand-10: #38352d; + --sand-11: #252521; + --sand-12: #121210; + --camo-0: #f9fbe7; + --camo-1: #e8ed9c; + --camo-2: #d2df4e; + --camo-3: #c2ce34; + --camo-4: #b5bb2e; + --camo-5: #a7a827; + --camo-6: #999621; + --camo-7: #8c851c; + --camo-8: #7e7416; + --camo-9: #6d6414; + --camo-10: #5d5411; + --camo-11: #4d460e; + --camo-12: #36300a; + --jungle-0: #ecfeb0; + --jungle-1: #def39a; + --jungle-2: #d0e884; + --jungle-3: #c2dd6e; + --jungle-4: #b5d15b; + --jungle-5: #a8c648; + --jungle-6: #9bbb36; + --jungle-7: #8fb024; + --jungle-8: #84a513; + --jungle-9: #7a9908; + --jungle-10: #658006; + --jungle-11: #516605; + --jungle-12: #3d4d04; + --gradient-1: linear-gradient(to bottom right, #1f005c, #5b0060, #870160, #ac255e, #ca485c, #e16b5c, #f39060, #ffb56b); + --gradient-2: linear-gradient(to bottom right, #48005c, #8300e2, #a269ff); + --gradient-3: radial-gradient(circle at top right, #0ff, rgba(0, 255, 255, 0)), radial-gradient(circle at bottom left, #ff1492, rgba(255, 20, 146, 0)); + --gradient-4: linear-gradient(to bottom right, #00f5a0, #00d9f5); + --gradient-5: conic-gradient(from -270deg at 75% 110%, #f0f, #fffaf0); + --gradient-6: conic-gradient(from -90deg at top left, #000, #fff); + --gradient-7: linear-gradient(to bottom right, #72c6ef, #004e8f); + --gradient-8: conic-gradient(from 90deg at 50% 0%, #111, 50%, #222, #111); + --gradient-9: conic-gradient(from .5turn at bottom center, #add8e6, #fff); + --gradient-10: conic-gradient(from 90deg at 40% -25%, gold, #f79d03, #ee6907, #e6390a, #de0d0d, #d61039, #cf1261, #c71585, #cf1261, #d61039, #de0d0d, #ee6907, #f79d03, gold, gold, gold); + --gradient-11: conic-gradient(at bottom left, #ff1493, cyan); + --gradient-12: conic-gradient(from 90deg at 25% -10%, #ff4500, #d3f340, #7bee85, #afeeee, #7bee85); + --gradient-13: radial-gradient(circle at 50% 200%, #000142, #3b0083, #b300c3, #ff059f, #ff4661, #ffad86, #fff3c7); + --gradient-14: conic-gradient(at top right, lime, cyan); + --gradient-15: linear-gradient(to bottom right, #c7d2fe, #fecaca, #fef3c7); + --gradient-16: radial-gradient(circle at 50% -250%, #374151, #111827, #000); + --gradient-17: conic-gradient(from -90deg at 50% -25%, blue, #8a2be2); + --gradient-18: linear-gradient(0deg, rgba(255, 0, 0, .8), rgba(255, 0, 0, 0) 75%), linear-gradient(60deg, rgba(255, 255, 0, .8), rgba(255, 255, 0, 0) 75%), linear-gradient(120deg, rgba(0, 255, 0, .8), rgba(0, 255, 0, 0) 75%), linear-gradient(180deg, rgba(0, 255, 255, .8), rgba(0, 255, 255, 0) 75%), linear-gradient(240deg, rgba(0, 0, 255, .8), rgba(0, 0, 255, 0) 75%), linear-gradient(300deg, rgba(255, 0, 255, .8), rgba(255, 0, 255, 0) 75%); + --gradient-19: linear-gradient(to bottom right, #ffe259, #ffa751); + --gradient-20: conic-gradient(from -135deg at -10% center, orange, #ff7715, #ff522a, #ff3f47, #ff5482, #ff69b4); + --gradient-21: conic-gradient(from -90deg at 25% 115%, red, #f06, #f0c, #c0f, #60f, #00f, #00f, #00f, #00f); + --gradient-22: linear-gradient(to bottom right, #acb6e5, #86fde8); + --gradient-23: linear-gradient(to bottom right, #536976, #292e49); + --gradient-24: conic-gradient(from .5turn at 0% 0%, #00c476, 10%, #82b0ff, 90%, #00c476); + --gradient-25: conic-gradient(at 125% 50%, #b78cf7, #ff7c94, #ffcf0d, #ff7c94, #b78cf7); + --gradient-26: linear-gradient(to bottom right, #9796f0, #fbc7d4); + --gradient-27: conic-gradient(from .5turn at bottom left, #ff1493, #639); + --gradient-28: conic-gradient(from -90deg at 50% 105%, #fff, orchid); + --gradient-29: radial-gradient(circle at top right, #bfb3ff, rgba(191, 179, 255, 0)), radial-gradient(circle at bottom left, #86acf9, rgba(134, 172, 249, 0)); + --gradient-30: radial-gradient(circle at top right, #00ff80, rgba(0, 255, 128, 0)), radial-gradient(circle at bottom left, #adffd6, rgba(173, 255, 214, 0)); + --noise-1: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.005' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23a)'/%3E%3C/svg%3E"); + --noise-2: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 300 300' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.05' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23a)'/%3E%3C/svg%3E"); + --noise-3: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.25' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23a)'/%3E%3C/svg%3E"); + --noise-4: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 2056 2056' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.5' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23a)'/%3E%3C/svg%3E"); + --noise-5: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 2056 2056' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.75' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23a)'/%3E%3C/svg%3E"); + --noise-filter-1: contrast(300%) brightness(100%); + --noise-filter-2: contrast(200%) brightness(150%); + --noise-filter-3: contrast(200%) brightness(250%); + --noise-filter-4: contrast(200%) brightness(500%); + --noise-filter-5: contrast(200%) brightness(1000%); + --animation-fade-in: fade-in .5s var(--ease-3); + --animation-fade-in-bloom: fade-in-bloom 2s var(--ease-3); + --animation-fade-out: fade-out .5s var(--ease-3); + --animation-fade-out-bloom: fade-out-bloom 2s var(--ease-3); + --animation-scale-up: scale-up .5s var(--ease-3); + --animation-scale-down: scale-down .5s var(--ease-3); + --animation-slide-out-up: slide-out-up .5s var(--ease-3); + --animation-slide-out-down: slide-out-down .5s var(--ease-3); + --animation-slide-out-right: slide-out-right .5s var(--ease-3); + --animation-slide-out-left: slide-out-left .5s var(--ease-3); + --animation-slide-in-up: slide-in-up .5s var(--ease-3); + --animation-slide-in-down: slide-in-down .5s var(--ease-3); + --animation-slide-in-right: slide-in-right .5s var(--ease-3); + --animation-slide-in-left: slide-in-left .5s var(--ease-3); + --animation-shake-x: shake-x .75s var(--ease-out-5); + --animation-shake-y: shake-y .75s var(--ease-out-5); + --animation-spin: spin 2s linear infinite; + --animation-ping: ping 5s var(--ease-out-3) infinite; + --animation-blink: blink 1s var(--ease-out-3) infinite; + --animation-float: float 3s var(--ease-in-out-3) infinite; + --animation-bounce: bounce 2s var(--ease-squish-2) infinite; + --animation-pulse: pulse 2s var(--ease-out-3) infinite; + --border-size-1: 1px; + --border-size-2: 2px; + --border-size-3: 5px; + --border-size-4: 10px; + --border-size-5: 25px; + --radius-1: 2px; + --radius-2: 5px; + --radius-3: 1rem; + --radius-4: 2rem; + --radius-5: 4rem; + --radius-6: 8rem; + --radius-round: 1e5px; + --radius-blob-1: 30% 70% 70% 30%/53% 30% 70% 47%; + --radius-blob-2: 53% 47% 34% 66%/63% 46% 54% 37%; + --radius-blob-3: 37% 63% 56% 44%/49% 56% 44% 51%; + --radius-blob-4: 63% 37% 37% 63%/43% 37% 63% 57%; + --radius-blob-5: 49% 51% 48% 52%/57% 44% 56% 43%; + --radius-conditional-1: clamp(0px, calc(100vw - 100%) * 1e5, var(--radius-1)); + --radius-conditional-2: clamp(0px, calc(100vw - 100%) * 1e5, var(--radius-2)); + --radius-conditional-3: clamp(0px, calc(100vw - 100%) * 1e5, var(--radius-3)); + --radius-conditional-4: clamp(0px, calc(100vw - 100%) * 1e5, var(--radius-4)); + --radius-conditional-5: clamp(0px, calc(100vw - 100%) * 1e5, var(--radius-5)); + --radius-conditional-6: clamp(0px, calc(100vw - 100%) * 1e5, var(--radius-6)) +} + +@media (prefers-color-scheme:dark) { + :where(html) { + --shadow-color: 220 40% 2%; + --shadow-strength: 25% + } +} + +@keyframes fade-in { + to { + opacity: 1 + } +} + +@keyframes fade-in-bloom { + 0% { + filter: brightness(1) blur(20px); + opacity: 0 + } + + 10% { + filter: brightness(2) blur(10px); + filter: brightness(.5) blur(10px); + opacity: 1 + } + + to { + filter: brightness(1) blur(0); + opacity: 1 + } +} + +@keyframes fade-out { + to { + opacity: 0 + } +} + +@keyframes fade-out-bloom { + to { + filter: brightness(1) blur(20px); + opacity: 0 + } + + 10% { + filter: brightness(2) blur(10px); + filter: brightness(.5) blur(10px); + opacity: 1 + } + + 0% { + filter: brightness(1) blur(0); + opacity: 1 + } +} + +@keyframes scale-up { + to { + transform: scale(1.25) + } +} + +@keyframes scale-down { + to { + transform: scale(.75) + } +} + +@keyframes slide-out-up { + to { + transform: translateY(-100%) + } +} + +@keyframes slide-out-down { + to { + transform: translateY(100%) + } +} + +@keyframes slide-out-right { + to { + transform: translateX(100%) + } +} + +@keyframes slide-out-left { + to { + transform: translateX(-100%) + } +} + +@keyframes slide-in-up { + 0% { + transform: translateY(100%) + } +} + +@keyframes slide-in-down { + 0% { + transform: translateY(-100%) + } +} + +@keyframes slide-in-right { + 0% { + transform: translateX(-100%) + } +} + +@keyframes slide-in-left { + 0% { + transform: translateX(100%) + } +} + +@keyframes shake-x { + + 0%, + to { + transform: translateX(0) + } + + 20% { + transform: translateX(-5%) + } + + 40% { + transform: translateX(5%) + } + + 60% { + transform: translateX(-5%) + } + + 80% { + transform: translateX(5%) + } +} + +@keyframes shake-y { + + 0%, + to { + transform: translateY(0) + } + + 20% { + transform: translateY(-5%) + } + + 40% { + transform: translateY(5%) + } + + 60% { + transform: translateY(-5%) + } + + 80% { + transform: translateY(5%) + } +} + +@keyframes spin { + to { + transform: rotate(1turn) + } +} + +@keyframes ping { + + 90%, + to { + opacity: 0; + transform: scale(2) + } +} + +@keyframes blink { + + 0%, + to { + opacity: 1 + } + + 50% { + opacity: .5 + } +} + +@keyframes float { + 50% { + transform: translateY(-25%) + } +} + +@keyframes bounce { + 25% { + transform: translateY(-20%) + } + + 40% { + transform: translateY(-3%) + } + + 0%, + 60%, + to { + transform: translateY(0) + } +} + +@keyframes pulse { + 50% { + transform: scale(.9) + } +} diff --git a/packages/css-tokenizer/test/community/open-props.mjs b/packages/css-tokenizer/test/community/open-props.mjs new file mode 100644 index 000000000..83ec89d9c --- /dev/null +++ b/packages/css-tokenizer/test/community/open-props.mjs @@ -0,0 +1,26 @@ +import { tokenizer, TokenType } from '@csstools/css-tokenizer'; +import fs from 'fs'; + +{ + const source = fs.readFileSync('./test/community/open-props.css').toString(); + + const t = tokenizer( + { + css: source, + }, + { + onParseError: (err) => { + // We only expect something like open props to tokenize without parser errors. + throw new Error(JSON.stringify(err)); + }, + }, + ); + + // eslint-disable-next-line no-constant-condition + while (true) { + const token = t.nextToken(); + if (token[0] === TokenType.EOF) { + break; + } + } +} diff --git a/packages/css-tokenizer/test/community/token-types.mjs b/packages/css-tokenizer/test/community/token-types.mjs new file mode 100644 index 000000000..97b1fcb8f --- /dev/null +++ b/packages/css-tokenizer/test/community/token-types.mjs @@ -0,0 +1,166 @@ +import { tokenizer, TokenType } from '@csstools/css-tokenizer'; +import postcssTokenizer from 'postcss/lib/tokenize'; +import assert from 'assert'; +import fs from 'fs'; + +const bootstrapSource = fs.readFileSync('./test/community/bootstrap.css').toString(); +const openPropsSource = fs.readFileSync('./test/community/open-props.css').toString(); + +const source = ` +/* a comment */ + + +--> +/* more comments */ + +.foo { + image: url(https://example.com/foo.jpg); + image: url("https://example.com/foo.jpg"); +} + +.foo { + image: url((); +} + +.foo { + content: "foo +bar"; +} + +#1 {} + +#foo {} + +.foo { + margin: 0; + margin: 1px; + line-height: 1%; + line-height: 1.2; +} + +${bootstrapSource} +${openPropsSource} +`; + +{ + const t = tokenizer( + { + css: source, + }, + { + commentsAreTokens: true, + onParseError: () => { + // noop + }, + }, + ); + + let tokens = []; + + // eslint-disable-next-line no-constant-condition + while (true) { + const token = t.nextToken(); + tokens.push(token); + + if (token[0] === TokenType.EOF) { + break; + } + } + + const tokenTypes = Array.from(new Set(tokens.map(x => csstoolsTokenToTypeWithSubIdentifiers(x)))); + tokenTypes.sort((a, b) => a.localeCompare(b)); + + assert.deepEqual( + tokenTypes, + [ + '(-token', + ')-token', + '[-token', + ']-token', + '{-token', + '}-token', + 'at-keyword-token', + 'bad-string-token', + 'bad-url-token', + 'CDC-token', + 'CDO-token', + 'colon-token', + 'comma-token', + 'comment', + 'delim-token', + 'dimension-token - integer', + 'dimension-token - number', + 'EOF-token', + 'function-token', + 'hash-token - id', + 'hash-token - unrestricted', + 'ident-token', + 'number-token - integer', + 'number-token - number', + 'percentage-token', + 'semicolon-token', + 'string-token', + 'url-token', + 'whitespace-token', + ], + ); +} + +function csstoolsTokenToTypeWithSubIdentifiers(token) { + if (token[0] === TokenType.Number) { + return `${token[0]} - ${token[4].type}`; + } + + if (token[0] === TokenType.Dimension) { + return `${token[0]} - ${token[4].type}`; + } + + if (token[0] === TokenType.Hash) { + return `${token[0]} - ${token[4].type}`; + } + + return token[0]; +} + +{ + const t = postcssTokenizer( + { + css: source, + }, + ); + + let tokens = []; + + // eslint-disable-next-line no-constant-condition + while (true) { + const token = t.nextToken(); + if (!token) { + break; + } + + tokens.push(token); + } + + const tokenTypes = Array.from(new Set(tokens.map(x => x[0]))); + tokenTypes.sort((a, b) => a.localeCompare(b)); + + assert.deepEqual( + tokenTypes, + [ + ';', + ':', + '(', + ')', + '[', + ']', + '{', + '}', + 'at-word', + 'brackets', + 'comment', + 'space', + 'string', + 'word', + ], + ); +} diff --git a/packages/css-tokenizer/test/complex/parse-error.mjs b/packages/css-tokenizer/test/complex/parse-error.mjs index 99517f52f..9b8f5f1cf 100644 --- a/packages/css-tokenizer/test/complex/parse-error.mjs +++ b/packages/css-tokenizer/test/complex/parse-error.mjs @@ -23,13 +23,20 @@ import assert from 'assert'; } assert.deepEqual( - parseErrors, + parseErrors.map((x) => { + return { + message: x.message, + sourceStart: x.sourceStart, + sourceEnd: x.sourceEnd, + parserState: x.parserState, + }; + }), [ { message: 'Unexpected EOF while consuming an escaped code point.', - start: 0, - end: 0, - state: ['4.3.7. Consume an escaped code point', 'Unexpected EOF'], + sourceStart: 0, + sourceEnd: 0, + parserState: ['4.3.7. Consume an escaped code point', 'Unexpected EOF'], }, ], ); diff --git a/packages/css-tokenizer/test/css-tokenizer-tests/test.mjs b/packages/css-tokenizer/test/css-tokenizer-tests/test.mjs new file mode 100644 index 000000000..937a3587b --- /dev/null +++ b/packages/css-tokenizer/test/css-tokenizer-tests/test.mjs @@ -0,0 +1,28 @@ +import assert from 'assert'; +import { collectTokens } from '../util/collect-tokens.mjs'; +import { testCorpus } from '@rmenke/css-tokenizer-tests'; +import { tokenizer } from '@csstools/css-tokenizer'; + +{ + for (const testCase in testCorpus) { + const t = tokenizer({ + css: testCorpus[testCase].css, + }, {commentsAreTokens: true}); + + assert.deepEqual( + collectTokens(t).map((x) => toUniversal(x)).slice(0, -1), + testCorpus[testCase].tokens, + `css-tokenizer-tests: ${testCase}`, + ); + } +} + +export function toUniversal(token) { + return { + type: token[0], + raw: token[1], + startIndex: token[2], + endIndex: token[3] + 1, + structured: token[4] ?? null, + }; +} diff --git a/packages/css-tokenizer/test/fuzz/0001.mjs b/packages/css-tokenizer/test/fuzz/0001.mjs new file mode 100644 index 000000000..72d2bc87f Binary files /dev/null and b/packages/css-tokenizer/test/fuzz/0001.mjs differ diff --git a/packages/css-tokenizer/test/inspect.mjs b/packages/css-tokenizer/test/inspect.mjs new file mode 100644 index 000000000..6cf401d40 --- /dev/null +++ b/packages/css-tokenizer/test/inspect.mjs @@ -0,0 +1,61 @@ +import { tokenizer, TokenType } from '@csstools/css-tokenizer'; +import fs from 'fs'; + +const bootstrapSource = fs.readFileSync('./test/community/bootstrap.css').toString(); +const openPropsSource = fs.readFileSync('./test/community/open-props.css').toString(); + +const source = ` +/* a comment */ + + +--> +/* more comments */ + +.foo { + image: url(https://example.com/foo.jpg); + image: url("https://example.com/foo.jpg"); +} + +.foo { + image: url((); +} + +.foo { + content: "foo +bar"; +} + +#1 {} + +#foo {} + +.foo { + margin: 0; + margin: 1px; + line-height: 1%; + line-height: 1.2; +} + +${bootstrapSource} +${openPropsSource} +`; + +// eslint-disable-next-line no-constant-condition +while (true) { + const t = tokenizer( + { + css: source, + }, + { + commentsAreTokens: true, + onParseError: () => { + // noop + }, + }, + ); + + // eslint-disable-next-line no-constant-condition + while (t.nextToken()[0] !== TokenType.EOF) { + // noop + } +} diff --git a/packages/css-tokenizer/test/mutations/ident.mjs b/packages/css-tokenizer/test/mutations/ident.mjs new file mode 100644 index 000000000..74d0fd39c --- /dev/null +++ b/packages/css-tokenizer/test/mutations/ident.mjs @@ -0,0 +1,279 @@ +import assert from 'assert'; +import { collectTokens } from '../util/collect-tokens.mjs'; +import { mutateIdent, tokenizer, stringify } from '@csstools/css-tokenizer'; + +{ + const token = ['ident-token', 'foo', 0, 2, { value: 'foo' }]; + mutateIdent(token, 'bar'); + + assert.deepEqual( + token, + ['ident-token', 'bar', 0, 2, { value: 'bar' }], + ); + + const raw = stringify(token); + assert.deepEqual( + raw, + 'bar', + ); + + const t = tokenizer({ + css: raw, + }); + + assert.deepEqual( + collectTokens(t), + [ + ['ident-token', 'bar', 0, 2, { value: 'bar' }], + ['EOF-token', '', -1, -1, undefined], + ], + ); +} + +{ + const token = ['ident-token', 'foo-bar', 0, 6, { value: 'foo-bar' }]; + mutateIdent(token, 'foo.bar'); + + assert.deepEqual( + token, + ['ident-token', 'foo\\2e bar', 0, 6, { value: 'foo.bar' }], + ); + + const raw = stringify(token); + assert.deepEqual( + raw, + 'foo\\2e bar', + ); + + const t = tokenizer({ + css: raw, + }); + + assert.deepEqual( + collectTokens(t), + [ + ['ident-token', 'foo\\2e bar', 0, 9, { value: 'foo.bar' }], + ['EOF-token', '', -1, -1, undefined], + ], + ); +} + +{ + const token = ['ident-token', 'foo', 0, 2, { value: 'foo' }]; + mutateIdent(token, '@foo'); + + assert.deepEqual( + token, + ['ident-token', '\\40 foo', 0, 2, { value: '@foo' }], + ); + + const raw = stringify(token); + assert.deepEqual( + raw, + '\\40 foo', + ); + + const t = tokenizer({ + css: raw, + }); + + assert.deepEqual( + collectTokens(t), + [ + ['ident-token', '\\40 foo', 0, 6, { value: '@foo' }], + ['EOF-token', '', -1, -1, undefined], + ], + ); +} + +{ + const token = ['ident-token', 'foo', 0, 2, { value: 'foo' }]; + mutateIdent(token, '-'); + + assert.deepEqual( + token, + ['ident-token', '\\2d ', 0, 2, { value: '-' }], + ); + + const raw = stringify(token); + assert.deepEqual( + raw, + '\\2d ', + ); + + const t = tokenizer({ + css: raw, + }); + + assert.deepEqual( + collectTokens(t), + [ + ['ident-token', '\\2d ', 0, 3, { value: '-' }], + ['EOF-token', '', -1, -1, undefined], + ], + ); +} + +{ + const token = ['ident-token', '--prop', 0, 2, { value: '--prop' }]; + mutateIdent(token, '--other-prop'); + + assert.deepEqual( + token, + ['ident-token', '--other-prop', 0, 2, { value: '--other-prop' }], + ); + + const raw = stringify(token); + assert.deepEqual( + raw, + '--other-prop', + ); + + const t = tokenizer({ + css: raw, + }); + + assert.deepEqual( + collectTokens(t), + [ + ['ident-token', '--other-prop', 0, 11, { value: '--other-prop' }], + ['EOF-token', '', -1, -1, undefined], + ], + ); +} + +{ + const token = ['ident-token', '--prop', 0, 2, { value: '--prop' }]; + mutateIdent(token, '--%other-prop'); + + assert.deepEqual( + token, + ['ident-token', '--\\25 other-prop', 0, 2, { value: '--%other-prop' }], + ); + + const raw = stringify(token); + assert.deepEqual( + raw, + '--\\25 other-prop', + ); + + const t = tokenizer({ + css: raw, + }); + + assert.deepEqual( + collectTokens(t), + [ + [ + 'ident-token', + '--\\25 other-prop', + 0, + 15, + { value: '--%other-prop' }, + ], + ['EOF-token', '', -1, -1, undefined], + ], + ); +} + +{ + const token = ['ident-token', '-moz-width', 0, 2, { value: '-moz-width' }]; + mutateIdent(token, '-webkit-width'); + + assert.deepEqual( + token, + ['ident-token', '-webkit-width', 0, 2, { value: '-webkit-width' }], + ); + + const raw = stringify(token); + assert.deepEqual( + raw, + '-webkit-width', + ); + + const t = tokenizer({ + css: raw, + }); + + assert.deepEqual( + collectTokens(t), + [ + [ + 'ident-token', + '-webkit-width', + 0, + 12, + { value: '-webkit-width' }, + ], + ['EOF-token', '', -1, -1, undefined], + ], + ); +} + +{ + const token = ['ident-token', '-moz-width', 0, 2, { value: '-moz-width' }]; + mutateIdent(token, '-@webkit-width'); + + assert.deepEqual( + token, + [ + 'ident-token', + '-\\40 webkit-width', + 0, + 2, + { value: '-@webkit-width' }, + ], + ); + + const raw = stringify(token); + assert.deepEqual( + raw, + '-\\40 webkit-width', + ); + + const t = tokenizer({ + css: raw, + }); + + assert.deepEqual( + collectTokens(t), + [ + [ + 'ident-token', + '-\\40 webkit-width', + 0, + 16, + { value: '-@webkit-width' }, + ], + ['EOF-token', '', -1, -1, undefined], + ], + ); +} + +{ + const token = ['ident-token', 'prop', 0, 2, { value: 'prop' }]; + mutateIdent(token, '$'); + + assert.deepEqual( + token, + ['ident-token', '\\24 ', 0, 2, { value: '$' }], + ); + + const raw = stringify(token); + assert.deepEqual( + raw, + '\\24 ', + ); + + const t = tokenizer({ + css: raw, + }); + + assert.deepEqual( + collectTokens(t), + [ + ['ident-token', '\\24 ', 0, 3, { value: '$' }], + ['EOF-token', '', -1, -1, undefined], + ], + ); +} diff --git a/packages/css-tokenizer/test/test-reader.mjs b/packages/css-tokenizer/test/test-reader.mjs index 69c59e706..efc078465 100644 --- a/packages/css-tokenizer/test/test-reader.mjs +++ b/packages/css-tokenizer/test/test-reader.mjs @@ -5,7 +5,7 @@ import { Reader } from '@csstools/css-tokenizer'; const r = new Reader('abc👨‍👨‍👧‍👦d'); { - const peeked = r.peekOneCodePoint(); + const peeked = r.codePointSource[r.cursor]; assert.deepEqual( peeked, 97, @@ -17,7 +17,10 @@ import { Reader } from '@csstools/css-tokenizer'; ); assert.deepEqual( - r.representation(), + [ + r.representationStart, + r.representationEnd, + ], [ 0, -1, @@ -26,7 +29,10 @@ import { Reader } from '@csstools/css-tokenizer'; } { - const peeked = r.peekTwoCodePoints(); + const peeked = [ + r.codePointSource[r.cursor], + r.codePointSource[r.cursor+1], + ]; assert.deepEqual( peeked, [97, 98], @@ -43,7 +49,10 @@ import { Reader } from '@csstools/css-tokenizer'; ); assert.deepEqual( - r.representation(), + [ + r.representationStart, + r.representationEnd, + ], [ 0, -1, @@ -52,7 +61,11 @@ import { Reader } from '@csstools/css-tokenizer'; } { - const peeked = r.peekThreeCodePoints(); + const peeked = [ + r.codePointSource[r.cursor], + r.codePointSource[r.cursor+1], + r.codePointSource[r.cursor+2], + ]; assert.deepEqual( peeked, [97, 98, 99], @@ -74,7 +87,10 @@ import { Reader } from '@csstools/css-tokenizer'; ); assert.deepEqual( - r.representation(), + [ + r.representationStart, + r.representationEnd, + ], [ 0, -1, @@ -95,7 +111,10 @@ import { Reader } from '@csstools/css-tokenizer'; ); assert.deepEqual( - r.representation(), + [ + r.representationStart, + r.representationEnd, + ], [ 0, 0, @@ -103,12 +122,13 @@ import { Reader } from '@csstools/css-tokenizer'; ); assert.deepEqual( - r.representationString(), + r.source.slice(r.representationStart, r.representationEnd + 1), 'a', ); } - r.resetRepresentation(); + r.representationStart = r.cursor; + r.representationEnd = -1; { const read1 = r.readCodePoint(); @@ -146,7 +166,10 @@ import { Reader } from '@csstools/css-tokenizer'; ); assert.deepEqual( - r.representation(), + [ + r.representationStart, + r.representationEnd, + ], [ 1, 4, @@ -166,7 +189,10 @@ import { Reader } from '@csstools/css-tokenizer'; r.readCodePoint(); assert.deepEqual( - r.representation(), + [ + r.representationStart, + r.representationEnd, + ], [ 1, 14, @@ -174,12 +200,7 @@ import { Reader } from '@csstools/css-tokenizer'; ); assert.deepEqual( - r.slice(r.representation()[0], r.representation()[1] + 1), - 'bc👨‍👨‍👧‍👦d', - ); - - assert.deepEqual( - r.representationString(), + r.source.slice(r.representationStart, r.representationEnd + 1), 'bc👨‍👨‍👧‍👦d', ); } diff --git a/packages/css-tokenizer/test/test-stringify.mjs b/packages/css-tokenizer/test/test-stringify.mjs new file mode 100644 index 000000000..d0b0362c1 --- /dev/null +++ b/packages/css-tokenizer/test/test-stringify.mjs @@ -0,0 +1,8 @@ +import assert from 'assert'; +import { stringify } from 'querystring'; + +{ + assert.equal(stringify(null), ''); + assert.equal(stringify(undefined), ''); + assert.equal(stringify(), ''); +} diff --git a/packages/css-tokenizer/test/test.mjs b/packages/css-tokenizer/test/test.mjs index 8299c98ce..3414f7bc2 100644 --- a/packages/css-tokenizer/test/test.mjs +++ b/packages/css-tokenizer/test/test.mjs @@ -1,16 +1,45 @@ // Reader import './test-reader.mjs'; + // Code points import './code-points/code-points.mjs'; import './code-points/ranges.mjs'; + +// Fuzz +import './fuzz/0001.mjs'; + // Tokens import './token/basic.mjs'; +import './token/cdo.mjs'; import './token/comment.mjs'; import './token/numeric.mjs'; +import './token/string.mjs'; import './token/url.mjs'; + // Complex import './complex/at-media-params.mjs'; import './complex/parse-error.mjs'; + // Community import './community/bootstrap.mjs'; +import './community/open-props.mjs'; import './community/postcss-parser-tests.mjs'; +import './community/token-types.mjs'; + +// Stringify +import './test-stringify.mjs'; + +// WPT +import './wpt/cdc-vs-ident-tokens.mjs'; +import './wpt/decimal-points-in-numbers.mjs'; +import './wpt/ident-three-code-points.mjs'; +import './wpt/inclusive-ranges.mjs'; + +// Mutations +import './mutations/ident.mjs'; + +// css-tokenizer-tests +// +// Keep this as the last test, +// it is only intended to increase test coverage by double checking more obscure cases. +import './css-tokenizer-tests/test.mjs'; diff --git a/packages/css-tokenizer/test/token/basic.mjs b/packages/css-tokenizer/test/token/basic.mjs index e5f471d6d..f9c0296b1 100644 --- a/packages/css-tokenizer/test/token/basic.mjs +++ b/packages/css-tokenizer/test/token/basic.mjs @@ -316,3 +316,46 @@ bar") and (fancy(baz))) {}`, ); } } + +{ + const t = tokenizer({ + css: '\\0', + }); + + assert.deepEqual( + collectTokens(t), + [ + ['ident-token', '\\0', 0, 1, { value: '�' }], + ['EOF-token', '', -1, -1, undefined], + ], + ); +} + +{ + const t = tokenizer({ + css: '\\', + }); + + assert.deepEqual( + collectTokens(t), + [ + ['ident-token', '\\', 0, 0, { value: '�' }], + ['EOF-token', '', -1, -1, undefined], + ], + ); +} + +{ + const t = tokenizer({ + css: `\\0 +`, + }); + + assert.deepEqual( + collectTokens(t), + [ + ['ident-token', '\\0\n', 0, 2, { value: '�' }], + ['EOF-token', '', -1, -1, undefined], + ], + ); +} diff --git a/packages/css-tokenizer/test/token/cdo.mjs b/packages/css-tokenizer/test/token/cdo.mjs new file mode 100644 index 000000000..583ab18f3 --- /dev/null +++ b/packages/css-tokenizer/test/token/cdo.mjs @@ -0,0 +1,50 @@ +import { tokenizer } from '@csstools/css-tokenizer'; +import assert from 'assert'; +import { collectTokens } from '../util/collect-tokens.mjs'; + +{ + const t = tokenizer({ + css: '--foo { color: blue; }', + }); + + assert.deepEqual( + collectTokens(t), + [ + ['CDC-token', '-->', 0, 2, undefined], + ['ident-token', '--foo', 3, 7, { value: '--foo' }], + ['whitespace-token', ' ', 8, 8, undefined], + ['{-token', '{', 9, 9, undefined], + ['whitespace-token', ' ', 10, 10, undefined], + ['ident-token', 'color', 11, 15, { value: 'color' }], + ['colon-token', ':', 16, 16, undefined], + ['whitespace-token', ' ', 17, 17, undefined], + ['ident-token', 'blue', 18, 21, { value: 'blue' }], + ['semicolon-token', ';', 22, 22, undefined], + ['whitespace-token', ' ', 23, 23, undefined], + ['}-token', '}', 24, 24, undefined], + ['EOF-token', '', -1, -1, undefined], + ], + ); +} diff --git a/packages/css-tokenizer/test/wpt/decimal-points-in-numbers.mjs b/packages/css-tokenizer/test/wpt/decimal-points-in-numbers.mjs new file mode 100644 index 000000000..2092a9d10 --- /dev/null +++ b/packages/css-tokenizer/test/wpt/decimal-points-in-numbers.mjs @@ -0,0 +1,102 @@ +import { tokenizer } from '@csstools/css-tokenizer'; +import assert from 'assert'; +import { collectTokens } from '../util/collect-tokens.mjs'; + +{ + const t = tokenizer({ + css: '1.0', + }); + + assert.deepEqual( + collectTokens(t), + [ + ['number-token', '1.0', 0, 2, { value: 1, type: 'number' }], + ['EOF-token', '', -1, -1, undefined], + ], + ); +} + +{ + const t = tokenizer({ + css: '.1', + }); + + assert.deepEqual( + collectTokens(t), + [ + ['number-token', '.1', 0, 1, { value: 0.1, type: 'number' }], + ['EOF-token', '', -1, -1, undefined], + ], + ); +} + +{ + const t = tokenizer({ + css: '1.', + }); + + assert.deepEqual( + collectTokens(t), + [ + ['number-token', '1', 0, 0, { value: 1, type: 'integer' }], + ['delim-token', '.', 1, 1, { value: '.' }], + ['EOF-token', '', -1, -1, undefined], + ], + ); +} + +{ + const t = tokenizer({ + css: '1.0px', + }); + + assert.deepEqual( + collectTokens(t), + [ + [ + 'dimension-token', + '1.0px', + 0, + 4, + { value: 1, type: 'number', unit: 'px' }, + ], + ['EOF-token', '', -1, -1, undefined], + ], + ); +} + +{ + const t = tokenizer({ + css: '.1px', + }); + + assert.deepEqual( + collectTokens(t), + [ + [ + 'dimension-token', + '.1px', + 0, + 3, + { value: 0.1, type: 'number', unit: 'px' }, + ], + ['EOF-token', '', -1, -1, undefined], + ], + ); +} + +{ + const t = tokenizer({ + css: '1.px', + }); + + assert.deepEqual( + collectTokens(t), + [ + ['number-token', '1', 0, 0, { value: 1, type: 'integer' }], + ['delim-token', '.', 1, 1, { value: '.' }], + ['ident-token', 'px', 2, 3, { value: 'px' }], + ['EOF-token', '', -1, -1, undefined], + ], + ); +} diff --git a/packages/css-tokenizer/test/wpt/ident-three-code-points.mjs b/packages/css-tokenizer/test/wpt/ident-three-code-points.mjs new file mode 100644 index 000000000..278632c25 --- /dev/null +++ b/packages/css-tokenizer/test/wpt/ident-three-code-points.mjs @@ -0,0 +1,121 @@ +import { tokenizer } from '@csstools/css-tokenizer'; +import assert from 'assert'; +import { collectTokens } from '../util/collect-tokens.mjs'; + +{ + const t = tokenizer({ + css: '#1', + }); + + assert.deepEqual( + collectTokens(t), + [ + ['hash-token', '#1', 0, 1, { value: '1', type: 'unrestricted' }], + ['EOF-token', '', -1, -1, undefined], + ], + ); +} + +{ + const t = tokenizer({ + css: '#-2', + }); + + assert.deepEqual( + collectTokens(t), + [ + [ + 'hash-token', + '#-2', + 0, + 2, + { value: '-2', type: 'unrestricted' }, + ], + ['EOF-token', '', -1, -1, undefined], + ], + ); +} + +{ + const t = tokenizer({ + css: '#--3', + }); + + assert.deepEqual( + collectTokens(t), + [ + ['hash-token', '#--3', 0, 3, { value: '--3', type: 'id' }], + ['EOF-token', '', -1, -1, undefined], + ], + ); +} + +{ + const t = tokenizer({ + css: '#---4', + }); + + assert.deepEqual( + collectTokens(t), + [ + ['hash-token', '#---4', 0, 4, { value: '---4', type: 'id' }], + ['EOF-token', '', -1, -1, undefined], + ], + ); +} + +{ + const t = tokenizer({ + css: '#a', + }); + + assert.deepEqual( + collectTokens(t), + [ + ['hash-token', '#a', 0, 1, { value: 'a', type: 'id' }], + ['EOF-token', '', -1, -1, undefined], + ], + ); +} + +{ + const t = tokenizer({ + css: '#-b', + }); + + assert.deepEqual( + collectTokens(t), + [ + ['hash-token', '#-b', 0, 2, { value: '-b', type: 'id' }], + ['EOF-token', '', -1, -1, undefined], + ], + ); +} + +{ + const t = tokenizer({ + css: '#--c', + }); + + assert.deepEqual( + collectTokens(t), + [ + ['hash-token', '#--c', 0, 3, { value: '--c', type: 'id' }], + ['EOF-token', '', -1, -1, undefined], + ], + ); +} + +{ + const t = tokenizer({ + css: '#---d', + }); + + assert.deepEqual( + collectTokens(t), + [ + ['hash-token', '#---d', 0, 4, { value: '---d', type: 'id' }], + ['EOF-token', '', -1, -1, undefined], + ], + ); +} diff --git a/packages/css-tokenizer/test/wpt/inclusive-ranges.mjs b/packages/css-tokenizer/test/wpt/inclusive-ranges.mjs new file mode 100644 index 000000000..671805f75 --- /dev/null +++ b/packages/css-tokenizer/test/wpt/inclusive-ranges.mjs @@ -0,0 +1,97 @@ +import { tokenizer } from '@csstools/css-tokenizer'; +import assert from 'assert'; +import { collectTokens } from '../util/collect-tokens.mjs'; + +{ + for (let i = 0; i < 9; i++) { + const t = tokenizer({ + css: i.toString(), + }); + + assert.deepEqual( + collectTokens(t), + [ + ['number-token', i.toString(), 0, 0, { value: i, type: 'integer' }], + ['EOF-token', '', -1, -1, undefined], + ], + ); + } + + for (let i = 10; i < 21; i++) { + const t = tokenizer({ + css: i.toString(), + }); + + assert.deepEqual( + collectTokens(t), + [ + ['number-token', i.toString(), 0, 1, { value: i, type: 'integer' }], + ['EOF-token', '', -1, -1, undefined], + ], + ); + } +} + +{ + for (let i = 1; i <= 8; i++) { + const t = tokenizer({ + css: 'foo' + String.fromCodePoint(i), + }); + + assert.deepEqual( + collectTokens(t), + [ + ['ident-token', 'foo', 0, 2, { value: 'foo' }], + ['delim-token', String.fromCodePoint(i), 3, 3, { value: String.fromCodePoint(i) }], + ['EOF-token', '', -1, -1, undefined], + ], + ); + } +} + +{ + for (let i = 0xe; i <= 0x1f; i++) { + const t = tokenizer({ + css: 'foo' + String.fromCodePoint(i), + }); + + assert.deepEqual( + collectTokens(t), + [ + ['ident-token', 'foo', 0, 2, { value: 'foo' }], + ['delim-token', String.fromCodePoint(i), 3, 3, { value: String.fromCodePoint(i) }], + ['EOF-token', '', -1, -1, undefined], + ], + ); + } +} + +{ + const t = tokenizer({ + css: 'foo' + String.fromCodePoint(0xb), + }); + + assert.deepEqual( + collectTokens(t), + [ + ['ident-token', 'foo', 0, 2, { value: 'foo' }], + ['delim-token', String.fromCodePoint(0xb), 3, 3, { value: String.fromCodePoint(0xb) }], + ['EOF-token', '', -1, -1, undefined], + ], + ); +} + +{ + const t = tokenizer({ + css: 'foo' + String.fromCodePoint(0x7f), + }); + + assert.deepEqual( + collectTokens(t), + [ + ['ident-token', 'foo', 0, 2, { value: 'foo' }], + ['delim-token', String.fromCodePoint(0x7f), 3, 3, { value: String.fromCodePoint(0x7f) }], + ['EOF-token', '', -1, -1, undefined], + ], + ); +} diff --git a/packages/media-query-list-parser/CHANGELOG.md b/packages/media-query-list-parser/CHANGELOG.md index cd7fc18e7..9ef64652a 100644 --- a/packages/media-query-list-parser/CHANGELOG.md +++ b/packages/media-query-list-parser/CHANGELOG.md @@ -1,3 +1,10 @@ +### Unreleased + +- Refactor `MediaFeatureBoolean` so that it follows the same structure as `MediaFeaturePlain` (breaking) +- Change the `ParseError` interface, this is now a subclass of `Error` (breaking) +- Add `getName` and `getNameToken` to all nodes that have a feature name. +- Add `@custom-media` parsing. + ### 1.0.0 (November 14, 2022) - Initial version diff --git a/packages/media-query-list-parser/dist/index.cjs b/packages/media-query-list-parser/dist/index.cjs index 203b85c88..51bfe7e17 100644 --- a/packages/media-query-list-parser/dist/index.cjs +++ b/packages/media-query-list-parser/dist/index.cjs @@ -1 +1 @@ -"use strict";var e,t,i,a=require("@csstools/css-parser-algorithms"),r=require("@csstools/css-tokenizer");exports.NodeType=void 0,(e=exports.NodeType||(exports.NodeType={})).GeneralEnclosed="general-enclosed",e.MediaAnd="media-and",e.MediaCondition="media-condition",e.MediaConditionListWithAnd="media-condition-list-and",e.MediaConditionListWithOr="media-condition-list-or",e.MediaFeature="media-feature",e.MediaFeatureBoolean="mf-boolean",e.MediaFeatureName="mf-name",e.MediaFeaturePlain="mf-plain",e.MediaFeatureRangeNameValue="mf-range-name-value",e.MediaFeatureRangeValueName="mf-range-value-name",e.MediaFeatureRangeValueNameValue="mf-range-value-name-value",e.MediaFeatureValue="mf-value",e.MediaInParens="media-in-parens",e.MediaNot="media-not",e.MediaOr="media-or",e.MediaQueryWithType="media-query-with-type",e.MediaQueryWithoutType="media-query-without-type",e.MediaQueryInvalid="media-query-invalid";class MediaCondition{type=exports.NodeType.MediaCondition;media;constructor(e){this.media=e}tokens(){return this.media.tokens()}toString(){return this.media.toString()}indexOf(e){return e===this.media?"media":-1}at(e){if("media"===e)return this.media}walk(e){return!1!==e({node:this.media,parent:this},"media")&&this.media.walk(e)}toJSON(){return{type:this.type,media:this.media.toJSON()}}isMediaCondition(){return MediaCondition.isMediaCondition(this)}static isMediaCondition(e){return!!e&&(e instanceof MediaCondition&&e.type===exports.NodeType.MediaCondition)}}class MediaInParens{type=exports.NodeType.MediaInParens;media;before;after;constructor(e,t=[],i=[]){this.media=e,this.before=t,this.after=i}tokens(){return[...this.before,...this.media.tokens(),...this.after]}toString(){return r.stringify(...this.before)+this.media.toString()+r.stringify(...this.after)}indexOf(e){return e===this.media?"media":-1}at(e){if("media"===e)return this.media}walk(e){return!1!==e({node:this.media,parent:this},"media")&&("walk"in this.media?this.media.walk(e):void 0)}toJSON(){return{type:this.type,media:this.media.toJSON(),before:this.before,after:this.after}}isMediaInParens(){return MediaInParens.isMediaInParens(this)}static isMediaInParens(e){return!!e&&(e instanceof MediaInParens&&e.type===exports.NodeType.MediaInParens)}}class MediaQueryWithType{type=exports.NodeType.MediaQueryWithType;modifier;mediaType;and;media=null;constructor(e,t,i,a){this.modifier=e,this.mediaType=t,i&&a&&(this.and=i,this.media=a)}getModifier(){if(!this.modifier.length)return"";for(let e=0;ee.tokens()))}toString(){return this.media.map((e=>e.toString())).join("")}walk(e){let t=!1;if(this.media.forEach(((i,a)=>{t||(!1!==e({node:i,parent:this},a)?"walk"in i&&!1===i.walk(e)&&(t=!0):t=!0)})),t)return!1}toJSON(){return{type:this.type,string:this.toString(),media:this.media}}isMediaQueryInvalid(){return MediaQueryInvalid.isMediaQueryInvalid(this)}static isMediaQueryInvalid(e){return!!e&&(e instanceof MediaQueryInvalid&&e.type===exports.NodeType.MediaQueryInvalid)}}class GeneralEnclosed{type=exports.NodeType.GeneralEnclosed;value;constructor(e){this.value=e}tokens(){return this.value.tokens()}toString(){return this.value.toString()}indexOf(e){return e===this.value?"value":-1}at(e){if("value"===e)return this.value}walk(e){return!1!==e({node:this.value,parent:this},"value")&&("walk"in this.value?this.value.walk(e):void 0)}toJSON(){return{type:this.type,tokens:this.tokens()}}isGeneralEnclosed(){return GeneralEnclosed.isGeneralEnclosed(this)}static isGeneralEnclosed(e){return!!e&&(e instanceof GeneralEnclosed&&e.type===exports.NodeType.GeneralEnclosed)}}class MediaAnd{type=exports.NodeType.MediaAnd;modifier;media;constructor(e,t){this.modifier=e,this.media=t}tokens(){return[...this.modifier,...this.media.tokens()]}toString(){return r.stringify(...this.modifier)+this.media.toString()}indexOf(e){return e===this.media?"media":-1}at(e){if("media"===e)return this.media}walk(e){return!1!==e({node:this.media,parent:this},"media")&&this.media.walk(e)}toJSON(){return{type:this.type,modifier:this.modifier,media:this.media.toJSON()}}isMediaAnd(){return MediaAnd.isMediaAnd(this)}static isMediaAnd(e){return!!e&&(e instanceof MediaAnd&&e.type===exports.NodeType.MediaAnd)}}class MediaConditionListWithAnd{type=exports.NodeType.MediaConditionListWithAnd;leading;list;before;after;constructor(e,t,i=[],a=[]){this.leading=e,this.list=t,this.before=i,this.after=a}tokens(){return[...this.before,...this.leading.tokens(),...this.list.flatMap((e=>e.tokens())),...this.after]}toString(){return r.stringify(...this.before)+this.leading.toString()+this.list.map((e=>e.toString())).join("")+r.stringify(...this.after)}indexOf(e){return e===this.leading?"leading":"media-and"===e.type?this.list.indexOf(e):-1}at(e){return"leading"===e?this.leading:"number"==typeof e?(e<0&&(e=this.list.length+e),this.list[e]):void 0}walk(e){if(!1===e({node:this.leading,parent:this},"leading"))return!1;if("walk"in this.leading&&!1===this.leading.walk(e))return!1;let t=!1;return this.list.forEach(((i,a)=>{t||(!1!==e({node:i,parent:this},a)?"walk"in i&&!1===i.walk(e)&&(t=!0):t=!0)})),!t&&void 0}toJSON(){return{type:this.type,leading:this.leading.toJSON(),list:this.list.map((e=>e.toJSON())),before:this.before,after:this.after}}isMediaConditionListWithAnd(){return MediaConditionListWithAnd.isMediaConditionListWithAnd(this)}static isMediaConditionListWithAnd(e){return!!e&&(e instanceof MediaConditionListWithAnd&&e.type===exports.NodeType.MediaConditionListWithAnd)}}class MediaConditionListWithOr{type=exports.NodeType.MediaConditionListWithOr;leading;list;before;after;constructor(e,t,i=[],a=[]){this.leading=e,this.list=t,this.before=i,this.after=a}tokens(){return[...this.before,...this.leading.tokens(),...this.list.flatMap((e=>e.tokens())),...this.after]}toString(){return r.stringify(...this.before)+this.leading.toString()+this.list.map((e=>e.toString())).join("")+r.stringify(...this.after)}indexOf(e){return e===this.leading?"leading":"media-or"===e.type?this.list.indexOf(e):-1}at(e){return"leading"===e?this.leading:"number"==typeof e?(e<0&&(e=this.list.length+e),this.list[e]):void 0}walk(e){if(!1===e({node:this.leading,parent:this},"leading"))return!1;if("walk"in this.leading&&!1===this.leading.walk(e))return!1;let t=!1;return this.list.forEach(((i,a)=>{t||(!1!==e({node:i,parent:this},a)?"walk"in i&&!1===i.walk(e)&&(t=!0):t=!0)})),!t&&void 0}toJSON(){return{type:this.type,leading:this.leading.toJSON(),list:this.list.map((e=>e.toJSON())),before:this.before,after:this.after}}isMediaConditionListWithOr(){return MediaConditionListWithOr.isMediaConditionListWithOr(this)}static isMediaConditionListWithOr(e){return!!e&&(e instanceof MediaConditionListWithOr&&e.type===exports.NodeType.MediaConditionListWithOr)}}function isNumber(e){return e.type===a.ComponentValueType.Token&&e.value[0]===r.TokenType.Number||e.type===a.ComponentValueType.Function&&"calc"===e.name[4].value}function isDimension(e){return e.type===a.ComponentValueType.Token&&e.value[0]===r.TokenType.Dimension}function isIdent(e){return e.type===a.ComponentValueType.Token&&e.value[0]===r.TokenType.Ident}class MediaFeatureName{type=exports.NodeType.MediaFeatureName;name;before;after;constructor(e,t=[],i=[]){this.name=e,this.before=t,this.after=i}getName(){return this.name.value[4].value}tokens(){return[...this.before,...this.name.tokens(),...this.after]}toString(){return r.stringify(...this.before)+this.name.toString()+r.stringify(...this.after)}indexOf(e){return e===this.name?"name":-1}at(e){if("name"===e)return this.name}toJSON(){return{type:this.type,name:this.getName(),tokens:this.tokens()}}isMediaFeatureName(){return MediaFeatureName.isMediaFeatureName(this)}static isMediaFeatureName(e){return!!e&&(e instanceof MediaFeatureName&&e.type===exports.NodeType.MediaFeatureName)}}function parseMediaFeatureName(e){let t=-1;for(let i=0;ie.tokens())),e.slice(t+1).flatMap((e=>e.tokens())))}class MediaFeatureBoolean{type=exports.NodeType.MediaFeatureBoolean;name;before;after;constructor(e,t=[],i=[]){this.name=e,this.before=t,this.after=i}getName(){return this.name.value[4].value}tokens(){return[...this.before,...this.name.tokens(),...this.after]}toString(){return r.stringify(...this.before)+this.name.toString()+r.stringify(...this.after)}indexOf(e){return e===this.name?"name":-1}at(e){if("name"===e)return this.name}toJSON(){return{type:this.type,name:this.getName(),tokens:this.tokens()}}isMediaFeatureBoolean(){return MediaFeatureBoolean.isMediaFeatureBoolean(this)}static isMediaFeatureBoolean(e){return!!e&&(e instanceof MediaFeatureBoolean&&e.type===exports.NodeType.MediaFeatureBoolean)}}function parseMediaFeatureBoolean(e){const t=parseMediaFeatureName(e);return!1===t?t:new MediaFeatureBoolean(t.name,t.before,t.after)}class MediaFeatureValue{type=exports.NodeType.MediaFeatureValue;value;before;after;constructor(e,t=[],i=[]){Array.isArray(e)&&1===e.length?this.value=e[0]:this.value=e,this.before=t,this.after=i}tokens(){return Array.isArray(this.value)?[...this.before,...this.value.flatMap((e=>e.tokens())),...this.after]:[...this.before,...this.value.tokens(),...this.after]}toString(){return Array.isArray(this.value)?r.stringify(...this.before)+this.value.map((e=>e.toString())).join("")+r.stringify(...this.after):r.stringify(...this.before)+this.value.toString()+r.stringify(...this.after)}indexOf(e){return e===this.value?"value":-1}at(e){if("value"===e)return this.value}walk(e){return!1!==e({node:this.value,parent:this},"value")&&("walk"in this.value?this.value.walk(e):void 0)}toJSON(){return Array.isArray(this.value)?{type:this.type,value:this.value.map((e=>e.toJSON())),tokens:this.tokens()}:{type:this.type,value:this.value.toJSON(),tokens:this.tokens()}}isMediaFeatureValue(){return MediaFeatureValue.isMediaFeatureValue(this)}static isMediaFeatureValue(e){return!!e&&(e instanceof MediaFeatureValue&&e.type===exports.NodeType.MediaFeatureValue)}}function parseMediaFeatureValue(e){let t=-1,i=-1;for(let r=0;re.tokens())),e.slice(i+1).flatMap((e=>e.tokens())))}function matchesRatioExactly(e){let t=-1,i=-1;const a=matchesRatio(e);if(-1===a)return-1;t=a[0],i=a[1];for(let t=i+1;t2)return!1;if(e[0][0]!==r.TokenType.Delim)return!1;if(1===e.length)switch(e[0][4].value){case exports.MediaFeatureEQ.EQ:return exports.MediaFeatureEQ.EQ;case exports.MediaFeatureLT.LT:return exports.MediaFeatureLT.LT;case exports.MediaFeatureGT.GT:return exports.MediaFeatureGT.GT;default:return!1}if(e[1][0]!==r.TokenType.Delim)return!1;if(e[1][4].value!==exports.MediaFeatureEQ.EQ)return!1;switch(e[0][4].value){case exports.MediaFeatureLT.LT:return exports.MediaFeatureLT.LT_OR_EQ;case exports.MediaFeatureGT.GT:return exports.MediaFeatureGT.GT_OR_EQ;default:return!1}}exports.MediaFeatureLT=void 0,(t=exports.MediaFeatureLT||(exports.MediaFeatureLT={})).LT="<",t.LT_OR_EQ="<=",exports.MediaFeatureGT=void 0,(i=exports.MediaFeatureGT||(exports.MediaFeatureGT={})).GT=">",i.GT_OR_EQ=">=",exports.MediaFeatureEQ=void 0,(exports.MediaFeatureEQ||(exports.MediaFeatureEQ={})).EQ="=";class MediaFeatureRangeNameValue{type=exports.NodeType.MediaFeatureRangeNameValue;name;operator;value;constructor(e,t,i){this.name=e,this.operator=t,this.value=i}operatorKind(){return comparisonFromTokens(this.operator)}tokens(){return[...this.name.tokens(),...this.operator,...this.value.tokens()]}toString(){return this.name.toString()+r.stringify(...this.operator)+this.value.toString()}indexOf(e){return e===this.name?"name":e===this.value?"value":-1}at(e){return"name"===e?this.name:"value"===e?this.value:void 0}walk(e){return!1!==e({node:this.value,parent:this},"value")&&("walk"in this.value?this.value.walk(e):void 0)}toJSON(){return{type:this.type,name:this.name.toJSON(),value:this.value.toJSON(),tokens:this.tokens()}}isMediaFeatureRangeNameValue(){return MediaFeatureRangeNameValue.isMediaFeatureRangeNameValue(this)}static isMediaFeatureRangeNameValue(e){return!!e&&(e instanceof MediaFeatureRangeNameValue&&e.type===exports.NodeType.MediaFeatureRangeNameValue)}}class MediaFeatureRangeValueName{type=exports.NodeType.MediaFeatureRangeValueName;name;operator;value;constructor(e,t,i){this.name=e,this.operator=t,this.value=i}operatorKind(){return comparisonFromTokens(this.operator)}tokens(){return[...this.value.tokens(),...this.operator,...this.name.tokens()]}toString(){return this.value.toString()+r.stringify(...this.operator)+this.name.toString()}indexOf(e){return e===this.name?"name":e===this.value?"value":-1}at(e){return"name"===e?this.name:"value"===e?this.value:void 0}walk(e){return!1!==e({node:this.value,parent:this},"value")&&("walk"in this.value?this.value.walk(e):void 0)}toJSON(){return{type:this.type,name:this.name.toJSON(),value:this.value.toJSON(),tokens:this.tokens()}}isMediaFeatureRangeValueName(){return MediaFeatureRangeValueName.isMediaFeatureRangeValueName(this)}static isMediaFeatureRangeValueName(e){return!!e&&(e instanceof MediaFeatureRangeValueName&&e.type===exports.NodeType.MediaFeatureRangeValueName)}}class MediaFeatureRangeValueNameValue{type=exports.NodeType.MediaFeatureRangeValueNameValue;name;valueOne;valueOneOperator;valueTwo;valueTwoOperator;constructor(e,t,i,a,r){this.name=e,this.valueOne=t,this.valueOneOperator=i,this.valueTwo=a,this.valueTwoOperator=r}valueOneOperatorKind(){return comparisonFromTokens(this.valueOneOperator)}valueTwoOperatorKind(){return comparisonFromTokens(this.valueTwoOperator)}tokens(){return[...this.valueOne.tokens(),...this.valueOneOperator,...this.name.tokens(),...this.valueTwoOperator,...this.valueTwo.tokens()]}toString(){return this.valueOne.toString()+r.stringify(...this.valueOneOperator)+this.name.toString()+r.stringify(...this.valueTwoOperator)+this.valueTwo.toString()}indexOf(e){return e===this.name?"name":e===this.valueOne?"valueOne":e===this.valueTwo?"valueTwo":-1}at(e){return"name"===e?this.name:"valueOne"===e?this.valueOne:"valueTwo"===e?this.valueTwo:void 0}walk(e){return!1!==e({node:this.valueOne,parent:this},"valueOne")&&((!("walk"in this.valueOne)||!1!==this.valueOne.walk(e))&&(!1!==e({node:this.valueTwo,parent:this},"valueTwo")&&((!("walk"in this.valueTwo)||!1!==this.valueTwo.walk(e))&&void 0)))}toJSON(){return{type:this.type,name:this.name.toJSON(),valueOne:this.valueOne.toJSON(),valueTwo:this.valueTwo.toJSON(),tokens:this.tokens()}}isMediaFeatureRangeValueNameValue(){return MediaFeatureRangeValueNameValue.isMediaFeatureRangeValueNameValue(this)}static isMediaFeatureRangeValueNameValue(e){return!!e&&(e instanceof MediaFeatureRangeValueNameValue&&e.type===exports.NodeType.MediaFeatureRangeValueNameValue)}}function parseMediaFeatureRange(e){let t=!1,i=!1;for(let n=0;ne.tokens())),-1!==i&&(s=e.slice(t+1,i+1).flatMap((e=>e.tokens())))):-1!==i&&(s=e.slice(0,i+1).flatMap((e=>e.tokens())));const d=parseMediaConditionWithoutOr(e.slice(Math.max(t,i,n)+1));return!1===d?new MediaQueryWithType(o,[...s,...e.slice(i+1).flatMap((e=>e.tokens()))]):new MediaQueryWithType(o,s,e.slice(i+1,n+1).flatMap((e=>e.tokens())),d)}}function parseMediaConditionListWithOr(e){let t=!1;const i=[];let r=-1,n=-1;for(let o=0;oe.tokens())),e.slice(n+1).flatMap((e=>e.tokens())))}function parseMediaConditionListWithAnd(e){let t=!1;const i=[];let r=-1,n=-1;for(let o=0;oe.tokens())),e.slice(n+1).flatMap((e=>e.tokens())))}function parseMediaCondition(e){const t=parseMediaNot(e);if(!1!==t)return new MediaCondition(t);const i=parseMediaConditionListWithAnd(e);if(!1!==i)return new MediaCondition(i);const a=parseMediaConditionListWithOr(e);if(!1!==a)return new MediaCondition(a);const r=parseMediaInParens(e);return!1!==r&&new MediaCondition(r)}function parseMediaConditionWithoutOr(e){const t=parseMediaNot(e);if(!1!==t)return new MediaCondition(t);const i=parseMediaConditionListWithAnd(e);if(!1!==i)return new MediaCondition(i);const a=parseMediaInParens(e);return!1!==a&&new MediaCondition(a)}function parseMediaInParens(e){let t=-1;for(let i=0;ie.tokens())),i.startToken],o=[i.endToken,...e.slice(t+1).flatMap((e=>e.tokens()))],s=parseMediaFeature(i,n,o);if(!1!==s)return new MediaInParens(s);const d=parseMediaCondition(i.value);return!1!==d?new MediaInParens(d,n,o):new MediaInParens(new GeneralEnclosed(i),e.slice(0,t).flatMap((e=>e.tokens())),e.slice(t+1).flatMap((e=>e.tokens())))}function parseMediaInParensFromSimpleBlock(e){if(e.startToken[0]!==r.TokenType.OpenParen)return!1;const t=parseMediaFeature(e,[e.startToken],[e.endToken]);if(!1!==t)return new MediaInParens(t);const i=parseMediaCondition(e.value);return!1!==i?new MediaInParens(i,[e.startToken],[e.endToken]):new MediaInParens(new GeneralEnclosed(e))}function parseMediaNot(e){let t=!1,i=null;for(let r=0;re.tokens())),t)}}}return i||!1}function parseMediaOr(e){let t=!1;for(let i=0;ie.tokens())),t)}}return!1}}return!1}function parseMediaAnd(e){let t=!1;for(let i=0;ie.tokens())),t)}}return!1}}return!1}function parseFromTokens(e,t){const i=a.parseCommaSeparatedListOfComponentValues(e,{onParseError:null==t?void 0:t.onParseError});return i.map(((e,a)=>{const r=parseMediaQuery(e);return 0==r&&!0===(null==t?void 0:t.preserveInvalidMediaQueries)?new MediaQueryInvalid(i[a]):r})).filter((e=>!!e))}function isMediaConditionListWithAnd(e){return MediaConditionListWithAnd.isMediaConditionListWithAnd(e)}function isMediaConditionListWithOr(e){return MediaConditionListWithOr.isMediaConditionListWithOr(e)}function isMediaFeatureRangeNameValue(e){return MediaFeatureRangeNameValue.isMediaFeatureRangeNameValue(e)}function isMediaFeatureRangeValueName(e){return MediaFeatureRangeValueName.isMediaFeatureRangeValueName(e)}function isMediaFeatureRangeValueNameValue(e){return MediaFeatureRangeValueNameValue.isMediaFeatureRangeValueNameValue(e)}function isMediaQueryWithType(e){return MediaQueryWithType.isMediaQueryWithType(e)}function isMediaQueryWithoutType(e){return MediaQueryWithoutType.isMediaQueryWithoutType(e)}function isMediaQueryInvalid(e){return MediaQueryInvalid.isMediaQueryInvalid(e)}exports.MediaQueryModifier=void 0,(o=exports.MediaQueryModifier||(exports.MediaQueryModifier={})).Not="not",o.Only="only",exports.MediaType=void 0,(s=exports.MediaType||(exports.MediaType={})).All="all",s.Print="print",s.Screen="screen",s.Tty="tty",s.Tv="tv",s.Projection="projection",s.Handheld="handheld",s.Braille="braille",s.Embossed="embossed",s.Aural="aural",s.Speech="speech",exports.GeneralEnclosed=GeneralEnclosed,exports.MediaAnd=MediaAnd,exports.MediaCondition=MediaCondition,exports.MediaConditionListWithAnd=MediaConditionListWithAnd,exports.MediaConditionListWithOr=MediaConditionListWithOr,exports.MediaFeature=MediaFeature,exports.MediaFeatureBoolean=MediaFeatureBoolean,exports.MediaFeatureName=MediaFeatureName,exports.MediaFeaturePlain=MediaFeaturePlain,exports.MediaFeatureRangeNameValue=MediaFeatureRangeNameValue,exports.MediaFeatureRangeValueName=MediaFeatureRangeValueName,exports.MediaFeatureRangeValueNameValue=MediaFeatureRangeValueNameValue,exports.MediaFeatureValue=MediaFeatureValue,exports.MediaInParens=MediaInParens,exports.MediaNot=MediaNot,exports.MediaOr=MediaOr,exports.MediaQueryInvalid=MediaQueryInvalid,exports.MediaQueryWithType=MediaQueryWithType,exports.MediaQueryWithoutType=MediaQueryWithoutType,exports.cloneMediaQuery=function cloneMediaQuery(e){const t=r.cloneTokens(e.tokens()),i=parseFromTokens(t,{preserveInvalidMediaQueries:!0})[0];if(!i)throw new Error(`Failed to clone media query for : "${r.stringify(...t)}"`);if(isMediaQueryInvalid(e)&&isMediaQueryInvalid(i))return i;if(isMediaQueryWithType(e)&&isMediaQueryWithType(i))return i;if(isMediaQueryWithoutType(e)&&isMediaQueryWithoutType(i))return i;throw new Error(`Failed to clone media query for : "${r.stringify(...t)}"`)},exports.comparisonFromTokens=comparisonFromTokens,exports.invertComparison=function invertComparison(e){switch(e){case exports.MediaFeatureEQ.EQ:return exports.MediaFeatureEQ.EQ;case exports.MediaFeatureLT.LT:return exports.MediaFeatureGT.GT;case exports.MediaFeatureLT.LT_OR_EQ:return exports.MediaFeatureGT.GT_OR_EQ;case exports.MediaFeatureGT.GT:return exports.MediaFeatureLT.LT;case exports.MediaFeatureGT.GT_OR_EQ:return exports.MediaFeatureLT.LT_OR_EQ;default:return!1}},exports.isGeneralEnclosed=function isGeneralEnclosed(e){return GeneralEnclosed.isGeneralEnclosed(e)},exports.isMediaAnd=function isMediaAnd(e){return MediaAnd.isMediaAnd(e)},exports.isMediaCondition=function isMediaCondition(e){return MediaCondition.isMediaCondition(e)},exports.isMediaConditionList=function isMediaConditionList(e){return isMediaConditionListWithAnd(e)||isMediaConditionListWithOr(e)},exports.isMediaConditionListWithAnd=isMediaConditionListWithAnd,exports.isMediaConditionListWithOr=isMediaConditionListWithOr,exports.isMediaFeature=function isMediaFeature(e){return MediaFeature.isMediaFeature(e)},exports.isMediaFeatureBoolean=function isMediaFeatureBoolean(e){return MediaFeatureBoolean.isMediaFeatureBoolean(e)},exports.isMediaFeatureName=function isMediaFeatureName(e){return MediaFeatureName.isMediaFeatureName(e)},exports.isMediaFeaturePlain=function isMediaFeaturePlain(e){return MediaFeaturePlain.isMediaFeaturePlain(e)},exports.isMediaFeatureRange=function isMediaFeatureRange(e){return isMediaFeatureRangeNameValue(e)||isMediaFeatureRangeValueName(e)||isMediaFeatureRangeValueNameValue(e)},exports.isMediaFeatureRangeNameValue=isMediaFeatureRangeNameValue,exports.isMediaFeatureRangeValueName=isMediaFeatureRangeValueName,exports.isMediaFeatureRangeValueNameValue=isMediaFeatureRangeValueNameValue,exports.isMediaFeatureValue=function isMediaFeatureValue(e){return MediaFeatureValue.isMediaFeatureValue(e)},exports.isMediaInParens=function isMediaInParens(e){return MediaInParens.isMediaInParens(e)},exports.isMediaNot=function isMediaNot(e){return MediaNot.isMediaNot(e)},exports.isMediaOr=function isMediaOr(e){return MediaOr.isMediaOr(e)},exports.isMediaQuery=function isMediaQuery(e){return isMediaQueryWithType(e)||isMediaQueryWithoutType(e)||isMediaQueryInvalid(e)},exports.isMediaQueryInvalid=isMediaQueryInvalid,exports.isMediaQueryWithType=isMediaQueryWithType,exports.isMediaQueryWithoutType=isMediaQueryWithoutType,exports.matchesComparison=matchesComparison,exports.matchesRatio=matchesRatio,exports.matchesRatioExactly=matchesRatioExactly,exports.modifierFromToken=modifierFromToken,exports.newMediaFeatureBoolean=function newMediaFeatureBoolean(e){return new MediaFeature(new MediaFeatureBoolean(new a.TokenNode([r.TokenType.Ident,e,-1,-1,{value:e}])),[[r.TokenType.OpenParen,"(",-1,-1,void 0]],[[r.TokenType.CloseParen,")",-1,-1,void 0]])},exports.newMediaFeaturePlain=function newMediaFeaturePlain(e,...t){return new MediaFeature(new MediaFeaturePlain(new MediaFeatureName(new a.TokenNode([r.TokenType.Ident,e,-1,-1,{value:e}])),[r.TokenType.Colon,":",-1,-1,void 0],new MediaFeatureValue(t.map((e=>new a.TokenNode(e))))),[[r.TokenType.OpenParen,"(",-1,-1,void 0]],[[r.TokenType.CloseParen,")",-1,-1,void 0]])},exports.parse=function parse(e,t){const i=r.tokenizer({css:e},{commentsAreTokens:!0,onParseError:null==t?void 0:t.onParseError}),a=[];for(;!i.endOfFile();)a.push(i.nextToken());return a.push(i.nextToken()),parseFromTokens(a,t)},exports.parseFromTokens=parseFromTokens,exports.typeFromToken=function typeFromToken(e){if(e[0]!==r.TokenType.Ident)return!1;switch(e[4].value.toLowerCase()){case exports.MediaType.All:return exports.MediaType.All;case exports.MediaType.Print:return exports.MediaType.Print;case exports.MediaType.Screen:return exports.MediaType.Screen;case exports.MediaType.Tty:return exports.MediaType.Tty;case exports.MediaType.Tv:return exports.MediaType.Tv;case exports.MediaType.Projection:return exports.MediaType.Projection;case exports.MediaType.Handheld:return exports.MediaType.Handheld;case exports.MediaType.Braille:return exports.MediaType.Braille;case exports.MediaType.Embossed:return exports.MediaType.Embossed;case exports.MediaType.Aural:return exports.MediaType.Aural;case exports.MediaType.Speech:return exports.MediaType.Speech;default:return!1}}; +"use strict";var e,t,i,a=require("@csstools/css-parser-algorithms"),r=require("@csstools/css-tokenizer");exports.NodeType=void 0,(e=exports.NodeType||(exports.NodeType={})).CustomMedia="custom-media",e.GeneralEnclosed="general-enclosed",e.MediaAnd="media-and",e.MediaCondition="media-condition",e.MediaConditionListWithAnd="media-condition-list-and",e.MediaConditionListWithOr="media-condition-list-or",e.MediaFeature="media-feature",e.MediaFeatureBoolean="mf-boolean",e.MediaFeatureName="mf-name",e.MediaFeaturePlain="mf-plain",e.MediaFeatureRangeNameValue="mf-range-name-value",e.MediaFeatureRangeValueName="mf-range-value-name",e.MediaFeatureRangeValueNameValue="mf-range-value-name-value",e.MediaFeatureValue="mf-value",e.MediaInParens="media-in-parens",e.MediaNot="media-not",e.MediaOr="media-or",e.MediaQueryWithType="media-query-with-type",e.MediaQueryWithoutType="media-query-without-type",e.MediaQueryInvalid="media-query-invalid";class MediaCondition{type=exports.NodeType.MediaCondition;media;constructor(e){this.media=e}tokens(){return this.media.tokens()}toString(){return this.media.toString()}indexOf(e){return e===this.media?"media":-1}at(e){if("media"===e)return this.media}walk(e){return!1!==e({node:this.media,parent:this},"media")&&this.media.walk(e)}toJSON(){return{type:this.type,media:this.media.toJSON()}}isMediaCondition(){return MediaCondition.isMediaCondition(this)}static isMediaCondition(e){return!!e&&(e instanceof MediaCondition&&e.type===exports.NodeType.MediaCondition)}}class MediaInParens{type=exports.NodeType.MediaInParens;media;before;after;constructor(e,t=[],i=[]){this.media=e,this.before=t,this.after=i}tokens(){return[...this.before,...this.media.tokens(),...this.after]}toString(){return r.stringify(...this.before)+this.media.toString()+r.stringify(...this.after)}indexOf(e){return e===this.media?"media":-1}at(e){if("media"===e)return this.media}walk(e){return!1!==e({node:this.media,parent:this},"media")&&("walk"in this.media?this.media.walk(e):void 0)}toJSON(){return{type:this.type,media:this.media.toJSON(),before:this.before,after:this.after}}isMediaInParens(){return MediaInParens.isMediaInParens(this)}static isMediaInParens(e){return!!e&&(e instanceof MediaInParens&&e.type===exports.NodeType.MediaInParens)}}class MediaQueryWithType{type=exports.NodeType.MediaQueryWithType;modifier;mediaType;and;media=null;constructor(e,t,i,a){this.modifier=e,this.mediaType=t,i&&a&&(this.and=i,this.media=a)}getModifier(){if(!this.modifier.length)return"";for(let e=0;ee.tokens()))}toString(){return this.media.map((e=>e.toString())).join("")}walk(e){let t=!1;if(this.media.forEach(((i,a)=>{t||(!1!==e({node:i,parent:this},a)?"walk"in i&&!1===i.walk(e)&&(t=!0):t=!0)})),t)return!1}toJSON(){return{type:this.type,string:this.toString(),media:this.media}}isMediaQueryInvalid(){return MediaQueryInvalid.isMediaQueryInvalid(this)}static isMediaQueryInvalid(e){return!!e&&(e instanceof MediaQueryInvalid&&e.type===exports.NodeType.MediaQueryInvalid)}}class GeneralEnclosed{type=exports.NodeType.GeneralEnclosed;value;constructor(e){this.value=e}tokens(){return this.value.tokens()}toString(){return this.value.toString()}indexOf(e){return e===this.value?"value":-1}at(e){if("value"===e)return this.value}walk(e){return!1!==e({node:this.value,parent:this},"value")&&("walk"in this.value?this.value.walk(e):void 0)}toJSON(){return{type:this.type,tokens:this.tokens()}}isGeneralEnclosed(){return GeneralEnclosed.isGeneralEnclosed(this)}static isGeneralEnclosed(e){return!!e&&(e instanceof GeneralEnclosed&&e.type===exports.NodeType.GeneralEnclosed)}}class MediaAnd{type=exports.NodeType.MediaAnd;modifier;media;constructor(e,t){this.modifier=e,this.media=t}tokens(){return[...this.modifier,...this.media.tokens()]}toString(){return r.stringify(...this.modifier)+this.media.toString()}indexOf(e){return e===this.media?"media":-1}at(e){if("media"===e)return this.media}walk(e){return!1!==e({node:this.media,parent:this},"media")&&this.media.walk(e)}toJSON(){return{type:this.type,modifier:this.modifier,media:this.media.toJSON()}}isMediaAnd(){return MediaAnd.isMediaAnd(this)}static isMediaAnd(e){return!!e&&(e instanceof MediaAnd&&e.type===exports.NodeType.MediaAnd)}}class MediaConditionListWithAnd{type=exports.NodeType.MediaConditionListWithAnd;leading;list;before;after;constructor(e,t,i=[],a=[]){this.leading=e,this.list=t,this.before=i,this.after=a}tokens(){return[...this.before,...this.leading.tokens(),...this.list.flatMap((e=>e.tokens())),...this.after]}toString(){return r.stringify(...this.before)+this.leading.toString()+this.list.map((e=>e.toString())).join("")+r.stringify(...this.after)}indexOf(e){return e===this.leading?"leading":"media-and"===e.type?this.list.indexOf(e):-1}at(e){return"leading"===e?this.leading:"number"==typeof e?(e<0&&(e=this.list.length+e),this.list[e]):void 0}walk(e){if(!1===e({node:this.leading,parent:this},"leading"))return!1;if("walk"in this.leading&&!1===this.leading.walk(e))return!1;let t=!1;return this.list.forEach(((i,a)=>{t||(!1!==e({node:i,parent:this},a)?"walk"in i&&!1===i.walk(e)&&(t=!0):t=!0)})),!t&&void 0}toJSON(){return{type:this.type,leading:this.leading.toJSON(),list:this.list.map((e=>e.toJSON())),before:this.before,after:this.after}}isMediaConditionListWithAnd(){return MediaConditionListWithAnd.isMediaConditionListWithAnd(this)}static isMediaConditionListWithAnd(e){return!!e&&(e instanceof MediaConditionListWithAnd&&e.type===exports.NodeType.MediaConditionListWithAnd)}}class MediaConditionListWithOr{type=exports.NodeType.MediaConditionListWithOr;leading;list;before;after;constructor(e,t,i=[],a=[]){this.leading=e,this.list=t,this.before=i,this.after=a}tokens(){return[...this.before,...this.leading.tokens(),...this.list.flatMap((e=>e.tokens())),...this.after]}toString(){return r.stringify(...this.before)+this.leading.toString()+this.list.map((e=>e.toString())).join("")+r.stringify(...this.after)}indexOf(e){return e===this.leading?"leading":"media-or"===e.type?this.list.indexOf(e):-1}at(e){return"leading"===e?this.leading:"number"==typeof e?(e<0&&(e=this.list.length+e),this.list[e]):void 0}walk(e){if(!1===e({node:this.leading,parent:this},"leading"))return!1;if("walk"in this.leading&&!1===this.leading.walk(e))return!1;let t=!1;return this.list.forEach(((i,a)=>{t||(!1!==e({node:i,parent:this},a)?"walk"in i&&!1===i.walk(e)&&(t=!0):t=!0)})),!t&&void 0}toJSON(){return{type:this.type,leading:this.leading.toJSON(),list:this.list.map((e=>e.toJSON())),before:this.before,after:this.after}}isMediaConditionListWithOr(){return MediaConditionListWithOr.isMediaConditionListWithOr(this)}static isMediaConditionListWithOr(e){return!!e&&(e instanceof MediaConditionListWithOr&&e.type===exports.NodeType.MediaConditionListWithOr)}}function isNumber(e){return e.type===a.ComponentValueType.Token&&e.value[0]===r.TokenType.Number||e.type===a.ComponentValueType.Function&&"calc"===e.name[4].value}function isDimension(e){return e.type===a.ComponentValueType.Token&&e.value[0]===r.TokenType.Dimension}function isIdent(e){return e.type===a.ComponentValueType.Token&&e.value[0]===r.TokenType.Ident}class MediaFeatureName{type=exports.NodeType.MediaFeatureName;name;before;after;constructor(e,t=[],i=[]){this.name=e,this.before=t,this.after=i}getName(){return this.name.value[4].value}getNameToken(){return this.name.value}tokens(){return[...this.before,...this.name.tokens(),...this.after]}toString(){return r.stringify(...this.before)+this.name.toString()+r.stringify(...this.after)}indexOf(e){return e===this.name?"name":-1}at(e){if("name"===e)return this.name}toJSON(){return{type:this.type,name:this.getName(),tokens:this.tokens()}}isMediaFeatureName(){return MediaFeatureName.isMediaFeatureName(this)}static isMediaFeatureName(e){return!!e&&(e instanceof MediaFeatureName&&e.type===exports.NodeType.MediaFeatureName)}}function parseMediaFeatureName(e){let t=-1;for(let i=0;ie.tokens())),e.slice(t+1).flatMap((e=>e.tokens())))}class MediaFeatureBoolean{type=exports.NodeType.MediaFeatureBoolean;name;constructor(e){this.name=e}getName(){return this.name.getName()}getNameToken(){return this.name.getNameToken()}tokens(){return this.name.tokens()}toString(){return this.name.toString()}indexOf(e){return e===this.name?"name":-1}at(e){if("name"===e)return this.name}toJSON(){return{type:this.type,name:this.name.toJSON(),tokens:this.tokens()}}isMediaFeatureBoolean(){return MediaFeatureBoolean.isMediaFeatureBoolean(this)}static isMediaFeatureBoolean(e){return!!e&&(e instanceof MediaFeatureBoolean&&e.type===exports.NodeType.MediaFeatureBoolean)}}function parseMediaFeatureBoolean(e){const t=parseMediaFeatureName(e);return!1===t?t:new MediaFeatureBoolean(t)}class MediaFeatureValue{type=exports.NodeType.MediaFeatureValue;value;before;after;constructor(e,t=[],i=[]){Array.isArray(e)&&1===e.length?this.value=e[0]:this.value=e,this.before=t,this.after=i}tokens(){return Array.isArray(this.value)?[...this.before,...this.value.flatMap((e=>e.tokens())),...this.after]:[...this.before,...this.value.tokens(),...this.after]}toString(){return Array.isArray(this.value)?r.stringify(...this.before)+this.value.map((e=>e.toString())).join("")+r.stringify(...this.after):r.stringify(...this.before)+this.value.toString()+r.stringify(...this.after)}indexOf(e){return e===this.value?"value":-1}at(e){if("value"===e)return this.value}walk(e){return!1!==e({node:this.value,parent:this},"value")&&("walk"in this.value?this.value.walk(e):void 0)}toJSON(){return Array.isArray(this.value)?{type:this.type,value:this.value.map((e=>e.toJSON())),tokens:this.tokens()}:{type:this.type,value:this.value.toJSON(),tokens:this.tokens()}}isMediaFeatureValue(){return MediaFeatureValue.isMediaFeatureValue(this)}static isMediaFeatureValue(e){return!!e&&(e instanceof MediaFeatureValue&&e.type===exports.NodeType.MediaFeatureValue)}}function parseMediaFeatureValue(e){let t=-1,i=-1;for(let r=0;re.tokens())),e.slice(i+1).flatMap((e=>e.tokens())))}function matchesRatioExactly(e){let t=-1,i=-1;const a=matchesRatio(e);if(-1===a)return-1;t=a[0],i=a[1];for(let t=i+1;t2)return!1;if(e[0][0]!==r.TokenType.Delim)return!1;if(1===e.length)switch(e[0][4].value){case exports.MediaFeatureEQ.EQ:return exports.MediaFeatureEQ.EQ;case exports.MediaFeatureLT.LT:return exports.MediaFeatureLT.LT;case exports.MediaFeatureGT.GT:return exports.MediaFeatureGT.GT;default:return!1}if(e[1][0]!==r.TokenType.Delim)return!1;if(e[1][4].value!==exports.MediaFeatureEQ.EQ)return!1;switch(e[0][4].value){case exports.MediaFeatureLT.LT:return exports.MediaFeatureLT.LT_OR_EQ;case exports.MediaFeatureGT.GT:return exports.MediaFeatureGT.GT_OR_EQ;default:return!1}}exports.MediaFeatureLT=void 0,(t=exports.MediaFeatureLT||(exports.MediaFeatureLT={})).LT="<",t.LT_OR_EQ="<=",exports.MediaFeatureGT=void 0,(i=exports.MediaFeatureGT||(exports.MediaFeatureGT={})).GT=">",i.GT_OR_EQ=">=",exports.MediaFeatureEQ=void 0,(exports.MediaFeatureEQ||(exports.MediaFeatureEQ={})).EQ="=";class MediaFeatureRangeNameValue{type=exports.NodeType.MediaFeatureRangeNameValue;name;operator;value;constructor(e,t,i){this.name=e,this.operator=t,this.value=i}operatorKind(){return comparisonFromTokens(this.operator)}getName(){return this.name.getName()}getNameToken(){return this.name.getNameToken()}tokens(){return[...this.name.tokens(),...this.operator,...this.value.tokens()]}toString(){return this.name.toString()+r.stringify(...this.operator)+this.value.toString()}indexOf(e){return e===this.name?"name":e===this.value?"value":-1}at(e){return"name"===e?this.name:"value"===e?this.value:void 0}walk(e){return!1!==e({node:this.value,parent:this},"value")&&("walk"in this.value?this.value.walk(e):void 0)}toJSON(){return{type:this.type,name:this.name.toJSON(),value:this.value.toJSON(),tokens:this.tokens()}}isMediaFeatureRangeNameValue(){return MediaFeatureRangeNameValue.isMediaFeatureRangeNameValue(this)}static isMediaFeatureRangeNameValue(e){return!!e&&(e instanceof MediaFeatureRangeNameValue&&e.type===exports.NodeType.MediaFeatureRangeNameValue)}}class MediaFeatureRangeValueName{type=exports.NodeType.MediaFeatureRangeValueName;name;operator;value;constructor(e,t,i){this.name=e,this.operator=t,this.value=i}operatorKind(){return comparisonFromTokens(this.operator)}getName(){return this.name.getName()}getNameToken(){return this.name.getNameToken()}tokens(){return[...this.value.tokens(),...this.operator,...this.name.tokens()]}toString(){return this.value.toString()+r.stringify(...this.operator)+this.name.toString()}indexOf(e){return e===this.name?"name":e===this.value?"value":-1}at(e){return"name"===e?this.name:"value"===e?this.value:void 0}walk(e){return!1!==e({node:this.value,parent:this},"value")&&("walk"in this.value?this.value.walk(e):void 0)}toJSON(){return{type:this.type,name:this.name.toJSON(),value:this.value.toJSON(),tokens:this.tokens()}}isMediaFeatureRangeValueName(){return MediaFeatureRangeValueName.isMediaFeatureRangeValueName(this)}static isMediaFeatureRangeValueName(e){return!!e&&(e instanceof MediaFeatureRangeValueName&&e.type===exports.NodeType.MediaFeatureRangeValueName)}}class MediaFeatureRangeValueNameValue{type=exports.NodeType.MediaFeatureRangeValueNameValue;name;valueOne;valueOneOperator;valueTwo;valueTwoOperator;constructor(e,t,i,a,r){this.name=e,this.valueOne=t,this.valueOneOperator=i,this.valueTwo=a,this.valueTwoOperator=r}valueOneOperatorKind(){return comparisonFromTokens(this.valueOneOperator)}valueTwoOperatorKind(){return comparisonFromTokens(this.valueTwoOperator)}getName(){return this.name.getName()}getNameToken(){return this.name.getNameToken()}tokens(){return[...this.valueOne.tokens(),...this.valueOneOperator,...this.name.tokens(),...this.valueTwoOperator,...this.valueTwo.tokens()]}toString(){return this.valueOne.toString()+r.stringify(...this.valueOneOperator)+this.name.toString()+r.stringify(...this.valueTwoOperator)+this.valueTwo.toString()}indexOf(e){return e===this.name?"name":e===this.valueOne?"valueOne":e===this.valueTwo?"valueTwo":-1}at(e){return"name"===e?this.name:"valueOne"===e?this.valueOne:"valueTwo"===e?this.valueTwo:void 0}walk(e){return!1!==e({node:this.valueOne,parent:this},"valueOne")&&((!("walk"in this.valueOne)||!1!==this.valueOne.walk(e))&&(!1!==e({node:this.valueTwo,parent:this},"valueTwo")&&((!("walk"in this.valueTwo)||!1!==this.valueTwo.walk(e))&&void 0)))}toJSON(){return{type:this.type,name:this.name.toJSON(),valueOne:this.valueOne.toJSON(),valueTwo:this.valueTwo.toJSON(),tokens:this.tokens()}}isMediaFeatureRangeValueNameValue(){return MediaFeatureRangeValueNameValue.isMediaFeatureRangeValueNameValue(this)}static isMediaFeatureRangeValueNameValue(e){return!!e&&(e instanceof MediaFeatureRangeValueNameValue&&e.type===exports.NodeType.MediaFeatureRangeValueNameValue)}}function parseMediaFeatureRange(e){let t=!1,i=!1;for(let n=0;ne.tokens())),-1!==i&&(s=e.slice(t+1,i+1).flatMap((e=>e.tokens())))):-1!==i&&(s=e.slice(0,i+1).flatMap((e=>e.tokens())));const u=parseMediaConditionWithoutOr(e.slice(Math.max(t,i,n)+1));return!1===u?new MediaQueryWithType(o,[...s,...e.slice(i+1).flatMap((e=>e.tokens()))]):new MediaQueryWithType(o,s,e.slice(i+1,n+1).flatMap((e=>e.tokens())),u)}}function parseMediaConditionListWithOr(e){let t=!1;const i=[];let r=-1,n=-1;for(let o=0;oe.tokens())),e.slice(n+1).flatMap((e=>e.tokens())))}function parseMediaConditionListWithAnd(e){let t=!1;const i=[];let r=-1,n=-1;for(let o=0;oe.tokens())),e.slice(n+1).flatMap((e=>e.tokens())))}function parseMediaCondition(e){const t=parseMediaNot(e);if(!1!==t)return new MediaCondition(t);const i=parseMediaConditionListWithAnd(e);if(!1!==i)return new MediaCondition(i);const a=parseMediaConditionListWithOr(e);if(!1!==a)return new MediaCondition(a);const r=parseMediaInParens(e);return!1!==r&&new MediaCondition(r)}function parseMediaConditionWithoutOr(e){const t=parseMediaNot(e);if(!1!==t)return new MediaCondition(t);const i=parseMediaConditionListWithAnd(e);if(!1!==i)return new MediaCondition(i);const a=parseMediaInParens(e);return!1!==a&&new MediaCondition(a)}function parseMediaInParens(e){let t=-1;for(let i=0;ie.tokens())),i.startToken],o=[i.endToken,...e.slice(t+1).flatMap((e=>e.tokens()))],s=parseMediaFeature(i,n,o);if(!1!==s)return new MediaInParens(s);const u=parseMediaCondition(i.value);return!1!==u?new MediaInParens(u,n,o):new MediaInParens(new GeneralEnclosed(i),e.slice(0,t).flatMap((e=>e.tokens())),e.slice(t+1).flatMap((e=>e.tokens())))}function parseMediaInParensFromSimpleBlock(e){if(e.startToken[0]!==r.TokenType.OpenParen)return!1;const t=parseMediaFeature(e,[e.startToken],[e.endToken]);if(!1!==t)return new MediaInParens(t);const i=parseMediaCondition(e.value);return!1!==i?new MediaInParens(i,[e.startToken],[e.endToken]):new MediaInParens(new GeneralEnclosed(e))}function parseMediaNot(e){let t=!1,i=null;for(let r=0;re.tokens())),t)}}}return i||!1}function parseMediaOr(e){let t=!1;for(let i=0;ie.tokens())),t)}}return!1}}return!1}function parseMediaAnd(e){let t=!1;for(let i=0;ie.tokens())),t)}}return!1}}return!1}function parseFromTokens(e,t){const i=a.parseCommaSeparatedListOfComponentValues(e,{onParseError:null==t?void 0:t.onParseError});return i.map(((e,a)=>{const r=parseMediaQuery(e);return 0==r&&!0===(null==t?void 0:t.preserveInvalidMediaQueries)?new MediaQueryInvalid(i[a]):r})).filter((e=>!!e))}exports.MediaQueryModifier=void 0,(o=exports.MediaQueryModifier||(exports.MediaQueryModifier={})).Not="not",o.Only="only";class CustomMedia{type=exports.NodeType.CustomMedia;name;mediaQueryList=null;trueOrFalseKeyword=null;constructor(e,t,i){this.name=e,this.mediaQueryList=t,this.trueOrFalseKeyword=i}getName(){for(let e=0;ee.toJSON()))}}isCustomMedia(){return CustomMedia.isCustomMedia(this)}static isCustomMedia(e){return!!e&&(e instanceof CustomMedia&&e.type===exports.NodeType.CustomMedia)}}function parseCustomMediaFromTokens(e,t){let i=[],a=e;for(let t=0;te.tokens()))}toString(){return this.media.map((e=>e.toString())).join("")}walk(e){let t=!1;if(this.media.forEach(((i,a)=>{t||(!1!==e({node:i,parent:this},a)?"walk"in i&&!1===i.walk(e)&&(t=!0):t=!0)})),t)return!1}toJSON(){return{type:this.type,string:this.toString(),media:this.media}}isMediaQueryInvalid(){return MediaQueryInvalid.isMediaQueryInvalid(this)}static isMediaQueryInvalid(e){return!!e&&(e instanceof MediaQueryInvalid&&e.type===l.MediaQueryInvalid)}}class GeneralEnclosed{type=l.GeneralEnclosed;value;constructor(e){this.value=e}tokens(){return this.value.tokens()}toString(){return this.value.toString()}indexOf(e){return e===this.value?"value":-1}at(e){if("value"===e)return this.value}walk(e){return!1!==e({node:this.value,parent:this},"value")&&("walk"in this.value?this.value.walk(e):void 0)}toJSON(){return{type:this.type,tokens:this.tokens()}}isGeneralEnclosed(){return GeneralEnclosed.isGeneralEnclosed(this)}static isGeneralEnclosed(e){return!!e&&(e instanceof GeneralEnclosed&&e.type===l.GeneralEnclosed)}}class MediaAnd{type=l.MediaAnd;modifier;media;constructor(e,t){this.modifier=e,this.media=t}tokens(){return[...this.modifier,...this.media.tokens()]}toString(){return s(...this.modifier)+this.media.toString()}indexOf(e){return e===this.media?"media":-1}at(e){if("media"===e)return this.media}walk(e){return!1!==e({node:this.media,parent:this},"media")&&this.media.walk(e)}toJSON(){return{type:this.type,modifier:this.modifier,media:this.media.toJSON()}}isMediaAnd(){return MediaAnd.isMediaAnd(this)}static isMediaAnd(e){return!!e&&(e instanceof MediaAnd&&e.type===l.MediaAnd)}}class MediaConditionListWithAnd{type=l.MediaConditionListWithAnd;leading;list;before;after;constructor(e,t,i=[],a=[]){this.leading=e,this.list=t,this.before=i,this.after=a}tokens(){return[...this.before,...this.leading.tokens(),...this.list.flatMap((e=>e.tokens())),...this.after]}toString(){return s(...this.before)+this.leading.toString()+this.list.map((e=>e.toString())).join("")+s(...this.after)}indexOf(e){return e===this.leading?"leading":"media-and"===e.type?this.list.indexOf(e):-1}at(e){return"leading"===e?this.leading:"number"==typeof e?(e<0&&(e=this.list.length+e),this.list[e]):void 0}walk(e){if(!1===e({node:this.leading,parent:this},"leading"))return!1;if("walk"in this.leading&&!1===this.leading.walk(e))return!1;let t=!1;return this.list.forEach(((i,a)=>{t||(!1!==e({node:i,parent:this},a)?"walk"in i&&!1===i.walk(e)&&(t=!0):t=!0)})),!t&&void 0}toJSON(){return{type:this.type,leading:this.leading.toJSON(),list:this.list.map((e=>e.toJSON())),before:this.before,after:this.after}}isMediaConditionListWithAnd(){return MediaConditionListWithAnd.isMediaConditionListWithAnd(this)}static isMediaConditionListWithAnd(e){return!!e&&(e instanceof MediaConditionListWithAnd&&e.type===l.MediaConditionListWithAnd)}}class MediaConditionListWithOr{type=l.MediaConditionListWithOr;leading;list;before;after;constructor(e,t,i=[],a=[]){this.leading=e,this.list=t,this.before=i,this.after=a}tokens(){return[...this.before,...this.leading.tokens(),...this.list.flatMap((e=>e.tokens())),...this.after]}toString(){return s(...this.before)+this.leading.toString()+this.list.map((e=>e.toString())).join("")+s(...this.after)}indexOf(e){return e===this.leading?"leading":"media-or"===e.type?this.list.indexOf(e):-1}at(e){return"leading"===e?this.leading:"number"==typeof e?(e<0&&(e=this.list.length+e),this.list[e]):void 0}walk(e){if(!1===e({node:this.leading,parent:this},"leading"))return!1;if("walk"in this.leading&&!1===this.leading.walk(e))return!1;let t=!1;return this.list.forEach(((i,a)=>{t||(!1!==e({node:i,parent:this},a)?"walk"in i&&!1===i.walk(e)&&(t=!0):t=!0)})),!t&&void 0}toJSON(){return{type:this.type,leading:this.leading.toJSON(),list:this.list.map((e=>e.toJSON())),before:this.before,after:this.after}}isMediaConditionListWithOr(){return MediaConditionListWithOr.isMediaConditionListWithOr(this)}static isMediaConditionListWithOr(e){return!!e&&(e instanceof MediaConditionListWithOr&&e.type===l.MediaConditionListWithOr)}}function isNumber(t){return t.type===e.Token&&t.value[0]===o.Number||t.type===e.Function&&"calc"===t.name[4].value}function isDimension(t){return t.type===e.Token&&t.value[0]===o.Dimension}function isIdent(t){return t.type===e.Token&&t.value[0]===o.Ident}class MediaFeatureName{type=l.MediaFeatureName;name;before;after;constructor(e,t=[],i=[]){this.name=e,this.before=t,this.after=i}getName(){return this.name.value[4].value}tokens(){return[...this.before,...this.name.tokens(),...this.after]}toString(){return s(...this.before)+this.name.toString()+s(...this.after)}indexOf(e){return e===this.name?"name":-1}at(e){if("name"===e)return this.name}toJSON(){return{type:this.type,name:this.getName(),tokens:this.tokens()}}isMediaFeatureName(){return MediaFeatureName.isMediaFeatureName(this)}static isMediaFeatureName(e){return!!e&&(e instanceof MediaFeatureName&&e.type===l.MediaFeatureName)}}function parseMediaFeatureName(t){let i=-1;for(let a=0;ae.tokens())),t.slice(i+1).flatMap((e=>e.tokens())))}class MediaFeatureBoolean{type=l.MediaFeatureBoolean;name;before;after;constructor(e,t=[],i=[]){this.name=e,this.before=t,this.after=i}getName(){return this.name.value[4].value}tokens(){return[...this.before,...this.name.tokens(),...this.after]}toString(){return s(...this.before)+this.name.toString()+s(...this.after)}indexOf(e){return e===this.name?"name":-1}at(e){if("name"===e)return this.name}toJSON(){return{type:this.type,name:this.getName(),tokens:this.tokens()}}isMediaFeatureBoolean(){return MediaFeatureBoolean.isMediaFeatureBoolean(this)}static isMediaFeatureBoolean(e){return!!e&&(e instanceof MediaFeatureBoolean&&e.type===l.MediaFeatureBoolean)}}function parseMediaFeatureBoolean(e){const t=parseMediaFeatureName(e);return!1===t?t:new MediaFeatureBoolean(t.name,t.before,t.after)}class MediaFeatureValue{type=l.MediaFeatureValue;value;before;after;constructor(e,t=[],i=[]){Array.isArray(e)&&1===e.length?this.value=e[0]:this.value=e,this.before=t,this.after=i}tokens(){return Array.isArray(this.value)?[...this.before,...this.value.flatMap((e=>e.tokens())),...this.after]:[...this.before,...this.value.tokens(),...this.after]}toString(){return Array.isArray(this.value)?s(...this.before)+this.value.map((e=>e.toString())).join("")+s(...this.after):s(...this.before)+this.value.toString()+s(...this.after)}indexOf(e){return e===this.value?"value":-1}at(e){if("value"===e)return this.value}walk(e){return!1!==e({node:this.value,parent:this},"value")&&("walk"in this.value?this.value.walk(e):void 0)}toJSON(){return Array.isArray(this.value)?{type:this.type,value:this.value.map((e=>e.toJSON())),tokens:this.tokens()}:{type:this.type,value:this.value.toJSON(),tokens:this.tokens()}}isMediaFeatureValue(){return MediaFeatureValue.isMediaFeatureValue(this)}static isMediaFeatureValue(e){return!!e&&(e instanceof MediaFeatureValue&&e.type===l.MediaFeatureValue)}}function parseMediaFeatureValue(t){let i=-1,a=-1;for(let n=0;ne.tokens())),t.slice(a+1).flatMap((e=>e.tokens())))}function matchesRatioExactly(e){let t=-1,i=-1;const a=matchesRatio(e);if(-1===a)return-1;t=a[0],i=a[1];for(let t=i+1;t2)return!1;if(e[0][0]!==o.Delim)return!1;if(1===e.length)switch(e[0][4].value){case f.EQ:return f.EQ;case h.LT:return h.LT;case m.GT:return m.GT;default:return!1}if(e[1][0]!==o.Delim)return!1;if(e[1][4].value!==f.EQ)return!1;switch(e[0][4].value){case h.LT:return h.LT_OR_EQ;case m.GT:return m.GT_OR_EQ;default:return!1}}function invertComparison(e){switch(e){case f.EQ:return f.EQ;case h.LT:return m.GT;case h.LT_OR_EQ:return m.GT_OR_EQ;case m.GT:return h.LT;case m.GT_OR_EQ:return h.LT_OR_EQ;default:return!1}}!function(e){e.LT="<",e.LT_OR_EQ="<="}(h||(h={})),function(e){e.GT=">",e.GT_OR_EQ=">="}(m||(m={})),function(e){e.EQ="="}(f||(f={}));class MediaFeatureRangeNameValue{type=l.MediaFeatureRangeNameValue;name;operator;value;constructor(e,t,i){this.name=e,this.operator=t,this.value=i}operatorKind(){return comparisonFromTokens(this.operator)}tokens(){return[...this.name.tokens(),...this.operator,...this.value.tokens()]}toString(){return this.name.toString()+s(...this.operator)+this.value.toString()}indexOf(e){return e===this.name?"name":e===this.value?"value":-1}at(e){return"name"===e?this.name:"value"===e?this.value:void 0}walk(e){return!1!==e({node:this.value,parent:this},"value")&&("walk"in this.value?this.value.walk(e):void 0)}toJSON(){return{type:this.type,name:this.name.toJSON(),value:this.value.toJSON(),tokens:this.tokens()}}isMediaFeatureRangeNameValue(){return MediaFeatureRangeNameValue.isMediaFeatureRangeNameValue(this)}static isMediaFeatureRangeNameValue(e){return!!e&&(e instanceof MediaFeatureRangeNameValue&&e.type===l.MediaFeatureRangeNameValue)}}class MediaFeatureRangeValueName{type=l.MediaFeatureRangeValueName;name;operator;value;constructor(e,t,i){this.name=e,this.operator=t,this.value=i}operatorKind(){return comparisonFromTokens(this.operator)}tokens(){return[...this.value.tokens(),...this.operator,...this.name.tokens()]}toString(){return this.value.toString()+s(...this.operator)+this.name.toString()}indexOf(e){return e===this.name?"name":e===this.value?"value":-1}at(e){return"name"===e?this.name:"value"===e?this.value:void 0}walk(e){return!1!==e({node:this.value,parent:this},"value")&&("walk"in this.value?this.value.walk(e):void 0)}toJSON(){return{type:this.type,name:this.name.toJSON(),value:this.value.toJSON(),tokens:this.tokens()}}isMediaFeatureRangeValueName(){return MediaFeatureRangeValueName.isMediaFeatureRangeValueName(this)}static isMediaFeatureRangeValueName(e){return!!e&&(e instanceof MediaFeatureRangeValueName&&e.type===l.MediaFeatureRangeValueName)}}class MediaFeatureRangeValueNameValue{type=l.MediaFeatureRangeValueNameValue;name;valueOne;valueOneOperator;valueTwo;valueTwoOperator;constructor(e,t,i,a,n){this.name=e,this.valueOne=t,this.valueOneOperator=i,this.valueTwo=a,this.valueTwoOperator=n}valueOneOperatorKind(){return comparisonFromTokens(this.valueOneOperator)}valueTwoOperatorKind(){return comparisonFromTokens(this.valueTwoOperator)}tokens(){return[...this.valueOne.tokens(),...this.valueOneOperator,...this.name.tokens(),...this.valueTwoOperator,...this.valueTwo.tokens()]}toString(){return this.valueOne.toString()+s(...this.valueOneOperator)+this.name.toString()+s(...this.valueTwoOperator)+this.valueTwo.toString()}indexOf(e){return e===this.name?"name":e===this.valueOne?"valueOne":e===this.valueTwo?"valueTwo":-1}at(e){return"name"===e?this.name:"valueOne"===e?this.valueOne:"valueTwo"===e?this.valueTwo:void 0}walk(e){return!1!==e({node:this.valueOne,parent:this},"valueOne")&&((!("walk"in this.valueOne)||!1!==this.valueOne.walk(e))&&(!1!==e({node:this.valueTwo,parent:this},"valueTwo")&&((!("walk"in this.valueTwo)||!1!==this.valueTwo.walk(e))&&void 0)))}toJSON(){return{type:this.type,name:this.name.toJSON(),valueOne:this.valueOne.toJSON(),valueTwo:this.valueTwo.toJSON(),tokens:this.tokens()}}isMediaFeatureRangeValueNameValue(){return MediaFeatureRangeValueNameValue.isMediaFeatureRangeValueNameValue(this)}static isMediaFeatureRangeValueNameValue(e){return!!e&&(e instanceof MediaFeatureRangeValueNameValue&&e.type===l.MediaFeatureRangeValueNameValue)}}function parseMediaFeatureRange(t){let i=!1,a=!1;for(let n=0;nnew t(e))))),[[o.OpenParen,"(",-1,-1,void 0]],[[o.CloseParen,")",-1,-1,void 0]])}class MediaNot{type=l.MediaNot;modifier;media;constructor(e,t){this.modifier=e,this.media=t}tokens(){return[...this.modifier,...this.media.tokens()]}toString(){return s(...this.modifier)+this.media.toString()}indexOf(e){return e===this.media?"media":-1}at(e){if("media"===e)return this.media}walk(e){return!1!==e({node:this.media,parent:this},"media")&&this.media.walk(e)}toJSON(){return{type:this.type,modifier:this.modifier,media:this.media.toJSON()}}isMediaNot(){return MediaNot.isMediaNot(this)}static isMediaNot(e){return!!e&&(e instanceof MediaNot&&e.type===l.MediaNot)}}class MediaOr{type=l.MediaOr;modifier;media;constructor(e,t){this.modifier=e,this.media=t}tokens(){return[...this.modifier,...this.media.tokens()]}toString(){return s(...this.modifier)+this.media.toString()}indexOf(e){return e===this.media?"media":-1}at(e){if("media"===e)return this.media}walk(e){return!1!==e({node:this.media,parent:this},"media")&&this.media.walk(e)}toJSON(){return{type:this.type,modifier:this.modifier,media:this.media.toJSON()}}isMediaOr(){return MediaOr.isMediaOr(this)}static isMediaOr(e){return!!e&&(e instanceof MediaOr&&e.type===l.MediaOr)}}var M,p;function modifierFromToken(e){if(e[0]!==o.Ident)return!1;switch(e[4].value.toLowerCase()){case M.Not:return M.Not;case M.Only:return M.Only;default:return!1}}function parseMediaQuery(e){{const t=parseMediaCondition(e);if(!1!==t)return new MediaQueryWithoutType(t)}{let t=-1,r=-1,s=-1;for(let u=0;ue.tokens())),-1!==r&&(d=e.slice(t+1,r+1).flatMap((e=>e.tokens())))):-1!==r&&(d=e.slice(0,r+1).flatMap((e=>e.tokens())));const l=parseMediaConditionWithoutOr(e.slice(Math.max(t,r,s)+1));return!1===l?new MediaQueryWithType(u,[...d,...e.slice(r+1).flatMap((e=>e.tokens()))]):new MediaQueryWithType(u,d,e.slice(r+1,s+1).flatMap((e=>e.tokens())),l)}}function parseMediaConditionListWithOr(t){let i=!1;const a=[];let n=-1,r=-1;for(let s=0;se.tokens())),t.slice(r+1).flatMap((e=>e.tokens())))}function parseMediaConditionListWithAnd(t){let i=!1;const a=[];let n=-1,r=-1;for(let s=0;se.tokens())),t.slice(r+1).flatMap((e=>e.tokens())))}function parseMediaCondition(e){const t=parseMediaNot(e);if(!1!==t)return new MediaCondition(t);const i=parseMediaConditionListWithAnd(e);if(!1!==i)return new MediaCondition(i);const a=parseMediaConditionListWithOr(e);if(!1!==a)return new MediaCondition(a);const n=parseMediaInParens(e);return!1!==n&&new MediaCondition(n)}function parseMediaConditionWithoutOr(e){const t=parseMediaNot(e);if(!1!==t)return new MediaCondition(t);const i=parseMediaConditionListWithAnd(e);if(!1!==i)return new MediaCondition(i);const a=parseMediaInParens(e);return!1!==a&&new MediaCondition(a)}function parseMediaInParens(t){let i=-1;for(let a=0;ae.tokens())),a.startToken],r=[a.endToken,...t.slice(i+1).flatMap((e=>e.tokens()))],s=parseMediaFeature(a,n,r);if(!1!==s)return new MediaInParens(s);const u=parseMediaCondition(a.value);return!1!==u?new MediaInParens(u,n,r):new MediaInParens(new GeneralEnclosed(a),t.slice(0,i).flatMap((e=>e.tokens())),t.slice(i+1).flatMap((e=>e.tokens())))}function parseMediaInParensFromSimpleBlock(e){if(e.startToken[0]!==o.OpenParen)return!1;const t=parseMediaFeature(e,[e.startToken],[e.endToken]);if(!1!==t)return new MediaInParens(t);const i=parseMediaCondition(e.value);return!1!==i?new MediaInParens(i,[e.startToken],[e.endToken]):new MediaInParens(new GeneralEnclosed(e))}function parseMediaNot(t){let i=!1,a=null;for(let n=0;ne.tokens())),e)}}}return a||!1}function parseMediaOr(t){let i=!1;for(let a=0;ae.tokens())),e)}}return!1}}return!1}function parseMediaAnd(t){let i=!1;for(let a=0;ae.tokens())),e)}}return!1}}return!1}function parseFromTokens(e,t){const i=r(e,{onParseError:null==t?void 0:t.onParseError});return i.map(((e,a)=>{const n=parseMediaQuery(e);return 0==n&&!0===(null==t?void 0:t.preserveInvalidMediaQueries)?new MediaQueryInvalid(i[a]):n})).filter((e=>!!e))}function parse(e,t){const i=u({css:e},{commentsAreTokens:!0,onParseError:null==t?void 0:t.onParseError}),a=[];for(;!i.endOfFile();)a.push(i.nextToken());return a.push(i.nextToken()),parseFromTokens(a,t)}function isGeneralEnclosed(e){return GeneralEnclosed.isGeneralEnclosed(e)}function isMediaAnd(e){return MediaAnd.isMediaAnd(e)}function isMediaConditionList(e){return isMediaConditionListWithAnd(e)||isMediaConditionListWithOr(e)}function isMediaConditionListWithAnd(e){return MediaConditionListWithAnd.isMediaConditionListWithAnd(e)}function isMediaConditionListWithOr(e){return MediaConditionListWithOr.isMediaConditionListWithOr(e)}function isMediaCondition(e){return MediaCondition.isMediaCondition(e)}function isMediaFeatureBoolean(e){return MediaFeatureBoolean.isMediaFeatureBoolean(e)}function isMediaFeatureName(e){return MediaFeatureName.isMediaFeatureName(e)}function isMediaFeatureValue(e){return MediaFeatureValue.isMediaFeatureValue(e)}function isMediaFeaturePlain(e){return MediaFeaturePlain.isMediaFeaturePlain(e)}function isMediaFeatureRange(e){return isMediaFeatureRangeNameValue(e)||isMediaFeatureRangeValueName(e)||isMediaFeatureRangeValueNameValue(e)}function isMediaFeatureRangeNameValue(e){return MediaFeatureRangeNameValue.isMediaFeatureRangeNameValue(e)}function isMediaFeatureRangeValueName(e){return MediaFeatureRangeValueName.isMediaFeatureRangeValueName(e)}function isMediaFeatureRangeValueNameValue(e){return MediaFeatureRangeValueNameValue.isMediaFeatureRangeValueNameValue(e)}function isMediaFeature(e){return MediaFeature.isMediaFeature(e)}function isMediaInParens(e){return MediaInParens.isMediaInParens(e)}function isMediaNot(e){return MediaNot.isMediaNot(e)}function isMediaOr(e){return MediaOr.isMediaOr(e)}function isMediaQuery(e){return isMediaQueryWithType(e)||isMediaQueryWithoutType(e)||isMediaQueryInvalid(e)}function isMediaQueryWithType(e){return MediaQueryWithType.isMediaQueryWithType(e)}function isMediaQueryWithoutType(e){return MediaQueryWithoutType.isMediaQueryWithoutType(e)}function isMediaQueryInvalid(e){return MediaQueryInvalid.isMediaQueryInvalid(e)}function typeFromToken(e){if(e[0]!==o.Ident)return!1;switch(e[4].value.toLowerCase()){case p.All:return p.All;case p.Print:return p.Print;case p.Screen:return p.Screen;case p.Tty:return p.Tty;case p.Tv:return p.Tv;case p.Projection:return p.Projection;case p.Handheld:return p.Handheld;case p.Braille:return p.Braille;case p.Embossed:return p.Embossed;case p.Aural:return p.Aural;case p.Speech:return p.Speech;default:return!1}}function cloneMediaQuery(e){const t=d(e.tokens()),i=parseFromTokens(t,{preserveInvalidMediaQueries:!0})[0];if(!i)throw new Error(`Failed to clone media query for : "${s(...t)}"`);if(isMediaQueryInvalid(e)&&isMediaQueryInvalid(i))return i;if(isMediaQueryWithType(e)&&isMediaQueryWithType(i))return i;if(isMediaQueryWithoutType(e)&&isMediaQueryWithoutType(i))return i;throw new Error(`Failed to clone media query for : "${s(...t)}"`)}!function(e){e.Not="not",e.Only="only"}(M||(M={})),function(e){e.All="all",e.Print="print",e.Screen="screen",e.Tty="tty",e.Tv="tv",e.Projection="projection",e.Handheld="handheld",e.Braille="braille",e.Embossed="embossed",e.Aural="aural",e.Speech="speech"}(p||(p={}));export{GeneralEnclosed,MediaAnd,MediaCondition,MediaConditionListWithAnd,MediaConditionListWithOr,MediaFeature,MediaFeatureBoolean,f as MediaFeatureEQ,m as MediaFeatureGT,h as MediaFeatureLT,MediaFeatureName,MediaFeaturePlain,MediaFeatureRangeNameValue,MediaFeatureRangeValueName,MediaFeatureRangeValueNameValue,MediaFeatureValue,MediaInParens,MediaNot,MediaOr,MediaQueryInvalid,M as MediaQueryModifier,MediaQueryWithType,MediaQueryWithoutType,p as MediaType,l as NodeType,cloneMediaQuery,comparisonFromTokens,invertComparison,isGeneralEnclosed,isMediaAnd,isMediaCondition,isMediaConditionList,isMediaConditionListWithAnd,isMediaConditionListWithOr,isMediaFeature,isMediaFeatureBoolean,isMediaFeatureName,isMediaFeaturePlain,isMediaFeatureRange,isMediaFeatureRangeNameValue,isMediaFeatureRangeValueName,isMediaFeatureRangeValueNameValue,isMediaFeatureValue,isMediaInParens,isMediaNot,isMediaOr,isMediaQuery,isMediaQueryInvalid,isMediaQueryWithType,isMediaQueryWithoutType,matchesComparison,matchesRatio,matchesRatioExactly,modifierFromToken,newMediaFeatureBoolean,newMediaFeaturePlain,parse,parseFromTokens,typeFromToken}; +import{ComponentValueType as e,TokenNode as t,parseListOfComponentValues as i,isWhitespaceNode as a,isCommentNode as n,isTokenNode as r,isSimpleBlockNode as s,parseCommaSeparatedListOfComponentValues as o}from"@csstools/css-parser-algorithms";import{stringify as u,TokenType as d,mutateIdent as l,tokenizer as h,cloneTokens as m}from"@csstools/css-tokenizer";var f,c,M,p;!function(e){e.CustomMedia="custom-media",e.GeneralEnclosed="general-enclosed",e.MediaAnd="media-and",e.MediaCondition="media-condition",e.MediaConditionListWithAnd="media-condition-list-and",e.MediaConditionListWithOr="media-condition-list-or",e.MediaFeature="media-feature",e.MediaFeatureBoolean="mf-boolean",e.MediaFeatureName="mf-name",e.MediaFeaturePlain="mf-plain",e.MediaFeatureRangeNameValue="mf-range-name-value",e.MediaFeatureRangeValueName="mf-range-value-name",e.MediaFeatureRangeValueNameValue="mf-range-value-name-value",e.MediaFeatureValue="mf-value",e.MediaInParens="media-in-parens",e.MediaNot="media-not",e.MediaOr="media-or",e.MediaQueryWithType="media-query-with-type",e.MediaQueryWithoutType="media-query-without-type",e.MediaQueryInvalid="media-query-invalid"}(f||(f={}));class MediaCondition{type=f.MediaCondition;media;constructor(e){this.media=e}tokens(){return this.media.tokens()}toString(){return this.media.toString()}indexOf(e){return e===this.media?"media":-1}at(e){if("media"===e)return this.media}walk(e){return!1!==e({node:this.media,parent:this},"media")&&this.media.walk(e)}toJSON(){return{type:this.type,media:this.media.toJSON()}}isMediaCondition(){return MediaCondition.isMediaCondition(this)}static isMediaCondition(e){return!!e&&(e instanceof MediaCondition&&e.type===f.MediaCondition)}}class MediaInParens{type=f.MediaInParens;media;before;after;constructor(e,t=[],i=[]){this.media=e,this.before=t,this.after=i}tokens(){return[...this.before,...this.media.tokens(),...this.after]}toString(){return u(...this.before)+this.media.toString()+u(...this.after)}indexOf(e){return e===this.media?"media":-1}at(e){if("media"===e)return this.media}walk(e){return!1!==e({node:this.media,parent:this},"media")&&("walk"in this.media?this.media.walk(e):void 0)}toJSON(){return{type:this.type,media:this.media.toJSON(),before:this.before,after:this.after}}isMediaInParens(){return MediaInParens.isMediaInParens(this)}static isMediaInParens(e){return!!e&&(e instanceof MediaInParens&&e.type===f.MediaInParens)}}class MediaQueryWithType{type=f.MediaQueryWithType;modifier;mediaType;and;media=null;constructor(e,t,i,a){this.modifier=e,this.mediaType=t,i&&a&&(this.and=i,this.media=a)}getModifier(){if(!this.modifier.length)return"";for(let e=0;ee.tokens()))}toString(){return this.media.map((e=>e.toString())).join("")}walk(e){let t=!1;if(this.media.forEach(((i,a)=>{t||(!1!==e({node:i,parent:this},a)?"walk"in i&&!1===i.walk(e)&&(t=!0):t=!0)})),t)return!1}toJSON(){return{type:this.type,string:this.toString(),media:this.media}}isMediaQueryInvalid(){return MediaQueryInvalid.isMediaQueryInvalid(this)}static isMediaQueryInvalid(e){return!!e&&(e instanceof MediaQueryInvalid&&e.type===f.MediaQueryInvalid)}}class GeneralEnclosed{type=f.GeneralEnclosed;value;constructor(e){this.value=e}tokens(){return this.value.tokens()}toString(){return this.value.toString()}indexOf(e){return e===this.value?"value":-1}at(e){if("value"===e)return this.value}walk(e){return!1!==e({node:this.value,parent:this},"value")&&("walk"in this.value?this.value.walk(e):void 0)}toJSON(){return{type:this.type,tokens:this.tokens()}}isGeneralEnclosed(){return GeneralEnclosed.isGeneralEnclosed(this)}static isGeneralEnclosed(e){return!!e&&(e instanceof GeneralEnclosed&&e.type===f.GeneralEnclosed)}}class MediaAnd{type=f.MediaAnd;modifier;media;constructor(e,t){this.modifier=e,this.media=t}tokens(){return[...this.modifier,...this.media.tokens()]}toString(){return u(...this.modifier)+this.media.toString()}indexOf(e){return e===this.media?"media":-1}at(e){if("media"===e)return this.media}walk(e){return!1!==e({node:this.media,parent:this},"media")&&this.media.walk(e)}toJSON(){return{type:this.type,modifier:this.modifier,media:this.media.toJSON()}}isMediaAnd(){return MediaAnd.isMediaAnd(this)}static isMediaAnd(e){return!!e&&(e instanceof MediaAnd&&e.type===f.MediaAnd)}}class MediaConditionListWithAnd{type=f.MediaConditionListWithAnd;leading;list;before;after;constructor(e,t,i=[],a=[]){this.leading=e,this.list=t,this.before=i,this.after=a}tokens(){return[...this.before,...this.leading.tokens(),...this.list.flatMap((e=>e.tokens())),...this.after]}toString(){return u(...this.before)+this.leading.toString()+this.list.map((e=>e.toString())).join("")+u(...this.after)}indexOf(e){return e===this.leading?"leading":"media-and"===e.type?this.list.indexOf(e):-1}at(e){return"leading"===e?this.leading:"number"==typeof e?(e<0&&(e=this.list.length+e),this.list[e]):void 0}walk(e){if(!1===e({node:this.leading,parent:this},"leading"))return!1;if("walk"in this.leading&&!1===this.leading.walk(e))return!1;let t=!1;return this.list.forEach(((i,a)=>{t||(!1!==e({node:i,parent:this},a)?"walk"in i&&!1===i.walk(e)&&(t=!0):t=!0)})),!t&&void 0}toJSON(){return{type:this.type,leading:this.leading.toJSON(),list:this.list.map((e=>e.toJSON())),before:this.before,after:this.after}}isMediaConditionListWithAnd(){return MediaConditionListWithAnd.isMediaConditionListWithAnd(this)}static isMediaConditionListWithAnd(e){return!!e&&(e instanceof MediaConditionListWithAnd&&e.type===f.MediaConditionListWithAnd)}}class MediaConditionListWithOr{type=f.MediaConditionListWithOr;leading;list;before;after;constructor(e,t,i=[],a=[]){this.leading=e,this.list=t,this.before=i,this.after=a}tokens(){return[...this.before,...this.leading.tokens(),...this.list.flatMap((e=>e.tokens())),...this.after]}toString(){return u(...this.before)+this.leading.toString()+this.list.map((e=>e.toString())).join("")+u(...this.after)}indexOf(e){return e===this.leading?"leading":"media-or"===e.type?this.list.indexOf(e):-1}at(e){return"leading"===e?this.leading:"number"==typeof e?(e<0&&(e=this.list.length+e),this.list[e]):void 0}walk(e){if(!1===e({node:this.leading,parent:this},"leading"))return!1;if("walk"in this.leading&&!1===this.leading.walk(e))return!1;let t=!1;return this.list.forEach(((i,a)=>{t||(!1!==e({node:i,parent:this},a)?"walk"in i&&!1===i.walk(e)&&(t=!0):t=!0)})),!t&&void 0}toJSON(){return{type:this.type,leading:this.leading.toJSON(),list:this.list.map((e=>e.toJSON())),before:this.before,after:this.after}}isMediaConditionListWithOr(){return MediaConditionListWithOr.isMediaConditionListWithOr(this)}static isMediaConditionListWithOr(e){return!!e&&(e instanceof MediaConditionListWithOr&&e.type===f.MediaConditionListWithOr)}}function isNumber(t){return t.type===e.Token&&t.value[0]===d.Number||t.type===e.Function&&"calc"===t.name[4].value}function isDimension(t){return t.type===e.Token&&t.value[0]===d.Dimension}function isIdent(t){return t.type===e.Token&&t.value[0]===d.Ident}class MediaFeatureName{type=f.MediaFeatureName;name;before;after;constructor(e,t=[],i=[]){this.name=e,this.before=t,this.after=i}getName(){return this.name.value[4].value}getNameToken(){return this.name.value}tokens(){return[...this.before,...this.name.tokens(),...this.after]}toString(){return u(...this.before)+this.name.toString()+u(...this.after)}indexOf(e){return e===this.name?"name":-1}at(e){if("name"===e)return this.name}toJSON(){return{type:this.type,name:this.getName(),tokens:this.tokens()}}isMediaFeatureName(){return MediaFeatureName.isMediaFeatureName(this)}static isMediaFeatureName(e){return!!e&&(e instanceof MediaFeatureName&&e.type===f.MediaFeatureName)}}function parseMediaFeatureName(t){let i=-1;for(let a=0;ae.tokens())),t.slice(i+1).flatMap((e=>e.tokens())))}class MediaFeatureBoolean{type=f.MediaFeatureBoolean;name;constructor(e){this.name=e}getName(){return this.name.getName()}getNameToken(){return this.name.getNameToken()}tokens(){return this.name.tokens()}toString(){return this.name.toString()}indexOf(e){return e===this.name?"name":-1}at(e){if("name"===e)return this.name}toJSON(){return{type:this.type,name:this.name.toJSON(),tokens:this.tokens()}}isMediaFeatureBoolean(){return MediaFeatureBoolean.isMediaFeatureBoolean(this)}static isMediaFeatureBoolean(e){return!!e&&(e instanceof MediaFeatureBoolean&&e.type===f.MediaFeatureBoolean)}}function parseMediaFeatureBoolean(e){const t=parseMediaFeatureName(e);return!1===t?t:new MediaFeatureBoolean(t)}class MediaFeatureValue{type=f.MediaFeatureValue;value;before;after;constructor(e,t=[],i=[]){Array.isArray(e)&&1===e.length?this.value=e[0]:this.value=e,this.before=t,this.after=i}tokens(){return Array.isArray(this.value)?[...this.before,...this.value.flatMap((e=>e.tokens())),...this.after]:[...this.before,...this.value.tokens(),...this.after]}toString(){return Array.isArray(this.value)?u(...this.before)+this.value.map((e=>e.toString())).join("")+u(...this.after):u(...this.before)+this.value.toString()+u(...this.after)}indexOf(e){return e===this.value?"value":-1}at(e){if("value"===e)return this.value}walk(e){return!1!==e({node:this.value,parent:this},"value")&&("walk"in this.value?this.value.walk(e):void 0)}toJSON(){return Array.isArray(this.value)?{type:this.type,value:this.value.map((e=>e.toJSON())),tokens:this.tokens()}:{type:this.type,value:this.value.toJSON(),tokens:this.tokens()}}isMediaFeatureValue(){return MediaFeatureValue.isMediaFeatureValue(this)}static isMediaFeatureValue(e){return!!e&&(e instanceof MediaFeatureValue&&e.type===f.MediaFeatureValue)}}function parseMediaFeatureValue(t){let i=-1,a=-1;for(let n=0;ne.tokens())),t.slice(a+1).flatMap((e=>e.tokens())))}function matchesRatioExactly(e){let t=-1,i=-1;const a=matchesRatio(e);if(-1===a)return-1;t=a[0],i=a[1];for(let t=i+1;t2)return!1;if(e[0][0]!==d.Delim)return!1;if(1===e.length)switch(e[0][4].value){case p.EQ:return p.EQ;case c.LT:return c.LT;case M.GT:return M.GT;default:return!1}if(e[1][0]!==d.Delim)return!1;if(e[1][4].value!==p.EQ)return!1;switch(e[0][4].value){case c.LT:return c.LT_OR_EQ;case M.GT:return M.GT_OR_EQ;default:return!1}}function invertComparison(e){switch(e){case p.EQ:return p.EQ;case c.LT:return M.GT;case c.LT_OR_EQ:return M.GT_OR_EQ;case M.GT:return c.LT;case M.GT_OR_EQ:return c.LT_OR_EQ;default:return!1}}!function(e){e.LT="<",e.LT_OR_EQ="<="}(c||(c={})),function(e){e.GT=">",e.GT_OR_EQ=">="}(M||(M={})),function(e){e.EQ="="}(p||(p={}));class MediaFeatureRangeNameValue{type=f.MediaFeatureRangeNameValue;name;operator;value;constructor(e,t,i){this.name=e,this.operator=t,this.value=i}operatorKind(){return comparisonFromTokens(this.operator)}getName(){return this.name.getName()}getNameToken(){return this.name.getNameToken()}tokens(){return[...this.name.tokens(),...this.operator,...this.value.tokens()]}toString(){return this.name.toString()+u(...this.operator)+this.value.toString()}indexOf(e){return e===this.name?"name":e===this.value?"value":-1}at(e){return"name"===e?this.name:"value"===e?this.value:void 0}walk(e){return!1!==e({node:this.value,parent:this},"value")&&("walk"in this.value?this.value.walk(e):void 0)}toJSON(){return{type:this.type,name:this.name.toJSON(),value:this.value.toJSON(),tokens:this.tokens()}}isMediaFeatureRangeNameValue(){return MediaFeatureRangeNameValue.isMediaFeatureRangeNameValue(this)}static isMediaFeatureRangeNameValue(e){return!!e&&(e instanceof MediaFeatureRangeNameValue&&e.type===f.MediaFeatureRangeNameValue)}}class MediaFeatureRangeValueName{type=f.MediaFeatureRangeValueName;name;operator;value;constructor(e,t,i){this.name=e,this.operator=t,this.value=i}operatorKind(){return comparisonFromTokens(this.operator)}getName(){return this.name.getName()}getNameToken(){return this.name.getNameToken()}tokens(){return[...this.value.tokens(),...this.operator,...this.name.tokens()]}toString(){return this.value.toString()+u(...this.operator)+this.name.toString()}indexOf(e){return e===this.name?"name":e===this.value?"value":-1}at(e){return"name"===e?this.name:"value"===e?this.value:void 0}walk(e){return!1!==e({node:this.value,parent:this},"value")&&("walk"in this.value?this.value.walk(e):void 0)}toJSON(){return{type:this.type,name:this.name.toJSON(),value:this.value.toJSON(),tokens:this.tokens()}}isMediaFeatureRangeValueName(){return MediaFeatureRangeValueName.isMediaFeatureRangeValueName(this)}static isMediaFeatureRangeValueName(e){return!!e&&(e instanceof MediaFeatureRangeValueName&&e.type===f.MediaFeatureRangeValueName)}}class MediaFeatureRangeValueNameValue{type=f.MediaFeatureRangeValueNameValue;name;valueOne;valueOneOperator;valueTwo;valueTwoOperator;constructor(e,t,i,a,n){this.name=e,this.valueOne=t,this.valueOneOperator=i,this.valueTwo=a,this.valueTwoOperator=n}valueOneOperatorKind(){return comparisonFromTokens(this.valueOneOperator)}valueTwoOperatorKind(){return comparisonFromTokens(this.valueTwoOperator)}getName(){return this.name.getName()}getNameToken(){return this.name.getNameToken()}tokens(){return[...this.valueOne.tokens(),...this.valueOneOperator,...this.name.tokens(),...this.valueTwoOperator,...this.valueTwo.tokens()]}toString(){return this.valueOne.toString()+u(...this.valueOneOperator)+this.name.toString()+u(...this.valueTwoOperator)+this.valueTwo.toString()}indexOf(e){return e===this.name?"name":e===this.valueOne?"valueOne":e===this.valueTwo?"valueTwo":-1}at(e){return"name"===e?this.name:"valueOne"===e?this.valueOne:"valueTwo"===e?this.valueTwo:void 0}walk(e){return!1!==e({node:this.valueOne,parent:this},"valueOne")&&((!("walk"in this.valueOne)||!1!==this.valueOne.walk(e))&&(!1!==e({node:this.valueTwo,parent:this},"valueTwo")&&((!("walk"in this.valueTwo)||!1!==this.valueTwo.walk(e))&&void 0)))}toJSON(){return{type:this.type,name:this.name.toJSON(),valueOne:this.valueOne.toJSON(),valueTwo:this.valueTwo.toJSON(),tokens:this.tokens()}}isMediaFeatureRangeValueNameValue(){return MediaFeatureRangeValueNameValue.isMediaFeatureRangeValueNameValue(this)}static isMediaFeatureRangeValueNameValue(e){return!!e&&(e instanceof MediaFeatureRangeValueNameValue&&e.type===f.MediaFeatureRangeValueNameValue)}}function parseMediaFeatureRange(t){let i=!1,a=!1;for(let n=0;ne.tokens())),-1!==i&&(u=e.slice(t+1,i+1).flatMap((e=>e.tokens())))):-1!==i&&(u=e.slice(0,i+1).flatMap((e=>e.tokens())));const l=parseMediaConditionWithoutOr(e.slice(Math.max(t,i,s)+1));return!1===l?new MediaQueryWithType(o,[...u,...e.slice(i+1).flatMap((e=>e.tokens()))]):new MediaQueryWithType(o,u,e.slice(i+1,s+1).flatMap((e=>e.tokens())),l)}}function parseMediaConditionListWithOr(t){let i=!1;const a=[];let n=-1,r=-1;for(let o=0;oe.tokens())),t.slice(r+1).flatMap((e=>e.tokens())))}function parseMediaConditionListWithAnd(t){let i=!1;const a=[];let n=-1,r=-1;for(let o=0;oe.tokens())),t.slice(r+1).flatMap((e=>e.tokens())))}function parseMediaCondition(e){const t=parseMediaNot(e);if(!1!==t)return new MediaCondition(t);const i=parseMediaConditionListWithAnd(e);if(!1!==i)return new MediaCondition(i);const a=parseMediaConditionListWithOr(e);if(!1!==a)return new MediaCondition(a);const n=parseMediaInParens(e);return!1!==n&&new MediaCondition(n)}function parseMediaConditionWithoutOr(e){const t=parseMediaNot(e);if(!1!==t)return new MediaCondition(t);const i=parseMediaConditionListWithAnd(e);if(!1!==i)return new MediaCondition(i);const a=parseMediaInParens(e);return!1!==a&&new MediaCondition(a)}function parseMediaInParens(t){let i=-1;for(let a=0;ae.tokens())),a.startToken],r=[a.endToken,...t.slice(i+1).flatMap((e=>e.tokens()))],o=parseMediaFeature(a,n,r);if(!1!==o)return new MediaInParens(o);const u=parseMediaCondition(a.value);return!1!==u?new MediaInParens(u,n,r):new MediaInParens(new GeneralEnclosed(a),t.slice(0,i).flatMap((e=>e.tokens())),t.slice(i+1).flatMap((e=>e.tokens())))}function parseMediaInParensFromSimpleBlock(e){if(e.startToken[0]!==d.OpenParen)return!1;const t=parseMediaFeature(e,[e.startToken],[e.endToken]);if(!1!==t)return new MediaInParens(t);const i=parseMediaCondition(e.value);return!1!==i?new MediaInParens(i,[e.startToken],[e.endToken]):new MediaInParens(new GeneralEnclosed(e))}function parseMediaNot(t){let i=!1,a=null;for(let n=0;ne.tokens())),e)}}}return a||!1}function parseMediaOr(t){let i=!1;for(let a=0;ae.tokens())),e)}}return!1}}return!1}function parseMediaAnd(t){let i=!1;for(let a=0;ae.tokens())),e)}}return!1}}return!1}function parseFromTokens(e,t){const i=o(e,{onParseError:null==t?void 0:t.onParseError});return i.map(((e,a)=>{const n=parseMediaQuery(e);return 0==n&&!0===(null==t?void 0:t.preserveInvalidMediaQueries)?new MediaQueryInvalid(i[a]):n})).filter((e=>!!e))}function parse(e,t){const i=h({css:e},{commentsAreTokens:!0,onParseError:null==t?void 0:t.onParseError}),a=[];for(;!i.endOfFile();)a.push(i.nextToken());return a.push(i.nextToken()),parseFromTokens(a,t)}!function(e){e.Not="not",e.Only="only"}(v||(v={}));class CustomMedia{type=f.CustomMedia;name;mediaQueryList=null;trueOrFalseKeyword=null;constructor(e,t,i){this.name=e,this.mediaQueryList=t,this.trueOrFalseKeyword=i}getName(){for(let e=0;ee.toJSON()))}}isCustomMedia(){return CustomMedia.isCustomMedia(this)}static isCustomMedia(e){return!!e&&(e instanceof CustomMedia&&e.type===f.CustomMedia)}}function parseCustomMediaFromTokens(e,t){let i=[],a=e;for(let t=0;t; + mediaQueryList: Array | null; + trueOrFalseKeyword: Array | null; + constructor(name: Array, mediaQueryList: Array | null, trueOrFalseKeyword?: Array); + getName(): string; + getNameToken(): CSSToken | null; + hasMediaQueryList(): boolean; + hasTrueKeyword(): boolean; + hasFalseKeyword(): boolean; + tokens(): Array; + toString(): string; + toJSON(): { + type: NodeType; + string: string; + nameValue: string; + name: CSSToken[]; + hasFalseKeyword: boolean; + hasTrueKeyword: boolean; + trueOrFalseKeyword: CSSToken[]; + mediaQueryList: ({ + type: NodeType; + string: string; + media: import("./media-condition").MediaCondition; + } | { + type: NodeType; + string: string; + media: import("@csstools/css-parser-algorithms").ComponentValue[]; + })[]; + }; + isCustomMedia(): this is CustomMedia; + static isCustomMedia(x: unknown): x is CustomMedia; +} diff --git a/packages/media-query-list-parser/dist/nodes/general-enclosed.d.ts b/packages/media-query-list-parser/dist/nodes/general-enclosed.d.ts index be8abeb56..554e4d8e9 100644 --- a/packages/media-query-list-parser/dist/nodes/general-enclosed.d.ts +++ b/packages/media-query-list-parser/dist/nodes/general-enclosed.d.ts @@ -8,7 +8,7 @@ export declare class GeneralEnclosed { tokens(): Array; toString(): string; indexOf(item: ComponentValue): number | string; - at(index: number | string): ComponentValue; + at(index: number | string): ComponentValue | undefined; walk(cb: (entry: { node: GeneralEnclosedWalkerEntry; parent: GeneralEnclosedWalkerParent; diff --git a/packages/media-query-list-parser/dist/nodes/media-and.d.ts b/packages/media-query-list-parser/dist/nodes/media-and.d.ts index 8e06f2988..39c69dd46 100644 --- a/packages/media-query-list-parser/dist/nodes/media-and.d.ts +++ b/packages/media-query-list-parser/dist/nodes/media-and.d.ts @@ -9,7 +9,7 @@ export declare class MediaAnd { tokens(): Array; toString(): string; indexOf(item: MediaInParens): number | string; - at(index: number | string): MediaInParens; + at(index: number | string): MediaInParens | null; walk(cb: (entry: { node: MediaAndWalkerEntry; parent: MediaAndWalkerParent; diff --git a/packages/media-query-list-parser/dist/nodes/media-condition-list.d.ts b/packages/media-query-list-parser/dist/nodes/media-condition-list.d.ts index b1a454bd8..c66e96832 100644 --- a/packages/media-query-list-parser/dist/nodes/media-condition-list.d.ts +++ b/packages/media-query-list-parser/dist/nodes/media-condition-list.d.ts @@ -14,7 +14,7 @@ export declare class MediaConditionListWithAnd { tokens(): Array; toString(): string; indexOf(item: MediaInParens | MediaAnd): number | string; - at(index: number | string): MediaInParens | MediaAnd; + at(index: number | string): MediaInParens | MediaAnd | undefined; walk(cb: (entry: { node: MediaConditionListWithAndWalkerEntry; parent: MediaConditionListWithAndWalkerParent; @@ -35,7 +35,7 @@ export declare class MediaConditionListWithOr { tokens(): Array; toString(): string; indexOf(item: MediaInParens | MediaOr): number | string; - at(index: number | string): MediaInParens | MediaOr; + at(index: number | string): MediaInParens | MediaOr | undefined; walk(cb: (entry: { node: MediaConditionListWithOrWalkerEntry; parent: MediaConditionListWithOrWalkerParent; diff --git a/packages/media-query-list-parser/dist/nodes/media-condition.d.ts b/packages/media-query-list-parser/dist/nodes/media-condition.d.ts index 9fcbd50b1..b61d0d176 100644 --- a/packages/media-query-list-parser/dist/nodes/media-condition.d.ts +++ b/packages/media-query-list-parser/dist/nodes/media-condition.d.ts @@ -10,7 +10,7 @@ export declare class MediaCondition { tokens(): Array; toString(): string; indexOf(item: MediaNot | MediaInParens | MediaConditionListWithAnd | MediaConditionListWithOr): number | string; - at(index: number | string): MediaInParens | MediaConditionListWithAnd | MediaConditionListWithOr | MediaNot; + at(index: number | string): MediaNot | MediaInParens | MediaConditionListWithAnd | MediaConditionListWithOr | undefined; walk(cb: (entry: { node: MediaConditionWalkerEntry; parent: MediaConditionWalkerParent; diff --git a/packages/media-query-list-parser/dist/nodes/media-feature-boolean.d.ts b/packages/media-query-list-parser/dist/nodes/media-feature-boolean.d.ts index a95933352..a5c64ec1f 100644 --- a/packages/media-query-list-parser/dist/nodes/media-feature-boolean.d.ts +++ b/packages/media-query-list-parser/dist/nodes/media-feature-boolean.d.ts @@ -1,20 +1,24 @@ import { ComponentValue } from '@csstools/css-parser-algorithms'; +import { MediaFeatureName } from './media-feature-name'; import { NodeType } from '../util/node-type'; import { CSSToken } from '@csstools/css-tokenizer'; export declare class MediaFeatureBoolean { type: NodeType; - name: ComponentValue; - before: Array; - after: Array; - constructor(name: ComponentValue, before?: Array, after?: Array); + name: MediaFeatureName; + constructor(name: MediaFeatureName); getName(): string; + getNameToken(): CSSToken; tokens(): Array; toString(): string; - indexOf(item: ComponentValue): number | string; - at(index: number | string): ComponentValue; + indexOf(item: MediaFeatureName): number | string; + at(index: number | string): MediaFeatureName | undefined; toJSON(): { type: NodeType; - name: string; + name: { + type: NodeType; + name: string; + tokens: CSSToken[]; + }; tokens: CSSToken[]; }; isMediaFeatureBoolean(): this is MediaFeatureBoolean; diff --git a/packages/media-query-list-parser/dist/nodes/media-feature-name.d.ts b/packages/media-query-list-parser/dist/nodes/media-feature-name.d.ts index 52a7aa9b8..332b421a1 100644 --- a/packages/media-query-list-parser/dist/nodes/media-feature-name.d.ts +++ b/packages/media-query-list-parser/dist/nodes/media-feature-name.d.ts @@ -8,10 +8,11 @@ export declare class MediaFeatureName { after: Array; constructor(name: ComponentValue, before?: Array, after?: Array); getName(): string; + getNameToken(): CSSToken; tokens(): Array; toString(): string; indexOf(item: ComponentValue): number | string; - at(index: number | string): ComponentValue; + at(index: number | string): ComponentValue | undefined; toJSON(): { type: NodeType; name: string; @@ -20,4 +21,4 @@ export declare class MediaFeatureName { isMediaFeatureName(): this is MediaFeatureName; static isMediaFeatureName(x: unknown): x is MediaFeatureName; } -export declare function parseMediaFeatureName(componentValues: Array): false | MediaFeatureName; +export declare function parseMediaFeatureName(componentValues: Array): MediaFeatureName | false; diff --git a/packages/media-query-list-parser/dist/nodes/media-feature-plain.d.ts b/packages/media-query-list-parser/dist/nodes/media-feature-plain.d.ts index 494e2cdd9..313db9553 100644 --- a/packages/media-query-list-parser/dist/nodes/media-feature-plain.d.ts +++ b/packages/media-query-list-parser/dist/nodes/media-feature-plain.d.ts @@ -9,10 +9,12 @@ export declare class MediaFeaturePlain { colon: TokenColon; value: MediaFeatureValue; constructor(name: MediaFeatureName, colon: TokenColon, value: MediaFeatureValue); + getName(): string; + getNameToken(): CSSToken; tokens(): Array; toString(): string; indexOf(item: MediaFeatureName | MediaFeatureValue): number | string; - at(index: number | string): MediaFeatureName | MediaFeatureValue; + at(index: number | string): MediaFeatureName | MediaFeatureValue | undefined; walk(cb: (entry: { node: MediaFeaturePlainWalkerEntry; parent: MediaFeaturePlainWalkerParent; @@ -36,4 +38,4 @@ export declare class MediaFeaturePlain { } export type MediaFeaturePlainWalkerEntry = MediaFeatureValueWalkerEntry | MediaFeatureValue; export type MediaFeaturePlainWalkerParent = MediaFeatureValueWalkerParent | MediaFeaturePlain; -export declare function parseMediaFeaturePlain(componentValues: Array): false | MediaFeaturePlain; +export declare function parseMediaFeaturePlain(componentValues: Array): MediaFeaturePlain | false; diff --git a/packages/media-query-list-parser/dist/nodes/media-feature-range.d.ts b/packages/media-query-list-parser/dist/nodes/media-feature-range.d.ts index 487304d61..a0f61f0ae 100644 --- a/packages/media-query-list-parser/dist/nodes/media-feature-range.d.ts +++ b/packages/media-query-list-parser/dist/nodes/media-feature-range.d.ts @@ -1,5 +1,6 @@ import { ComponentValue } from '@csstools/css-parser-algorithms'; import { CSSToken, TokenDelim } from '@csstools/css-tokenizer'; +import { MediaFeatureComparison } from './media-feature-comparison'; import { MediaFeatureName } from './media-feature-name'; import { MediaFeatureValue, MediaFeatureValueWalkerEntry, MediaFeatureValueWalkerParent } from './media-feature-value'; import { NodeType } from '../util/node-type'; @@ -10,11 +11,13 @@ export declare class MediaFeatureRangeNameValue { operator: [TokenDelim, TokenDelim] | [TokenDelim]; value: MediaFeatureValue; constructor(name: MediaFeatureName, operator: [TokenDelim, TokenDelim] | [TokenDelim], value: MediaFeatureValue); - operatorKind(): false | import("./media-feature-comparison").MediaFeatureComparison; + operatorKind(): MediaFeatureComparison | false; + getName(): string; + getNameToken(): CSSToken; tokens(): Array; toString(): string; indexOf(item: MediaFeatureName | MediaFeatureValue): number | string; - at(index: number | string): MediaFeatureName | MediaFeatureValue; + at(index: number | string): MediaFeatureName | MediaFeatureValue | undefined; walk(cb: (entry: { node: MediaFeatureRangeWalkerEntry; parent: MediaFeatureRangeWalkerParent; @@ -42,11 +45,13 @@ export declare class MediaFeatureRangeValueName { operator: [TokenDelim, TokenDelim] | [TokenDelim]; value: MediaFeatureValue; constructor(name: MediaFeatureName, operator: [TokenDelim, TokenDelim] | [TokenDelim], value: MediaFeatureValue); - operatorKind(): false | import("./media-feature-comparison").MediaFeatureComparison; + operatorKind(): MediaFeatureComparison | false; + getName(): string; + getNameToken(): CSSToken; tokens(): Array; toString(): string; indexOf(item: MediaFeatureName | MediaFeatureValue): number | string; - at(index: number | string): MediaFeatureName | MediaFeatureValue; + at(index: number | string): MediaFeatureName | MediaFeatureValue | undefined; walk(cb: (entry: { node: MediaFeatureRangeWalkerEntry; parent: MediaFeatureRangeWalkerParent; @@ -76,12 +81,14 @@ export declare class MediaFeatureRangeValueNameValue { valueTwo: MediaFeatureValue; valueTwoOperator: [TokenDelim, TokenDelim] | [TokenDelim]; constructor(name: MediaFeatureName, valueOne: MediaFeatureValue, valueOneOperator: [TokenDelim, TokenDelim] | [TokenDelim], valueTwo: MediaFeatureValue, valueTwoOperator: [TokenDelim, TokenDelim] | [TokenDelim]); - valueOneOperatorKind(): false | import("./media-feature-comparison").MediaFeatureComparison; - valueTwoOperatorKind(): false | import("./media-feature-comparison").MediaFeatureComparison; + valueOneOperatorKind(): MediaFeatureComparison | false; + valueTwoOperatorKind(): MediaFeatureComparison | false; + getName(): string; + getNameToken(): CSSToken; tokens(): Array; toString(): string; indexOf(item: MediaFeatureName | MediaFeatureValue): number | string; - at(index: number | string): MediaFeatureName | MediaFeatureValue; + at(index: number | string): MediaFeatureName | MediaFeatureValue | undefined; walk(cb: (entry: { node: MediaFeatureRangeWalkerEntry; parent: MediaFeatureRangeWalkerParent; @@ -110,5 +117,5 @@ export declare class MediaFeatureRangeValueNameValue { } export type MediaFeatureRangeWalkerEntry = MediaFeatureValueWalkerEntry | MediaFeatureValue; export type MediaFeatureRangeWalkerParent = MediaFeatureValueWalkerParent | MediaFeatureRange; -export declare function parseMediaFeatureRange(componentValues: Array): false | MediaFeatureRangeNameValue | MediaFeatureRangeValueName | MediaFeatureRangeValueNameValue; +export declare function parseMediaFeatureRange(componentValues: Array): MediaFeatureRange | false; export declare const mediaDescriptors: Set; diff --git a/packages/media-query-list-parser/dist/nodes/media-feature-value.d.ts b/packages/media-query-list-parser/dist/nodes/media-feature-value.d.ts index 22af30ea6..47d4ed14f 100644 --- a/packages/media-query-list-parser/dist/nodes/media-feature-value.d.ts +++ b/packages/media-query-list-parser/dist/nodes/media-feature-value.d.ts @@ -10,7 +10,7 @@ export declare class MediaFeatureValue { tokens(): Array; toString(): string; indexOf(item: ComponentValue): number | string; - at(index: number | string): ComponentValue | ComponentValue[]; + at(index: number | string): ComponentValue | Array | undefined; walk(cb: (entry: { node: MediaFeatureValueWalkerEntry; parent: MediaFeatureValueWalkerParent; @@ -25,6 +25,6 @@ export declare class MediaFeatureValue { } export type MediaFeatureValueWalkerEntry = ComponentValue | Array; export type MediaFeatureValueWalkerParent = ContainerNode | MediaFeatureValue; -export declare function parseMediaFeatureValue(componentValues: Array): false | MediaFeatureValue; +export declare function parseMediaFeatureValue(componentValues: Array): MediaFeatureValue | false; export declare function matchesRatioExactly(componentValues: Array): -1 | number[]; export declare function matchesRatio(componentValues: Array): -1 | number[]; diff --git a/packages/media-query-list-parser/dist/nodes/media-feature.d.ts b/packages/media-query-list-parser/dist/nodes/media-feature.d.ts index 3f7474784..54ecf9639 100644 --- a/packages/media-query-list-parser/dist/nodes/media-feature.d.ts +++ b/packages/media-query-list-parser/dist/nodes/media-feature.d.ts @@ -10,10 +10,12 @@ export declare class MediaFeature { before: Array; after: Array; constructor(feature: MediaFeaturePlain | MediaFeatureBoolean | MediaFeatureRange, before?: Array, after?: Array); + getName(): string; + getNameToken(): CSSToken; tokens(): Array; toString(): string; indexOf(item: MediaFeaturePlain | MediaFeatureBoolean | MediaFeatureRange): number | string; - at(index: number | string): MediaFeatureBoolean | MediaFeaturePlain | MediaFeatureRange; + at(index: number | string): MediaFeatureBoolean | MediaFeaturePlain | MediaFeatureRange | undefined; walk(cb: (entry: { node: MediaFeatureWalkerEntry; parent: MediaFeatureWalkerParent; @@ -21,39 +23,12 @@ export declare class MediaFeature { toJSON(): { type: NodeType; feature: { - type: NodeType; - name: string; - tokens: CSSToken[]; - } | { - type: NodeType; - name: { - type: NodeType; - name: string; - tokens: CSSToken[]; - }; - value: { - type: NodeType; - value: any; - tokens: CSSToken[]; - }; - tokens: CSSToken[]; - } | { type: NodeType; name: { type: NodeType; name: string; tokens: CSSToken[]; }; - valueOne: { - type: NodeType; - value: any; - tokens: CSSToken[]; - }; - valueTwo: { - type: NodeType; - value: any; - tokens: CSSToken[]; - }; tokens: CSSToken[]; }; before: CSSToken[]; diff --git a/packages/media-query-list-parser/dist/nodes/media-in-parens.d.ts b/packages/media-query-list-parser/dist/nodes/media-in-parens.d.ts index e8f2e701a..4f4d4bb5a 100644 --- a/packages/media-query-list-parser/dist/nodes/media-in-parens.d.ts +++ b/packages/media-query-list-parser/dist/nodes/media-in-parens.d.ts @@ -20,7 +20,7 @@ export declare class MediaInParens { tokens(): Array; toString(): string; indexOf(item: MediaCondition | MediaFeature | GeneralEnclosed): number | string; - at(index: number | string): GeneralEnclosed | MediaFeature | MediaCondition; + at(index: number | string): MediaCondition | MediaFeature | GeneralEnclosed | undefined; walk(cb: (entry: { node: MediaInParensWalkerEntry; parent: MediaInParensWalkerParent; diff --git a/packages/media-query-list-parser/dist/nodes/media-not.d.ts b/packages/media-query-list-parser/dist/nodes/media-not.d.ts index 2476c2c86..f4650ed5c 100644 --- a/packages/media-query-list-parser/dist/nodes/media-not.d.ts +++ b/packages/media-query-list-parser/dist/nodes/media-not.d.ts @@ -9,7 +9,7 @@ export declare class MediaNot { tokens(): Array; toString(): string; indexOf(item: MediaInParens): number | string; - at(index: number | string): MediaInParens; + at(index: number | string): MediaInParens | undefined; walk(cb: (entry: { node: MediaNotWalkerEntry; parent: MediaNotWalkerParent; diff --git a/packages/media-query-list-parser/dist/nodes/media-or.d.ts b/packages/media-query-list-parser/dist/nodes/media-or.d.ts index c9740613e..ca4c75a54 100644 --- a/packages/media-query-list-parser/dist/nodes/media-or.d.ts +++ b/packages/media-query-list-parser/dist/nodes/media-or.d.ts @@ -9,7 +9,7 @@ export declare class MediaOr { tokens(): Array; toString(): string; indexOf(item: MediaInParens): number | string; - at(index: number | string): MediaInParens; + at(index: number | string): MediaInParens | undefined; walk(cb: (entry: { node: MediaOrWalkerEntry; parent: MediaOrWalkerParent; diff --git a/packages/media-query-list-parser/dist/nodes/media-query.d.ts b/packages/media-query-list-parser/dist/nodes/media-query.d.ts index 01044e2cd..c0ba2e9fc 100644 --- a/packages/media-query-list-parser/dist/nodes/media-query.d.ts +++ b/packages/media-query-list-parser/dist/nodes/media-query.d.ts @@ -13,10 +13,10 @@ export declare class MediaQueryWithType { getModifier(): string; negateQuery(): MediaQuery; getMediaType(): string; - tokens(): CSSToken[]; + tokens(): Array; toString(): string; indexOf(item: MediaCondition): number | string; - at(index: number | string): MediaCondition; + at(index: number | string): MediaCondition | undefined; walk(cb: (entry: { node: MediaQueryWithTypeWalkerEntry; parent: MediaQueryWithTypeWalkerParent; @@ -42,7 +42,7 @@ export declare class MediaQueryWithoutType { tokens(): Array; toString(): string; indexOf(item: MediaCondition): number | string; - at(index: number | string): MediaCondition; + at(index: number | string): MediaCondition | undefined; walk(cb: (entry: { node: MediaQueryWithoutTypeWalkerEntry; parent: MediaQueryWithoutTypeWalkerParent; diff --git a/packages/media-query-list-parser/dist/parser/parse-custom-media.d.ts b/packages/media-query-list-parser/dist/parser/parse-custom-media.d.ts new file mode 100644 index 000000000..1be3d0f36 --- /dev/null +++ b/packages/media-query-list-parser/dist/parser/parse-custom-media.d.ts @@ -0,0 +1,8 @@ +import { CSSToken, ParseError } from '@csstools/css-tokenizer'; +import { CustomMedia } from '../nodes/custom-media'; +export type Options = { + preserveInvalidMediaQueries?: boolean; + onParseError?: (error: ParseError) => void; +}; +export declare function parseCustomMediaFromTokens(tokens: Array, options?: Options): CustomMedia | false; +export declare function parseCustomMedia(source: string, options?: Options): CustomMedia | false; diff --git a/packages/media-query-list-parser/dist/parser/parse.d.ts b/packages/media-query-list-parser/dist/parser/parse.d.ts index c4848a6c3..a09339bda 100644 --- a/packages/media-query-list-parser/dist/parser/parse.d.ts +++ b/packages/media-query-list-parser/dist/parser/parse.d.ts @@ -1,9 +1,8 @@ -import { ParserError } from '@csstools/css-parser-algorithms/dist/interfaces/error'; -import { CSSToken } from '@csstools/css-tokenizer'; +import { CSSToken, ParseError } from '@csstools/css-tokenizer'; import { MediaQuery } from '../nodes/media-query'; export type Options = { preserveInvalidMediaQueries?: boolean; - onParseError?: (error: ParserError) => void; + onParseError?: (error: ParseError) => void; }; export declare function parseFromTokens(tokens: Array, options?: Options): MediaQuery[]; export declare function parse(source: string, options?: Options): MediaQuery[]; diff --git a/packages/media-query-list-parser/dist/util/node-type.d.ts b/packages/media-query-list-parser/dist/util/node-type.d.ts index 24da5c0ba..935e6eb54 100644 --- a/packages/media-query-list-parser/dist/util/node-type.d.ts +++ b/packages/media-query-list-parser/dist/util/node-type.d.ts @@ -1,4 +1,5 @@ export declare enum NodeType { + CustomMedia = "custom-media", GeneralEnclosed = "general-enclosed", MediaAnd = "media-and", MediaCondition = "media-condition", diff --git a/packages/media-query-list-parser/dist/util/type-predicates.d.ts b/packages/media-query-list-parser/dist/util/type-predicates.d.ts index c4dc9fc46..2313f29b0 100644 --- a/packages/media-query-list-parser/dist/util/type-predicates.d.ts +++ b/packages/media-query-list-parser/dist/util/type-predicates.d.ts @@ -12,6 +12,7 @@ import { MediaInParens } from '../nodes/media-in-parens'; import { MediaNot } from '../nodes/media-not'; import { MediaOr } from '../nodes/media-or'; import { MediaQuery, MediaQueryInvalid, MediaQueryWithoutType, MediaQueryWithType } from '../nodes/media-query'; +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 isMediaConditionList(x: unknown): x is MediaConditionList; diff --git a/packages/media-query-list-parser/src/index.ts b/packages/media-query-list-parser/src/index.ts index 57680d295..e7233e83c 100644 --- a/packages/media-query-list-parser/src/index.ts +++ b/packages/media-query-list-parser/src/index.ts @@ -1,6 +1,8 @@ export { parse, parseFromTokens } from './parser/parse'; +export { parseCustomMedia, parseCustomMediaFromTokens } from './parser/parse-custom-media'; export { NodeType } from './util/node-type'; export { + isCustomMedia, isGeneralEnclosed, isMediaAnd, isMediaCondition, @@ -25,6 +27,7 @@ export { isMediaQueryWithoutType, } from './util/type-predicates'; +export { CustomMedia } from './nodes/custom-media'; export { GeneralEnclosed } from './nodes/general-enclosed'; export { MediaAnd } from './nodes/media-and'; export { MediaCondition } from './nodes/media-condition'; diff --git a/packages/media-query-list-parser/src/nodes/custom-media.ts b/packages/media-query-list-parser/src/nodes/custom-media.ts new file mode 100644 index 000000000..5275574c1 --- /dev/null +++ b/packages/media-query-list-parser/src/nodes/custom-media.ts @@ -0,0 +1,151 @@ +import { CSSToken, stringify, TokenType } from '@csstools/css-tokenizer'; +import { NodeType } from '../util/node-type'; +import { MediaQuery } from './media-query'; + +export class CustomMedia { + type = NodeType.CustomMedia; + + name: Array; + mediaQueryList: Array | null = null; + trueOrFalseKeyword: Array | null = null; + + constructor(name: Array, mediaQueryList: Array | null, trueOrFalseKeyword?: Array) { + this.name = name; + this.mediaQueryList = mediaQueryList; + this.trueOrFalseKeyword = trueOrFalseKeyword; + } + + getName(): string { + for (let i = 0; i < this.name.length; i++) { + const token = this.name[i]; + if (token[0] === TokenType.Ident) { + return token[4].value; + } + } + + return ''; + } + + getNameToken(): CSSToken | null { + for (let i = 0; i < this.name.length; i++) { + const token = this.name[i]; + if (token[0] === TokenType.Ident) { + return token; + } + } + + return null; + } + + hasMediaQueryList(): boolean { + return !!this.mediaQueryList; + } + + hasTrueKeyword(): boolean { + if (!this.trueOrFalseKeyword) { + return false; + } + + for (let i = 0; i < this.trueOrFalseKeyword.length; i++) { + const token = this.trueOrFalseKeyword[i]; + if (token[0] === TokenType.Comment) { + continue; + } + + if (token[0] === TokenType.Whitespace) { + continue; + } + + if (token[0] === TokenType.Ident) { + return token[4].value.toLowerCase() === 'true'; + } + + return false; + } + + return false; + } + + hasFalseKeyword(): boolean { + if (!this.trueOrFalseKeyword) { + return false; + } + + for (let i = 0; i < this.trueOrFalseKeyword.length; i++) { + const token = this.trueOrFalseKeyword[i]; + if (token[0] === TokenType.Comment) { + continue; + } + + if (token[0] === TokenType.Whitespace) { + continue; + } + + if (token[0] === TokenType.Ident) { + return token[4].value.toLowerCase() === 'false'; + } + + return false; + } + + return false; + } + + tokens(): Array { + if (this.trueOrFalseKeyword) { + return [ + ...this.name, + ...this.trueOrFalseKeyword, + ]; + } + + const tokens: Array = []; + for (let i = 0; i < this.mediaQueryList.length; i++) { + const mediaQuery = this.mediaQueryList[i]; + + if (i !== 0) { + tokens.push([TokenType.Comma, ',', -1, -1, undefined]); + } + + tokens.push(...mediaQuery.tokens()); + } + + return [ + ...this.name, + ...tokens, + ]; + } + + toString(): string { + return stringify(...this.tokens()); + } + + toJSON() { + return { + type: this.type, + string: this.toString(), + nameValue: this.getName(), + name: this.name, + hasFalseKeyword: this.hasFalseKeyword(), + hasTrueKeyword: this.hasTrueKeyword(), + trueOrFalseKeyword: this.trueOrFalseKeyword, + mediaQueryList: this.mediaQueryList?.map((x) => x.toJSON()), + }; + } + + isCustomMedia(): this is CustomMedia { + return CustomMedia.isCustomMedia(this); + } + + static isCustomMedia(x: unknown): x is CustomMedia { + if (!x) { + return false; + } + + if (!(x instanceof CustomMedia)) { + return false; + } + + return x.type === NodeType.CustomMedia; + } +} diff --git a/packages/media-query-list-parser/src/nodes/general-enclosed.ts b/packages/media-query-list-parser/src/nodes/general-enclosed.ts index 73d443cba..b4a3e07b6 100644 --- a/packages/media-query-list-parser/src/nodes/general-enclosed.ts +++ b/packages/media-query-list-parser/src/nodes/general-enclosed.ts @@ -27,7 +27,7 @@ export class GeneralEnclosed { return -1; } - at(index: number | string) { + at(index: number | string): ComponentValue | undefined { if (index === 'value') { return this.value; } diff --git a/packages/media-query-list-parser/src/nodes/media-and.ts b/packages/media-query-list-parser/src/nodes/media-and.ts index 376d79f8b..3cf8fdcf3 100644 --- a/packages/media-query-list-parser/src/nodes/media-and.ts +++ b/packages/media-query-list-parser/src/nodes/media-and.ts @@ -32,7 +32,7 @@ export class MediaAnd { return -1; } - at(index: number | string) { + at(index: number | string): MediaInParens | null { if (index === 'media') { return this.media; } diff --git a/packages/media-query-list-parser/src/nodes/media-condition-list.ts b/packages/media-query-list-parser/src/nodes/media-condition-list.ts index 9ba0e4499..c81cac810 100644 --- a/packages/media-query-list-parser/src/nodes/media-condition-list.ts +++ b/packages/media-query-list-parser/src/nodes/media-condition-list.ts @@ -46,7 +46,7 @@ export class MediaConditionListWithAnd { return -1; } - at(index: number | string) { + at(index: number | string): MediaInParens | MediaAnd | undefined { if (index === 'leading') { return this.leading; } @@ -165,7 +165,7 @@ export class MediaConditionListWithOr { return -1; } - at(index: number | string) { + at(index: number | string): MediaInParens | MediaOr | undefined { if (index === 'leading') { return this.leading; } diff --git a/packages/media-query-list-parser/src/nodes/media-condition.ts b/packages/media-query-list-parser/src/nodes/media-condition.ts index 187c9a70e..68d6d40f8 100644 --- a/packages/media-query-list-parser/src/nodes/media-condition.ts +++ b/packages/media-query-list-parser/src/nodes/media-condition.ts @@ -29,7 +29,7 @@ export class MediaCondition { return -1; } - at(index: number | string) { + at(index: number | string): MediaNot | MediaInParens | MediaConditionListWithAnd | MediaConditionListWithOr | undefined { if (index === 'media') { return this.media; } diff --git a/packages/media-query-list-parser/src/nodes/media-feature-boolean.ts b/packages/media-query-list-parser/src/nodes/media-feature-boolean.ts index f53344500..81a967097 100644 --- a/packages/media-query-list-parser/src/nodes/media-feature-boolean.ts +++ b/packages/media-query-list-parser/src/nodes/media-feature-boolean.ts @@ -1,39 +1,34 @@ -import { ComponentValue, TokenNode } from '@csstools/css-parser-algorithms'; -import { parseMediaFeatureName } from './media-feature-name'; +import { ComponentValue } from '@csstools/css-parser-algorithms'; +import { MediaFeatureName, parseMediaFeatureName } from './media-feature-name'; import { NodeType } from '../util/node-type'; -import { CSSToken, stringify, TokenIdent } from '@csstools/css-tokenizer'; +import { CSSToken } from '@csstools/css-tokenizer'; export class MediaFeatureBoolean { type = NodeType.MediaFeatureBoolean; - name: ComponentValue; - before: Array; - after: Array; + name: MediaFeatureName; - constructor(name: ComponentValue, before: Array = [], after: Array = []) { + constructor(name: MediaFeatureName) { this.name = name; - this.before = before; - this.after = after; } - getName() { - const token = (((this.name as TokenNode).value as CSSToken) as TokenIdent); - return token[4].value; + getName(): string { + return this.name.getName(); + } + + getNameToken(): CSSToken { + return this.name.getNameToken(); } tokens(): Array { - return [ - ...this.before, - ...this.name.tokens(), - ...this.after, - ]; + return this.name.tokens(); } toString(): string { - return stringify(...this.before) + this.name.toString() + stringify(...this.after); + return this.name.toString(); } - indexOf(item: ComponentValue): number | string { + indexOf(item: MediaFeatureName): number | string { if (item === this.name) { return 'name'; } @@ -41,7 +36,7 @@ export class MediaFeatureBoolean { return -1; } - at(index: number | string) { + at(index: number | string): MediaFeatureName | undefined { if (index === 'name') { return this.name; } @@ -50,7 +45,7 @@ export class MediaFeatureBoolean { toJSON() { return { type: this.type, - name: this.getName(), + name: this.name.toJSON(), tokens: this.tokens(), }; } @@ -78,5 +73,5 @@ export function parseMediaFeatureBoolean(componentValues: Array) return mediaFeatureName; } - return new MediaFeatureBoolean(mediaFeatureName.name, mediaFeatureName.before, mediaFeatureName.after); + return new MediaFeatureBoolean(mediaFeatureName); } diff --git a/packages/media-query-list-parser/src/nodes/media-feature-name.ts b/packages/media-query-list-parser/src/nodes/media-feature-name.ts index 85bd0e592..6312732a7 100644 --- a/packages/media-query-list-parser/src/nodes/media-feature-name.ts +++ b/packages/media-query-list-parser/src/nodes/media-feature-name.ts @@ -16,11 +16,16 @@ export class MediaFeatureName { this.after = after; } - getName() { + getName(): string { const token = (((this.name as TokenNode).value as CSSToken) as TokenIdent); return token[4].value; } + getNameToken(): CSSToken { + const token = (((this.name as TokenNode).value as CSSToken) as TokenIdent); + return token; + } + tokens(): Array { return [ ...this.before, @@ -41,7 +46,7 @@ export class MediaFeatureName { return -1; } - at(index: number | string) { + at(index: number | string): ComponentValue | undefined { if (index === 'name') { return this.name; } @@ -72,7 +77,7 @@ export class MediaFeatureName { } } -export function parseMediaFeatureName(componentValues: Array) { +export function parseMediaFeatureName(componentValues: Array): MediaFeatureName | false { let singleIdentTokenIndex = -1; for (let i = 0; i < componentValues.length; i++) { diff --git a/packages/media-query-list-parser/src/nodes/media-feature-plain.ts b/packages/media-query-list-parser/src/nodes/media-feature-plain.ts index 4d1a32d5f..7042dfaf8 100644 --- a/packages/media-query-list-parser/src/nodes/media-feature-plain.ts +++ b/packages/media-query-list-parser/src/nodes/media-feature-plain.ts @@ -17,6 +17,14 @@ export class MediaFeaturePlain { this.value = value; } + getName(): string { + return this.name.getName(); + } + + getNameToken(): CSSToken { + return this.name.getNameToken(); + } + tokens(): Array { return [ ...this.name.tokens(), @@ -41,7 +49,7 @@ export class MediaFeaturePlain { return -1; } - at(index: number | string) { + at(index: number | string): MediaFeatureName | MediaFeatureValue | undefined { if (index === 'name') { return this.name; } @@ -88,7 +96,7 @@ export class MediaFeaturePlain { export type MediaFeaturePlainWalkerEntry = MediaFeatureValueWalkerEntry | MediaFeatureValue; export type MediaFeaturePlainWalkerParent = MediaFeatureValueWalkerParent | MediaFeaturePlain; -export function parseMediaFeaturePlain(componentValues: Array) { +export function parseMediaFeaturePlain(componentValues: Array): MediaFeaturePlain | false { let a: Array = []; let b: Array = []; let colon: TokenColon | null = null; diff --git a/packages/media-query-list-parser/src/nodes/media-feature-range.ts b/packages/media-query-list-parser/src/nodes/media-feature-range.ts index 7f9f5aff4..ac2dedfcf 100644 --- a/packages/media-query-list-parser/src/nodes/media-feature-range.ts +++ b/packages/media-query-list-parser/src/nodes/media-feature-range.ts @@ -1,6 +1,6 @@ import { ComponentValue, ComponentValueType, TokenNode } from '@csstools/css-parser-algorithms'; import { CSSToken, stringify, TokenDelim, TokenType } from '@csstools/css-tokenizer'; -import { comparisonFromTokens, matchesComparison, MediaFeatureEQ, MediaFeatureGT, MediaFeatureLT } from './media-feature-comparison'; +import { comparisonFromTokens, matchesComparison, MediaFeatureComparison, MediaFeatureEQ, MediaFeatureGT, MediaFeatureLT } from './media-feature-comparison'; import { MediaFeatureName, parseMediaFeatureName } from './media-feature-name'; import { MediaFeatureValue, MediaFeatureValueWalkerEntry, MediaFeatureValueWalkerParent, parseMediaFeatureValue } from './media-feature-value'; import { NodeType } from '../util/node-type'; @@ -22,10 +22,18 @@ export class MediaFeatureRangeNameValue { this.value = value; } - operatorKind() { + operatorKind(): MediaFeatureComparison | false { return comparisonFromTokens(this.operator); } + getName(): string { + return this.name.getName(); + } + + getNameToken(): CSSToken { + return this.name.getNameToken(); + } + tokens(): Array { return [ ...this.name.tokens(), @@ -50,7 +58,7 @@ export class MediaFeatureRangeNameValue { return -1; } - at(index: number | string) { + at(index: number | string): MediaFeatureName | MediaFeatureValue | undefined { if (index === 'name') { return this.name; } @@ -109,10 +117,18 @@ export class MediaFeatureRangeValueName { this.value = value; } - operatorKind() { + operatorKind(): MediaFeatureComparison | false { return comparisonFromTokens(this.operator); } + getName(): string { + return this.name.getName(); + } + + getNameToken(): CSSToken { + return this.name.getNameToken(); + } + tokens(): Array { return [ ...this.value.tokens(), @@ -137,7 +153,7 @@ export class MediaFeatureRangeValueName { return -1; } - at(index: number | string) { + at(index: number | string): MediaFeatureName | MediaFeatureValue | undefined { if (index === 'name') { return this.name; } @@ -200,14 +216,22 @@ export class MediaFeatureRangeValueNameValue { this.valueTwoOperator = valueTwoOperator; } - valueOneOperatorKind() { + valueOneOperatorKind(): MediaFeatureComparison | false { return comparisonFromTokens(this.valueOneOperator); } - valueTwoOperatorKind() { + valueTwoOperatorKind(): MediaFeatureComparison | false { return comparisonFromTokens(this.valueTwoOperator); } + getName(): string { + return this.name.getName(); + } + + getNameToken(): CSSToken { + return this.name.getNameToken(); + } + tokens(): Array { return [ ...this.valueOne.tokens(), @@ -238,7 +262,7 @@ export class MediaFeatureRangeValueNameValue { return -1; } - at(index: number | string) { + at(index: number | string): MediaFeatureName | MediaFeatureValue | undefined { if (index === 'name') { return this.name; } @@ -304,7 +328,7 @@ export class MediaFeatureRangeValueNameValue { export type MediaFeatureRangeWalkerEntry = MediaFeatureValueWalkerEntry | MediaFeatureValue; export type MediaFeatureRangeWalkerParent = MediaFeatureValueWalkerParent | MediaFeatureRange; -export function parseMediaFeatureRange(componentValues: Array) { +export function parseMediaFeatureRange(componentValues: Array): MediaFeatureRange | false { let comparisonOne: false | [number, number] = false; let comparisonTwo: false | [number, number] = false; @@ -461,6 +485,7 @@ export function parseMediaFeatureRange(componentValues: Array) { ); } +// https://www.w3.org/TR/mediaqueries-5/#media-descriptor-table export const mediaDescriptors = new Set([ 'any-hover', 'any-pointer', diff --git a/packages/media-query-list-parser/src/nodes/media-feature-value.ts b/packages/media-query-list-parser/src/nodes/media-feature-value.ts index 642a7487d..7ff15fbe7 100644 --- a/packages/media-query-list-parser/src/nodes/media-feature-value.ts +++ b/packages/media-query-list-parser/src/nodes/media-feature-value.ts @@ -53,7 +53,7 @@ export class MediaFeatureValue { return -1; } - at(index: number | string) { + at(index: number | string): ComponentValue | Array | undefined { if (index === 'value') { return this.value; } @@ -105,7 +105,7 @@ export class MediaFeatureValue { export type MediaFeatureValueWalkerEntry = ComponentValue | Array; export type MediaFeatureValueWalkerParent = ContainerNode | MediaFeatureValue; -export function parseMediaFeatureValue(componentValues: Array) { +export function parseMediaFeatureValue(componentValues: Array): MediaFeatureValue | false { let candidateIndexStart = -1; let candidateIndexEnd = -1; diff --git a/packages/media-query-list-parser/src/nodes/media-feature.ts b/packages/media-query-list-parser/src/nodes/media-feature.ts index d9a9a4137..5babeb5b4 100644 --- a/packages/media-query-list-parser/src/nodes/media-feature.ts +++ b/packages/media-query-list-parser/src/nodes/media-feature.ts @@ -1,5 +1,5 @@ -import { SimpleBlockNode, TokenNode } from '@csstools/css-parser-algorithms'; -import { CSSToken, stringify, TokenType } from '@csstools/css-tokenizer'; +import { SimpleBlockNode, TokenNode, parseListOfComponentValues } from '@csstools/css-parser-algorithms'; +import { CSSToken, mutateIdent, stringify, TokenType } from '@csstools/css-tokenizer'; import { MediaFeatureBoolean, parseMediaFeatureBoolean } from './media-feature-boolean'; import { MediaFeatureName } from './media-feature-name'; import { MediaFeaturePlain, MediaFeaturePlainWalkerEntry, MediaFeaturePlainWalkerParent, parseMediaFeaturePlain } from './media-feature-plain'; @@ -20,6 +20,14 @@ export class MediaFeature { this.after = after; } + getName(): string { + return this.feature.getName(); + } + + getNameToken(): CSSToken { + return this.feature.getNameToken(); + } + tokens(): Array { return [ ...this.before, @@ -40,7 +48,7 @@ export class MediaFeature { return -1; } - at(index: number | string) { + at(index: number | string): MediaFeatureBoolean | MediaFeaturePlain | MediaFeatureRange | undefined { if (index === 'feature') { return this.feature; } @@ -108,10 +116,13 @@ export function parseMediaFeature(simpleBlock: SimpleBlockNode, before: Array) { +export function newMediaFeaturePlain(name: string, ...value: Array): MediaFeature { + const nameToken: CSSToken = [TokenType.Ident, '', -1, -1, { value: '' }]; + mutateIdent(nameToken, name); + + const componentValues = parseListOfComponentValues(value); + return new MediaFeature( new MediaFeaturePlain( new MediaFeatureName( - new TokenNode([TokenType.Ident, name, -1, -1, { value: name }]), + new TokenNode(nameToken), ), [TokenType.Colon, ':', -1, -1, undefined], new MediaFeatureValue( - value.map((x) => new TokenNode(x)), + componentValues.length === 1 ? componentValues[0] : componentValues, ), ), [ diff --git a/packages/media-query-list-parser/src/nodes/media-in-parens.ts b/packages/media-query-list-parser/src/nodes/media-in-parens.ts index 02aff59a3..395f7774a 100644 --- a/packages/media-query-list-parser/src/nodes/media-in-parens.ts +++ b/packages/media-query-list-parser/src/nodes/media-in-parens.ts @@ -45,7 +45,7 @@ export class MediaInParens { return -1; } - at(index: number | string) { + at(index: number | string): MediaCondition | MediaFeature | GeneralEnclosed | undefined { if (index === 'media') { return this.media; } diff --git a/packages/media-query-list-parser/src/nodes/media-not.ts b/packages/media-query-list-parser/src/nodes/media-not.ts index 1a81910e6..dc6571bfa 100644 --- a/packages/media-query-list-parser/src/nodes/media-not.ts +++ b/packages/media-query-list-parser/src/nodes/media-not.ts @@ -32,7 +32,7 @@ export class MediaNot { return -1; } - at(index: number | string) { + at(index: number | string): MediaInParens | undefined { if (index === 'media') { return this.media; } diff --git a/packages/media-query-list-parser/src/nodes/media-or.ts b/packages/media-query-list-parser/src/nodes/media-or.ts index 9d45e629f..97b5591bc 100644 --- a/packages/media-query-list-parser/src/nodes/media-or.ts +++ b/packages/media-query-list-parser/src/nodes/media-or.ts @@ -32,7 +32,7 @@ export class MediaOr { return -1; } - at(index: number | string) { + at(index: number | string): MediaInParens | undefined { if (index === 'media') { return this.media; } diff --git a/packages/media-query-list-parser/src/nodes/media-query.ts b/packages/media-query-list-parser/src/nodes/media-query.ts index e6a1a6531..c6f419ef6 100644 --- a/packages/media-query-list-parser/src/nodes/media-query.ts +++ b/packages/media-query-list-parser/src/nodes/media-query.ts @@ -25,7 +25,7 @@ export class MediaQueryWithType { } } - getModifier() { + getModifier(): string { if (!this.modifier.length) { return ''; } @@ -68,7 +68,7 @@ export class MediaQueryWithType { return copy; } - getMediaType() { + getMediaType(): string { if (!this.mediaType.length) { return ''; } @@ -83,7 +83,7 @@ export class MediaQueryWithType { return ''; } - tokens() { + tokens(): Array { if (this.and && this.media) { return [ ...this.modifier, @@ -99,7 +99,7 @@ export class MediaQueryWithType { ]; } - toString() { + toString(): string { if (this.and && this.media) { return stringify(...this.modifier) + stringify(...this.mediaType) + stringify(...this.and) + this.media.toString(); } @@ -115,7 +115,7 @@ export class MediaQueryWithType { return -1; } - at(index: number | string) { + at(index: number | string): MediaCondition | undefined { if (index === 'media') { return this.media; } @@ -232,7 +232,7 @@ export class MediaQueryWithoutType { return -1; } - at(index: number | string) { + at(index: number | string): MediaCondition | undefined { if (index === 'media') { return this.media; } diff --git a/packages/media-query-list-parser/src/parser/parse-custom-media.ts b/packages/media-query-list-parser/src/parser/parse-custom-media.ts new file mode 100644 index 000000000..6ebcbdab2 --- /dev/null +++ b/packages/media-query-list-parser/src/parser/parse-custom-media.ts @@ -0,0 +1,85 @@ +import { CSSToken, tokenizer, ParseError, TokenType, TokenIdent, cloneTokens } from '@csstools/css-tokenizer'; +import { CustomMedia } from '../nodes/custom-media'; +import { parseFromTokens } from './parse'; + +export type Options = { + preserveInvalidMediaQueries?: boolean, + onParseError?: (error: ParseError) => void +} + +export function parseCustomMediaFromTokens(tokens: Array, options?: Options): CustomMedia | false { + let name: Array = []; + let remainder = tokens; + for (let i = 0; i < tokens.length; i++) { + if (tokens[i][0] === TokenType.Comment) { + continue; + } + if (tokens[i][0] === TokenType.Whitespace) { + continue; + } + + if (tokens[i][0] === TokenType.Ident) { + const identToken = tokens[i] as TokenIdent; + if (identToken[4].value.startsWith('--')) { + name = tokens.slice(0, i + 1); + remainder = tokens.slice(i + 1); + break; + } + } + + return false; + } + + let hasOnlyTrueOrFalse = true; + for (let i = 0; i < remainder.length; i++) { + if (remainder[i][0] === TokenType.Comment) { + continue; + } + if (remainder[i][0] === TokenType.Whitespace) { + continue; + } + + if (remainder[i][0] === TokenType.Ident) { + const identToken = remainder[i] as TokenIdent; + const identValue = identToken[4].value.toLowerCase(); + if (identValue === 'false') { + continue; + } + + if (identValue === 'true') { + continue; + } + } + + if (remainder[i][0] === TokenType.EOF) { + break; + } + + hasOnlyTrueOrFalse = false; + } + + if (hasOnlyTrueOrFalse) { + return new CustomMedia(name, null, remainder); + } + + return new CustomMedia(name, parseFromTokens(cloneTokens(remainder), options)); +} + +export function parseCustomMedia(source: string, options?: Options): CustomMedia | false { + const t = tokenizer({ css: source }, { + commentsAreTokens: true, + onParseError: options?.onParseError, + }); + + const tokens = []; + + { + while (!t.endOfFile()) { + tokens.push(t.nextToken()); + } + + tokens.push(t.nextToken()); // EOF-token + } + + return parseCustomMediaFromTokens(tokens, options); +} diff --git a/packages/media-query-list-parser/src/parser/parse-media-query.ts b/packages/media-query-list-parser/src/parser/parse-media-query.ts index 9e95b638e..04c8077b1 100644 --- a/packages/media-query-list-parser/src/parser/parse-media-query.ts +++ b/packages/media-query-list-parser/src/parser/parse-media-query.ts @@ -1,3 +1,4 @@ +import { isSimpleBlockNode } from '@csstools/css-parser-algorithms'; import { ComponentValue, ComponentValueType, isCommentNode, isTokenNode, isWhitespaceNode, SimpleBlockNode } from '@csstools/css-parser-algorithms'; import { CSSToken, TokenIdent, TokenType } from '@csstools/css-tokenizer'; import { GeneralEnclosed } from '../nodes/general-enclosed'; @@ -137,8 +138,9 @@ export function parseMediaConditionListWithOr(componentValues: Array) { continue; } - if (componentValue.type === ComponentValueType.SimpleBlock) { + if (isSimpleBlockNode(componentValue)) { if (singleSimpleBlockIndex !== -1) { return false; } @@ -302,6 +305,8 @@ export function parseMediaInParens(componentValues: Array) { return false; } + simpleBlock.normalize(); + const before = [ ...componentValues.slice(0, singleSimpleBlockIndex).flatMap((x) => { return x.tokens(); @@ -383,8 +388,9 @@ export function parseMediaNot(componentValues: Array) { return false; } - if (sawNot && componentValue.type === ComponentValueType.SimpleBlock) { - const media = parseMediaInParensFromSimpleBlock(componentValue as SimpleBlockNode); + if (sawNot && isSimpleBlockNode(componentValue)) { + componentValue.normalize(); + const media = parseMediaInParensFromSimpleBlock(componentValue); if (media === false) { return false; } @@ -436,7 +442,8 @@ export function parseMediaOr(componentValues: Array) { return false; } - if (sawOr && componentValue.type === ComponentValueType.SimpleBlock) { + if (sawOr && isSimpleBlockNode(componentValue)) { + componentValue.normalize(); const media = parseMediaInParensFromSimpleBlock(componentValue as SimpleBlockNode); if (media === false) { return false; @@ -486,7 +493,8 @@ export function parseMediaAnd(componentValues: Array) { return false; } - if (sawAnd && componentValue.type === ComponentValueType.SimpleBlock) { + if (sawAnd && isSimpleBlockNode(componentValue)) { + componentValue.normalize(); const media = parseMediaInParensFromSimpleBlock(componentValue as SimpleBlockNode); if (media === false) { return false; diff --git a/packages/media-query-list-parser/src/parser/parse.ts b/packages/media-query-list-parser/src/parser/parse.ts index 950ed7153..2f85b7cd0 100644 --- a/packages/media-query-list-parser/src/parser/parse.ts +++ b/packages/media-query-list-parser/src/parser/parse.ts @@ -1,12 +1,11 @@ import { parseCommaSeparatedListOfComponentValues } from '@csstools/css-parser-algorithms'; -import { ParserError } from '@csstools/css-parser-algorithms/dist/interfaces/error'; -import { CSSToken, tokenizer } from '@csstools/css-tokenizer'; +import { CSSToken, tokenizer, ParseError } from '@csstools/css-tokenizer'; import { MediaQuery, MediaQueryInvalid } from '../nodes/media-query'; import { parseMediaQuery } from './parse-media-query'; export type Options = { preserveInvalidMediaQueries?: boolean, - onParseError?: (error: ParserError) => void + onParseError?: (error: ParseError) => void } export function parseFromTokens(tokens: Array, options?: Options) { diff --git a/packages/media-query-list-parser/src/util/node-type.ts b/packages/media-query-list-parser/src/util/node-type.ts index dd8fd4374..482665cae 100644 --- a/packages/media-query-list-parser/src/util/node-type.ts +++ b/packages/media-query-list-parser/src/util/node-type.ts @@ -1,4 +1,5 @@ export enum NodeType { + CustomMedia = 'custom-media', GeneralEnclosed = 'general-enclosed', MediaAnd = 'media-and', MediaCondition = 'media-condition', diff --git a/packages/media-query-list-parser/src/util/type-predicates.ts b/packages/media-query-list-parser/src/util/type-predicates.ts index 659f048b4..8293f2482 100644 --- a/packages/media-query-list-parser/src/util/type-predicates.ts +++ b/packages/media-query-list-parser/src/util/type-predicates.ts @@ -1,3 +1,4 @@ +import { CustomMedia } from '../nodes/custom-media'; import { GeneralEnclosed } from '../nodes/general-enclosed'; import { MediaAnd } from '../nodes/media-and'; import { MediaCondition } from '../nodes/media-condition'; @@ -13,6 +14,10 @@ import { MediaNot } from '../nodes/media-not'; import { MediaOr } from '../nodes/media-or'; import { MediaQuery, MediaQueryInvalid, MediaQueryWithoutType, MediaQueryWithType } from '../nodes/media-query'; +export function isCustomMedia(x: unknown): x is GeneralEnclosed { + return CustomMedia.isCustomMedia(x); +} + export function isGeneralEnclosed(x: unknown): x is GeneralEnclosed { return GeneralEnclosed.isGeneralEnclosed(x); } diff --git a/packages/media-query-list-parser/test/api/options.mjs b/packages/media-query-list-parser/test/api/options.mjs index 9268779f4..70e488882 100644 --- a/packages/media-query-list-parser/test/api/options.mjs +++ b/packages/media-query-list-parser/test/api/options.mjs @@ -42,7 +42,46 @@ import { parse } from '@csstools/media-query-list-parser'; assert.equal( resultAST.length, - 0, + 1, + ); +} + +{ + let error; + const resultAST = parse('(foo', { + onParseError: (err) => { + error = err; + }, + }); + + assert.equal( + resultAST.length, + 1, + ); + + assert.deepEqual( + { + message: error.message, + sourceStart: error.sourceStart, + sourceEnd: error.sourceEnd, + parserState: error.parserState, + }, + { + message: 'Unexpected EOF while consuming a simple block.', + sourceStart: 0, + sourceEnd: -1, + parserState: ['5.4.8. Consume a simple block', 'Unexpected EOF'], + }, + ); +} + + +{ + const resultAST = parse('(foo'); + + assert.equal( + resultAST.length, + 1, ); } @@ -56,16 +95,21 @@ import { parse } from '@csstools/media-query-list-parser'; assert.equal( resultAST.length, - 0, + 1, ); assert.deepEqual( - error, + { + message: error.message, + sourceStart: error.sourceStart, + sourceEnd: error.sourceEnd, + parserState: error.parserState, + }, { message: 'Unexpected EOF while consuming a simple block.', - start: 0, - end: -1, - state: ['5.4.8. Consume a simple block', 'Unexpected EOF'], + sourceStart: 0, + sourceEnd: -1, + parserState: ['5.4.8. Consume a simple block', 'Unexpected EOF'], }, ); } diff --git a/packages/media-query-list-parser/test/cases/custom-media/0001.expect.json b/packages/media-query-list-parser/test/cases/custom-media/0001.expect.json new file mode 100644 index 000000000..7de2e9586 --- /dev/null +++ b/packages/media-query-list-parser/test/cases/custom-media/0001.expect.json @@ -0,0 +1,44 @@ +{ + "type": "custom-media", + "string": "--foo screen", + "nameValue": "--foo", + "name": [ + [ + "ident-token", + "--foo", + 0, + 4, + { + "value": "--foo" + } + ] + ], + "hasFalseKeyword": false, + "hasTrueKeyword": false, + "mediaQueryList": [ + { + "type": "media-query-with-type", + "string": " screen", + "modifier": [], + "mediaType": [ + [ + "whitespace-token", + " ", + 5, + 5, + null + ], + [ + "ident-token", + "screen", + 6, + 11, + { + "value": "screen" + } + ] + ], + "media": null + } + ] +} \ No newline at end of file diff --git a/packages/media-query-list-parser/test/cases/custom-media/0001.mjs b/packages/media-query-list-parser/test/cases/custom-media/0001.mjs new file mode 100644 index 000000000..5f97dff75 --- /dev/null +++ b/packages/media-query-list-parser/test/cases/custom-media/0001.mjs @@ -0,0 +1,13 @@ +import assert from 'assert'; +import { runTestCustomMedia } from '../../util/run-test-custom-media.mjs'; + +runTestCustomMedia( + '--foo screen', + 'custom-media/0001', + (actual, expected) => { + assert.deepStrictEqual( + actual, + expected, + ); + }, +); diff --git a/packages/media-query-list-parser/test/cases/custom-media/0002.expect.json b/packages/media-query-list-parser/test/cases/custom-media/0002.expect.json new file mode 100644 index 000000000..65db2ca18 --- /dev/null +++ b/packages/media-query-list-parser/test/cases/custom-media/0002.expect.json @@ -0,0 +1,43 @@ +{ + "type": "custom-media", + "string": "--foo true", + "nameValue": "--foo", + "name": [ + [ + "ident-token", + "--foo", + 0, + 4, + { + "value": "--foo" + } + ] + ], + "hasFalseKeyword": false, + "hasTrueKeyword": true, + "trueOrFalseKeyword": [ + [ + "whitespace-token", + " ", + 5, + 5, + null + ], + [ + "ident-token", + "true", + 6, + 9, + { + "value": "true" + } + ], + [ + "EOF-token", + "", + -1, + -1, + null + ] + ] +} \ No newline at end of file diff --git a/packages/media-query-list-parser/test/cases/custom-media/0002.mjs b/packages/media-query-list-parser/test/cases/custom-media/0002.mjs new file mode 100644 index 000000000..22185f771 --- /dev/null +++ b/packages/media-query-list-parser/test/cases/custom-media/0002.mjs @@ -0,0 +1,13 @@ +import assert from 'assert'; +import { runTestCustomMedia } from '../../util/run-test-custom-media.mjs'; + +runTestCustomMedia( + '--foo true', + 'custom-media/0002', + (actual, expected) => { + assert.deepStrictEqual( + actual, + expected, + ); + }, +); diff --git a/packages/media-query-list-parser/test/cases/custom-media/0003.expect.json b/packages/media-query-list-parser/test/cases/custom-media/0003.expect.json new file mode 100644 index 000000000..3a6a16d98 --- /dev/null +++ b/packages/media-query-list-parser/test/cases/custom-media/0003.expect.json @@ -0,0 +1,57 @@ +{ + "type": "custom-media", + "string": "/* comment : 1 */--foo/* comment : 2 */true/* comment : 3 */", + "nameValue": "--foo", + "name": [ + [ + "comment", + "/* comment : 1 */", + 0, + 16, + null + ], + [ + "ident-token", + "--foo", + 17, + 21, + { + "value": "--foo" + } + ] + ], + "hasFalseKeyword": false, + "hasTrueKeyword": true, + "trueOrFalseKeyword": [ + [ + "comment", + "/* comment : 2 */", + 22, + 38, + null + ], + [ + "ident-token", + "true", + 39, + 42, + { + "value": "true" + } + ], + [ + "comment", + "/* comment : 3 */", + 43, + 59, + null + ], + [ + "EOF-token", + "", + -1, + -1, + null + ] + ] +} \ No newline at end of file diff --git a/packages/media-query-list-parser/test/cases/custom-media/0003.mjs b/packages/media-query-list-parser/test/cases/custom-media/0003.mjs new file mode 100644 index 000000000..ad6d52ee7 --- /dev/null +++ b/packages/media-query-list-parser/test/cases/custom-media/0003.mjs @@ -0,0 +1,13 @@ +import assert from 'assert'; +import { runTestCustomMedia } from '../../util/run-test-custom-media.mjs'; + +runTestCustomMedia( + '/* comment : 1 */--foo/* comment : 2 */true/* comment : 3 */', + 'custom-media/0003', + (actual, expected) => { + assert.deepStrictEqual( + actual, + expected, + ); + }, +); diff --git a/packages/media-query-list-parser/test/cases/custom-media/0004.expect.json b/packages/media-query-list-parser/test/cases/custom-media/0004.expect.json new file mode 100644 index 000000000..99f6a1fd6 --- /dev/null +++ b/packages/media-query-list-parser/test/cases/custom-media/0004.expect.json @@ -0,0 +1,43 @@ +{ + "type": "custom-media", + "string": "--foo f\\61 LSE", + "nameValue": "--foo", + "name": [ + [ + "ident-token", + "--foo", + 0, + 4, + { + "value": "--foo" + } + ] + ], + "hasFalseKeyword": true, + "hasTrueKeyword": false, + "trueOrFalseKeyword": [ + [ + "whitespace-token", + " ", + 5, + 5, + null + ], + [ + "ident-token", + "f\\61 LSE", + 6, + 13, + { + "value": "faLSE" + } + ], + [ + "EOF-token", + "", + -1, + -1, + null + ] + ] +} \ No newline at end of file diff --git a/packages/media-query-list-parser/test/cases/custom-media/0004.mjs b/packages/media-query-list-parser/test/cases/custom-media/0004.mjs new file mode 100644 index 000000000..95504121f --- /dev/null +++ b/packages/media-query-list-parser/test/cases/custom-media/0004.mjs @@ -0,0 +1,13 @@ +import assert from 'assert'; +import { runTestCustomMedia } from '../../util/run-test-custom-media.mjs'; + +runTestCustomMedia( + '--foo f\\61 LSE', + 'custom-media/0004', + (actual, expected) => { + assert.deepStrictEqual( + actual, + expected, + ); + }, +); diff --git a/packages/media-query-list-parser/test/cases/custom-media/0005.expect.json b/packages/media-query-list-parser/test/cases/custom-media/0005.expect.json new file mode 100644 index 000000000..bd95a4576 --- /dev/null +++ b/packages/media-query-list-parser/test/cases/custom-media/0005.expect.json @@ -0,0 +1,57 @@ +{ + "type": "custom-media", + "string": "/* comment: 1 */--foo/* comment: 2 */false/* comment: 3 */", + "nameValue": "--foo", + "name": [ + [ + "comment", + "/* comment: 1 */", + 0, + 15, + null + ], + [ + "ident-token", + "--foo", + 16, + 20, + { + "value": "--foo" + } + ] + ], + "hasFalseKeyword": true, + "hasTrueKeyword": false, + "trueOrFalseKeyword": [ + [ + "comment", + "/* comment: 2 */", + 21, + 36, + null + ], + [ + "ident-token", + "false", + 37, + 41, + { + "value": "false" + } + ], + [ + "comment", + "/* comment: 3 */", + 42, + 57, + null + ], + [ + "EOF-token", + "", + -1, + -1, + null + ] + ] +} \ No newline at end of file diff --git a/packages/media-query-list-parser/test/cases/custom-media/0005.mjs b/packages/media-query-list-parser/test/cases/custom-media/0005.mjs new file mode 100644 index 000000000..ba5f18b9b --- /dev/null +++ b/packages/media-query-list-parser/test/cases/custom-media/0005.mjs @@ -0,0 +1,13 @@ +import assert from 'assert'; +import { runTestCustomMedia } from '../../util/run-test-custom-media.mjs'; + +runTestCustomMedia( + '/* comment: 1 */--foo/* comment: 2 */false/* comment: 3 */', + 'custom-media/0005', + (actual, expected) => { + assert.deepStrictEqual( + actual, + expected, + ); + }, +); diff --git a/packages/media-query-list-parser/test/cases/custom-media/0006.expect.json b/packages/media-query-list-parser/test/cases/custom-media/0006.expect.json new file mode 100644 index 000000000..473d3122d --- /dev/null +++ b/packages/media-query-list-parser/test/cases/custom-media/0006.expect.json @@ -0,0 +1,104 @@ +{ + "type": "custom-media", + "string": "--foo false or screen", + "nameValue": "--foo", + "name": [ + [ + "ident-token", + "--foo", + 0, + 4, + { + "value": "--foo" + } + ] + ], + "hasFalseKeyword": false, + "hasTrueKeyword": false, + "mediaQueryList": [ + { + "type": "media-query-invalid", + "string": " false or screen", + "media": [ + { + "type": "whitespace", + "tokens": [ + [ + "whitespace-token", + " ", + 5, + 5, + null + ] + ] + }, + { + "type": "token", + "tokens": [ + [ + "ident-token", + "false", + 6, + 10, + { + "value": "false" + } + ] + ] + }, + { + "type": "whitespace", + "tokens": [ + [ + "whitespace-token", + " ", + 11, + 11, + null + ] + ] + }, + { + "type": "token", + "tokens": [ + [ + "ident-token", + "or", + 12, + 13, + { + "value": "or" + } + ] + ] + }, + { + "type": "whitespace", + "tokens": [ + [ + "whitespace-token", + " ", + 14, + 14, + null + ] + ] + }, + { + "type": "token", + "tokens": [ + [ + "ident-token", + "screen", + 15, + 20, + { + "value": "screen" + } + ] + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/packages/media-query-list-parser/test/cases/custom-media/0006.mjs b/packages/media-query-list-parser/test/cases/custom-media/0006.mjs new file mode 100644 index 000000000..e3971cc1e --- /dev/null +++ b/packages/media-query-list-parser/test/cases/custom-media/0006.mjs @@ -0,0 +1,13 @@ +import assert from 'assert'; +import { runTestCustomMedia } from '../../util/run-test-custom-media.mjs'; + +runTestCustomMedia( + '--foo false or screen', + 'custom-media/0006', + (actual, expected) => { + assert.deepStrictEqual( + actual, + expected, + ); + }, +); diff --git a/packages/media-query-list-parser/test/cases/custom-media/0007.expect.json b/packages/media-query-list-parser/test/cases/custom-media/0007.expect.json new file mode 100644 index 000000000..7de2e9586 --- /dev/null +++ b/packages/media-query-list-parser/test/cases/custom-media/0007.expect.json @@ -0,0 +1,44 @@ +{ + "type": "custom-media", + "string": "--foo screen", + "nameValue": "--foo", + "name": [ + [ + "ident-token", + "--foo", + 0, + 4, + { + "value": "--foo" + } + ] + ], + "hasFalseKeyword": false, + "hasTrueKeyword": false, + "mediaQueryList": [ + { + "type": "media-query-with-type", + "string": " screen", + "modifier": [], + "mediaType": [ + [ + "whitespace-token", + " ", + 5, + 5, + null + ], + [ + "ident-token", + "screen", + 6, + 11, + { + "value": "screen" + } + ] + ], + "media": null + } + ] +} \ No newline at end of file diff --git a/packages/media-query-list-parser/test/cases/custom-media/0007.mjs b/packages/media-query-list-parser/test/cases/custom-media/0007.mjs new file mode 100644 index 000000000..bbb9139d9 --- /dev/null +++ b/packages/media-query-list-parser/test/cases/custom-media/0007.mjs @@ -0,0 +1,13 @@ +import assert from 'assert'; +import { runTestCustomMedia } from '../../util/run-test-custom-media.mjs'; + +runTestCustomMedia( + '--foo screen', + 'custom-media/0007', + (actual, expected) => { + assert.deepStrictEqual( + actual, + expected, + ); + }, +); diff --git a/packages/media-query-list-parser/test/cases/custom-media/0008.expect.json b/packages/media-query-list-parser/test/cases/custom-media/0008.expect.json new file mode 100644 index 000000000..783e5ad26 --- /dev/null +++ b/packages/media-query-list-parser/test/cases/custom-media/0008.expect.json @@ -0,0 +1,153 @@ +{ + "type": "custom-media", + "string": "--foo (min-width: 300px)", + "nameValue": "--foo", + "name": [ + [ + "ident-token", + "--foo", + 0, + 4, + { + "value": "--foo" + } + ] + ], + "hasFalseKeyword": false, + "hasTrueKeyword": false, + "mediaQueryList": [ + { + "type": "media-query-without-type", + "string": " (min-width: 300px)", + "media": { + "type": "media-condition", + "media": { + "type": "media-in-parens", + "media": { + "type": "media-feature", + "feature": { + "type": "mf-plain", + "name": { + "type": "mf-name", + "name": "min-width", + "tokens": [ + [ + "ident-token", + "min-width", + 7, + 15, + { + "value": "min-width" + } + ] + ] + }, + "value": { + "type": "mf-value", + "value": { + "type": "token", + "tokens": [ + [ + "dimension-token", + "300px", + 18, + 22, + { + "value": 300, + "type": "integer", + "unit": "px" + } + ] + ] + }, + "tokens": [ + [ + "whitespace-token", + " ", + 17, + 17, + null + ], + [ + "dimension-token", + "300px", + 18, + 22, + { + "value": 300, + "type": "integer", + "unit": "px" + } + ] + ] + }, + "tokens": [ + [ + "ident-token", + "min-width", + 7, + 15, + { + "value": "min-width" + } + ], + [ + "colon-token", + ":", + 16, + 16, + null + ], + [ + "whitespace-token", + " ", + 17, + 17, + null + ], + [ + "dimension-token", + "300px", + 18, + 22, + { + "value": 300, + "type": "integer", + "unit": "px" + } + ] + ] + }, + "before": [ + [ + "whitespace-token", + " ", + 5, + 5, + null + ], + [ + "(-token", + "(", + 6, + 6, + null + ] + ], + "after": [ + [ + ")-token", + ")", + 23, + 23, + null + ] + ] + }, + "before": [], + "after": [] + } + } + } + ] +} \ No newline at end of file diff --git a/packages/media-query-list-parser/test/cases/custom-media/0008.mjs b/packages/media-query-list-parser/test/cases/custom-media/0008.mjs new file mode 100644 index 000000000..67b218755 --- /dev/null +++ b/packages/media-query-list-parser/test/cases/custom-media/0008.mjs @@ -0,0 +1,13 @@ +import assert from 'assert'; +import { runTestCustomMedia } from '../../util/run-test-custom-media.mjs'; + +runTestCustomMedia( + '--foo (min-width: 300px)', + 'custom-media/0008', + (actual, expected) => { + assert.deepStrictEqual( + actual, + expected, + ); + }, +); diff --git a/packages/media-query-list-parser/test/cases/custom-media/0009.expect.json b/packages/media-query-list-parser/test/cases/custom-media/0009.expect.json new file mode 100644 index 000000000..7a1ac6fbf --- /dev/null +++ b/packages/media-query-list-parser/test/cases/custom-media/0009.expect.json @@ -0,0 +1,360 @@ +{ + "type": "custom-media", + "string": "--foo screen and (min-width: 300px), print and (max-height: 500px)", + "nameValue": "--foo", + "name": [ + [ + "ident-token", + "--foo", + 0, + 4, + { + "value": "--foo" + } + ] + ], + "hasFalseKeyword": false, + "hasTrueKeyword": false, + "mediaQueryList": [ + { + "type": "media-query-with-type", + "string": " screen and (min-width: 300px)", + "modifier": [], + "mediaType": [ + [ + "whitespace-token", + " ", + 5, + 5, + null + ], + [ + "ident-token", + "screen", + 6, + 11, + { + "value": "screen" + } + ] + ], + "and": [ + [ + "whitespace-token", + " ", + 12, + 12, + null + ], + [ + "ident-token", + "and", + 13, + 15, + { + "value": "and" + } + ] + ], + "media": { + "type": "media-condition", + "media": { + "type": "media-in-parens", + "media": { + "type": "media-feature", + "feature": { + "type": "mf-plain", + "name": { + "type": "mf-name", + "name": "min-width", + "tokens": [ + [ + "ident-token", + "min-width", + 18, + 26, + { + "value": "min-width" + } + ] + ] + }, + "value": { + "type": "mf-value", + "value": { + "type": "token", + "tokens": [ + [ + "dimension-token", + "300px", + 29, + 33, + { + "value": 300, + "type": "integer", + "unit": "px" + } + ] + ] + }, + "tokens": [ + [ + "whitespace-token", + " ", + 28, + 28, + null + ], + [ + "dimension-token", + "300px", + 29, + 33, + { + "value": 300, + "type": "integer", + "unit": "px" + } + ] + ] + }, + "tokens": [ + [ + "ident-token", + "min-width", + 18, + 26, + { + "value": "min-width" + } + ], + [ + "colon-token", + ":", + 27, + 27, + null + ], + [ + "whitespace-token", + " ", + 28, + 28, + null + ], + [ + "dimension-token", + "300px", + 29, + 33, + { + "value": 300, + "type": "integer", + "unit": "px" + } + ] + ] + }, + "before": [ + [ + "whitespace-token", + " ", + 16, + 16, + null + ], + [ + "(-token", + "(", + 17, + 17, + null + ] + ], + "after": [ + [ + ")-token", + ")", + 34, + 34, + null + ] + ] + }, + "before": [], + "after": [] + } + } + }, + { + "type": "media-query-with-type", + "string": " print and (max-height: 500px)", + "modifier": [], + "mediaType": [ + [ + "whitespace-token", + " ", + 36, + 36, + null + ], + [ + "ident-token", + "print", + 37, + 41, + { + "value": "print" + } + ] + ], + "and": [ + [ + "whitespace-token", + " ", + 42, + 42, + null + ], + [ + "ident-token", + "and", + 43, + 45, + { + "value": "and" + } + ] + ], + "media": { + "type": "media-condition", + "media": { + "type": "media-in-parens", + "media": { + "type": "media-feature", + "feature": { + "type": "mf-plain", + "name": { + "type": "mf-name", + "name": "max-height", + "tokens": [ + [ + "ident-token", + "max-height", + 48, + 57, + { + "value": "max-height" + } + ] + ] + }, + "value": { + "type": "mf-value", + "value": { + "type": "token", + "tokens": [ + [ + "dimension-token", + "500px", + 60, + 64, + { + "value": 500, + "type": "integer", + "unit": "px" + } + ] + ] + }, + "tokens": [ + [ + "whitespace-token", + " ", + 59, + 59, + null + ], + [ + "dimension-token", + "500px", + 60, + 64, + { + "value": 500, + "type": "integer", + "unit": "px" + } + ] + ] + }, + "tokens": [ + [ + "ident-token", + "max-height", + 48, + 57, + { + "value": "max-height" + } + ], + [ + "colon-token", + ":", + 58, + 58, + null + ], + [ + "whitespace-token", + " ", + 59, + 59, + null + ], + [ + "dimension-token", + "500px", + 60, + 64, + { + "value": 500, + "type": "integer", + "unit": "px" + } + ] + ] + }, + "before": [ + [ + "whitespace-token", + " ", + 46, + 46, + null + ], + [ + "(-token", + "(", + 47, + 47, + null + ] + ], + "after": [ + [ + ")-token", + ")", + 65, + 65, + null + ] + ] + }, + "before": [], + "after": [] + } + } + } + ] +} \ No newline at end of file diff --git a/packages/media-query-list-parser/test/cases/custom-media/0009.mjs b/packages/media-query-list-parser/test/cases/custom-media/0009.mjs new file mode 100644 index 000000000..7fe554419 --- /dev/null +++ b/packages/media-query-list-parser/test/cases/custom-media/0009.mjs @@ -0,0 +1,13 @@ +import assert from 'assert'; +import { runTestCustomMedia } from '../../util/run-test-custom-media.mjs'; + +runTestCustomMedia( + '--foo screen and (min-width: 300px), print and (max-height: 500px)', + 'custom-media/0009', + (actual, expected) => { + assert.deepStrictEqual( + actual, + expected, + ); + }, +); diff --git a/packages/media-query-list-parser/test/cases/custom-media/0010.expect.json b/packages/media-query-list-parser/test/cases/custom-media/0010.expect.json new file mode 100644 index 000000000..02e4a84d6 --- /dev/null +++ b/packages/media-query-list-parser/test/cases/custom-media/0010.expect.json @@ -0,0 +1 @@ +false \ No newline at end of file diff --git a/packages/media-query-list-parser/test/cases/custom-media/0010.mjs b/packages/media-query-list-parser/test/cases/custom-media/0010.mjs new file mode 100644 index 000000000..81d7ba1de --- /dev/null +++ b/packages/media-query-list-parser/test/cases/custom-media/0010.mjs @@ -0,0 +1,13 @@ +import assert from 'assert'; +import { runTestCustomMedia } from '../../util/run-test-custom-media.mjs'; + +runTestCustomMedia( + '(max-height: 500px)', + 'custom-media/0010', + (actual, expected) => { + assert.deepStrictEqual( + actual, + expected, + ); + }, +); diff --git a/packages/media-query-list-parser/test/cases/custom-media/0011.expect.json b/packages/media-query-list-parser/test/cases/custom-media/0011.expect.json new file mode 100644 index 000000000..fe16aac1f --- /dev/null +++ b/packages/media-query-list-parser/test/cases/custom-media/0011.expect.json @@ -0,0 +1,107 @@ +{ + "type": "custom-media", + "string": "--foo (max-height: does-not-exist(foo))", + "nameValue": "--foo", + "name": [ + [ + "ident-token", + "--foo", + 0, + 4, + { + "value": "--foo" + } + ] + ], + "hasFalseKeyword": false, + "hasTrueKeyword": false, + "mediaQueryList": [ + { + "type": "media-query-without-type", + "string": " (max-height: does-not-exist(foo))", + "media": { + "type": "media-condition", + "media": { + "type": "media-in-parens", + "media": { + "type": "general-enclosed", + "tokens": [ + [ + "(-token", + "(", + 6, + 6, + null + ], + [ + "ident-token", + "max-height", + 7, + 16, + { + "value": "max-height" + } + ], + [ + "colon-token", + ":", + 17, + 17, + null + ], + [ + "whitespace-token", + " ", + 18, + 18, + null + ], + [ + "function-token", + "does-not-exist(", + 19, + 33, + { + "value": "does-not-exist" + } + ], + [ + "ident-token", + "foo", + 34, + 36, + { + "value": "foo" + } + ], + [ + ")-token", + ")", + 37, + 37, + null + ], + [ + ")-token", + ")", + 38, + 38, + null + ] + ] + }, + "before": [ + [ + "whitespace-token", + " ", + 5, + 5, + null + ] + ], + "after": [] + } + } + } + ] +} \ No newline at end of file diff --git a/packages/media-query-list-parser/test/cases/custom-media/0011.mjs b/packages/media-query-list-parser/test/cases/custom-media/0011.mjs new file mode 100644 index 000000000..e51fc6688 --- /dev/null +++ b/packages/media-query-list-parser/test/cases/custom-media/0011.mjs @@ -0,0 +1,14 @@ +import assert from 'assert'; +import { runTestCustomMedia } from '../../util/run-test-custom-media.mjs'; + +runTestCustomMedia( + '--foo (max-height: does-not-exist(foo))', + 'custom-media/0011', + (actual, expected) => { + assert.deepStrictEqual( + actual, + expected, + ); + }, + 1, +); diff --git a/packages/media-query-list-parser/test/cases/media-not/0001.expect.json b/packages/media-query-list-parser/test/cases/media-not/0001.expect.json index 5a9906847..320316fb1 100644 --- a/packages/media-query-list-parser/test/cases/media-not/0001.expect.json +++ b/packages/media-query-list-parser/test/cases/media-not/0001.expect.json @@ -30,7 +30,21 @@ "type": "media-feature", "feature": { "type": "mf-boolean", - "name": "color", + "name": { + "type": "mf-name", + "name": "color", + "tokens": [ + [ + "ident-token", + "color", + 5, + 9, + { + "value": "color" + } + ] + ] + }, "tokens": [ [ "ident-token", diff --git a/packages/media-query-list-parser/test/cases/mf-boolean/0001.expect.json b/packages/media-query-list-parser/test/cases/mf-boolean/0001.expect.json index af1643cf2..7e81de349 100644 --- a/packages/media-query-list-parser/test/cases/mf-boolean/0001.expect.json +++ b/packages/media-query-list-parser/test/cases/mf-boolean/0001.expect.json @@ -10,7 +10,21 @@ "type": "media-feature", "feature": { "type": "mf-boolean", - "name": "color", + "name": { + "type": "mf-name", + "name": "color", + "tokens": [ + [ + "ident-token", + "color", + 1, + 5, + { + "value": "color" + } + ] + ] + }, "tokens": [ [ "ident-token", diff --git a/packages/media-query-list-parser/test/cases/mf-boolean/0002.expect.json b/packages/media-query-list-parser/test/cases/mf-boolean/0002.expect.json index 91c136e7d..beceedde8 100644 --- a/packages/media-query-list-parser/test/cases/mf-boolean/0002.expect.json +++ b/packages/media-query-list-parser/test/cases/mf-boolean/0002.expect.json @@ -10,7 +10,35 @@ "type": "media-feature", "feature": { "type": "mf-boolean", - "name": "color", + "name": { + "type": "mf-name", + "name": "color", + "tokens": [ + [ + "comment", + "/* comment 2 */", + 16, + 30, + null + ], + [ + "ident-token", + "color", + 31, + 35, + { + "value": "color" + } + ], + [ + "comment", + "/* comment 3 */", + 36, + 50, + null + ] + ] + }, "tokens": [ [ "comment", diff --git a/packages/media-query-list-parser/test/cases/mf-boolean/0003.expect.json b/packages/media-query-list-parser/test/cases/mf-boolean/0003.expect.json index af07020b7..4694b9ec9 100644 --- a/packages/media-query-list-parser/test/cases/mf-boolean/0003.expect.json +++ b/packages/media-query-list-parser/test/cases/mf-boolean/0003.expect.json @@ -10,7 +10,21 @@ "type": "media-feature", "feature": { "type": "mf-boolean", - "name": "true", + "name": { + "type": "mf-name", + "name": "true", + "tokens": [ + [ + "ident-token", + "true", + 1, + 4, + { + "value": "true" + } + ] + ] + }, "tokens": [ [ "ident-token", diff --git a/packages/media-query-list-parser/test/cases/mf-boolean/0004.expect.json b/packages/media-query-list-parser/test/cases/mf-boolean/0004.expect.json index ec092ecf0..ba317254b 100644 --- a/packages/media-query-list-parser/test/cases/mf-boolean/0004.expect.json +++ b/packages/media-query-list-parser/test/cases/mf-boolean/0004.expect.json @@ -10,7 +10,21 @@ "type": "media-feature", "feature": { "type": "mf-boolean", - "name": "false", + "name": { + "type": "mf-name", + "name": "false", + "tokens": [ + [ + "ident-token", + "false", + 1, + 5, + { + "value": "false" + } + ] + ] + }, "tokens": [ [ "ident-token", diff --git a/packages/media-query-list-parser/test/cases/mf-plain/0006.expect.json b/packages/media-query-list-parser/test/cases/mf-plain/0006.expect.json new file mode 100644 index 000000000..2c77dcca1 --- /dev/null +++ b/packages/media-query-list-parser/test/cases/mf-plain/0006.expect.json @@ -0,0 +1,351 @@ +[ + { + "type": "media-query-without-type", + "string": "(min-width: calc(10px + 2px))", + "media": { + "type": "media-condition", + "media": { + "type": "media-in-parens", + "media": { + "type": "media-feature", + "feature": { + "type": "mf-plain", + "name": { + "type": "mf-name", + "name": "min-width", + "tokens": [ + [ + "ident-token", + "min-width", + 1, + 9, + { + "value": "min-width" + } + ] + ] + }, + "value": { + "type": "mf-value", + "value": { + "type": "function", + "name": "calc", + "tokens": [ + [ + "function-token", + "calc(", + 12, + 16, + { + "value": "calc" + } + ], + [ + "dimension-token", + "10px", + 17, + 20, + { + "value": 10, + "type": "integer", + "unit": "px" + } + ], + [ + "whitespace-token", + " ", + 21, + 21, + null + ], + [ + "delim-token", + "+", + 22, + 22, + { + "value": "+" + } + ], + [ + "whitespace-token", + " ", + 23, + 23, + null + ], + [ + "dimension-token", + "2px", + 24, + 26, + { + "value": 2, + "type": "integer", + "unit": "px" + } + ], + [ + ")-token", + ")", + 27, + 27, + null + ] + ], + "value": [ + { + "type": "token", + "tokens": [ + [ + "dimension-token", + "10px", + 17, + 20, + { + "value": 10, + "type": "integer", + "unit": "px" + } + ] + ] + }, + { + "type": "whitespace", + "tokens": [ + [ + "whitespace-token", + " ", + 21, + 21, + null + ] + ] + }, + { + "type": "token", + "tokens": [ + [ + "delim-token", + "+", + 22, + 22, + { + "value": "+" + } + ] + ] + }, + { + "type": "whitespace", + "tokens": [ + [ + "whitespace-token", + " ", + 23, + 23, + null + ] + ] + }, + { + "type": "token", + "tokens": [ + [ + "dimension-token", + "2px", + 24, + 26, + { + "value": 2, + "type": "integer", + "unit": "px" + } + ] + ] + } + ] + }, + "tokens": [ + [ + "whitespace-token", + " ", + 11, + 11, + null + ], + [ + "function-token", + "calc(", + 12, + 16, + { + "value": "calc" + } + ], + [ + "dimension-token", + "10px", + 17, + 20, + { + "value": 10, + "type": "integer", + "unit": "px" + } + ], + [ + "whitespace-token", + " ", + 21, + 21, + null + ], + [ + "delim-token", + "+", + 22, + 22, + { + "value": "+" + } + ], + [ + "whitespace-token", + " ", + 23, + 23, + null + ], + [ + "dimension-token", + "2px", + 24, + 26, + { + "value": 2, + "type": "integer", + "unit": "px" + } + ], + [ + ")-token", + ")", + 27, + 27, + null + ] + ] + }, + "tokens": [ + [ + "ident-token", + "min-width", + 1, + 9, + { + "value": "min-width" + } + ], + [ + "colon-token", + ":", + 10, + 10, + null + ], + [ + "whitespace-token", + " ", + 11, + 11, + null + ], + [ + "function-token", + "calc(", + 12, + 16, + { + "value": "calc" + } + ], + [ + "dimension-token", + "10px", + 17, + 20, + { + "value": 10, + "type": "integer", + "unit": "px" + } + ], + [ + "whitespace-token", + " ", + 21, + 21, + null + ], + [ + "delim-token", + "+", + 22, + 22, + { + "value": "+" + } + ], + [ + "whitespace-token", + " ", + 23, + 23, + null + ], + [ + "dimension-token", + "2px", + 24, + 26, + { + "value": 2, + "type": "integer", + "unit": "px" + } + ], + [ + ")-token", + ")", + 27, + 27, + null + ] + ] + }, + "before": [ + [ + "(-token", + "(", + 0, + 0, + null + ] + ], + "after": [ + [ + ")-token", + ")", + 28, + 28, + null + ] + ] + }, + "before": [], + "after": [] + } + } + } +] \ No newline at end of file diff --git a/packages/media-query-list-parser/test/cases/mf-plain/0006.mjs b/packages/media-query-list-parser/test/cases/mf-plain/0006.mjs new file mode 100644 index 000000000..76cfa33a6 --- /dev/null +++ b/packages/media-query-list-parser/test/cases/mf-plain/0006.mjs @@ -0,0 +1,13 @@ +import assert from 'assert'; +import { runTest } from '../../util/run-test.mjs'; + +runTest( + '(min-width: calc(10px + 2px))', + 'mf-plain/0006', + (actual, expected) => { + assert.deepStrictEqual( + actual, + expected, + ); + }, +); diff --git a/packages/media-query-list-parser/test/cases/mf-plain/0007.expect.json b/packages/media-query-list-parser/test/cases/mf-plain/0007.expect.json new file mode 100644 index 000000000..049a166df --- /dev/null +++ b/packages/media-query-list-parser/test/cases/mf-plain/0007.expect.json @@ -0,0 +1,351 @@ +[ + { + "type": "media-query-without-type", + "string": "(min-width: c\\61 lc(10px + 2px))", + "media": { + "type": "media-condition", + "media": { + "type": "media-in-parens", + "media": { + "type": "media-feature", + "feature": { + "type": "mf-plain", + "name": { + "type": "mf-name", + "name": "min-width", + "tokens": [ + [ + "ident-token", + "min-width", + 1, + 9, + { + "value": "min-width" + } + ] + ] + }, + "value": { + "type": "mf-value", + "value": { + "type": "function", + "name": "calc", + "tokens": [ + [ + "function-token", + "c\\61 lc(", + 12, + 19, + { + "value": "calc" + } + ], + [ + "dimension-token", + "10px", + 20, + 23, + { + "value": 10, + "type": "integer", + "unit": "px" + } + ], + [ + "whitespace-token", + " ", + 24, + 24, + null + ], + [ + "delim-token", + "+", + 25, + 25, + { + "value": "+" + } + ], + [ + "whitespace-token", + " ", + 26, + 26, + null + ], + [ + "dimension-token", + "2px", + 27, + 29, + { + "value": 2, + "type": "integer", + "unit": "px" + } + ], + [ + ")-token", + ")", + 30, + 30, + null + ] + ], + "value": [ + { + "type": "token", + "tokens": [ + [ + "dimension-token", + "10px", + 20, + 23, + { + "value": 10, + "type": "integer", + "unit": "px" + } + ] + ] + }, + { + "type": "whitespace", + "tokens": [ + [ + "whitespace-token", + " ", + 24, + 24, + null + ] + ] + }, + { + "type": "token", + "tokens": [ + [ + "delim-token", + "+", + 25, + 25, + { + "value": "+" + } + ] + ] + }, + { + "type": "whitespace", + "tokens": [ + [ + "whitespace-token", + " ", + 26, + 26, + null + ] + ] + }, + { + "type": "token", + "tokens": [ + [ + "dimension-token", + "2px", + 27, + 29, + { + "value": 2, + "type": "integer", + "unit": "px" + } + ] + ] + } + ] + }, + "tokens": [ + [ + "whitespace-token", + " ", + 11, + 11, + null + ], + [ + "function-token", + "c\\61 lc(", + 12, + 19, + { + "value": "calc" + } + ], + [ + "dimension-token", + "10px", + 20, + 23, + { + "value": 10, + "type": "integer", + "unit": "px" + } + ], + [ + "whitespace-token", + " ", + 24, + 24, + null + ], + [ + "delim-token", + "+", + 25, + 25, + { + "value": "+" + } + ], + [ + "whitespace-token", + " ", + 26, + 26, + null + ], + [ + "dimension-token", + "2px", + 27, + 29, + { + "value": 2, + "type": "integer", + "unit": "px" + } + ], + [ + ")-token", + ")", + 30, + 30, + null + ] + ] + }, + "tokens": [ + [ + "ident-token", + "min-width", + 1, + 9, + { + "value": "min-width" + } + ], + [ + "colon-token", + ":", + 10, + 10, + null + ], + [ + "whitespace-token", + " ", + 11, + 11, + null + ], + [ + "function-token", + "c\\61 lc(", + 12, + 19, + { + "value": "calc" + } + ], + [ + "dimension-token", + "10px", + 20, + 23, + { + "value": 10, + "type": "integer", + "unit": "px" + } + ], + [ + "whitespace-token", + " ", + 24, + 24, + null + ], + [ + "delim-token", + "+", + 25, + 25, + { + "value": "+" + } + ], + [ + "whitespace-token", + " ", + 26, + 26, + null + ], + [ + "dimension-token", + "2px", + 27, + 29, + { + "value": 2, + "type": "integer", + "unit": "px" + } + ], + [ + ")-token", + ")", + 30, + 30, + null + ] + ] + }, + "before": [ + [ + "(-token", + "(", + 0, + 0, + null + ] + ], + "after": [ + [ + ")-token", + ")", + 31, + 31, + null + ] + ] + }, + "before": [], + "after": [] + } + } + } +] \ No newline at end of file diff --git a/packages/media-query-list-parser/test/cases/mf-plain/0007.mjs b/packages/media-query-list-parser/test/cases/mf-plain/0007.mjs new file mode 100644 index 000000000..3f5b8a294 --- /dev/null +++ b/packages/media-query-list-parser/test/cases/mf-plain/0007.mjs @@ -0,0 +1,13 @@ +import assert from 'assert'; +import { runTest } from '../../util/run-test.mjs'; + +runTest( + '(min-width: c\\61 lc(10px + 2px))', + 'mf-plain/0007', + (actual, expected) => { + assert.deepStrictEqual( + actual, + expected, + ); + }, +); diff --git a/packages/media-query-list-parser/test/cases/mf-plain/0008.expect.json b/packages/media-query-list-parser/test/cases/mf-plain/0008.expect.json new file mode 100644 index 000000000..3dcecbc18 --- /dev/null +++ b/packages/media-query-list-parser/test/cases/mf-plain/0008.expect.json @@ -0,0 +1,81 @@ +[ + { + "type": "media-query-without-type", + "string": "(min-width: token('foo'))", + "media": { + "type": "media-condition", + "media": { + "type": "media-in-parens", + "media": { + "type": "general-enclosed", + "tokens": [ + [ + "(-token", + "(", + 0, + 0, + null + ], + [ + "ident-token", + "min-width", + 1, + 9, + { + "value": "min-width" + } + ], + [ + "colon-token", + ":", + 10, + 10, + null + ], + [ + "whitespace-token", + " ", + 11, + 11, + null + ], + [ + "function-token", + "token(", + 12, + 17, + { + "value": "token" + } + ], + [ + "string-token", + "'foo'", + 18, + 22, + { + "value": "foo" + } + ], + [ + ")-token", + ")", + 23, + 23, + null + ], + [ + ")-token", + ")", + 24, + 24, + null + ] + ] + }, + "before": [], + "after": [] + } + } + } +] \ No newline at end of file diff --git a/packages/media-query-list-parser/test/cases/mf-plain/0008.mjs b/packages/media-query-list-parser/test/cases/mf-plain/0008.mjs new file mode 100644 index 000000000..d467ff39a --- /dev/null +++ b/packages/media-query-list-parser/test/cases/mf-plain/0008.mjs @@ -0,0 +1,14 @@ +import assert from 'assert'; +import { runTest } from '../../util/run-test.mjs'; + +runTest( + '(min-width: token(\'foo\'))', + 'mf-plain/0008', + (actual, expected) => { + assert.deepStrictEqual( + actual, + expected, + ); + }, + 1, +); diff --git a/packages/media-query-list-parser/test/cases/query-with-type/0008.expect.json b/packages/media-query-list-parser/test/cases/query-with-type/0008.expect.json index 68d2f7108..327ef6ac7 100644 --- a/packages/media-query-list-parser/test/cases/query-with-type/0008.expect.json +++ b/packages/media-query-list-parser/test/cases/query-with-type/0008.expect.json @@ -42,7 +42,21 @@ "type": "media-feature", "feature": { "type": "mf-boolean", - "name": "height", + "name": { + "type": "mf-name", + "name": "height", + "tokens": [ + [ + "ident-token", + "height", + 12, + 17, + { + "value": "height" + } + ] + ] + }, "tokens": [ [ "ident-token", diff --git a/packages/media-query-list-parser/test/cases/specification-examples/0003.expect.json b/packages/media-query-list-parser/test/cases/specification-examples/0003.expect.json index 632d6888f..d9e88c40a 100644 --- a/packages/media-query-list-parser/test/cases/specification-examples/0003.expect.json +++ b/packages/media-query-list-parser/test/cases/specification-examples/0003.expect.json @@ -40,7 +40,21 @@ "type": "media-feature", "feature": { "type": "mf-boolean", - "name": "color", + "name": { + "type": "mf-name", + "name": "color", + "tokens": [ + [ + "ident-token", + "color", + 12, + 16, + { + "value": "color" + } + ] + ] + }, "tokens": [ [ "ident-token", @@ -132,7 +146,21 @@ "type": "media-feature", "feature": { "type": "mf-boolean", - "name": "color", + "name": { + "type": "mf-name", + "name": "color", + "tokens": [ + [ + "ident-token", + "color", + 36, + 40, + { + "value": "color" + } + ] + ] + }, "tokens": [ [ "ident-token", diff --git a/packages/media-query-list-parser/test/cases/specification-examples/0010.expect.json b/packages/media-query-list-parser/test/cases/specification-examples/0010.expect.json index 86d043cf0..b4dae1431 100644 --- a/packages/media-query-list-parser/test/cases/specification-examples/0010.expect.json +++ b/packages/media-query-list-parser/test/cases/specification-examples/0010.expect.json @@ -36,7 +36,21 @@ "type": "media-feature", "feature": { "type": "mf-boolean", - "name": "color", + "name": { + "type": "mf-name", + "name": "color", + "tokens": [ + [ + "ident-token", + "color", + 6, + 10, + { + "value": "color" + } + ] + ] + }, "tokens": [ [ "ident-token", @@ -126,7 +140,21 @@ "type": "media-feature", "feature": { "type": "mf-boolean", - "name": "hover", + "name": { + "type": "mf-name", + "name": "hover", + "tokens": [ + [ + "ident-token", + "hover", + 18, + 22, + { + "value": "hover" + } + ] + ] + }, "tokens": [ [ "ident-token", diff --git a/packages/media-query-list-parser/test/cases/specification-examples/0011.expect.json b/packages/media-query-list-parser/test/cases/specification-examples/0011.expect.json index 388ba1017..e5428a881 100644 --- a/packages/media-query-list-parser/test/cases/specification-examples/0011.expect.json +++ b/packages/media-query-list-parser/test/cases/specification-examples/0011.expect.json @@ -36,7 +36,21 @@ "type": "media-feature", "feature": { "type": "mf-boolean", - "name": "color", + "name": { + "type": "mf-name", + "name": "color", + "tokens": [ + [ + "ident-token", + "color", + 6, + 10, + { + "value": "color" + } + ] + ] + }, "tokens": [ [ "ident-token", @@ -105,7 +119,21 @@ "type": "media-feature", "feature": { "type": "mf-boolean", - "name": "hover", + "name": { + "type": "mf-name", + "name": "hover", + "tokens": [ + [ + "ident-token", + "hover", + 17, + 21, + { + "value": "hover" + } + ] + ] + }, "tokens": [ [ "ident-token", diff --git a/packages/media-query-list-parser/test/cases/specification-examples/0012.expect.json b/packages/media-query-list-parser/test/cases/specification-examples/0012.expect.json index 0ff982293..d9808f695 100644 --- a/packages/media-query-list-parser/test/cases/specification-examples/0012.expect.json +++ b/packages/media-query-list-parser/test/cases/specification-examples/0012.expect.json @@ -36,7 +36,21 @@ "type": "media-feature", "feature": { "type": "mf-boolean", - "name": "color", + "name": { + "type": "mf-name", + "name": "color", + "tokens": [ + [ + "ident-token", + "color", + 6, + 10, + { + "value": "color" + } + ] + ] + }, "tokens": [ [ "ident-token", @@ -150,7 +164,21 @@ "type": "media-feature", "feature": { "type": "mf-boolean", - "name": "hover", + "name": { + "type": "mf-name", + "name": "hover", + "tokens": [ + [ + "ident-token", + "hover", + 24, + 28, + { + "value": "hover" + } + ] + ] + }, "tokens": [ [ "ident-token", diff --git a/packages/media-query-list-parser/test/cases/specification-examples/0013.expect.json b/packages/media-query-list-parser/test/cases/specification-examples/0013.expect.json index 24a092e18..0f0138d02 100644 --- a/packages/media-query-list-parser/test/cases/specification-examples/0013.expect.json +++ b/packages/media-query-list-parser/test/cases/specification-examples/0013.expect.json @@ -12,7 +12,21 @@ "type": "media-feature", "feature": { "type": "mf-boolean", - "name": "color", + "name": { + "type": "mf-name", + "name": "color", + "tokens": [ + [ + "ident-token", + "color", + 1, + 5, + { + "value": "color" + } + ] + ] + }, "tokens": [ [ "ident-token", @@ -87,7 +101,21 @@ "type": "media-feature", "feature": { "type": "mf-boolean", - "name": "pointer", + "name": { + "type": "mf-name", + "name": "pointer", + "tokens": [ + [ + "ident-token", + "pointer", + 14, + 20, + { + "value": "pointer" + } + ] + ] + }, "tokens": [ [ "ident-token", @@ -156,7 +184,21 @@ "type": "media-feature", "feature": { "type": "mf-boolean", - "name": "hover", + "name": { + "type": "mf-name", + "name": "hover", + "tokens": [ + [ + "ident-token", + "hover", + 27, + 31, + { + "value": "hover" + } + ] + ] + }, "tokens": [ [ "ident-token", diff --git a/packages/media-query-list-parser/test/cases/specification-examples/0014.expect.json b/packages/media-query-list-parser/test/cases/specification-examples/0014.expect.json index 26292910c..9d2414f11 100644 --- a/packages/media-query-list-parser/test/cases/specification-examples/0014.expect.json +++ b/packages/media-query-list-parser/test/cases/specification-examples/0014.expect.json @@ -18,7 +18,21 @@ "type": "media-feature", "feature": { "type": "mf-boolean", - "name": "color", + "name": { + "type": "mf-name", + "name": "color", + "tokens": [ + [ + "ident-token", + "color", + 2, + 6, + { + "value": "color" + } + ] + ] + }, "tokens": [ [ "ident-token", @@ -87,7 +101,21 @@ "type": "media-feature", "feature": { "type": "mf-boolean", - "name": "pointer", + "name": { + "type": "mf-name", + "name": "pointer", + "tokens": [ + [ + "ident-token", + "pointer", + 14, + 20, + { + "value": "pointer" + } + ] + ] + }, "tokens": [ [ "ident-token", @@ -181,7 +209,21 @@ "type": "media-feature", "feature": { "type": "mf-boolean", - "name": "hover", + "name": { + "type": "mf-name", + "name": "hover", + "tokens": [ + [ + "ident-token", + "hover", + 28, + 32, + { + "value": "hover" + } + ] + ] + }, "tokens": [ [ "ident-token", diff --git a/packages/media-query-list-parser/test/cases/specification-examples/0019.expect.json b/packages/media-query-list-parser/test/cases/specification-examples/0019.expect.json index 67bb1b659..74aa8072c 100644 --- a/packages/media-query-list-parser/test/cases/specification-examples/0019.expect.json +++ b/packages/media-query-list-parser/test/cases/specification-examples/0019.expect.json @@ -16,7 +16,21 @@ "type": "media-feature", "feature": { "type": "mf-boolean", - "name": "color", + "name": { + "type": "mf-name", + "name": "color", + "tokens": [ + [ + "ident-token", + "color", + 2, + 6, + { + "value": "color" + } + ] + ] + }, "tokens": [ [ "ident-token", @@ -85,7 +99,21 @@ "type": "media-feature", "feature": { "type": "mf-boolean", - "name": "width", + "name": { + "type": "mf-name", + "name": "width", + "tokens": [ + [ + "ident-token", + "width", + 14, + 18, + { + "value": "width" + } + ] + ] + }, "tokens": [ [ "ident-token", diff --git a/packages/media-query-list-parser/test/cases/various/0008.expect.json b/packages/media-query-list-parser/test/cases/various/0008.expect.json new file mode 100644 index 000000000..f81abbe28 --- /dev/null +++ b/packages/media-query-list-parser/test/cases/various/0008.expect.json @@ -0,0 +1,128 @@ +[ + { + "type": "media-query-without-type", + "string": "(min-width: 300px)", + "media": { + "type": "media-condition", + "media": { + "type": "media-in-parens", + "media": { + "type": "media-feature", + "feature": { + "type": "mf-plain", + "name": { + "type": "mf-name", + "name": "min-width", + "tokens": [ + [ + "ident-token", + "min-width", + 1, + 9, + { + "value": "min-width" + } + ] + ] + }, + "value": { + "type": "mf-value", + "value": { + "type": "token", + "tokens": [ + [ + "dimension-token", + "300px", + 12, + 16, + { + "value": 300, + "type": "integer", + "unit": "px" + } + ] + ] + }, + "tokens": [ + [ + "whitespace-token", + " ", + 11, + 11, + null + ], + [ + "dimension-token", + "300px", + 12, + 16, + { + "value": 300, + "type": "integer", + "unit": "px" + } + ] + ] + }, + "tokens": [ + [ + "ident-token", + "min-width", + 1, + 9, + { + "value": "min-width" + } + ], + [ + "colon-token", + ":", + 10, + 10, + null + ], + [ + "whitespace-token", + " ", + 11, + 11, + null + ], + [ + "dimension-token", + "300px", + 12, + 16, + { + "value": 300, + "type": "integer", + "unit": "px" + } + ] + ] + }, + "before": [ + [ + "(-token", + "(", + 0, + 0, + null + ] + ], + "after": [ + [ + ")-token", + ")", + -1, + -1, + null + ] + ] + }, + "before": [], + "after": [] + } + } + } +] \ No newline at end of file diff --git a/packages/media-query-list-parser/test/cases/various/0008.mjs b/packages/media-query-list-parser/test/cases/various/0008.mjs new file mode 100644 index 000000000..a14b89947 --- /dev/null +++ b/packages/media-query-list-parser/test/cases/various/0008.mjs @@ -0,0 +1,13 @@ +import assert from 'assert'; +import { runTest } from '../../util/run-test.mjs'; + +runTest( + '(min-width: 300px', + 'various/0008', + (actual, expected) => { + assert.deepStrictEqual( + actual, + expected, + ); + }, +); diff --git a/packages/media-query-list-parser/test/get-name/0001.mjs b/packages/media-query-list-parser/test/get-name/0001.mjs new file mode 100644 index 000000000..c2e3a94ff --- /dev/null +++ b/packages/media-query-list-parser/test/get-name/0001.mjs @@ -0,0 +1,130 @@ +import assert from 'assert'; +import { newMediaFeatureBoolean, newMediaFeaturePlain, parse } from '@csstools/media-query-list-parser'; +import { TokenType } from '@csstools/css-tokenizer'; + +{ + const feature = newMediaFeaturePlain('min-width', [TokenType.Dimension, '300px', 0, 0, { value: 300, unit: 'px' }]); + assert.strictEqual( + feature.getName(), + 'min-width', + ); + + assert.deepStrictEqual( + feature.getNameToken(), + ['ident-token', 'min-width', -1, -1, { value: 'min-width' }], + ); + + assert.strictEqual( + feature.feature.getName(), + 'min-width', + ); + + assert.deepStrictEqual( + feature.feature.getNameToken(), + ['ident-token', 'min-width', -1, -1, { value: 'min-width' }], + ); + + assert.strictEqual( + feature.feature.name.getName(), + 'min-width', + ); + + assert.deepStrictEqual( + feature.feature.name.getNameToken(), + ['ident-token', 'min-width', -1, -1, { value: 'min-width' }], + ); +} + +{ + const feature = newMediaFeaturePlain('min width', [TokenType.Dimension, '300px', 0, 0, { value: 300, unit: 'px' }]); + assert.strictEqual( + feature.getName(), + 'min width', + ); + + assert.deepStrictEqual( + feature.getNameToken(), + ['ident-token', 'min\\20 width', -1, -1, { value: 'min width' }], + ); +} + +{ + const feature = newMediaFeatureBoolean('width'); + assert.strictEqual( + feature.getName(), + 'width', + ); + + assert.deepStrictEqual( + feature.getNameToken(), + ['ident-token', 'width', -1, -1, { value: 'width' }], + ); + + assert.strictEqual( + feature.feature.getName(), + 'width', + ); + + assert.deepStrictEqual( + feature.feature.getNameToken(), + ['ident-token', 'width', -1, -1, { value: 'width' }], + ); + + assert.strictEqual( + feature.feature.name.getName(), + 'width', + ); + + assert.deepStrictEqual( + feature.feature.name.getNameToken(), + ['ident-token', 'width', -1, -1, { value: 'width' }], + ); +} + +{ + const feature = newMediaFeatureBoolean('w dth'); + assert.strictEqual( + feature.getName(), + 'w dth', + ); + + assert.deepStrictEqual( + feature.getNameToken(), + ['ident-token', 'w\\20 dth', -1, -1, { value: 'w dth' }], + ); +} + +{ + const queries = parse('(300px < width < 400px)'); + const feature = queries[0]?.media?.media?.media; + + assert.strictEqual( + feature.getName(), + 'width', + ); + + assert.deepStrictEqual( + feature.getNameToken(), + ['ident-token', 'width', 9, 13, { value: 'width' }], + ); + + assert.strictEqual( + feature.feature.getName(), + 'width', + ); + + assert.deepStrictEqual( + feature.feature.getNameToken(), + ['ident-token', 'width', 9, 13, { value: 'width' }], + ); + + assert.strictEqual( + feature.feature.name.getName(), + 'width', + ); + + assert.deepStrictEqual( + feature.feature.name.getNameToken(), + ['ident-token', 'width', 9, 13, { value: 'width' }], + ); +} diff --git a/packages/media-query-list-parser/test/new-feature/0001.mjs b/packages/media-query-list-parser/test/new-feature/0001.mjs new file mode 100644 index 000000000..0a53ea6e2 --- /dev/null +++ b/packages/media-query-list-parser/test/new-feature/0001.mjs @@ -0,0 +1,179 @@ +import assert from 'assert'; +import { newMediaFeaturePlain } from '@csstools/media-query-list-parser'; +import { TokenType } from '@csstools/css-tokenizer'; + +{ + const feature = newMediaFeaturePlain( + 'min-width', + [TokenType.Function, 'calc(', -1, -1, { value: 'calc' }], + [TokenType.Dimension, '300px', -1, -1, { value: 300, unit: 'px' }], + [TokenType.CloseParen, ')', -1, -1, undefined], + ); + + assert.deepStrictEqual( + feature.toJSON(), + { + 'type': 'media-feature', + 'feature': { + 'type': 'mf-plain', + 'name': { + 'type': 'mf-name', + 'name': 'min-width', + 'tokens': [ + [ + 'ident-token', + 'min-width', + -1, + -1, + { + 'value': 'min-width', + }, + ], + ], + }, + 'value': { + 'type': 'mf-value', + 'value': { + 'type': 'function', + 'name': 'calc', + 'tokens': [ + [ + 'function-token', + 'calc(', + -1, + -1, + { + 'value': 'calc', + }, + ], + [ + 'dimension-token', + '300px', + -1, + -1, + { + 'value': 300, + 'unit': 'px', + }, + ], + [ + ')-token', + ')', + -1, + -1, + undefined, + ], + ], + 'value': [ + { + 'type': 'token', + 'tokens': [ + [ + 'dimension-token', + '300px', + -1, + -1, + { + 'value': 300, + 'unit': 'px', + }, + ], + ], + }, + ], + }, + 'tokens': [ + [ + 'function-token', + 'calc(', + -1, + -1, + { + 'value': 'calc', + }, + ], + [ + 'dimension-token', + '300px', + -1, + -1, + { + 'value': 300, + 'unit': 'px', + }, + ], + [ + ')-token', + ')', + -1, + -1, + undefined, + ], + ], + }, + 'tokens': [ + [ + 'ident-token', + 'min-width', + -1, + -1, + { + 'value': 'min-width', + }, + ], + [ + 'colon-token', + ':', + -1, + -1, + undefined, + ], + [ + 'function-token', + 'calc(', + -1, + -1, + { + 'value': 'calc', + }, + ], + [ + 'dimension-token', + '300px', + -1, + -1, + { + 'value': 300, + 'unit': 'px', + }, + ], + [ + ')-token', + ')', + -1, + -1, + undefined, + ], + ], + }, + 'before': [ + [ + '(-token', + '(', + -1, + -1, + undefined, + ], + ], + 'after': [ + [ + ')-token', + ')', + -1, + -1, + undefined, + ], + ], + }, + ); +} diff --git a/packages/media-query-list-parser/test/test.mjs b/packages/media-query-list-parser/test/test.mjs index 67bbf4beb..ecac15c0a 100644 --- a/packages/media-query-list-parser/test/test.mjs +++ b/packages/media-query-list-parser/test/test.mjs @@ -1,5 +1,17 @@ import './api/options.mjs'; +import './cases/custom-media/0001.mjs'; +import './cases/custom-media/0002.mjs'; +import './cases/custom-media/0003.mjs'; +import './cases/custom-media/0004.mjs'; +import './cases/custom-media/0005.mjs'; +import './cases/custom-media/0006.mjs'; +import './cases/custom-media/0007.mjs'; +import './cases/custom-media/0008.mjs'; +import './cases/custom-media/0009.mjs'; +import './cases/custom-media/0010.mjs'; +import './cases/custom-media/0011.mjs'; + import './cases/media-not/0001.mjs'; import './cases/mf-boolean/0001.mjs'; @@ -13,6 +25,9 @@ import './cases/mf-plain/0002.mjs'; import './cases/mf-plain/0003.mjs'; import './cases/mf-plain/0004.mjs'; import './cases/mf-plain/0005.mjs'; +import './cases/mf-plain/0006.mjs'; +import './cases/mf-plain/0007.mjs'; +import './cases/mf-plain/0008.mjs'; import './cases/mf-range/0001.mjs'; import './cases/mf-range/0002.mjs'; @@ -65,5 +80,10 @@ import './cases/various/0004.mjs'; import './cases/various/0005.mjs'; import './cases/various/0006.mjs'; import './cases/various/0007.mjs'; +import './cases/various/0008.mjs'; + +import './get-name/0001.mjs'; + +import './new-feature/0001.mjs'; import './serialize/0001.mjs'; diff --git a/packages/media-query-list-parser/test/util/run-test-custom-media.mjs b/packages/media-query-list-parser/test/util/run-test-custom-media.mjs new file mode 100644 index 000000000..756520bbf --- /dev/null +++ b/packages/media-query-list-parser/test/util/run-test-custom-media.mjs @@ -0,0 +1,48 @@ +import fs from 'fs'; +import path from 'path'; +import { isGeneralEnclosed, parseCustomMedia } from '@csstools/media-query-list-parser'; + +export function runTestCustomMedia(source, testPath, assertEqual, expectGeneralEnclosed = 0) { + const resultAST = parseCustomMedia(source, { + preserveInvalidMediaQueries: true, + }); + + const resultAST_JSON = JSON.stringify(resultAST, null, '\t'); + + if (process.env['REWRITE_EXPECTS'] === 'true') { + fs.writeFileSync(path.join(process.cwd(), `./test/cases/${testPath}.expect.json`), resultAST_JSON); + fs.writeFileSync(path.join(process.cwd(), `./test/cases/${testPath}.result.json`), resultAST_JSON); + } else { + const expectData = JSON.parse(fs.readFileSync(path.join(process.cwd(), `./test/cases/${testPath}.expect.json`)).toString()); + if (resultAST === false) { + assertEqual(resultAST, expectData); + return; + } + + assertEqual( + resultAST.toString(), + expectData.string, + ); + + assertEqual( + JSON.parse(resultAST_JSON), + expectData, + ); + + let generalEnclosedCounter = 0; + if (resultAST.mediaQueryList) { + resultAST.mediaQueryList.map((x) => { + x.walk((entry) => { + if (isGeneralEnclosed(entry.node)) { + generalEnclosedCounter++; + } + }); + }); + } + + assertEqual( + generalEnclosedCounter, + expectGeneralEnclosed, + ); + } +} diff --git a/plugins/postcss-design-tokens/dist/index.cjs b/plugins/postcss-design-tokens/dist/index.cjs index fe371d8a0..edb425604 100644 --- a/plugins/postcss-design-tokens/dist/index.cjs +++ b/plugins/postcss-design-tokens/dist/index.cjs @@ -1 +1 @@ -"use strict";var e=require("postcss-value-parser"),t=require("path"),n=require("fs"),r=require("module"),o=require("@csstools/css-parser-algorithms"),s=require("@csstools/css-tokenizer");function toposort(e,t){let n=e.length;const r=new Array(n),o={};let s=n;const a=makeOutgoingEdges(t),i=makeNodesHash(e);for(t.forEach((function(e){if(!i.has(e[0])||!i.has(e[1]))throw new Error("Unknown token. Make sure to provide all tokens used in aliases.")}));s--;)o[s]||visit(e[s],s,new Set);return r;function visit(e,t,s){if(s.has(e)){let t;try{t=", token was: "+JSON.stringify(e)}catch(e){t=""}throw new Error("Cyclic dependency"+t)}if(!i.has(e))throw new Error("Found unknown token. Make sure to provided all involved tokens. Unknown token: "+JSON.stringify(e));if(o[t])return;o[t]=!0;let u=a.get(e)||new Set;if(u=Array.from(u),t=u.length){s.add(e);do{const e=u[--t];visit(e,i.get(e),s)}while(t);s.delete(e)}r[--n]=e}}function makeOutgoingEdges(e){const t=new Map;for(let n=0,r=e.length;napplyTransformsToValue(o,e),name:String(e.name??"")||t,comment:String(e.comment??"")||void 0,metadata:{name:String(e.name??"")?t:void 0,path:[...n,t],filePath:r,isSource:!0}}}const a=new Map;function applyTransformsToValue(t,n){if(!t)return"";if(!n)return t;if(!n.toUnit)return t;const r=e.unit(t??"");if(!r||r.unit===n.toUnit)return t;if(!r.unit){if(a.has(n.toUnit)){if(a.get(n.toUnit))return`${t}${n.toUnit}`;throw new Error(`Invalid unit "${n.toUnit}" for "${t}"`)}try{const r=e.unit(`${t}${n.toUnit}`);if(r&&r.unit===n.toUnit)return a.set(n.toUnit,!0),`${t}${n.toUnit}`;a.set(n.toUnit,!1)}catch(e){a.set(n.toUnit,!1)}throw new Error(`Invalid unit "${n.toUnit}" for "${t}"`)}var o,s;return"rem"===r.unit&&"px"===n.toUnit?remToPx(parseFloat(r.number),(null==(o=n.pluginOptions)?void 0:o.rootFontSize)??16):"px"===r.unit&&"rem"===n.toUnit?pxToRem(parseFloat(r.number),(null==(s=n.pluginOptions)?void 0:s.rootFontSize)??16):t}function remToPx(e,t){return`${formatFloat(e*t)}px`}function pxToRem(e,t){return`${formatFloat(e/t)}rem`}function formatFloat(e){if(Number.isInteger(e))return e.toString();let t=e.toFixed(5);for(let e=t.length;e>0&&"."!==t[e];e--)"0"===t[e]||(t=t.slice(0,e+1));return t}function dereferenceTokenValues(e){const t=new Set,n=new Map;for(const[r,o]of e.entries()){const e=parseReferences(o.value);e.length&&(t.add(r),n.set(r,e))}for(const[r,o]of n.entries()){for(let n=0;n"value-reference"===e.type)))continue;const s=o.map((e=>e.value)).join(""),a=e.get(r);a.value=s,a.cssValue=e=>applyTransformsToValue(s,e),e.set(r,a),t.delete(r),n.delete(r)}if(0===t.size)return e;{const r=Array.from(e.keys()),o=[];for(const[e,t]of n.entries())for(let n=0;n"value-reference"===e.type)))throw new Error('Token "'+r+'" can not be fully resolved');const s=o.map((e=>e.value)).join(""),a=e.get(r);a.value=s,a.cssValue=e=>applyTransformsToValue(s,e),e.set(r,a),t.delete(r),n.delete(r)}if(0===t.size)return e}return e}function parseReferences(e){if("string"!=typeof e)return[];if(-1===e.indexOf("{"))return[];const t=[];let n=!1,r=!1,o="";for(let s=0;s0&&(t.push({type:"value-non-reference",value:o}),o=""),r=!0;break;case"}":if(!r)throw new Error('Unexpected "}" in "'+e+'" at '+s);if(0===o.length)throw new Error('Empty alias "{}" in "'+e+'" at '+s);{let e=o.trim();".value"===e.slice(-6)&&(e=e.slice(0,-6)),t.push({type:"value-reference",raw:e}),o=""}n=!0,r=!1;break;default:o+=a}}if(r)throw new Error('Unexpected end of alias in "'+e+'"');return o.length>0&&t.push({type:"value-non-reference",value:o}),n?t:[]}function extractTokens(e,t,n){const r=new Map;for(const o in e)if(Object.hasOwnProperty.call(e,o)){if(null===e[o]||"object"!=typeof e[o]||Array.isArray(e[o]))throw new Error(`Parsing error at "${[...t,o].join(".")}"`);const s=Object(e[o]);if(!s)throw new Error(`Parsing error at "${[...t,o].join(".")}"`);if(void 0!==s.value){const e=extractStyleDictionaryV3Token(s,o,t,n);r.set(e.metadata.path.join("."),e);continue}for(const[e,a]of extractTokens(s,[...t,o],n).entries())r.set(e,a)}return r}function extractStyleDictionaryV3Tokens(e,t){return dereferenceTokenValues(extractTokens(e,[],t))}function extractStyleDictionaryTokens(e,t,n){if("3"===e)return extractStyleDictionaryV3Tokens(t,n);throw new Error("Unsupported version: "+e)}const i="6b4e71e7-4787-42f7-a092-8684961895db",u=r.createRequire("undefined"==typeof document?new(require("url").URL)("file:"+__filename).href:document.currentScript&&document.currentScript.src||new URL("index.cjs",document.baseURI).href);function parseImport(t){const n=e(t),r={filePath:"",format:"standard",conditions:[i]};return n.walk((e=>{"function"===e.type&&"url"===e.value&&(r.filePath=e.nodes[0].value),"function"===e.type&&"format"===e.value&&(r.format=e.nodes[0].value),"function"===e.type&&"when"===e.value&&(r.conditions=e.nodes.filter((e=>"string"===e.type)).map((e=>e.value)))})),r.conditions.length||(r.conditions=[i]),r}async function tokensFromImport(e,r,o,s){const{filePath:a,format:i,conditions:l}=parseImport(o);if(!l.every((t=>e.includes(t))))return!1;let c="";if(a.startsWith("node_modules://"))try{c=u.resolve(a.slice(15),{paths:[t.dirname(r)]})}catch(e){throw new Error(`Failed to read ${a} with error ${e.message}`)}else c=t.resolve(t.dirname(r),a);if(s.has(c))return!1;s.add(c);const f=await n.promises.readFile(c,"utf8"),p=JSON.parse(f);if("style-dictionary3"===i)return{filePath:t.resolve(a),tokens:extractStyleDictionaryTokens("3",p,c)};throw new Error("Unsupported format: "+i)}function mergeTokens(e,t){const n=new Map(e);for(const[e,r]of t)n.set(e,r);return n}function parsePluginOptions(e){const t={importAtRuleName:"design-tokens",is:[i],unitsAndValues:{rootFontSize:16},valueFunctionName:"design-token"};return e?("object"!=typeof e||(Array.isArray(e.is)&&(t.is=e.is.filter((e=>"string"==typeof e))),0===t.is.length&&(t.is=[i]),"object"==typeof e.unitsAndValues&&"number"==typeof e.unitsAndValues.rootFontSize&&((n=e.unitsAndValues.rootFontSize)>0&&n!==1/0)&&(t.unitsAndValues.rootFontSize=e.unitsAndValues.rootFontSize),"string"==typeof e.valueFunctionName&&(t.valueFunctionName=e.valueFunctionName),"string"==typeof e.importAtRuleName&&(t.importAtRuleName=e.importAtRuleName)),t):t;var n}function parseComponentValuesFromTokens(e){return o.parseListOfComponentValues(e,{onParseError:e=>{throw new Error(JSON.stringify(e))}})}function parseComponentValues(e){const t=s.tokenizer({css:e},{commentsAreTokens:!0,onParseError:e=>{throw new Error(JSON.stringify(e))}}),n=[];for(;!t.endOfFile();)n.push(t.nextToken());return n.push(t.nextToken()),parseComponentValuesFromTokens(n)}function transform(e,t,n,r,o){const s=parseComponentValues(r);let a=!1;return s.forEach(((r,i)=>{if("walk"in r){{const u=transformComponentValue(r,e,t,n,o);if(u)return s.splice(i,1,...u),a=!0,!1}r.walk(((r,s)=>{if("string"==typeof s)return;const i=transformComponentValue(r.node,e,t,n,o);return i?(r.parent.value.splice(s,1,...i),a=!0,!1):void 0}))}})),a?s.map((e=>e.toString())).join(""):r}function transformComponentValue(e,t,n,r,a){if(!o.isFunctionNode(e))return;if(e.nameTokenValue().toLowerCase()!==a.valueFunctionName)return;let i="",u="",l="";for(let t=0;t{const t=parsePluginOptions(e);return{postcssPlugin:"postcss-design-tokens",prepare(){let e=new Map,n=new Set;return{OnceExit(){e=new Map,n=new Set},Once:async(r,{result:o})=>{const s=[];r.walkAtRules((e=>{var n,r;if(e.name.toLowerCase()!==t.importAtRuleName)return;if(null==e||null==(n=e.source)||null==(r=n.input)||!r.file)return;const o=e.source.input.file,a=e.params;e.remove(),s.push({filePath:o,params:a,node:e})}));for(const r of s.values()){let s;try{if(s=await tokensFromImport(t.is,r.filePath,r.params,n),!s)continue}catch(e){r.node.warn(o,`Failed to import design tokens from "${r.params}" with error:\n\t`+e.message);continue}o.messages.push({type:"dependency",plugin:"postcss-design-tokens",file:s.filePath,parent:r.filePath}),e=mergeTokens(e,s.tokens)}},Declaration(n,{result:r}){if(n.value.toLowerCase().includes(t.valueFunctionName))try{const o=transform(e,r,n,n.value,t);if(o===n.value)return;n.value=o}catch(e){n.warn(r,`Failed to parse and transform "${n.value}"`)}},AtRule(n,{result:r}){if(n.params.toLowerCase().includes(t.valueFunctionName))try{const o=transform(e,r,n,n.params,t);if(o===n.params)return;n.params=o}catch(e){n.warn(r,`Failed to parse and transform "${n.params}"`)}}}}}};creator.postcss=!0,module.exports=creator; +"use strict";var e=require("postcss-value-parser"),t=require("path"),n=require("fs"),r=require("module"),o=require("@csstools/css-parser-algorithms"),s=require("@csstools/css-tokenizer");function toposort(e,t){let n=e.length;const r=new Array(n),o={};let s=n;const a=makeOutgoingEdges(t),i=makeNodesHash(e);for(t.forEach((function(e){if(!i.has(e[0])||!i.has(e[1]))throw new Error("Unknown token. Make sure to provide all tokens used in aliases.")}));s--;)o[s]||visit(e[s],s,new Set);return r;function visit(e,t,s){if(s.has(e)){let t;try{t=", token was: "+JSON.stringify(e)}catch(e){t=""}throw new Error("Cyclic dependency"+t)}if(!i.has(e))throw new Error("Found unknown token. Make sure to provided all involved tokens. Unknown token: "+JSON.stringify(e));if(o[t])return;o[t]=!0;let u=a.get(e)||new Set;if(u=Array.from(u),t=u.length){s.add(e);do{const e=u[--t];visit(e,i.get(e),s)}while(t);s.delete(e)}r[--n]=e}}function makeOutgoingEdges(e){const t=new Map;for(let n=0,r=e.length;napplyTransformsToValue(o,e),name:String(e.name??"")||t,comment:String(e.comment??"")||void 0,metadata:{name:String(e.name??"")?t:void 0,path:[...n,t],filePath:r,isSource:!0}}}const a=new Map;function applyTransformsToValue(t,n){if(!t)return"";if(!n)return t;if(!n.toUnit)return t;const r=e.unit(t??"");if(!r||r.unit===n.toUnit)return t;if(!r.unit){if(a.has(n.toUnit)){if(a.get(n.toUnit))return`${t}${n.toUnit}`;throw new Error(`Invalid unit "${n.toUnit}" for "${t}"`)}try{const r=e.unit(`${t}${n.toUnit}`);if(r&&r.unit===n.toUnit)return a.set(n.toUnit,!0),`${t}${n.toUnit}`;a.set(n.toUnit,!1)}catch(e){a.set(n.toUnit,!1)}throw new Error(`Invalid unit "${n.toUnit}" for "${t}"`)}var o,s;return"rem"===r.unit&&"px"===n.toUnit?remToPx(parseFloat(r.number),(null==(o=n.pluginOptions)?void 0:o.rootFontSize)??16):"px"===r.unit&&"rem"===n.toUnit?pxToRem(parseFloat(r.number),(null==(s=n.pluginOptions)?void 0:s.rootFontSize)??16):t}function remToPx(e,t){return`${formatFloat(e*t)}px`}function pxToRem(e,t){return`${formatFloat(e/t)}rem`}function formatFloat(e){if(Number.isInteger(e))return e.toString();let t=e.toFixed(5);for(let e=t.length;e>0&&"."!==t[e];e--)"0"===t[e]||(t=t.slice(0,e+1));return t}function dereferenceTokenValues(e){const t=new Set,n=new Map;for(const[r,o]of e.entries()){const e=parseReferences(o.value);e.length&&(t.add(r),n.set(r,e))}for(const[r,o]of n.entries()){for(let n=0;n"value-reference"===e.type)))continue;const s=o.map((e=>e.value)).join(""),a=e.get(r);a.value=s,a.cssValue=e=>applyTransformsToValue(s,e),e.set(r,a),t.delete(r),n.delete(r)}if(0===t.size)return e;{const r=Array.from(e.keys()),o=[];for(const[e,t]of n.entries())for(let n=0;n"value-reference"===e.type)))throw new Error('Token "'+r+'" can not be fully resolved');const s=o.map((e=>e.value)).join(""),a=e.get(r);a.value=s,a.cssValue=e=>applyTransformsToValue(s,e),e.set(r,a),t.delete(r),n.delete(r)}if(0===t.size)return e}return e}function parseReferences(e){if("string"!=typeof e)return[];if(-1===e.indexOf("{"))return[];const t=[];let n=!1,r=!1,o="";for(let s=0;s0&&(t.push({type:"value-non-reference",value:o}),o=""),r=!0;break;case"}":if(!r)throw new Error('Unexpected "}" in "'+e+'" at '+s);if(0===o.length)throw new Error('Empty alias "{}" in "'+e+'" at '+s);{let e=o.trim();".value"===e.slice(-6)&&(e=e.slice(0,-6)),t.push({type:"value-reference",raw:e}),o=""}n=!0,r=!1;break;default:o+=a}}if(r)throw new Error('Unexpected end of alias in "'+e+'"');return o.length>0&&t.push({type:"value-non-reference",value:o}),n?t:[]}function extractTokens(e,t,n){const r=new Map;for(const o in e)if(Object.hasOwnProperty.call(e,o)){if(null===e[o]||"object"!=typeof e[o]||Array.isArray(e[o]))throw new Error(`Parsing error at "${[...t,o].join(".")}"`);const s=Object(e[o]);if(!s)throw new Error(`Parsing error at "${[...t,o].join(".")}"`);if(void 0!==s.value){const e=extractStyleDictionaryV3Token(s,o,t,n);r.set(e.metadata.path.join("."),e);continue}for(const[e,a]of extractTokens(s,[...t,o],n).entries())r.set(e,a)}return r}function extractStyleDictionaryV3Tokens(e,t){return dereferenceTokenValues(extractTokens(e,[],t))}function extractStyleDictionaryTokens(e,t,n){if("3"===e)return extractStyleDictionaryV3Tokens(t,n);throw new Error("Unsupported version: "+e)}const i="6b4e71e7-4787-42f7-a092-8684961895db",u=r.createRequire("undefined"==typeof document?new(require("url").URL)("file:"+__filename).href:document.currentScript&&document.currentScript.src||new URL("index.cjs",document.baseURI).href);function parseImport(t){const n=e(t),r={filePath:"",format:"standard",conditions:[i]};return n.walk((e=>{"function"===e.type&&"url"===e.value&&(r.filePath=e.nodes[0].value),"function"===e.type&&"format"===e.value&&(r.format=e.nodes[0].value),"function"===e.type&&"when"===e.value&&(r.conditions=e.nodes.filter((e=>"string"===e.type)).map((e=>e.value)))})),r.conditions.length||(r.conditions=[i]),r}async function tokensFromImport(e,r,o,s){const{filePath:a,format:i,conditions:l}=parseImport(o);if(!l.every((t=>e.includes(t))))return!1;let c="";if(a.startsWith("node_modules://"))try{c=u.resolve(a.slice(15),{paths:[t.dirname(r)]})}catch(e){throw new Error(`Failed to read ${a} with error ${e.message}`)}else c=t.resolve(t.dirname(r),a);if(s.has(c))return!1;s.add(c);const f=await n.promises.readFile(c,"utf8"),p=JSON.parse(f);if("style-dictionary3"===i)return{filePath:t.resolve(a),tokens:extractStyleDictionaryTokens("3",p,c)};throw new Error("Unsupported format: "+i)}function mergeTokens(e,t){const n=new Map(e);for(const[e,r]of t)n.set(e,r);return n}function parsePluginOptions(e){const t={importAtRuleName:"design-tokens",is:[i],unitsAndValues:{rootFontSize:16},valueFunctionName:"design-token"};return e?("object"!=typeof e||(Array.isArray(e.is)&&(t.is=e.is.filter((e=>"string"==typeof e))),0===t.is.length&&(t.is=[i]),"object"==typeof e.unitsAndValues&&"number"==typeof e.unitsAndValues.rootFontSize&&((n=e.unitsAndValues.rootFontSize)>0&&n!==1/0)&&(t.unitsAndValues.rootFontSize=e.unitsAndValues.rootFontSize),"string"==typeof e.valueFunctionName&&(t.valueFunctionName=e.valueFunctionName),"string"==typeof e.importAtRuleName&&(t.importAtRuleName=e.importAtRuleName)),t):t;var n}function parseComponentValuesFromTokens(e){return o.parseListOfComponentValues(e,{onParseError:e=>{throw new Error(JSON.stringify(e))}})}function parseComponentValues(e){const t=s.tokenizer({css:e},{commentsAreTokens:!0,onParseError:e=>{throw new Error(JSON.stringify(e))}}),n=[];for(;!t.endOfFile();)n.push(t.nextToken());return n.push(t.nextToken()),parseComponentValuesFromTokens(n)}function transform(e,t,n,r,o){const s=parseComponentValues(r);let a=!1;return s.forEach(((r,i)=>{if("walk"in r){{const u=transformComponentValue(r,e,t,n,o);if(u)return s.splice(i,1,...u),a=!0,!1}r.walk(((r,s)=>{if("string"==typeof s)return;const i=transformComponentValue(r.node,e,t,n,o);return i?(r.parent.value.splice(s,1,...i),a=!0,!1):void 0}))}})),a?s.map((e=>e.toString())).join(""):r}function transformComponentValue(e,t,n,r,a){if(!o.isFunctionNode(e))return;if(e.getName().toLowerCase()!==a.valueFunctionName)return;let i="",u="",l="";for(let t=0;t{const t=parsePluginOptions(e);return{postcssPlugin:"postcss-design-tokens",prepare(){let e=new Map,n=new Set;return{OnceExit(){e=new Map,n=new Set},Once:async(r,{result:o})=>{const s=[];r.walkAtRules((e=>{var n,r;if(e.name.toLowerCase()!==t.importAtRuleName)return;if(null==e||null==(n=e.source)||null==(r=n.input)||!r.file)return;const o=e.source.input.file,a=e.params;e.remove(),s.push({filePath:o,params:a,node:e})}));for(const r of s.values()){let s;try{if(s=await tokensFromImport(t.is,r.filePath,r.params,n),!s)continue}catch(e){r.node.warn(o,`Failed to import design tokens from "${r.params}" with error:\n\t`+e.message);continue}o.messages.push({type:"dependency",plugin:"postcss-design-tokens",file:s.filePath,parent:r.filePath}),e=mergeTokens(e,s.tokens)}},Declaration(n,{result:r}){if(n.value.toLowerCase().includes(t.valueFunctionName))try{const o=transform(e,r,n,n.value,t);if(o===n.value)return;n.value=o}catch(e){n.warn(r,`Failed to parse and transform "${n.value}"`)}},AtRule(n,{result:r}){if(n.params.toLowerCase().includes(t.valueFunctionName))try{const o=transform(e,r,n,n.params,t);if(o===n.params)return;n.params=o}catch(e){n.warn(r,`Failed to parse and transform "${n.params}"`)}}}}}};creator.postcss=!0,module.exports=creator; diff --git a/plugins/postcss-design-tokens/dist/index.mjs b/plugins/postcss-design-tokens/dist/index.mjs index d9aa32453..6512aaab4 100644 --- a/plugins/postcss-design-tokens/dist/index.mjs +++ b/plugins/postcss-design-tokens/dist/index.mjs @@ -1 +1 @@ -import e from"postcss-value-parser";import t from"path";import{promises as n}from"fs";import r from"module";import{parseListOfComponentValues as o,isFunctionNode as a,isWhitespaceNode as s,isCommentNode as i,isTokenNode as u}from"@csstools/css-parser-algorithms";import{tokenizer as l,TokenType as c}from"@csstools/css-tokenizer";function toposort(e,t){let n=e.length;const r=new Array(n),o={};let a=n;const s=makeOutgoingEdges(t),i=makeNodesHash(e);for(t.forEach((function(e){if(!i.has(e[0])||!i.has(e[1]))throw new Error("Unknown token. Make sure to provide all tokens used in aliases.")}));a--;)o[a]||visit(e[a],a,new Set);return r;function visit(e,t,a){if(a.has(e)){let t;try{t=", token was: "+JSON.stringify(e)}catch(e){t=""}throw new Error("Cyclic dependency"+t)}if(!i.has(e))throw new Error("Found unknown token. Make sure to provided all involved tokens. Unknown token: "+JSON.stringify(e));if(o[t])return;o[t]=!0;let u=s.get(e)||new Set;if(u=Array.from(u),t=u.length){a.add(e);do{const e=u[--t];visit(e,i.get(e),a)}while(t);a.delete(e)}r[--n]=e}}function makeOutgoingEdges(e){const t=new Map;for(let n=0,r=e.length;napplyTransformsToValue(o,e),name:String(e.name??"")||t,comment:String(e.comment??"")||void 0,metadata:{name:String(e.name??"")?t:void 0,path:[...n,t],filePath:r,isSource:!0}}}const f=new Map;function applyTransformsToValue(t,n){if(!t)return"";if(!n)return t;if(!n.toUnit)return t;const r=e.unit(t??"");if(!r||r.unit===n.toUnit)return t;if(!r.unit){if(f.has(n.toUnit)){if(f.get(n.toUnit))return`${t}${n.toUnit}`;throw new Error(`Invalid unit "${n.toUnit}" for "${t}"`)}try{const r=e.unit(`${t}${n.toUnit}`);if(r&&r.unit===n.toUnit)return f.set(n.toUnit,!0),`${t}${n.toUnit}`;f.set(n.toUnit,!1)}catch(e){f.set(n.toUnit,!1)}throw new Error(`Invalid unit "${n.toUnit}" for "${t}"`)}var o,a;return"rem"===r.unit&&"px"===n.toUnit?remToPx(parseFloat(r.number),(null==(o=n.pluginOptions)?void 0:o.rootFontSize)??16):"px"===r.unit&&"rem"===n.toUnit?pxToRem(parseFloat(r.number),(null==(a=n.pluginOptions)?void 0:a.rootFontSize)??16):t}function remToPx(e,t){return`${formatFloat(e*t)}px`}function pxToRem(e,t){return`${formatFloat(e/t)}rem`}function formatFloat(e){if(Number.isInteger(e))return e.toString();let t=e.toFixed(5);for(let e=t.length;e>0&&"."!==t[e];e--)"0"===t[e]||(t=t.slice(0,e+1));return t}function dereferenceTokenValues(e){const t=new Set,n=new Map;for(const[r,o]of e.entries()){const e=parseReferences(o.value);e.length&&(t.add(r),n.set(r,e))}for(const[r,o]of n.entries()){for(let n=0;n"value-reference"===e.type)))continue;const a=o.map((e=>e.value)).join(""),s=e.get(r);s.value=a,s.cssValue=e=>applyTransformsToValue(a,e),e.set(r,s),t.delete(r),n.delete(r)}if(0===t.size)return e;{const r=Array.from(e.keys()),o=[];for(const[e,t]of n.entries())for(let n=0;n"value-reference"===e.type)))throw new Error('Token "'+r+'" can not be fully resolved');const a=o.map((e=>e.value)).join(""),s=e.get(r);s.value=a,s.cssValue=e=>applyTransformsToValue(a,e),e.set(r,s),t.delete(r),n.delete(r)}if(0===t.size)return e}return e}function parseReferences(e){if("string"!=typeof e)return[];if(-1===e.indexOf("{"))return[];const t=[];let n=!1,r=!1,o="";for(let a=0;a0&&(t.push({type:"value-non-reference",value:o}),o=""),r=!0;break;case"}":if(!r)throw new Error('Unexpected "}" in "'+e+'" at '+a);if(0===o.length)throw new Error('Empty alias "{}" in "'+e+'" at '+a);{let e=o.trim();".value"===e.slice(-6)&&(e=e.slice(0,-6)),t.push({type:"value-reference",raw:e}),o=""}n=!0,r=!1;break;default:o+=s}}if(r)throw new Error('Unexpected end of alias in "'+e+'"');return o.length>0&&t.push({type:"value-non-reference",value:o}),n?t:[]}function extractTokens(e,t,n){const r=new Map;for(const o in e)if(Object.hasOwnProperty.call(e,o)){if(null===e[o]||"object"!=typeof e[o]||Array.isArray(e[o]))throw new Error(`Parsing error at "${[...t,o].join(".")}"`);const a=Object(e[o]);if(!a)throw new Error(`Parsing error at "${[...t,o].join(".")}"`);if(void 0!==a.value){const e=extractStyleDictionaryV3Token(a,o,t,n);r.set(e.metadata.path.join("."),e);continue}for(const[e,s]of extractTokens(a,[...t,o],n).entries())r.set(e,s)}return r}function extractStyleDictionaryV3Tokens(e,t){return dereferenceTokenValues(extractTokens(e,[],t))}function extractStyleDictionaryTokens(e,t,n){if("3"===e)return extractStyleDictionaryV3Tokens(t,n);throw new Error("Unsupported version: "+e)}const p="6b4e71e7-4787-42f7-a092-8684961895db",m=r.createRequire(import.meta.url);function parseImport(t){const n=e(t),r={filePath:"",format:"standard",conditions:[p]};return n.walk((e=>{"function"===e.type&&"url"===e.value&&(r.filePath=e.nodes[0].value),"function"===e.type&&"format"===e.value&&(r.format=e.nodes[0].value),"function"===e.type&&"when"===e.value&&(r.conditions=e.nodes.filter((e=>"string"===e.type)).map((e=>e.value)))})),r.conditions.length||(r.conditions=[p]),r}async function tokensFromImport(e,r,o,a){const{filePath:s,format:i,conditions:u}=parseImport(o);if(!u.every((t=>e.includes(t))))return!1;let l="";if(s.startsWith("node_modules://"))try{l=m.resolve(s.slice(15),{paths:[t.dirname(r)]})}catch(e){throw new Error(`Failed to read ${s} with error ${e.message}`)}else l=t.resolve(t.dirname(r),s);if(a.has(l))return!1;a.add(l);const c=await n.readFile(l,"utf8"),f=JSON.parse(c);if("style-dictionary3"===i)return{filePath:t.resolve(s),tokens:extractStyleDictionaryTokens("3",f,l)};throw new Error("Unsupported format: "+i)}function mergeTokens(e,t){const n=new Map(e);for(const[e,r]of t)n.set(e,r);return n}function parsePluginOptions(e){const t={importAtRuleName:"design-tokens",is:[p],unitsAndValues:{rootFontSize:16},valueFunctionName:"design-token"};return e?("object"!=typeof e||(Array.isArray(e.is)&&(t.is=e.is.filter((e=>"string"==typeof e))),0===t.is.length&&(t.is=[p]),"object"==typeof e.unitsAndValues&&"number"==typeof e.unitsAndValues.rootFontSize&&((n=e.unitsAndValues.rootFontSize)>0&&n!==1/0)&&(t.unitsAndValues.rootFontSize=e.unitsAndValues.rootFontSize),"string"==typeof e.valueFunctionName&&(t.valueFunctionName=e.valueFunctionName),"string"==typeof e.importAtRuleName&&(t.importAtRuleName=e.importAtRuleName)),t):t;var n}function parseComponentValuesFromTokens(e){return o(e,{onParseError:e=>{throw new Error(JSON.stringify(e))}})}function parseComponentValues(e){const t=l({css:e},{commentsAreTokens:!0,onParseError:e=>{throw new Error(JSON.stringify(e))}}),n=[];for(;!t.endOfFile();)n.push(t.nextToken());return n.push(t.nextToken()),parseComponentValuesFromTokens(n)}function transform(e,t,n,r,o){const a=parseComponentValues(r);let s=!1;return a.forEach(((r,i)=>{if("walk"in r){{const u=transformComponentValue(r,e,t,n,o);if(u)return a.splice(i,1,...u),s=!0,!1}r.walk(((r,a)=>{if("string"==typeof a)return;const i=transformComponentValue(r.node,e,t,n,o);return i?(r.parent.value.splice(a,1,...i),s=!0,!1):void 0}))}})),s?a.map((e=>e.toString())).join(""):r}function transformComponentValue(e,t,n,r,o){if(!a(e))return;if(e.nameTokenValue().toLowerCase()!==o.valueFunctionName)return;let l="",f="",p="";for(let t=0;t{const t=parsePluginOptions(e);return{postcssPlugin:"postcss-design-tokens",prepare(){let e=new Map,n=new Set;return{OnceExit(){e=new Map,n=new Set},Once:async(r,{result:o})=>{const a=[];r.walkAtRules((e=>{var n,r;if(e.name.toLowerCase()!==t.importAtRuleName)return;if(null==e||null==(n=e.source)||null==(r=n.input)||!r.file)return;const o=e.source.input.file,s=e.params;e.remove(),a.push({filePath:o,params:s,node:e})}));for(const r of a.values()){let a;try{if(a=await tokensFromImport(t.is,r.filePath,r.params,n),!a)continue}catch(e){r.node.warn(o,`Failed to import design tokens from "${r.params}" with error:\n\t`+e.message);continue}o.messages.push({type:"dependency",plugin:"postcss-design-tokens",file:a.filePath,parent:r.filePath}),e=mergeTokens(e,a.tokens)}},Declaration(n,{result:r}){if(n.value.toLowerCase().includes(t.valueFunctionName))try{const o=transform(e,r,n,n.value,t);if(o===n.value)return;n.value=o}catch(e){n.warn(r,`Failed to parse and transform "${n.value}"`)}},AtRule(n,{result:r}){if(n.params.toLowerCase().includes(t.valueFunctionName))try{const o=transform(e,r,n,n.params,t);if(o===n.params)return;n.params=o}catch(e){n.warn(r,`Failed to parse and transform "${n.params}"`)}}}}}};creator.postcss=!0;export{creator as default}; +import e from"postcss-value-parser";import t from"path";import{promises as n}from"fs";import r from"module";import{parseListOfComponentValues as o,isFunctionNode as a,isWhitespaceNode as s,isCommentNode as i,isTokenNode as u}from"@csstools/css-parser-algorithms";import{tokenizer as l,TokenType as c}from"@csstools/css-tokenizer";function toposort(e,t){let n=e.length;const r=new Array(n),o={};let a=n;const s=makeOutgoingEdges(t),i=makeNodesHash(e);for(t.forEach((function(e){if(!i.has(e[0])||!i.has(e[1]))throw new Error("Unknown token. Make sure to provide all tokens used in aliases.")}));a--;)o[a]||visit(e[a],a,new Set);return r;function visit(e,t,a){if(a.has(e)){let t;try{t=", token was: "+JSON.stringify(e)}catch(e){t=""}throw new Error("Cyclic dependency"+t)}if(!i.has(e))throw new Error("Found unknown token. Make sure to provided all involved tokens. Unknown token: "+JSON.stringify(e));if(o[t])return;o[t]=!0;let u=s.get(e)||new Set;if(u=Array.from(u),t=u.length){a.add(e);do{const e=u[--t];visit(e,i.get(e),a)}while(t);a.delete(e)}r[--n]=e}}function makeOutgoingEdges(e){const t=new Map;for(let n=0,r=e.length;napplyTransformsToValue(o,e),name:String(e.name??"")||t,comment:String(e.comment??"")||void 0,metadata:{name:String(e.name??"")?t:void 0,path:[...n,t],filePath:r,isSource:!0}}}const f=new Map;function applyTransformsToValue(t,n){if(!t)return"";if(!n)return t;if(!n.toUnit)return t;const r=e.unit(t??"");if(!r||r.unit===n.toUnit)return t;if(!r.unit){if(f.has(n.toUnit)){if(f.get(n.toUnit))return`${t}${n.toUnit}`;throw new Error(`Invalid unit "${n.toUnit}" for "${t}"`)}try{const r=e.unit(`${t}${n.toUnit}`);if(r&&r.unit===n.toUnit)return f.set(n.toUnit,!0),`${t}${n.toUnit}`;f.set(n.toUnit,!1)}catch(e){f.set(n.toUnit,!1)}throw new Error(`Invalid unit "${n.toUnit}" for "${t}"`)}var o,a;return"rem"===r.unit&&"px"===n.toUnit?remToPx(parseFloat(r.number),(null==(o=n.pluginOptions)?void 0:o.rootFontSize)??16):"px"===r.unit&&"rem"===n.toUnit?pxToRem(parseFloat(r.number),(null==(a=n.pluginOptions)?void 0:a.rootFontSize)??16):t}function remToPx(e,t){return`${formatFloat(e*t)}px`}function pxToRem(e,t){return`${formatFloat(e/t)}rem`}function formatFloat(e){if(Number.isInteger(e))return e.toString();let t=e.toFixed(5);for(let e=t.length;e>0&&"."!==t[e];e--)"0"===t[e]||(t=t.slice(0,e+1));return t}function dereferenceTokenValues(e){const t=new Set,n=new Map;for(const[r,o]of e.entries()){const e=parseReferences(o.value);e.length&&(t.add(r),n.set(r,e))}for(const[r,o]of n.entries()){for(let n=0;n"value-reference"===e.type)))continue;const a=o.map((e=>e.value)).join(""),s=e.get(r);s.value=a,s.cssValue=e=>applyTransformsToValue(a,e),e.set(r,s),t.delete(r),n.delete(r)}if(0===t.size)return e;{const r=Array.from(e.keys()),o=[];for(const[e,t]of n.entries())for(let n=0;n"value-reference"===e.type)))throw new Error('Token "'+r+'" can not be fully resolved');const a=o.map((e=>e.value)).join(""),s=e.get(r);s.value=a,s.cssValue=e=>applyTransformsToValue(a,e),e.set(r,s),t.delete(r),n.delete(r)}if(0===t.size)return e}return e}function parseReferences(e){if("string"!=typeof e)return[];if(-1===e.indexOf("{"))return[];const t=[];let n=!1,r=!1,o="";for(let a=0;a0&&(t.push({type:"value-non-reference",value:o}),o=""),r=!0;break;case"}":if(!r)throw new Error('Unexpected "}" in "'+e+'" at '+a);if(0===o.length)throw new Error('Empty alias "{}" in "'+e+'" at '+a);{let e=o.trim();".value"===e.slice(-6)&&(e=e.slice(0,-6)),t.push({type:"value-reference",raw:e}),o=""}n=!0,r=!1;break;default:o+=s}}if(r)throw new Error('Unexpected end of alias in "'+e+'"');return o.length>0&&t.push({type:"value-non-reference",value:o}),n?t:[]}function extractTokens(e,t,n){const r=new Map;for(const o in e)if(Object.hasOwnProperty.call(e,o)){if(null===e[o]||"object"!=typeof e[o]||Array.isArray(e[o]))throw new Error(`Parsing error at "${[...t,o].join(".")}"`);const a=Object(e[o]);if(!a)throw new Error(`Parsing error at "${[...t,o].join(".")}"`);if(void 0!==a.value){const e=extractStyleDictionaryV3Token(a,o,t,n);r.set(e.metadata.path.join("."),e);continue}for(const[e,s]of extractTokens(a,[...t,o],n).entries())r.set(e,s)}return r}function extractStyleDictionaryV3Tokens(e,t){return dereferenceTokenValues(extractTokens(e,[],t))}function extractStyleDictionaryTokens(e,t,n){if("3"===e)return extractStyleDictionaryV3Tokens(t,n);throw new Error("Unsupported version: "+e)}const p="6b4e71e7-4787-42f7-a092-8684961895db",m=r.createRequire(import.meta.url);function parseImport(t){const n=e(t),r={filePath:"",format:"standard",conditions:[p]};return n.walk((e=>{"function"===e.type&&"url"===e.value&&(r.filePath=e.nodes[0].value),"function"===e.type&&"format"===e.value&&(r.format=e.nodes[0].value),"function"===e.type&&"when"===e.value&&(r.conditions=e.nodes.filter((e=>"string"===e.type)).map((e=>e.value)))})),r.conditions.length||(r.conditions=[p]),r}async function tokensFromImport(e,r,o,a){const{filePath:s,format:i,conditions:u}=parseImport(o);if(!u.every((t=>e.includes(t))))return!1;let l="";if(s.startsWith("node_modules://"))try{l=m.resolve(s.slice(15),{paths:[t.dirname(r)]})}catch(e){throw new Error(`Failed to read ${s} with error ${e.message}`)}else l=t.resolve(t.dirname(r),s);if(a.has(l))return!1;a.add(l);const c=await n.readFile(l,"utf8"),f=JSON.parse(c);if("style-dictionary3"===i)return{filePath:t.resolve(s),tokens:extractStyleDictionaryTokens("3",f,l)};throw new Error("Unsupported format: "+i)}function mergeTokens(e,t){const n=new Map(e);for(const[e,r]of t)n.set(e,r);return n}function parsePluginOptions(e){const t={importAtRuleName:"design-tokens",is:[p],unitsAndValues:{rootFontSize:16},valueFunctionName:"design-token"};return e?("object"!=typeof e||(Array.isArray(e.is)&&(t.is=e.is.filter((e=>"string"==typeof e))),0===t.is.length&&(t.is=[p]),"object"==typeof e.unitsAndValues&&"number"==typeof e.unitsAndValues.rootFontSize&&((n=e.unitsAndValues.rootFontSize)>0&&n!==1/0)&&(t.unitsAndValues.rootFontSize=e.unitsAndValues.rootFontSize),"string"==typeof e.valueFunctionName&&(t.valueFunctionName=e.valueFunctionName),"string"==typeof e.importAtRuleName&&(t.importAtRuleName=e.importAtRuleName)),t):t;var n}function parseComponentValuesFromTokens(e){return o(e,{onParseError:e=>{throw new Error(JSON.stringify(e))}})}function parseComponentValues(e){const t=l({css:e},{commentsAreTokens:!0,onParseError:e=>{throw new Error(JSON.stringify(e))}}),n=[];for(;!t.endOfFile();)n.push(t.nextToken());return n.push(t.nextToken()),parseComponentValuesFromTokens(n)}function transform(e,t,n,r,o){const a=parseComponentValues(r);let s=!1;return a.forEach(((r,i)=>{if("walk"in r){{const u=transformComponentValue(r,e,t,n,o);if(u)return a.splice(i,1,...u),s=!0,!1}r.walk(((r,a)=>{if("string"==typeof a)return;const i=transformComponentValue(r.node,e,t,n,o);return i?(r.parent.value.splice(a,1,...i),s=!0,!1):void 0}))}})),s?a.map((e=>e.toString())).join(""):r}function transformComponentValue(e,t,n,r,o){if(!a(e))return;if(e.getName().toLowerCase()!==o.valueFunctionName)return;let l="",f="",p="";for(let t=0;t{const t=parsePluginOptions(e);return{postcssPlugin:"postcss-design-tokens",prepare(){let e=new Map,n=new Set;return{OnceExit(){e=new Map,n=new Set},Once:async(r,{result:o})=>{const a=[];r.walkAtRules((e=>{var n,r;if(e.name.toLowerCase()!==t.importAtRuleName)return;if(null==e||null==(n=e.source)||null==(r=n.input)||!r.file)return;const o=e.source.input.file,s=e.params;e.remove(),a.push({filePath:o,params:s,node:e})}));for(const r of a.values()){let a;try{if(a=await tokensFromImport(t.is,r.filePath,r.params,n),!a)continue}catch(e){r.node.warn(o,`Failed to import design tokens from "${r.params}" with error:\n\t`+e.message);continue}o.messages.push({type:"dependency",plugin:"postcss-design-tokens",file:a.filePath,parent:r.filePath}),e=mergeTokens(e,a.tokens)}},Declaration(n,{result:r}){if(n.value.toLowerCase().includes(t.valueFunctionName))try{const o=transform(e,r,n,n.value,t);if(o===n.value)return;n.value=o}catch(e){n.warn(r,`Failed to parse and transform "${n.value}"`)}},AtRule(n,{result:r}){if(n.params.toLowerCase().includes(t.valueFunctionName))try{const o=transform(e,r,n,n.params,t);if(o===n.params)return;n.params=o}catch(e){n.warn(r,`Failed to parse and transform "${n.params}"`)}}}}}};creator.postcss=!0;export{creator as default}; diff --git a/plugins/postcss-design-tokens/src/transform.ts b/plugins/postcss-design-tokens/src/transform.ts index e641fdb98..2e034d99a 100644 --- a/plugins/postcss-design-tokens/src/transform.ts +++ b/plugins/postcss-design-tokens/src/transform.ts @@ -51,7 +51,7 @@ function transformComponentValue(node: ComponentValue, tokens: Mape&&([e,r]=[r,e]);;){if(0==r)return e;if(0==(e%=r))return r;r%=e}}const t=new Set(["aspect-ratio","min-aspect-ratio","max-aspect-ratio","device-aspect-ratio","min-device-aspect-ratio","max-device-aspect-ratio"]);function transformMediaQueryList(r,n){const o=e.parse(r,{preserveInvalidMediaQueries:!0,onParseError:()=>{throw new Error(`Unable to parse media query "${r}"`)}}),a=new Set(o.map((e=>e.toString())));return o.flatMap((r=>{if(e.isMediaQueryInvalid(r))return[r.toString()];const o=e.cloneMediaQuery(r);o.walk((r=>{const n=r.node;if(e.isMediaFeaturePlain(n)||e.isMediaFeatureRangeNameValue(n)||e.isMediaFeatureRangeValueName(n)){const e=n.name.getName().toLowerCase();if(!t.has(e))return;transformMediaFeatureValue(n.value)}else if(e.isMediaFeatureRangeValueNameValue(n)){const e=n.name.getName().toLowerCase();if(!t.has(e))return;transformMediaFeatureValue(n.valueOne);transformMediaFeatureValue(n.valueTwo)}else;}));const i=r.toString(),u=o.toString();return u===i||a.has(u)?[i]:n?[i,u]:[u]})).join(",")}const creator=e=>{const r=Object.assign({preserve:!1},e);return{postcssPlugin:"postcss-media-queries-aspect-ratio-number-values",AtRule(e,{result:n}){if("media"!==e.name.toLowerCase())return;const o=e.params.toLowerCase();if(!(o.includes("aspect-ratio")||o.includes("min-aspect-ratio")||o.includes("max-aspect-ratio")||o.includes("device-aspect-ratio")||o.includes("min-device-aspect-ratio")||o.includes("max-device-aspect-ratio")))return;let a=e.params;try{if(a=transformMediaQueryList(e.params,r.preserve),a===e.params)return}catch(r){return void e.warn(n,`Failed to transform @media params for "${e.params}" with message: "${r.message}"`)}e.cloneBefore({params:a}),e.remove()}}};creator.postcss=!0,module.exports=creator; +"use strict";var e=require("@csstools/media-query-list-parser"),r=require("@csstools/css-parser-algorithms"),o=require("@csstools/css-tokenizer");const n=1e5,t=2147483647;function transformMediaFeatureValue(a){if(Array.isArray(a.value)&&e.matchesRatioExactly(a.value)){const e=[];for(let n=0;ne&&([e,r]=[r,e]);;){if(0==r)return e;if(0==(e%=r))return r;r%=e}}const a=new Set(["aspect-ratio","min-aspect-ratio","max-aspect-ratio","device-aspect-ratio","min-device-aspect-ratio","max-device-aspect-ratio"]);function transformMediaQueryList(r,o){const n=e.parse(r,{preserveInvalidMediaQueries:!0,onParseError:()=>{throw new Error(`Unable to parse media query "${r}"`)}}),t=new Set(n.map((e=>e.toString())));return n.flatMap((r=>{if(e.isMediaQueryInvalid(r))return[r.toString()];const n=e.cloneMediaQuery(r);n.walk((r=>{const o=r.node;if(e.isMediaFeaturePlain(o)||e.isMediaFeatureRangeNameValue(o)||e.isMediaFeatureRangeValueName(o)){const e=o.name.getName().toLowerCase();if(!a.has(e))return;transformMediaFeatureValue(o.value)}else if(e.isMediaFeatureRangeValueNameValue(o)){const e=o.name.getName().toLowerCase();if(!a.has(e))return;transformMediaFeatureValue(o.valueOne);transformMediaFeatureValue(o.valueTwo)}else;}));const i=r.toString(),u=n.toString();return u===i||t.has(u)?[i]:o?[i,u]:[u]})).join(",")}const creator=e=>{const r=Object.assign({preserve:!1},e);return{postcssPlugin:"postcss-media-queries-aspect-ratio-number-values",AtRule(e,{result:o}){if("media"!==e.name.toLowerCase())return;const n=e.params.toLowerCase();if(!(n.includes("aspect-ratio")||n.includes("min-aspect-ratio")||n.includes("max-aspect-ratio")||n.includes("device-aspect-ratio")||n.includes("min-device-aspect-ratio")||n.includes("max-device-aspect-ratio")))return;let t=e.params;try{if(t=transformMediaQueryList(e.params,r.preserve),t===e.params)return}catch(r){return void e.warn(o,`Failed to transform @media params for "${e.params}" with message: "${r.message}"`)}e.cloneBefore({params:t}),e.remove()}}};creator.postcss=!0,module.exports=creator; diff --git a/plugins/postcss-media-queries-aspect-ratio-number-values/dist/index.mjs b/plugins/postcss-media-queries-aspect-ratio-number-values/dist/index.mjs index cddd84cc3..3dc2caac3 100644 --- a/plugins/postcss-media-queries-aspect-ratio-number-values/dist/index.mjs +++ b/plugins/postcss-media-queries-aspect-ratio-number-values/dist/index.mjs @@ -1 +1 @@ -import{matchesRatioExactly as e,parse as r,isMediaQueryInvalid as t,cloneMediaQuery as a,isMediaFeaturePlain as n,isMediaFeatureRangeNameValue as o,isMediaFeatureRangeValueName as u,isMediaFeatureRangeValueNameValue as i}from"@csstools/media-query-list-parser";import{isTokenNode as s,isFunctionNode as l,TokenNode as c,FunctionNode as m,SimpleBlockNode as v,WhitespaceNode as d}from"@csstools/css-parser-algorithms";import{TokenType as p,NumberType as f}from"@csstools/css-tokenizer";const g=1e5,w=2147483647;function transformMediaFeatureValue(r){if(Array.isArray(r.value)&&e(r.value)){const e=[];for(let t=0;te&&([e,r]=[r,e]);;){if(0==r)return e;if(0==(e%=r))return r;r%=e}}const h=new Set(["aspect-ratio","min-aspect-ratio","max-aspect-ratio","device-aspect-ratio","min-device-aspect-ratio","max-device-aspect-ratio"]);function transformMediaQueryList(e,s){const l=r(e,{preserveInvalidMediaQueries:!0,onParseError:()=>{throw new Error(`Unable to parse media query "${e}"`)}}),c=new Set(l.map((e=>e.toString())));return l.flatMap((e=>{if(t(e))return[e.toString()];const r=a(e);r.walk((e=>{const r=e.node;if(n(r)||o(r)||u(r)){const e=r.name.getName().toLowerCase();if(!h.has(e))return;transformMediaFeatureValue(r.value)}else if(i(r)){const e=r.name.getName().toLowerCase();if(!h.has(e))return;transformMediaFeatureValue(r.valueOne);transformMediaFeatureValue(r.valueTwo)}else;}));const l=e.toString(),m=r.toString();return m===l||c.has(m)?[l]:s?[l,m]:[m]})).join(",")}const creator=e=>{const r=Object.assign({preserve:!1},e);return{postcssPlugin:"postcss-media-queries-aspect-ratio-number-values",AtRule(e,{result:t}){if("media"!==e.name.toLowerCase())return;const a=e.params.toLowerCase();if(!(a.includes("aspect-ratio")||a.includes("min-aspect-ratio")||a.includes("max-aspect-ratio")||a.includes("device-aspect-ratio")||a.includes("min-device-aspect-ratio")||a.includes("max-device-aspect-ratio")))return;let n=e.params;try{if(n=transformMediaQueryList(e.params,r.preserve),n===e.params)return}catch(r){return void e.warn(t,`Failed to transform @media params for "${e.params}" with message: "${r.message}"`)}e.cloneBefore({params:n}),e.remove()}}};creator.postcss=!0;export{creator as default}; +import{matchesRatioExactly as e,parse as r,isMediaQueryInvalid as t,cloneMediaQuery as a,isMediaFeaturePlain as n,isMediaFeatureRangeNameValue as o,isMediaFeatureRangeValueName as i,isMediaFeatureRangeValueNameValue as u}from"@csstools/media-query-list-parser";import{isTokenNode as s,isFunctionNode as l,TokenNode as c,FunctionNode as m,SimpleBlockNode as v,WhitespaceNode as d}from"@csstools/css-parser-algorithms";import{TokenType as p,NumberType as f}from"@csstools/css-tokenizer";const g=1e5,w=2147483647;function transformMediaFeatureValue(r){if(Array.isArray(r.value)&&e(r.value)){const e=[];for(let t=0;te&&([e,r]=[r,e]);;){if(0==r)return e;if(0==(e%=r))return r;r%=e}}const N=new Set(["aspect-ratio","min-aspect-ratio","max-aspect-ratio","device-aspect-ratio","min-device-aspect-ratio","max-device-aspect-ratio"]);function transformMediaQueryList(e,s){const l=r(e,{preserveInvalidMediaQueries:!0,onParseError:()=>{throw new Error(`Unable to parse media query "${e}"`)}}),c=new Set(l.map((e=>e.toString())));return l.flatMap((e=>{if(t(e))return[e.toString()];const r=a(e);r.walk((e=>{const r=e.node;if(n(r)||o(r)||i(r)){const e=r.name.getName().toLowerCase();if(!N.has(e))return;transformMediaFeatureValue(r.value)}else if(u(r)){const e=r.name.getName().toLowerCase();if(!N.has(e))return;transformMediaFeatureValue(r.valueOne);transformMediaFeatureValue(r.valueTwo)}else;}));const l=e.toString(),m=r.toString();return m===l||c.has(m)?[l]:s?[l,m]:[m]})).join(",")}const creator=e=>{const r=Object.assign({preserve:!1},e);return{postcssPlugin:"postcss-media-queries-aspect-ratio-number-values",AtRule(e,{result:t}){if("media"!==e.name.toLowerCase())return;const a=e.params.toLowerCase();if(!(a.includes("aspect-ratio")||a.includes("min-aspect-ratio")||a.includes("max-aspect-ratio")||a.includes("device-aspect-ratio")||a.includes("min-device-aspect-ratio")||a.includes("max-device-aspect-ratio")))return;let n=e.params;try{if(n=transformMediaQueryList(e.params,r.preserve),n===e.params)return}catch(r){return void e.warn(t,`Failed to transform @media params for "${e.params}" with message: "${r.message}"`)}e.cloneBefore({params:n}),e.remove()}}};creator.postcss=!0;export{creator as default}; diff --git a/plugins/postcss-media-queries-aspect-ratio-number-values/src/transform-media-feature-value.ts b/plugins/postcss-media-queries-aspect-ratio-number-values/src/transform-media-feature-value.ts index 9a664ac05..4d9c25950 100644 --- a/plugins/postcss-media-queries-aspect-ratio-number-values/src/transform-media-feature-value.ts +++ b/plugins/postcss-media-queries-aspect-ratio-number-values/src/transform-media-feature-value.ts @@ -16,7 +16,7 @@ export function transformMediaFeatureValue(value: MediaFeatureValue) { continue; } - if (isFunctionNode(node) && node.nameTokenValue().toLowerCase() === 'calc') { + if (isFunctionNode(node) && node.getName().toLowerCase() === 'calc') { nodes.push(node); continue; } @@ -67,7 +67,7 @@ export function transformMediaFeatureValue(value: MediaFeatureValue) { // Calc { - if (isFunctionNode(firstValue) && firstValue.nameTokenValue().toLowerCase() === 'calc') { + if (isFunctionNode(firstValue) && firstValue.getName().toLowerCase() === 'calc') { // Avoid infinite loops if (firstValue.toString().includes(precision.toString())) { return; @@ -76,7 +76,7 @@ export function transformMediaFeatureValue(value: MediaFeatureValue) { firstValueModified = modifyCalc(firstValue); } - if (isFunctionNode(secondValue) && secondValue.nameTokenValue().toLowerCase() === 'calc') { + if (isFunctionNode(secondValue) && secondValue.getName().toLowerCase() === 'calc') { // Avoid infinite loops if (secondValue.toString().includes(precision.toString())) { return; @@ -199,7 +199,7 @@ export function transformMediaFeatureValue(value: MediaFeatureValue) { // Single calc as // "calc(5 / 3)" -> "calc((5 / 3) * 1000000)/1000000" - if (isFunctionNode(componentValue) && componentValue.nameTokenValue().toLowerCase() === 'calc') { + if (isFunctionNode(componentValue) && componentValue.getName().toLowerCase() === 'calc') { componentValues.splice( i, 1,