From b27ef8bb439b83342e716eb5f75ac568fe7cbe82 Mon Sep 17 00:00:00 2001 From: Thierry Bela Nanga Date: Sun, 4 Oct 2026 18:27:54 -0400 Subject: [PATCH 01/24] add arena --- dist/index.cjs | 54 +++++++++++++++++++++++++++++++++++++ dist/index.d.ts | 6 ++++- dist/lib/parser/arena.js | 54 +++++++++++++++++++++++++++++++++++++ dist/node.js | 2 ++ src/@types/index.d.ts | 6 ++++- src/lib/parser/arena.ts | 57 ++++++++++++++++++++++++++++++++++++++++ src/node.ts | 2 ++ 7 files changed, 179 insertions(+), 2 deletions(-) create mode 100644 dist/lib/parser/arena.js create mode 100644 src/lib/parser/arena.ts diff --git a/dist/index.cjs b/dist/index.cjs index b8f89a979..0a3816024 100644 --- a/dist/index.cjs +++ b/dist/index.cjs @@ -34466,6 +34466,59 @@ function validateSyncArguments(options, prefix = "options.") { } } +class Arena { + stringIds = new Map(); + strings = []; + root = 1; + current = 1; + dataTypes; + parents; + nextSibling; + previousSibling; + firstToken; + firstChild; + lastChild; + name; + stringValue; + numericValue; + unit; + flags; + left; + right; + op; + op2; + constructor(size = 1024) { + this.dataTypes = new Uint8Array(size); + this.parents = new Uint32Array(size); + this.nextSibling = new Uint32Array(size); + this.previousSibling = new Uint32Array(size); + this.firstToken = new Uint32Array(size); + this.firstChild = new Uint32Array(size); + this.lastChild = new Uint32Array(size); + this.name = new Uint32Array(size); + this.stringValue = new Uint32Array(size); + this.numericValue = new Float64Array(size); + this.unit = new Uint32Array(size); + this.flags = new Uint32Array(size); + this.left = new Uint32Array(size); + this.right = new Uint32Array(size); + this.op = new Uint32Array(size); + this.op2 = new Uint32Array(size); + } + internString(str) { + let id = this.stringIds.get(str); + if (id != null) { + return id; + } + this.stringIds.set(str, this.strings.length); + this.strings.push(str); + return this.strings.length - 1; + } + resolveString(id) { + return this.strings[id]; + } +} + /** * * @param node @@ -34658,6 +34711,7 @@ function parseSync(...args) { options ??= {}; options.src ??= ""; options.sourcesMap ??= new Map(); + options.arena ??= new Arena(); Object.assign(options, { resolve, dirname, diff --git a/dist/index.d.ts b/dist/index.d.ts index 5c17e370e..051be2e47 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -5222,7 +5222,7 @@ interface MinifyOptions { * enable ast minification */ minifyAST?: boolean; - + /** * Parse color tokens */ @@ -5584,6 +5584,10 @@ export declare interface ParserSyncOptions ValidationOptions, PropertyListOptions, ParseSourceOptions { + /** + * Arena instance. if not provided, a new instance will be created + */ + arena?: Arena; /** * Source file to be used for sourcemap */ diff --git a/dist/lib/parser/arena.js b/dist/lib/parser/arena.js new file mode 100644 index 000000000..479fc3d40 --- /dev/null +++ b/dist/lib/parser/arena.js @@ -0,0 +1,54 @@ +class Arena { + stringIds = new Map(); + strings = []; + root = 1; + current = 1; + dataTypes; + parents; + nextSibling; + previousSibling; + firstToken; + firstChild; + lastChild; + name; + stringValue; + numericValue; + unit; + flags; + left; + right; + op; + op2; + constructor(size = 1024) { + this.dataTypes = new Uint8Array(size); + this.parents = new Uint32Array(size); + this.nextSibling = new Uint32Array(size); + this.previousSibling = new Uint32Array(size); + this.firstToken = new Uint32Array(size); + this.firstChild = new Uint32Array(size); + this.lastChild = new Uint32Array(size); + this.name = new Uint32Array(size); + this.stringValue = new Uint32Array(size); + this.numericValue = new Float64Array(size); + this.unit = new Uint32Array(size); + this.flags = new Uint32Array(size); + this.left = new Uint32Array(size); + this.right = new Uint32Array(size); + this.op = new Uint32Array(size); + this.op2 = new Uint32Array(size); + } + internString(str) { + let id = this.stringIds.get(str); + if (id != null) { + return id; + } + this.stringIds.set(str, this.strings.length); + this.strings.push(str); + return this.strings.length - 1; + } + resolveString(id) { + return this.strings[id]; + } +} + +export { Arena }; diff --git a/dist/node.js b/dist/node.js index c8740ff41..623874460 100644 --- a/dist/node.js +++ b/dist/node.js @@ -15,6 +15,7 @@ import { resolve as resolve$1 } from 'node:path'; import { SourceFile } from './lib/parser/source.js'; import { cwd } from 'node:process'; import { parseResult, validateSyncArguments } from './utils/sync.js'; +import { Arena } from './lib/parser/arena.js'; export { minify } from './lib/ast/minify.js'; export { expand } from './lib/ast/expand.js'; export { WalkerEvent, WalkerOptionEnum, walk, walkValues } from './lib/ast/walk.js'; @@ -170,6 +171,7 @@ function parseSync(...args) { options ??= {}; options.src ??= ""; options.sourcesMap ??= new Map(); + options.arena ??= new Arena(); Object.assign(options, { resolve, dirname, diff --git a/src/@types/index.d.ts b/src/@types/index.d.ts index 17f4a960b..8a0eac25a 100644 --- a/src/@types/index.d.ts +++ b/src/@types/index.d.ts @@ -135,7 +135,7 @@ export interface MinifyOptions { * enable ast minification */ minifyAST?: boolean; - + /** * Parse color tokens */ @@ -497,6 +497,10 @@ export declare interface ParserSyncOptions ValidationOptions, PropertyListOptions, ParseSourceOptions { + /** + * Arena instance. if not provided, a new instance will be created + */ + arena?: Arena; /** * Source file to be used for sourcemap */ diff --git a/src/lib/parser/arena.ts b/src/lib/parser/arena.ts new file mode 100644 index 000000000..7fa0591ff --- /dev/null +++ b/src/lib/parser/arena.ts @@ -0,0 +1,57 @@ +export class Arena { + public stringIds: Map = new Map(); + public strings: string[] = []; + public root: number = 1; + public current: number = 1; + + public dataTypes: Uint8Array; + public parents: Uint32Array; + public nextSibling: Uint32Array; + public previousSibling: Uint32Array; + public firstToken: Uint32Array; + public firstChild: Uint32Array; + public lastChild: Uint32Array; + public name: Uint32Array; + public stringValue: Uint32Array; + public numericValue: Float64Array; + public unit: Uint32Array; + public flags: Uint32Array; + public left: Uint32Array; + public right: Uint32Array; + public op: Uint32Array; + public op2: Uint32Array; + + constructor(size: number = 1024) { + this.dataTypes = new Uint8Array(size); + this.parents = new Uint32Array(size); + this.nextSibling = new Uint32Array(size); + this.previousSibling = new Uint32Array(size); + this.firstToken = new Uint32Array(size); + this.firstChild = new Uint32Array(size); + this.lastChild = new Uint32Array(size); + this.name = new Uint32Array(size); + this.stringValue = new Uint32Array(size); + this.numericValue = new Float64Array(size); + this.unit = new Uint32Array(size); + this.flags = new Uint32Array(size); + this.left = new Uint32Array(size); + this.right = new Uint32Array(size); + this.op = new Uint32Array(size); + this.op2 = new Uint32Array(size); + } + + public internString(str: string): number { + let id = this.stringIds.get(str) as number; + if (id != null) { + return id; + } + + this.stringIds.set(str, this.strings.length); + this.strings.push(str); + return this.strings.length - 1; + } + + resolveString(id: number): string { + return this.strings[id]; + } +} diff --git a/src/node.ts b/src/node.ts index df1190a6a..af0b7b690 100644 --- a/src/node.ts +++ b/src/node.ts @@ -28,6 +28,7 @@ import { resolve as resolvePath } from "node:path"; import { SourceFile } from "./lib/parser/source.ts"; import { cwd } from "node:process"; import { parseResult, validateSyncArguments } from "./utils/sync.ts"; +import { Arena } from "./lib/parser/arena.ts"; export type * from "./@types/index.d.ts"; export type * from "./@types/ast.d.ts"; @@ -294,6 +295,7 @@ export function parseSync( options.src ??= ""; options.sourcesMap ??= new Map(); + options.arena ??= new Arena(); Object.assign(options, { resolve, From 261d067d6c2a40112f0a70d835f749ff225981e7 Mon Sep 17 00:00:00 2001 From: Thierry Bela Nanga Date: Tue, 6 Oct 2026 16:05:10 -0400 Subject: [PATCH 02/24] work in progress --- jsr.json | 2 +- package.json | 2 +- src/@types/index.d.ts | 14 +- src/@types/walker.d.ts | 8 +- src/lib/ast/types.ts | 62 +- src/lib/ast/walk.ts | 8 +- src/lib/parser/arena.ts | 828 +++++++++++++++++++++++- src/lib/parser/parse.ts | 369 ++++++----- src/lib/parser/utils/declaration.ts | 277 +++++--- src/lib/parser/utils/selector.ts | 947 ++++++++++++++++++---------- src/lib/printer/render.ts | 27 +- src/lib/syntax/syntax.ts | 71 ++- src/lib/validation/match.ts | 221 ++++--- src/lib/validation/types.d.ts | 14 +- src/lib/validation/utils/list.ts | 12 +- src/node.ts | 6 +- src/web.ts | 7 +- 17 files changed, 2049 insertions(+), 826 deletions(-) diff --git a/jsr.json b/jsr.json index 280febcf1..c21376e27 100644 --- a/jsr.json +++ b/jsr.json @@ -1,6 +1,6 @@ { "name": "@tbela99/css-parser", - "version": "1.6.6", + "version": "2.0.0-alpha.1", "publish": { "include": [ "src", diff --git a/package.json b/package.json index cf4064835..96d8d5d0f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@tbela99/css-parser", "description": "CSS parser, minifier and validator for node and the browser", - "version": "1.6.6", + "version": "2.0.0-alpha.1", "exports": { ".": "./dist/node.js", "./node": "./dist/node.js", diff --git a/src/@types/index.d.ts b/src/@types/index.d.ts index 8a0eac25a..f21195cf6 100644 --- a/src/@types/index.d.ts +++ b/src/@types/index.d.ts @@ -42,7 +42,7 @@ export declare interface ErrorDescription { /** * Error node */ - node?: Token | AstNode | null; + node?: number | null; /** * Error location */ @@ -61,6 +61,12 @@ export declare interface ErrorDescription { * CSS validation options */ export interface ValidationOptions { + + /** + * Arena + */ + arena: Arena; + /** * Nested rule context * @private @@ -710,6 +716,12 @@ export declare interface ResolvedPath { * Ast node render options */ export declare interface RenderOptions { + + /** + * Arena + */ + arena: Arena; + /** * Source file to be used as CSS input file for sourcemap resolution */ diff --git a/src/@types/walker.d.ts b/src/@types/walker.d.ts index c2737ab3d..a108b6d8e 100644 --- a/src/@types/walker.d.ts +++ b/src/@types/walker.d.ts @@ -29,7 +29,7 @@ export declare interface WalkerOptions { /** * node walker option */ -export declare type WalkerOption = WalkerOptionEnum | AstNode | Token | null; +export declare type WalkerOption = WalkerOptionEnum | number | null; /** * returned value: * - {@link WalkerOptionEnum.Ignore}: ignore this node and its children @@ -51,11 +51,9 @@ export declare type WalkerFilter = (node: AstNode) => WalkerOption; * - {@link Token}: */ export declare type WalkerValueFilter = ( - node: AstNode | Token, - parent?: AstNode | Token | AstNode[] | Token[] | null, + node: number, event?: WalkerEvent, - parents?: Generator, -) => WalkerOption | AstNode | Token | AstNode[] | Token[] | null; +) => WalkerOption | number[]; /** * walker result diff --git a/src/lib/ast/types.ts b/src/lib/ast/types.ts index 0b9c169ac..24f74bb7f 100644 --- a/src/lib/ast/types.ts +++ b/src/lib/ast/types.ts @@ -17,76 +17,46 @@ export enum EnumAstNodeStatus { /** * Node passed validation */ - Validated, + Validated = 1 << 0, /** * Node is invalid */ - Invalid, + Invalid = 1 << 1, /** * node is not validated */ - Unvalidated, + Unvalidated = 1 << 2, /** * Node did not pass validation, but is preserved. */ - ValidationFailed, + ValidationFailed = 1 << 3, /** * Parsing the node is not supported yet or the node syntax definition does not exist. */ - Unknown, + Unknown = 1 << 4, /** * Failed to parse the node. */ - Unparsed, + Unparsed = 1 << 5, /** * Node is disallowed in the context */ - Disallowed, + Disallowed = 1 << 6, /** * Node is malformed */ - Malformed, -} - -export declare type AstNodePropertyType = "state" | "errors" | "location" | "tokens" | "parent" | "selector"; - -/** - * Enum of validation levels - * @deprecated - */ -export enum ValidationLevel { - /** - * disable validation - */ - None, - /** - * validate selectors - */ - Selector = 0x1, - /** - * validate at-rules - */ - AtRule = 0x2, + Malformed = 1 << 7, /** - * validate declarations - */ - Declaration = 0x4, - /** - * validate selectors and at-rules - */ - Default = Selector | AtRule, // selectors + at-rules - /** - * validate selectors, at-rules and declarations + * Node has children */ - All = Selector | AtRule | Declaration, // selectors + at-rules + declarations + HasChildren = 1 << 8, - /** - * Report only. Apply validation and report nodes that are marked as invalid - */ - ReportOnly = 0x8, + HasValues = 1 << 9, } +export declare type AstNodePropertyType = "state" | "errors" | "location" | "tokens" | "parent" | "selector"; + /** * Enum of all token types */ @@ -834,15 +804,15 @@ export enum ColorType { /** * deprecated system colors */ - SYS, + SYS = 1, /** * deprecated system colors */ - DPSYS, + DPSYS , /** * named colors */ - LIT, + LIT , /** * colors as hex values */ diff --git a/src/lib/ast/walk.ts b/src/lib/ast/walk.ts index fec4532e5..103c437ec 100644 --- a/src/lib/ast/walk.ts +++ b/src/lib/ast/walk.ts @@ -21,19 +21,19 @@ export enum WalkerOptionEnum { /** * ignore the current node and its children */ - Ignore = 0x1, + Ignore = 1 << 0, /** * stop walking the tree */ - Stop = 0x2, + Stop = 1 << 1, /** * ignore the current node and process its children */ - Children = 0x4, + Children = 1 << 2, /** * ignore the current node children */ - IgnoreChildren = 0x8, + IgnoreChildren = 1 << 3, } /** diff --git a/src/lib/parser/arena.ts b/src/lib/parser/arena.ts index 7fa0591ff..c802075bc 100644 --- a/src/lib/parser/arena.ts +++ b/src/lib/parser/arena.ts @@ -1,3 +1,23 @@ +import { + AstNode, + AstRuleList, + ErrorDescription, + ErrorDescription, + FunctionToken, + ParensToken, + Token, + WalkAttributesResult, + WalkerEvent, + WalkerFilter, + WalkerOption, + WalkerOptionEnum, + WalkerOptions, + WalkerValueFilter, +} from "../../node.ts"; +import { EnumAstNodeStatus, EnumToken } from "../ast/types.ts"; +import { PARENT, TOKENS } from "../syntax/constants.ts"; +import { equalsIgnoreCase } from "./utils/text.ts"; + export class Arena { public stringIds: Map = new Map(); public strings: string[] = []; @@ -9,6 +29,7 @@ export class Arena { public nextSibling: Uint32Array; public previousSibling: Uint32Array; public firstToken: Uint32Array; + public lastToken: Uint32Array; public firstChild: Uint32Array; public lastChild: Uint32Array; public name: Uint32Array; @@ -16,16 +37,29 @@ export class Arena { public numericValue: Float64Array; public unit: Uint32Array; public flags: Uint32Array; - public left: Uint32Array; - public right: Uint32Array; - public op: Uint32Array; - public op2: Uint32Array; + // public left: Uint32Array; + // public right: Uint32Array; + // public op: Uint32Array; + // public op2: Uint32Array; + public colorType: Uint8Array; + + /** + * token spans. 8 bits for source type. 28 bits for start position, 28 bits for end position. This allows us to store the start and end positions of tokens in a single 64-bit integer, which is more efficient than storing them separately. + */ + public spans: BigUint64Array; - constructor(size: number = 1024) { + public errors: Map = new Map(); + /** + * rendered tokens cache + */ + public renderedTokens: Uint32Array; + + constructor(size: number = 4096) { this.dataTypes = new Uint8Array(size); this.parents = new Uint32Array(size); this.nextSibling = new Uint32Array(size); this.previousSibling = new Uint32Array(size); + this.lastToken = new Uint32Array(size); this.firstToken = new Uint32Array(size); this.firstChild = new Uint32Array(size); this.lastChild = new Uint32Array(size); @@ -34,12 +68,24 @@ export class Arena { this.numericValue = new Float64Array(size); this.unit = new Uint32Array(size); this.flags = new Uint32Array(size); - this.left = new Uint32Array(size); - this.right = new Uint32Array(size); - this.op = new Uint32Array(size); - this.op2 = new Uint32Array(size); + // this.left = new Uint32Array(size); + // this.right = new Uint32Array(size); + // this.op = new Uint32Array(size); + // this.op2 = new Uint32Array(size); + this.renderedTokens = new Uint32Array(size); + this.colorType = new Uint8Array(size); + this.spans = new BigUint64Array(size); + } + + public allocate(): number { + const id = ++this.current; + if (this.current >= this.dataTypes.length) { + this.resize(this.dataTypes.length * 2); + } + return id; } + // Interns a string and returns its unique ID. If the string has already been interned, it returns the existing ID. public internString(str: string): number { let id = this.stringIds.get(str) as number; if (id != null) { @@ -48,10 +94,770 @@ export class Arena { this.stringIds.set(str, this.strings.length); this.strings.push(str); - return this.strings.length - 1; + return this.strings.length; + } + + /** + * set node data for a given id. The first argument is the id, the second is the type, the third is the string value, the fourth is the numeric value, and the fifth is the unit. + * @param id + * @param type + * @param stringValue + */ + public set(id: number, type: EnumToken): void; + + /** + * set node data for a given id. The first argument is the id, the second is the type, the third is the string value, the fourth is the numeric value, and the fifth is the unit. + * @param id + * @param type + * @param stringValue + */ + public set(id: number, type: EnumToken, stringValue: number): void; + /** + * set node data for a given id. The first argument is the id, the second is the type, the third is the string value, and the fourth is the numeric value. + * @param id + * @param type + * @param stringValue + * @param numericValue + */ + public set(id: number, type: EnumToken, stringValue: number, numericValue: number): void; + /** + * set node data for a given id. The first argument is the id, the second is the type, the third is the string value, the fourth is the numeric value, and the fifth is the unit. + * @param id + * @param type + * @param stringValue + * @param numericValue + * @param unit + */ + public set(id: number, type: EnumToken, stringValue: number, numericValue: number, unit: number): void; + + /** + * set node data for a given id. The first argument is the id, the second is the type, the third is the string value, the fourth is the numeric value, and the fifth is the unit. + * @param id + * @param args + */ + public set(id: number, ...args: number[]): void { + this.dataTypes[id] = args[0]; + + if (args.length > 1) { + // 0 is null string, so we can use it to indicate that there is no string value for this node. + this.stringValue[id] = args[1]; + } + + if (args.length > 2) { + this.numericValue[id] = args[2]; + } + + if (args.length > 3) { + this.unit[id] = args[3]; + } + } + + getType(id: number): EnumToken { + return this.dataTypes[id]; + } + + setType(id: number, type: EnumToken): void { + this.dataTypes[id] = type; + } + + setName(id: number, nameId: number): void { + this.name[id] = nameId; + } + + getName(id: number): string { + return this.strings[this.name[id]] ?? null; + } + + setValue(id: number, valueId: number): void { + this.stringValue[id] = valueId; + } + + getValue(id: number): string { + return this.strings[this.stringValue[id]] ?? null; + } + + setNumbericValue(id: number, numericValue: number): void { + this.numericValue[id] = numericValue; + } + + getNumericValue(id: number): number { + return this.numericValue[id]; + } + + setParent(id: number, parentId: number): void { + this.parents[id] = parentId; + } + + setFirstChild(id: number, firstChildId: number): void { + this.firstChild[id] = firstChildId; + } + + setLastChild(id: number, lastChildId: number): void { + this.lastChild[id] = lastChildId; + } + + setNextSibling(id: number, nextSiblingId: number): void { + this.nextSibling[id] = nextSiblingId; + } + + setPreviousSibling(id: number, previousSiblingId: number): void { + this.previousSibling[id] = previousSiblingId; + } + + setFirstToken(id: number, firstTokenId: number): void { + this.firstToken[id] = firstTokenId; + } + + setColorType(id: number, colorType: number): void { + this.colorType[id] = colorType; + } + + setFlags(id: number, flags: number): void { + this.flags[id] = flags; + } + + getFlags(id: number): number { + return this.flags[id]; + } + + hasFlags(id: number, flag: number): boolean { + return (this.flags[id] & flag) != 0; + } + + getChildAt(id: number, index: number): number | null { + let child = this.firstChild[id]; + + while (index > 0 && child != 0) { + child = this.nextSibling[child]; + index--; + } + + return child == 0 ? null : child; + } + + setChildAt(id: number, index: number, child: number): void { + if (index == 0) { + if (child != this.firstChild[id]) { + this.detach(child); + + if (this.firstChild[id] != 0) { + this.nextSibling[child] = this.nextSibling[this.firstChild[id]]; + this.previousSibling[this.nextSibling[this.firstChild[id]]] = child; + } + + this.firstChild[id] = child; + this.parents[child] = id; + } + + return; + } + + index--; + let currentChild = this.firstChild[id]; + while (index > 0 && this.nextSibling[currentChild] != 0) { + currentChild = this.nextSibling[currentChild]; + index--; + } + + this.detach(child); + + if (index > 0) { + // append + this.nextSibling[currentChild] = child; + this.previousSibling[child] = currentChild; + this.nextSibling[child] = 0; + } else { + // prepend + this.previousSibling[child] = this.previousSibling[currentChild]; + this.nextSibling[child] = this.nextSibling[currentChild]; + + this.nextSibling[this.previousSibling[currentChild]] = child; + this.previousSibling[currentChild] = child; + + this.previousSibling[currentChild] = 0; + this.nextSibling[currentChild] = 0; + this.parents[currentChild] = 0; + } + + this.parents[child] = id; + } + + setLeft(id: number, child: number): void { + this.setChildAt(id, 0, child); + } + + setRight(id: number, rightId: number): void { + this.setChildAt(id, 1, rightId); + } + + setOp(id: number, opId: number): void { + this.setChildAt(id, 2, opId); + } + + setOp2(id: number, op2Id: number): void { + this.setChildAt(id, 3, op2Id); + } + + setRenderedTokens(id: number, renderedTokensId: number): void { + this.renderedTokens[id] = renderedTokensId; + } + + public setSpan(id: number, sourceId: number, start: number, end: number): void { + this.spans[id] = BigInt(((sourceId & 0xff) << 56) | ((start & 0xfffffff) << 28) | (end & 0xfffffff)); + } + + public getSourceId(id: number): number { + return Number((this.spans[id] >> 56n) & 0xffn); + } + + public getSpanStart(id: number): number { + return Number((this.spans[id] >> 28n) & 0xfffffffn); + } + + setSpandStart(id: number, start: number): void { + this.spans[id] = + BigInt((this.getSourceId(id) & 0xff) << 56) | + BigInt((start & 0xfffffff) << 28) | + BigInt(this.getSpanEnd(id) & 0xfffffff); } - resolveString(id: number): string { + public getSpanEnd(id: number): number { + return Number(this.spans[id] & 0xfffffffn); + } + + public setSpanEnd(id: number, end: number): void { + this.spans[id] = + BigInt((this.getSourceId(id) & 0xff) << 56) | + BigInt((this.getSpanStart(id) & 0xfffffff) << 28) | + BigInt(end & 0xfffffff); + } + + getParent(id: number): number { + return this.parents[id]; + } + + public resolveString(id: number): string { return this.strings[id]; } + + detach(id: number): void { + let parent = this.parents[id]; + + if (this.lastChild[parent] == id) { + this.lastChild[parent] = this.previousSibling[id]; + } + + if (this.nextSibling[id] != 0) { + // first child + if (this.previousSibling[id] == 0) { + this.firstChild[parent] = this.nextSibling[id]; + } else { + // not first child + this.nextSibling[this.previousSibling[id]] = this.nextSibling[id]; + } + } + + this.nextSibling[id] = 0; + this.previousSibling[id] = 0; + this.parents[id] = 0; + } + + *walk(node: number, filter?: WalkerFilter | WalkerOptions | null, reverse?: boolean): Generator { + const parents: number[] = [node]; + + let options: WalkerOptions | null = filter as WalkerOptions | null; + let isNumeric: boolean = false; + let children: boolean = true; + let attributes: boolean = false; + let i: number = 0; + let firstChild: number; + + if (options != null && typeof options == "object") { + filter = options.filter as WalkerFilter; + reverse = options.reverse; + attributes = options.attributes as boolean; + children = (options.children as boolean) ?? true; + } + + while ((node = parents[i++])) { + let option: WalkerOption = null; + + if (filter != null) { + // @ts-expect-error + option = filter(node); + isNumeric = typeof option == "number"; + + if (isNumeric) { + if ((option as number) & WalkerOptionEnum.Ignore) { + continue; + } + + if ((option as number) & WalkerOptionEnum.Stop) { + break; + } + } + } + + if (!isNumeric || ((option as number) & WalkerOptionEnum.Children) === 0) { + yield node; + } + + if (attributes) { + firstChild = + this.flags[node] & EnumAstNodeStatus.HasValues ? this.firstChild[node] : this.firstToken[node]; + + if (firstChild != 0) { + const siblings = this.siblings(firstChild); + parents.splice(i, 0, ...(reverse ? siblings.toReversed() : siblings)); + } + // else if (this.flags[node] & EnumAstNodeStatus.HasValues) { + // // @ts-expect-error + // for (const val of node.val) { + // if (val[PARENT] != node) { + // val[PARENT] = node; + // } + // } + + // // @ts-expect-error + // parents.splice(i, 0, ...(reverse ? node.val.toReversed() : node.val)); + // } + } + + if ( + children && + this.flags[node] & EnumAstNodeStatus.HasChildren && + (!isNumeric || ((option as number) & WalkerOptionEnum.IgnoreChildren) === 0) + ) { + firstChild = this.firstChild[node]; + if (firstChild != 0) { + const siblings = this.siblings(firstChild); + parents.splice(i, 0, ...(reverse ? siblings.toReversed() : siblings)); + } + + // // @ts-expect-error + // parents.splice(i, 0, ...(reverse ? node.chi!.toReversed() : node.chi)); + + // for (const child of (node).chi) { + // if (child[PARENT] != node) { + // child[PARENT] = node; + // } + // } + } + } + } + + *walkValues( + values: number[], + root: number | null = null, + filter?: + | WalkerValueFilter + | null + | { + event?: WalkerEvent; + fn?: WalkerValueFilter; + type?: EnumToken | EnumToken[] | ((token: number) => boolean); + }, + reverse?: boolean, + ): Generator { + const stack: number[] = values.slice(); + let previous: number | null = null; + + if (filter != null && typeof filter == "function") { + filter = { + event: WalkerEvent.Enter, + fn: filter, + }; + } else if (filter == null) { + filter = { + event: WalkerEvent.Enter, + }; + } + + let isNumeric: boolean = false; + let value: number; + let option: WalkerOption | number[]; + let node: number | null; + let i: number = -1; + const eventType = filter.event ?? WalkerEvent.Enter; + // @ts-ignore + // const result = { + // value: null, + // parent: null, + // previousValue: null, + // nextValue: null, + // // + // root: root ?? null, + // parents: function* () { + // // @ts-ignore + // let result = node?.[PARENT] ?? root; + // while (result != null) { + // yield result; + // result = result[PARENT] ?? null; + // } + // }, + // } as WalkAttributesResult; + + while (++i < stack.length) { + value = stack[i]; + option = null; + + if (filter.fn != null && eventType & WalkerEvent.Enter) { + const isValid: boolean = + filter.type == null || + this.dataTypes[value] == filter.type || + (Array.isArray(filter.type) && filter.type.includes(this.dataTypes[value])) || + (typeof filter.type == "function" && filter.type(value)); + + if (isValid) { + option = filter.fn(value, WalkerEvent.Enter); + + isNumeric = typeof option == "number"; + + if (isNumeric && (option as number) & WalkerOptionEnum.Stop) { + return; + } + + if (isNumeric && (option as number) & WalkerOptionEnum.Ignore) { + continue; + } + + // + if (option != null && Array.isArray(option)) { + // const op = Array.isArray(option) ? option : [option]; + + stack.splice(i, 0, ...(reverse ? option.toReversed() : option)); + } + } + } + + yield value; + + if (!isNumeric || ((option as number) & WalkerOptionEnum.IgnoreChildren) === 0) { + if (this.flags[value] & EnumAstNodeStatus.HasChildren) { + + for (const child of this.children(value)) { + + if (reverse) { + stack.unshift(child); + } else { + stack.push(child); + } + } + } + + // else { + // const values: Token[] = []; + + // if ("l" in value && value.l != null) { + // // @ts-expect-error + // values.push(value.l); + + // // @ts-expect-error + // if (value.l[PARENT] != value) { + // // @ts-expect-error + // value.l[PARENT] = value; + // } + // } + + // if ("op" in value && typeof value.op == "object") { + // // @ts-expect-error + // values.push(value.op); + + // // @ts-expect-error + // if (value.op[PARENT] != value) { + // // @ts-expect-error + // value.op[PARENT] = value; + // } + // } + + // if ("r" in value && value.r != null) { + // if (Array.isArray(value.r)) { + // for (const r of value.r) { + // // + // values.push(r); + + // if (r[PARENT] != value) { + // // + // r[PARENT] = value; + // } + // } + // } else { + // // + // values.push(value.r); + + // if (value.r[PARENT] != value) { + // // + // value.r[PARENT] = value; + // } + // } + // } + + // if (values.length > 0) { + // for (const v of values) { + // if (reverse) { + // stack.unshift(v); + // } else { + // stack.push(v); + // } + // } + // } + // } + } + + if (eventType & WalkerEvent.Leave && filter.fn != null) { + const isValid: boolean = + filter.type == null || + this.dataTypes[value] == filter.type || + (Array.isArray(filter.type) && filter.type.includes(this.dataTypes[value])) || + (typeof filter.type == "function" && filter.type(value)); + + if (isValid) { + // @ts-ignore + option = filter.fn(value, value[PARENT], WalkerEvent.Leave); + + // @ts-ignore + if (option != null && (Array.isArray(option))) { + // const op = Array.isArray(option) ? option : [option]; + stack.splice(i, 0, ...(reverse ? option.toReversed() : option)); + + // for (const o of op) { + // if (o[PARENT] != value) { + // o[PARENT] = value; + // } + // } + } + } + } + + previous = value; + } + } + + findChild(id: number, predicate: (id: number) => boolean): number | null { + let child = this.firstChild[id]; + while (child != 0) { + if (predicate(child)) { + return child; + } + child = this.nextSibling[child]; + } + + return null; + } + + siblings(id: number): number[] { + let siblings: number[] = []; + let child = this.firstChild[this.parents[id]]; + while (child != 0) { + siblings.push(child); + child = this.nextSibling[child]; + } + return siblings; + } + + children(id: number): number[] { + let children: number[] = []; + let child = this.firstChild[id]; + while (child != 0) { + children.push(child); + child = this.nextSibling[child]; + } + return children; + } + + setChildren(id: number, ...children: number[]): void { + if (this.lastChild[id] != 0) { + this.lastChild[id] = 0; + } + + if (this.firstChild[id] != 0) { + this.detach(this.firstChild[id]); + } + + this.appendChildren(id, ...children); + } + + setTokens(id: number, ...children: number[]): void { + if (this.lastToken[id] != 0) { + this.lastToken[id] = 0; + } + + if (this.firstToken[id] != 0) { + this.detach(this.firstToken[id]); + } + + this.appendTokens(id, ...children); + } + + appendChildren(id: number, ...children: number[]): void { + if (children.length == 0) { + return; + } + + this.detach(children[0]); + + let lastChild: number = this.lastChild[id]; + + if (lastChild == 0) { + this.firstChild[id] = children[0]; + } else { + this.nextSibling[lastChild] = children[0]; + } + + this.parents[children[0]] = id; + + for (let i = 1; i < children.length - 1; i++) { + this.detach(children[i]); + this.previousSibling[children[i]] = children[i - 1]; + this.nextSibling[children[i - 1]] = children[i]; + this.parents[children[i]] = id; + } + + this.lastChild[id] = children[children.length - 1]; + } + + appendTokens(id: number, ...children: number[]): void { + if (children.length == 0) { + return; + } + + this.detach(children[0]); + + let lastChild: number = this.lastToken[id]; + + if (lastChild == 0) { + this.firstToken[id] = children[0]; + } else { + this.nextSibling[lastChild] = children[0]; + } + + this.parents[children[0]] = id; + + for (let i = 1; i < children.length - 1; i++) { + this.detach(children[i]); + this.previousSibling[children[i]] = children[i - 1]; + this.nextSibling[children[i - 1]] = children[i]; + this.parents[children[i]] = id; + } + + this.lastToken[id] = children[children.length - 1]; + } + + public equalsIgnoreCase(node: number, key: "nam" | "val", string: string): boolean { + const stringId = this.stringIds.get(string); + + if (stringId == null) { + return false; + } + + const id = key == "nam" ? this.name[node] : this.stringValue[node]; + + if (id == stringId) { + return true; + } + + if (string.length != this.strings[id].length) { + return false; + } + + return equalsIgnoreCase(this.strings[id], string); + } + + public startsWith(node: number, key: "nam" | "val", string: string): boolean { + const stringId = this.stringIds.get(string); + + if (stringId == null) { + return false; + } + + const id = key == "nam" ? this.name[node] : this.stringValue[node]; + + if (id == stringId) { + return true; + } + + if (string.length != this.strings[id].length) { + return false; + } + + return this.strings[id].startsWith(string); + } + + public resize(newSize: number) { + if (newSize <= this.dataTypes.length) { + return; + } + + const newDataTypes = new Uint8Array(newSize); + newDataTypes.set(this.dataTypes); + this.dataTypes = newDataTypes; + + const newParents = new Uint32Array(newSize); + newParents.set(this.parents); + this.parents = newParents; + + const newNextSibling = new Uint32Array(newSize); + newNextSibling.set(this.nextSibling); + this.nextSibling = newNextSibling; + + const newPreviousSibling = new Uint32Array(newSize); + newPreviousSibling.set(this.previousSibling); + this.previousSibling = newPreviousSibling; + + const newFirstToken = new Uint32Array(newSize); + newFirstToken.set(this.firstToken); + this.firstToken = newFirstToken; + + const newLastToken = new Uint32Array(newSize); + newLastToken.set(this.lastToken); + this.lastToken = newLastToken; + + const newFirstChild = new Uint32Array(newSize); + newFirstChild.set(this.firstChild); + this.firstChild = newFirstChild; + + const newLastChild = new Uint32Array(newSize); + newLastChild.set(this.lastChild); + this.lastChild = newLastChild; + + const newName = new Uint32Array(newSize); + newName.set(this.name); + this.name = newName; + + const newStringValue = new Uint32Array(newSize); + newStringValue.set(this.stringValue); + this.stringValue = newStringValue; + + const newNumericValue = new Float64Array(newSize); + newNumericValue.set(this.numericValue); + this.numericValue = newNumericValue; + + const newUnit = new Uint32Array(newSize); + newUnit.set(this.unit); + this.unit = newUnit; + + const newFlags = new Uint32Array(newSize); + newFlags.set(this.flags); + this.flags = newFlags; + + const newRenderedTokens = new Uint32Array(newSize); + newRenderedTokens.set(this.renderedTokens); + this.renderedTokens = newRenderedTokens; + + const newColorType = new Uint8Array(newSize); + newColorType.set(this.colorType); + this.colorType = newColorType; + + const newSpans = new BigUint64Array(newSize); + newSpans.set(this.spans); + this.spans = newSpans; + } + + setErrors(id: number, errors: ErrorDescription[]) { + this.errors.set(id, errors); + } + + getErrors(id: number): ErrorDescription[] | null { + return this.errors.get(id) ?? null; + } } diff --git a/src/lib/parser/parse.ts b/src/lib/parser/parse.ts index d79c8dbd7..5028bcfee 100644 --- a/src/lib/parser/parse.ts +++ b/src/lib/parser/parse.ts @@ -80,6 +80,7 @@ import { matchGenericSyntax } from "./utils/at-rule-generic.ts"; import { memoize } from "./utils/cache.ts"; import { SourceFile } from "./source.ts"; import { dirname } from "../fs/resolve.ts"; +import { Arena, ARENA_ROOT } from "./arena.ts"; function renderTokens(tokens: Token[] | null | undefined, options?: any): string { if (tokens == null || tokens.length === 0) return ""; @@ -679,7 +680,7 @@ export function doParseSync(tokenizer: Tokenizer, options: ParserSyncOptions = { const startTime: number = performance.now(); const errors: ErrorDescription[] = []; - const stack: Array = []; + const stack: Array = []; const stats: ParseResultStats = { src: options.src ?? "", bytesIn: 0, @@ -693,22 +694,24 @@ export function doParseSync(tokenizer: Tokenizer, options: ParserSyncOptions = { imports: [], }; - const invalidNodes: Array = []; + const invalidNodes: Array = []; + const arena = options.arena!; let ast: AstStyleSheet = { typ: EnumToken.StyleSheetNodeType, chi: [], }; - let tokens: Token[] = []; - let context: AstRuleList = ast; + let tokens: number[] = []; + let context: number = arena.root; let item: Token; - let node: AstAtRule | AstRule | AstKeyframesRule | AstKeyframesAtRule | AstDeclaration | AstComment | null; + let node: number | null; // @ts-ignore ignore error let parensMatch: number = 0; let curlyBracketMatch: number = 0; + let tokenId: number; // let currentItemIndex: number; ast[LOCSRCID] = options.source!.id; @@ -718,128 +721,159 @@ export function doParseSync(tokenizer: Tokenizer, options: ParserSyncOptions = { tokenizer.next(); // item = (iter as Array)[currentItemIndex]; + tokenId = arena.allocate(); + if (tokenizer.unit != null) { - item = { - typ: tokenizer.typ as EnumToken, - val: tokenizer.val, - unit: tokenizer.unit, - } as DimensionToken; + + arena.set(tokenId, tokenizer.typ, 0, tokenizer.val, arena.internString(tokenizer.unit)); + + // item = { + // typ: tokenizer.typ as EnumToken, + // val: tokenizer.val, + // unit: tokenizer.unit, + // } as DimensionToken; } else if (tokenizer.nam != null) { - item = { - typ: tokenizer.typ as EnumToken, - nam: tokenizer.nam, - } as Token; + + arena.set(tokenId, tokenizer.typ); + arena.setName(tokenId, tokenizer.nam); + // item = { + // typ: tokenizer.typ as EnumToken, + // nam: tokenizer.nam, + // } as Token; } else if (tokenizer.val === null) { - item = { - typ: tokenizer.typ as EnumToken, - } as Token; + arena.set(tokenId, tokenizer.typ); + // item = { + // typ: tokenizer.typ as EnumToken, + // } as Token; } else if (tokenizer.kin != null) { - item = { - typ: tokenizer.typ as EnumToken, - val: tokenizer.val, - kin: tokenizer.kin, - } as ColorToken; + + arena.set(tokenId, tokenizer.typ, arena.internString(tokenizer.val)); + arena.setColorType(tokenId, tokenizer.kin); + + // item = { + // typ: tokenizer.typ as EnumToken, + // val: tokenizer.val, + // kin: tokenizer.kin, + // } as ColorToken; } else { - item = { - typ: tokenizer.typ as EnumToken, - val: tokenizer.val, - } as Token; + arena.set(tokenId, tokenizer.typ, arena.internString(tokenizer.val)); + // item = { + // typ: tokenizer.typ as EnumToken, + // val: tokenizer.val, + // } as Token; } - item[LOCSRCID] = tokenizer.srcId as number; - item[LOCSTA] = tokenizer.sta as number; - item[LOCEND] = tokenizer.end as number; + arena.setSpan(tokenId, tokenizer.srcId, tokenizer.sta, tokenizer.end); + + // item[LOCSRCID] = tokenizer.srcId as number; + // item[LOCSTA] = tokenizer.sta as number; + // item[LOCEND] = tokenizer.end as number; stats.bytesIn = tokenizer.bytesIn as number; stats.tokensCount++; - if (BadTokensTypes.includes(item.typ)) { - tokens.push(item); + if (BadTokensTypes.includes(tokenizer.typ as EnumToken)) { + tokens.push(tokenId); errors.push({ action: "drop", message: "Bad token", syntax: null, - node: item, - location: options.source!.getSourceLocation(item[LOCSTA]), + node: tokenId, + location: options.source!.getSourceLocation(tokenizer.sta as number), }); // bad token continue; } - if (item.typ === EnumToken.StartParensTokenType || tokensfuncDefMap.has(item.typ)) { + if (tokenizer.typ === EnumToken.StartParensTokenType || tokensfuncDefMap.has(tokenizer.typ as EnumToken)) { parensMatch++; - } else if (item.typ === EnumToken.EndParensTokenType && parensMatch > 0) { + } else if (tokenizer.typ === EnumToken.EndParensTokenType && parensMatch > 0) { parensMatch--; } - if (item.typ === EnumToken.BlockStartTokenType) { + if (tokenizer.typ === EnumToken.BlockStartTokenType) { curlyBracketMatch++; - } else if (item.typ === EnumToken.BlockEndTokenType && curlyBracketMatch > 0) { + } else if (tokenizer.typ === EnumToken.BlockEndTokenType && curlyBracketMatch > 0) { curlyBracketMatch--; } - tokens.push(item); + tokens.push(tokenId); if ( parensMatch === 0 && - (item.typ === EnumToken.SemiColonTokenType || - item.typ === EnumToken.BlockStartTokenType || - item.typ === EnumToken.EOFTokenType) + (tokenizer.typ === EnumToken.SemiColonTokenType || + tokenizer.typ === EnumToken.BlockStartTokenType || + tokenizer.typ === EnumToken.EOFTokenType) ) { node = parseNode(tokens, context, options as ParserOptions, errors, stats, invalidNodes); if (node != null) { - if ("chi" in node) { - stack.push(node as AstAtRule | AstRule | AstKeyframesRule); - context = node as AstRuleList; + if (arena.hasFlags(node, EnumAstNodeStatus.HasChildren)) { + stack.push(node ); + context = node; } - } else if (item.typ == EnumToken.BlockStartTokenType) { + } else if (tokenizer.typ == EnumToken.BlockStartTokenType) { let inBlock: number = 1; tokens.length = 0; - tokens.push(item); + tokens.push(tokenId); do { tokenizer.next(); + tokenId = arena.allocate(); + if (tokenizer.unit != null) { - item = { - typ: tokenizer.typ as EnumToken, - val: tokenizer.val, - unit: tokenizer.unit, - } as DimensionToken; + + arena.set(tokenId, tokenizer.typ, 0, tokenizer.val, arena.internString(tokenizer.unit)); + // item = { + // typ: tokenizer.typ as EnumToken, + // val: tokenizer.val, + // unit: tokenizer.unit, + // } as DimensionToken; } else if (tokenizer.nam != null) { - item = { - typ: tokenizer.typ as EnumToken, - nam: tokenizer.nam, - } as Token; + + arena.set(tokenId, tokenizer.typ); + arena.setName(tokenId, tokenizer.nam); + // item = { + // typ: tokenizer.typ as EnumToken, + // nam: tokenizer.nam, + // } as Token; } else if (tokenizer.val === null) { - item = { - typ: tokenizer.typ as EnumToken, - } as Token; + arena.set(tokenId, tokenizer.typ); + // item = { + // typ: tokenizer.typ as EnumToken, + // } as Token; } else if (tokenizer.kin != null) { - item = { - typ: tokenizer.typ as EnumToken, - val: tokenizer.val, - kin: tokenizer.kin, - } as ColorToken; + + arena.set(tokenId, tokenizer.typ, arena.internString(tokenizer.val)); + arena.setColorType(tokenId, tokenizer.kin); + // item = { + // typ: tokenizer.typ as EnumToken, + // val: tokenizer.val, + // kin: tokenizer.kin, + // } as ColorToken; } else { - item = { - typ: tokenizer.typ as EnumToken, - val: tokenizer.val, - } as Token; + + arena.set(tokenId, tokenizer.typ, arena.internString(tokenizer.val)); + // item = { + // typ: tokenizer.typ as EnumToken, + // val: tokenizer.val, + // } as Token; } - item[LOCSRCID] = tokenizer.srcId as number; - item[LOCSTA] = tokenizer.sta as number; - item[LOCEND] = tokenizer.end as number; + arena.setSpan(tokenId, tokenizer.srcId, tokenizer.sta, tokenizer.end); - tokens.push(item); + // item[LOCSRCID] = tokenizer.srcId as number; + // item[LOCSTA] = tokenizer.sta as number; + // item[LOCEND] = tokenizer.end as number; - if (item.typ === EnumToken.BlockStartTokenType) { + tokens.push(tokenId); + + if (tokenizer.typ === EnumToken.BlockStartTokenType) { inBlock++; - } else if (item.typ === EnumToken.BlockEndTokenType) { + } else if (tokenizer.typ === EnumToken.BlockEndTokenType) { inBlock--; } } while (inBlock != 0 && !tokenizer.done()); @@ -848,27 +882,29 @@ export function doParseSync(tokenizer: Tokenizer, options: ParserSyncOptions = { errors.push({ action: "drop", message: "invalid block", - location: options.source!.getSourceLocation(tokens[0][LOCSTA]!), + location: options.source!.getSourceLocation(tokenizer.sta!), }); } } tokens.length = 0; - } else if ((parensMatch === 0 || curlyBracketMatch === 0) && item.typ === EnumToken.BlockEndTokenType) { + } else if ((parensMatch === 0 || curlyBracketMatch === 0) && tokenizer.typ === EnumToken.BlockEndTokenType) { parseNode(tokens, context, options as ParserOptions, errors, stats, invalidNodes); - context[LOCEND] = item[LOCEND]; - const previousNode = stack.pop() as AstRuleList; - context = (stack[stack.length - 1] ?? ast) as AstRuleList; + // context[LOCEND] = tokenizer.end as number; + + const previousNode = stack.pop() as number; + context = (stack[stack.length - 1] ?? arena.root); if ( options.removeEmpty && previousNode != null && - previousNode.chi!.length == 0 && - context.chi![context.chi!.length - 1] == previousNode + arena.firstChild(previousNode) == 0 && + arena.lastChild(context) == previousNode ) { - context.chi!.pop(); + + arena.detach(previousNode); } tokens.length = 0; @@ -882,9 +918,9 @@ export function doParseSync(tokenizer: Tokenizer, options: ParserSyncOptions = { node = parseNode(tokens, context, options as ParserOptions, errors, stats, invalidNodes); if (node != null) { - if ("chi" in node /* && node.typ != EnumToken.InvalidRuleNodeType */) { + if (arena.hasFlags(node, EnumAstNodeStatus.HasChildren)) { stack.push(node); - context = node as AstRuleList; + context = node; } } } @@ -3177,27 +3213,33 @@ export async function doParse(iter: Tokenizer | Promise, options: Par } function parseNode( - tokens: Token[], - context: AstRuleList, + tokens: number[], + context: number, options: ParserOptions, errors: ErrorDescription[], stats: ParseResultStats, - invalidNodes: AstNode[], -): AstRule | AstAtRule | AstKeyframesRule | AstKeyframesAtRule | AstDeclaration | AstComment | null { + invalidNodes: number[], +): number| null { let i: number = 0; - if (tokens.at(-1)?.typ === EnumToken.EOFTokenType) { + const arena: Arena = options.arena!; + const contextType: number = arena.getType(context); + let token: number; + let typ: EnumToken; + + if (arena.getType(tokens.at(-1)!) === EnumToken.EOFTokenType) { tokens.pop(); // check parenthesis are balanced let matchCount: number = 0; for (let i = 0; i < tokens.length; i++) { - const token: Token = tokens[i]; + token = tokens[i]; + typ = arena.getType(token); - if (token.typ === EnumToken.StartParensTokenType || tokensfuncDefMap.has(token.typ)) { + if (typ === EnumToken.StartParensTokenType || tokensfuncDefMap.has(typ)) { matchCount++; - } else if (token.typ === EnumToken.EndParensTokenType) { + } else if (typ === EnumToken.EndParensTokenType) { matchCount--; } } @@ -3205,44 +3247,65 @@ function parseNode( if (matchCount > 0) { let k: number = tokens.length; + if (k > 0) { + + typ = arena.getType(tokens[k - 1]) ; + while ( k-- > 0 && - (tokens[k].typ === EnumToken.WhitespaceTokenType || tokens[k].typ === EnumToken.CommentTokenType) - ); + typ === EnumToken.WhitespaceTokenType || typ === EnumToken.CommentTokenType + ) { + + typ = arena.getType(tokens[k - 1]); + } - if (tokens[k]?.typ == EnumToken.SemiColonTokenType) { + if (typ == EnumToken.SemiColonTokenType) { matchCount = 0; } + let tokenId: number; + let sourceId: number = arena.getSourceId(tokens[k]); + let spanStart: number = arena.getSpanStart(tokens[k]); + let spanEnd: number = arena.getSpanEnd(tokens[k]); + while (matchCount > 0) { - tokens.push({ - typ: EnumToken.EndParensTokenType, - [LOCSRCID]: tokens[k]?.[LOCSRCID], - [LOCSTA]: tokens[k]?.[LOCSTA], - [LOCEND]: tokens[k]?.[LOCEND], - }); + + tokenId = arena.allocate(); + + arena.set(tokenId, EnumToken.EndParensTokenType); + arena.setSpan(tokenId, sourceId, spanStart, spanEnd); + // tokens.push({ + // typ: EnumToken.EndParensTokenType, + // [LOCSRCID]: tokens[k]?.[LOCSRCID], + // [LOCSTA]: tokens[k]?.[LOCSTA], + // [LOCEND]: tokens[k]?.[LOCEND], + // }); matchCount--; } + } + } } for (; i < tokens.length; i++) { - if (tokens[i].typ === EnumToken.CDOCOMMTokenType && context.typ !== EnumToken.StyleSheetNodeType) { + if (arena.getType(tokens[i]) === EnumToken.CDOCOMMTokenType && contextType !== EnumToken.StyleSheetNodeType) { errors.push({ action: "drop", message: `CDOCOMM not allowed here ${JSON.stringify(tokens[i], null, 1)}`, node: tokens[i], - location: options.source!.getSourceLocation(tokens[i][LOCSTA]!), + location: options.source!.getSourceLocation(arena.getSpanStart(tokens[i])), }); - tokens[i].typ = EnumToken.InvalidCommentTokenType; + arena.set(tokens[i], EnumToken.InvalidCommentTokenType); continue; } + typ = arena.getType(tokens[i]); + if ( - tokens[i].typ === EnumToken.CommentTokenType || - tokens[i].typ === EnumToken.CDOCOMMTokenType || - tokens[i].typ === EnumToken.WhitespaceTokenType + typ === EnumToken.CommentTokenType || + typ === EnumToken.CDOCOMMTokenType || + typ === EnumToken.WhitespaceTokenType ) { continue; } @@ -3251,30 +3314,35 @@ function parseNode( } if (i > 0) { - context.chi!.push( - ...(tokens.splice(0, i) as AstNode[]).filter((n: AstNode) => n.typ !== EnumToken.WhitespaceTokenType), + + arena.appendChildren( + context, + ...(tokens.splice(0, i) ).filter((n: number) => arena.getType(n) !== EnumToken.WhitespaceTokenType) ); i = 0; } for (; i < tokens.length; i++) { - if (tokens[i].typ == EnumToken.CommentTokenType || tokens[i].typ == EnumToken.CDOCOMMTokenType) { - if (tokens[i].typ == EnumToken.CDOCOMMTokenType && context.typ != EnumToken.StyleSheetNodeType) { + + typ = arena.getType(tokens[i]); + if (typ == EnumToken.CommentTokenType || typ == EnumToken.CDOCOMMTokenType) { + if (typ == EnumToken.CDOCOMMTokenType && contextType != EnumToken.StyleSheetNodeType) { errors.push({ action: "drop", message: `CDOCOMM not allowed here ${JSON.stringify(tokens[i], null, 1)}`, node: tokens[i], - location: options.source!.getSourceLocation(tokens[i][LOCSTA]!), + location: options.source!.getSourceLocation(arena.getSpanStart(tokens[i])), }); - tokens[i].typ = EnumToken.InvalidCommentTokenType; + arena.setType(tokens[i],EnumToken.InvalidCommentTokenType); continue; } - tokens[i][ROOT] = context[ROOT]; - context.chi!.push(tokens[i] as AstNode); + // tokens[i][ROOT] = context[ROOT]; + arena.appendChildren(context, tokens[i]); + // context.chi!.push(tokens[i] as AstNode); stats.nodesCount++; - } else if (tokens[i].typ != EnumToken.WhitespaceTokenType) { + } else if (typ != EnumToken.WhitespaceTokenType) { break; } } @@ -3283,12 +3351,18 @@ function parseNode( return null; } - let delim: Token = tokens.at(-1) as Token; + // for (const token of tokens.slice(1)) { + + // console.error(EnumToken[arena.getType(token)]); + // } + + let delim: number = tokens.at(-1) as number; + typ = arena.getType(delim); if ( - delim.typ == EnumToken.SemiColonTokenType || - delim.typ == EnumToken.BlockStartTokenType || - delim.typ == EnumToken.BlockEndTokenType + typ == EnumToken.SemiColonTokenType || + typ == EnumToken.BlockStartTokenType || + typ == EnumToken.BlockEndTokenType ) { tokens.pop(); } @@ -3297,27 +3371,31 @@ function parseNode( return null; } - if (tokens[0]?.typ == EnumToken.AtRuleTokenType) { + typ = arena.getType(tokens[0]); + + if (typ == EnumToken.AtRuleTokenType) { let nestedRule: boolean = false; - let parent: AstNode = context; + let parent: number = context; let node; - while (parent != null) { - if (parent.typ == EnumToken.RuleNodeType) { + while (parent != 0) { + + typ = arena.getType(parent); + if (typ == EnumToken.RuleNodeType) { nestedRule = true; break; } - parent = parent[PARENT] as AstNode; + parent = arena.getParent(parent) ; } node = parseAtRule( tokens, - context as AstStyleSheet | AstAtRule | AstRule, + context, { ...options, nestedRule }, errors, - delim.typ == EnumToken.BlockStartTokenType, + arena.getType(delim) == EnumToken.BlockStartTokenType, ); if (node == null) { @@ -3335,50 +3413,51 @@ function parseNode( } stats.nodesCount++; - context.chi!.push(node); + arena.appendChildren(context, node); + // context.chi!.push(node); - node[ROOT] = context[ROOT]; - node[PARENT] = context; + // arena.setParent(node, context); + + // node[ROOT] = context[ROOT]; + // node[PARENT] = context; // @ts-ignore return node; } else { stats.nodesCount++; // rule - if (delim.typ == EnumToken.BlockStartTokenType) { - const node = parseSelector(tokens, context as AstRule | AstAtRule, options, errors); + if (arena.getType(delim) == EnumToken.BlockStartTokenType) { + const node = parseSelector(tokens, context , options, errors); - context.chi!.push(node); - node[PARENT] = context; - node[ROOT] = context[ROOT]; + arena.appendChildren(context, node); + // context.chi!.push(node); + // node[PARENT] = context; + // node[ROOT] = context[ROOT]; if ( - (node as AstNode)[STATE] == EnumAstNodeStatus.Invalid || - (node as AstNode)[STATE] == EnumAstNodeStatus.Disallowed || - (node as AstNode)[STATE] == EnumAstNodeStatus.Unknown || - (node as AstNode)[STATE] == EnumAstNodeStatus.Unparsed || - (node as AstNode)[STATE] == EnumAstNodeStatus.Malformed + arena.hasFlags(node, EnumAstNodeStatus.Invalid | EnumAstNodeStatus.Disallowed | EnumAstNodeStatus.Unknown | EnumAstNodeStatus.Unparsed | EnumAstNodeStatus.Malformed) ) { invalidNodes.push(node); } return node; } else { - const node = parseDeclaration(tokens, context as AstRule | AstAtRule, options, errors) as AstDeclaration; - node[PARENT] = context; - node[ROOT] = context[ROOT]; + const node = parseDeclaration(tokens, context , options, errors) as AstDeclaration; + // node[PARENT] = context; + // node[ROOT] = context[ROOT]; - if (context.typ === EnumToken.StyleSheetNodeType && node.typ === EnumToken.DeclarationNodeType) { + if (contextType === EnumToken.StyleSheetNodeType && node.typ === EnumToken.DeclarationNodeType) { node[STATE] = EnumAstNodeStatus.Invalid; errors.push({ message: " not allowed in ", action: "drop", node, - location: options.source!.getSourceLocation(node[LOCSTA]!), + location: options.source!.getSourceLocation(arena.getSpanStart(node)), }); } else if (options.lenient || node.typ === EnumToken.DeclarationNodeType) { - context.chi!.push(node); + // context.chi!.push(node); + arena.appendChildren(context, node); } if ( diff --git a/src/lib/parser/utils/declaration.ts b/src/lib/parser/utils/declaration.ts index 57ae99028..36f66fb07 100644 --- a/src/lib/parser/utils/declaration.ts +++ b/src/lib/parser/utils/declaration.ts @@ -91,30 +91,36 @@ function parseGridTemplate(template: string): string { */ export function parseDeclaration( - tokens: Token[], - parent: AstRule | AstAtRule | AstKeyframesRule | AstStyleSheet | AtRuleToken | null, + tokens: number[], + parent: number| null, options: ParserOptions, errors: ErrorDescription[], -): AstDeclaration | RawNodeToken { - const name = tokens.shift() as IdentToken | DashedIdentToken; +): number { + const name = tokens.shift() as number; + const arena = options.arena!; let i: number; let rules: ValidationSyntaxRule | null = null; let syntaxRules = null; let success: boolean = true; + let val: string = arena.getValue(name); + + let token: number; + let typ: EnumToken; let validate = typeof options.validation === "boolean" ? options.validation : !!options.validation; - if (renamedStandardProperties.has(name.val)) { - name.val = renamedStandardProperties.get(name.val) as string; + if (renamedStandardProperties.has(val)) { + arena.setValue(name, arena.internString(renamedStandardProperties.get(val) as string)); } for (i = 0; i < tokens.length; i++) { - const token = tokens[i]; + token = tokens[i]; + typ = arena.getType(token); if ( - token.typ == EnumToken.WhitespaceTokenType || - token.typ == EnumToken.CommentTokenType || - token.typ == EnumToken.InvalidCommentTokenType + typ == EnumToken.WhitespaceTokenType || + typ == EnumToken.CommentTokenType || + typ == EnumToken.InvalidCommentTokenType ) { continue; } @@ -122,60 +128,85 @@ export function parseDeclaration( break; } + typ = arena.getType(name); + if ( - (name.typ !== EnumToken.IdenTokenType && name.typ !== EnumToken.DashedIdenTokenType) || - tokens[i]?.typ !== EnumToken.ColonTokenType + (typ !== EnumToken.IdenTokenType && typ !== EnumToken.DashedIdenTokenType) || + arena.getType(tokens[i]) !== EnumToken.ColonTokenType ) { - if (tokens[tokens.length - 1]?.[LOCEND] != null) { - name[LOCEND] = tokens[tokens.length - 1]?.[LOCEND]; - } + // if (tokens[tokens.length - 1]?.[LOCEND] != null) { + // name[LOCEND] = tokens[tokens.length - 1]?.[LOCEND]; + // } + + const node = arena.allocate(); - name[STATE] = EnumAstNodeStatus.Unparsed; - name[ERRORS] = [ + arena.setType(node, EnumToken.RawNodeTokenType); + arena.setFlags(node, EnumAstNodeStatus.Unparsed); + arena.setErrors(node, [ + { action: "drop", node: name, - location: options.source!.getSourceLocation(name[LOCSTA]!), + location: options.source!.getSourceLocation(arena.getSpanStart(name)), message: "invalid declaration", } as ErrorDescription, - ]; - - return Object.assign({ - typ: EnumToken.RawNodeTokenType, - val: tokens, - }) as RawNodeToken; + ]); + + arena.setTokens(node, tokens); + arena.setSpan(node, arena.getSourceId(name), arena.getSpanStart(name), arena.getSpanEnd(tokens.at(-1) ?? name)); + + return node; + + // name[STATE] = EnumAstNodeStatus.Unparsed; + // name[ERRORS] = [ + // { + // action: "drop", + // node: name, + // location: options.source!.getSourceLocation(name[LOCSTA]!), + // message: "invalid declaration", + // } as ErrorDescription, + // ]; + + // return Object.assign({ + // typ: EnumToken.RawNodeTokenType, + // val: tokens, + // }) as RawNodeToken; } - tokens = trimArray(tokens.slice(i + 1)); + tokens = trimArray(tokens.slice(i + 1), options); + + if (validate && typ === EnumToken.IdenTokenType) { + + typ = arena.getType(parent); + val = arena.getValue(parent).toLowerCase(); - if (validate && name.typ === EnumToken.IdenTokenType) { if ( parent != null && - parent.typ === EnumToken.AtRuleNodeType && - parent.nam != "import" && - parent.nam != "supports" && - parent.nam != "media" + typ === EnumToken.AtRuleNodeType && + val != "import" && + val != "supports" && + val!= "media" ) { // check @at-rule description // pick up the syntax or reject immediately // else do this - rules = getSyntaxRule(ValidationSyntaxGroupEnum.AtRules, "@" + parent.nam); + rules = getSyntaxRule(ValidationSyntaxGroupEnum.AtRules, "@" + val); if (rules != null) { const propertyDescriptors = rules.getPropertyDescriptors(); if (propertyDescriptors != null) { - syntaxRules = propertyDescriptors[name.val.toLowerCase()]; + syntaxRules = propertyDescriptors[val.toLowerCase()]; } else { syntaxRules = rules.acceptAnyDeclaration && rules.acceptAnyRule - ? getParsedSyntax(ValidationSyntaxGroupEnum.Declarations, name.val.toLowerCase()) + ? getParsedSyntax(ValidationSyntaxGroupEnum.Declarations, val) : rules.getBlockRules(); } } } else { - syntaxRules = getParsedSyntax(ValidationSyntaxGroupEnum.Declarations, name.val.toLowerCase()); + syntaxRules = getParsedSyntax(ValidationSyntaxGroupEnum.Declarations, val); } // or @@ -187,71 +218,85 @@ export function parseDeclaration( ((syntaxRules[0] as ValidationPropertyToken).val === "declaration-list" || (syntaxRules[0] as ValidationPropertyToken).val === "declaration-rule-list")) ) { + + typ = arena.getType(parent); + val = arena.getValue(parent).toLowerCase(); + if ( - parent?.typ === EnumToken.AtRuleNodeType && - (parent.nam === "swash" || - parent.nam === "annotation" || - parent.nam === "ornaments" || - parent.nam === "stylistic" || - parent.nam === "historical-forms") + parent != null && + typ === EnumToken.AtRuleNodeType && + (val === "swash" || + val=== "annotation" || + val === "ornaments" || + val === "stylistic" || + val === "historical-forms") ) { syntaxRules = getParsedSyntax(ValidationSyntaxGroupEnum.Syntaxes, "font-feature-custom-ident"); - } else if (parent?.typ === EnumToken.AtRuleNodeType && parent.nam === "styleset") { + } else if (typ === EnumToken.AtRuleNodeType && val === "styleset") { syntaxRules = getParsedSyntax(ValidationSyntaxGroupEnum.Syntaxes, "font-feature-custom-ident-list"); - } else if (parent?.typ === EnumToken.AtRuleNodeType && parent.nam === "character-variant") { + } else if (typ === EnumToken.AtRuleNodeType && val=== "character-variant") { syntaxRules = getParsedSyntax( ValidationSyntaxGroupEnum.Syntaxes, "font-feature-custom-ident-character-variant", ); } else { - syntaxRules = getParsedSyntax(ValidationSyntaxGroupEnum.Declarations, name.val.toLowerCase()); + syntaxRules = getParsedSyntax(ValidationSyntaxGroupEnum.Declarations, val); } } } - if (tokens.length === 0 && name.typ !== EnumToken.DashedIdenTokenType) { + typ = arena.getType(name); + + if (tokens.length === 0 && typ !== EnumToken.DashedIdenTokenType) { errors.push({ action: "drop", message: "declaration value missing", node: name, - location: options.source!.getSourceLocation(name[LOCSTA]!), + location: options.source!.getSourceLocation(arena.getSpanStart(name)), }); - if (tokens[tokens.length - 1]?.[LOCEND] != null) { - name[LOCEND] = tokens[tokens.length - 1][LOCEND]; - } + // if (tokens[tokens.length - 1]?.[LOCEND] != null) { + // name[LOCEND] = tokens[tokens.length - 1][LOCEND]; + // } - name[STATE] = EnumAstNodeStatus.Invalid; - name[ERRORS] = [errors[errors.length - 1]]; + arena.setFlags(name, EnumAstNodeStatus.Invalid | EnumAstNodeStatus.HasValues); + arena.setErrors(name, [errors[errors.length - 1]]); + arena.setName(name, arena.getValue(name)); + arena.set(name, EnumToken.DeclarationNodeType, 0); + arena.setChildren(name, tokens); - // @ts-expect-error - return Object.assign(name, { - typ: EnumToken.DeclarationNodeType, - nam: name.val, - val: tokens, - }) as AstDeclaration; + return name; + + // name[STATE] = EnumAstNodeStatus.Invalid; + // name[ERRORS] = [errors[errors.length - 1]]; + + // return Object.assign(name, { + // typ: EnumToken.DeclarationNodeType, + // nam: name.val, + // val: tokens, + // }) as AstDeclaration; } - const stack: Token[] = []; + const stack: number[] = []; let result: ValidationMatch | null = null; - let token: Token; + // let token: Token; let index: number; if (syntaxRules != null) { const doNotValidate: boolean = options.validation === false; - result = doNotValidate ? null : matchAllSyntaxes(syntaxRules, createValidationContext(tokens), options); + result = doNotValidate ? null : matchAllSyntaxes(syntaxRules, createValidationContext(tokens, options), options); if (doNotValidate || result != null) { success = doNotValidate || (result?.success as boolean); if (success) { - for (const { value } of walkValues(tokens)) { - if (value.typ === EnumToken.IdenTokenType && isColor(value)) { - parseColor(value); + for (const value of arena.walkValues(tokens)) { + if (arena.getType(value) === EnumToken.IdenTokenType && isColor(value, arena)) { + parseColor(value, arena); } else if (value.typ === EnumToken.ColorTokenType) { - if (isColor(value)) { - parseColor(value); + if (isColor(value, arena)) { + parseColor(value, arena); } } } @@ -268,55 +313,68 @@ export function parseDeclaration( for (i = 0; i < tokens.length; i++) { token = tokens[i]; + typ = arena.getType(token); + val = arena.getValue(token); + if ( - tokensfuncDefMap.has(token.typ) || - token.typ == EnumToken.StartParensTokenType || - token.typ == EnumToken.BlockStartTokenType + tokensfuncDefMap.has(typ) || + typ == EnumToken.StartParensTokenType || + typ == EnumToken.BlockStartTokenType ) { stack.push(token); continue; } - if (token.typ === EnumToken.ColonTokenType && stack.length == 0) { + if (typ === EnumToken.ColonTokenType && stack.length == 0) { stack.push(token); continue; } - switch (token.typ) { + const stackType = arena.getType(stack.at(-1)!); + + switch (typ) { case EnumToken.Literal: - if ((token as LiteralToken).val === "/" && stack.at(-1)?.typ == EnumToken.MathFunctionTokenDefType) { - Object.assign(token, { - typ: EnumToken.Div, - }); + if (arena.equalsIgnoreCase(token, 'val', '/') && stackType == EnumToken.MathFunctionTokenDefType) { + + arena.setType(token, EnumToken.Div); + // Object.assign(token, { + // typ: EnumToken.Div, + // }); } else if ( - (token as LiteralToken).val === "-" && - stack.at(-1)?.typ == EnumToken.MathFunctionTokenDefType + arena.equalsIgnoreCase(token, 'val', '-')&& + stackType == EnumToken.MathFunctionTokenDefType ) { - Object.assign(token, { - typ: EnumToken.Sub, - }); + arena.setType(token, EnumToken.Sub); + // Object.assign(token, { + // typ: EnumToken.Sub, + // }); } break; case EnumToken.Star: case EnumToken.Plus: - Object.assign(token, { - typ: tokensMap.get(token.typ), - }); + + arena.setType(token, tokensMap.get(typ)); + // Object.assign(token, { + // typ: tokensMap.get(typ), + // }); break; case EnumToken.BlockEndTokenType: - if (stack.at(-1)?.typ === EnumToken.BlockStartTokenType) { + if (stackType === EnumToken.BlockStartTokenType) { // stack.pop();index = tokens.indexOf(stack.at(-1)!); index = tokens.indexOf(stack.at(-1)!); tokens.splice(i, 1); - Object.assign(tokens[index], { - typ: EnumToken.WrappedValuesTokenType, - chi: trimArray(tokens.splice(index + 1, i - index - 1)), - }); + arena.setType(tokens[index], EnumToken.WrappedValuesTokenType); + arena.setChildren(tokens[index], trimArray(tokens.splice(index + 1, i - index - 1), options)); + + // Object.assign(tokens[index], { + // typ: EnumToken.WrappedValuesTokenType, + // chi: trimArray(tokens.splice(index + 1, i - index - 1), options), + // }); i = index; stack.pop(); @@ -325,29 +383,44 @@ export function parseDeclaration( break; case EnumToken.EndParensTokenType: - if (stack.at(-1)?.typ === EnumToken.StartParensTokenType || tokensfuncDefMap.has(stack.at(-1)?.typ)) { + if (stackType === EnumToken.StartParensTokenType || tokensfuncDefMap.has(stackType)) { index = tokens.indexOf(stack.at(-1)!); tokens.splice(i, 1); - Object.assign(tokens[index], { - typ: - stack.at(-1)?.typ === EnumToken.StartParensTokenType + arena.setType(tokens[index], stackType === EnumToken.StartParensTokenType ? EnumToken.ParensTokenType - : (tokensfuncDefMap.get(stack.at(-1)?.typ) as EnumToken), - chi: trimArray(tokens.splice(index + 1, i - index - 1)), - }); + : (tokensfuncDefMap.get(stackType) as EnumToken)); + + arena.setChildren(tokens[index], trimArray(tokens.splice(index + 1, i - index - 1), options)); + + // Object.assign(tokens[index], { + // typ: + // stack.at(-1)?.typ === EnumToken.StartParensTokenType + // ? EnumToken.ParensTokenType + // : (tokensfuncDefMap.get(stack.at(-1)?.typ) as EnumToken), + // chi: trimArray(tokens.splice(index + 1, i - index - 1)), + // }); if ( - tokens[index].typ === EnumToken.WildCardFunctionTokenType && - equalsIgnoreCase((tokens[index] as FunctionToken).val, "if") + arena.getType(tokens[index]) === EnumToken.WildCardFunctionTokenType && + arena.equalsIgnoreCase(tokens[index] , 'val' , 'if') ) { - if ((tokens[index] as FunctionToken).chi.at(-1)?.typ === EnumToken.SemiColonTokenType) { - (tokens[index] as FunctionToken).chi.pop(); + const lastChild = arena.getLastChild(tokens[index]); + + if (lastChild != 0 && arena.getType(lastChild) === EnumToken.SemiColonTokenType) { + + arena.detach(lastChild); } + // if ((tokens[index] as FunctionToken).chi.at(-1)?.typ === EnumToken.SemiColonTokenType) { + // (tokens[index] as FunctionToken).chi.pop(); + // } + let foundElse: boolean = false; + const children = arena.children(tokens[index]); + (tokens[index] as FunctionToken).chi = splitTokenList( (tokens[index] as FunctionToken).chi, [EnumToken.SemiColonTokenType], @@ -525,9 +598,9 @@ export function parseDeclaration( }) as AstDeclaration; } - for (const { value } of walkValues(tokens)) { - if (value.typ === EnumToken.IdenTokenType && isColor(value)) { - parseColor(value); + for (const value of arena.walk(tokens)) { + if (value.typ === EnumToken.IdenTokenType && isColor(value, arena)) { + parseColor(value, arena); } } @@ -581,8 +654,8 @@ export function parseDeclaration( } } - const left = trimArray(index == -1 ? tokens : tokens.slice(0, index)); - const right = index == -1 ? null : trimArray(tokens.slice(index + 1)); + const left = trimArray(index == -1 ? tokens : tokens.slice(0, index), options); + const right = index == -1 ? null : trimArray(tokens.slice(index + 1), options); tokens = [ { typ: EnumToken.ComposesSelectorNodeType, diff --git a/src/lib/parser/utils/selector.ts b/src/lib/parser/utils/selector.ts index 7e11f67c5..73cb68564 100644 --- a/src/lib/parser/utils/selector.ts +++ b/src/lib/parser/utils/selector.ts @@ -47,51 +47,53 @@ import { equalsIgnoreCase } from "./text.ts"; */ export function parseSelector( - tokens: Token[], - context: AtRuleToken | AstRule | AstAtRule | AstKeyframesRule | AstKeyframesAtRule | AstStyleSheet | null, + tokens: number[], + context: number | null, options: ParserOptions, errors: ErrorDescription[], -): AstRule | AstKeyframesRule { - if (context?.typ === EnumToken.KeyframesAtRuleNodeType) { +): number { + const arena = options.arena!; + const contextTyp = arena.getType(context!); + let typ: EnumToken; + + if (arena.getType(context) === EnumToken.KeyframesAtRuleNodeType) { const result = matchAllSyntaxes( getParsedSyntax(ValidationSyntaxGroupEnum.Syntaxes, "keyframe-selectors"), - createValidationContext(tokens), + createValidationContext(tokens, options), options, ); - const parts = splitTokenList(tokens); + const parts = splitTokenList(tokens, options); for (const part of parts) { - trimArray(part); + trimArray(part, options); if (options.minify) { - const filtered = part.filter( - (token) => token.typ !== EnumToken.WhitespaceTokenType && token.typ !== EnumToken.CommentTokenType, - ); + const filtered = part.filter((token) => { + typ = arena.getType(token); + typ !== EnumToken.WhitespaceTokenType && typ !== EnumToken.CommentTokenType; + }); if (filtered.length === 1) { - if ( - filtered[0].typ === EnumToken.IdenTokenType && - "from" === (filtered[0] as IdentToken).val.toLowerCase() - ) { - filtered[0] = { - typ: EnumToken.PercentageTokenType, - val: 0, - [LOCSRCID]: filtered[0][LOCSRCID], - [LOCSTA]: filtered[0][LOCSTA], - [LOCEND]: filtered[0][LOCEND], - }; - } else if ( - filtered[0].typ === EnumToken.PercentageTokenType && - 100 === (filtered[0] as PercentageToken).val - ) { - filtered[0] = { - typ: EnumToken.IdenTokenType, - val: "to", - [LOCSRCID]: filtered[0][LOCSRCID], - [LOCSTA]: filtered[0][LOCSTA], - [LOCEND]: filtered[0][LOCEND], - }; + typ = arena.getType(filtered[0]); + if (typ === EnumToken.IdenTokenType && arena.equalsIgnoreCase(filtered[0], "val", "from")) { + arena.set(filtered[0], EnumToken.PercentageTokenType, 0, 0); + // filtered[0] = { + // typ: EnumToken.PercentageTokenType, + // val: 0, + // [LOCSRCID]: filtered[0][LOCSRCID], + // [LOCSTA]: filtered[0][LOCSTA], + // [LOCEND]: filtered[0][LOCEND], + // }; + } else if (typ === EnumToken.PercentageTokenType && 100 === arena.getNumerciValue(filtered[0])) { + arena.set(filtered[0], 0, EnumToken.IdenTokenType, arena.internString("to"), 0); + // filtered[0] = { + // typ: EnumToken.IdenTokenType, + // val: "to", + // [LOCSRCID]: filtered[0][LOCSRCID], + // [LOCSTA]: filtered[0][LOCSTA], + // [LOCEND]: filtered[0][LOCEND], + // }; } part.splice(0, part.length, ...filtered); @@ -104,7 +106,9 @@ export function parseSelector( tokens.length, ...parts.reduce((acc, curr) => { if (acc.length > 0) { - acc.push({ typ: EnumToken.CommaTokenType }); + const id = arena.allocate(); + arena.set(id, EnumToken.CommaTokenType); + acc.push(id); } for (const c of curr) { @@ -112,13 +116,17 @@ export function parseSelector( } return acc; - }, [] as Token[]), + }, [] as number[]), ); + const node = arena.allocate(); + + arena.set(node, EnumToken.KeyframesRuleNodeType); + return { typ: EnumToken.KeyframesRuleNodeType, sel: [ - ...splitTokenList(trimArray(tokens)).reduce((acc, curr: Token[]) => { + ...splitTokenList(trimArray(tokens, options), options).reduce((acc, curr: number[]) => { acc.add(curr.reduce((acc, curr) => acc + renderValue(curr, { minify: false }), "")); return acc; }, new Set()), @@ -133,20 +141,20 @@ export function parseSelector( } as AstKeyframesRule; } - const stack: Token[] = []; + const stack: number[] = []; const uniq = new Map(); let allowed: boolean = true; let index: number; - let parent: AstRuleList = context as AstRuleList; + let parent: number = context as number; let nested: boolean = false; let val: string; - if (context?.typ !== EnumToken.StyleSheetNodeType && context?.typ !== EnumToken.RuleNodeType) { + if (contextTyp !== EnumToken.StyleSheetNodeType && contextTyp !== EnumToken.RuleNodeType) { allowed = false; - if (context?.typ === EnumToken.AtRuleNodeType) { - const syntaxRule = getSyntaxRule(ValidationSyntaxGroupEnum.AtRules, "@" + (context as AstAtRule).nam); + if (contextTyp === EnumToken.AtRuleNodeType) { + const syntaxRule = getSyntaxRule(ValidationSyntaxGroupEnum.AtRules, "@" + arena.getName(context)); allowed = syntaxRule?.acceptAnyRule ?? false; @@ -171,152 +179,262 @@ export function parseSelector( } do { - if (parent?.typ === EnumToken.AtRuleNodeType && "media" === (parent as AstAtRule).nam) { - parent = parent[PARENT] as AstRuleList; + if (contextTyp === EnumToken.AtRuleNodeType && arena.equalsIgnoreCase(parent, "nam", "media")) { + parent = arena.getParent(parent); continue; } - nested = parent?.typ == EnumToken.RuleNodeType; - parent = parent?.[PARENT] as AstRuleList; - } while (!nested && parent != null); + nested = arena.getType(parent) == EnumToken.RuleNodeType; + parent = arena.getParent(parent); + + } while (!nested && parent != 0); const all = new Array(tokens.length); let write = 0; + let token: number; + let next: number; + let newToken: number; for (let read = 0; read < tokens.length; read++) { - let token = tokens[read]; + token = tokens[read]; + typ = arena.getType(token); - if (token.typ == EnumToken.ColonTokenType) { - const next = tokens[read + 1]; + if (typ == EnumToken.ColonTokenType) { + next = tokens[read + 1]; + + if (next != null && arena.getType(next) == EnumToken.IdenTokenType) { + newToken = arena.allocate(); + all[write++] = newToken; + + arena.set(newToken, EnumToken.PseudoClassTokenType, arena.interString(":" + arena.getValue(next))); + arena.setSpan(newToken, arena.getSourceId(token), arena.getStart(token), arena.getEnd(next)); - if (next?.typ == EnumToken.IdenTokenType) { - all[write++] = { - ...token, - typ: EnumToken.PseudoElementTokenType, - val: ":" + (next as IdentToken).val, - [LOCEND]: next[LOCEND], - } as Token; read++; continue; } - if (next?.typ == EnumToken.FunctionTokenDefType) { - val = ":" + (next as IdentToken).val; - - all[write++] = { - ...token, - typ: - val + "()" in getSyntaxConfig().selectors - ? EnumToken.PseudoClassFunctionTokenDefType - : next.typ, - val, - [LOCEND]: next[LOCEND], - } as Token; + if (next != null && arena.getType(next) == EnumToken.FunctionTokenDefType) { + val = ":" + arena.getValue(next); + + // all[write++] = { + // ...token, + // typ: + // val + "()" in getSyntaxConfig().selectors + // ? EnumToken.PseudoClassFunctionTokenDefType + // : next.typ, + // val, + // [LOCEND]: next[LOCEND], + // } as Token; + + newToken = arena.allocate(); + all[write++] = newToken; + + arena.set( + newToken, + val + "()" in getSyntaxConfig().selectors + ? EnumToken.PseudoClassFunctionTokenDefType + : arena.getType(next), + arena.interString(":" + arena.getValue(next)), + ); + arena.setSpan(newToken, arena.getSourceId(token), arena.getStart(token), arena.getEnd(next)); + read++; continue; } } - if (token.typ == EnumToken.DoubleColonTokenType) { + if (arena.getType(token) == EnumToken.DoubleColonTokenType) { const next = tokens[read + 1]; - val = ":" + (next as IdentToken).val; - - if (next?.typ == EnumToken.IdenTokenType) { - all[write++] = { - ...token, - typ: EnumToken.PseudoClassTokenType, - val: (pseudoElements.includes(val) ? "" : ":") + val, - [LOCEND]: next[LOCEND], - } as Token; + val = ":" + arena.getValue(next); + + if (arena.getType(next) == EnumToken.IdenTokenType) { + // all[write++] = { + // ...token, + // typ: EnumToken.PseudoClassTokenType, + // val: (pseudoElements.includes(val) ? "" : ":") + val, + // [LOCEND]: next[LOCEND], + // } as Token; + + newToken = arena.allocate(); + all[write++] = newToken; + + arena.set( + newToken, + EnumToken.PseudoClassTokenType, + arena.interString((pseudoElements.includes(val) ? "" : ":") + val), + ); + arena.setSpan(newToken, arena.getSourceId(token), arena.getStart(token), arena.getEnd(next)); + read++; continue; } - if (next?.typ == EnumToken.FunctionTokenDefType) { - val = "::" + (next as IdentToken).val; - all[write++] = { - ...token, - typ: - val + "()" in getSyntaxConfig().selectors - ? EnumToken.PseudoClassFunctionTokenDefType - : EnumToken.FunctionTokenDefType, - val, - [LOCEND]: next[LOCEND], - } as Token; + if (arena.getType(next) == EnumToken.FunctionTokenDefType) { + val = "::" + arena.getValue(next); + // all[write++] = { + // ...token, + // typ: + // val + "()" in getSyntaxConfig().selectors + // ? EnumToken.PseudoClassFunctionTokenDefType + // : EnumToken.FunctionTokenDefType, + // val, + // [LOCEND]: next[LOCEND], + // } as Token; + + newToken = arena.allocate(); + all[write++] = newToken; + + arena.set( + newToken, + val + "()" in getSyntaxConfig().selectors + ? EnumToken.PseudoClassFunctionTokenDefType + : EnumToken.FunctionTokenDefType, + arena.interString(val), + ); + arena.setSpan(newToken, arena.getSourceId(token), arena.getStart(token), arena.getEnd(next)); + read++; continue; } } - if (token.typ == EnumToken.ColorTokenType) { - if (isHash((token as ColorToken).val)) { - token.typ = EnumToken.HashTokenType; - all[write++] = token as Token; + if (arena.getType(token) == EnumToken.ColorTokenType) { + if (isHash(arena.getValue(token))) { + arena.set(token, EnumToken.HashTokenType); + all[write++] = token; continue; } - return { - typ: EnumToken.RuleNodeType, - sel: [ - ...tokens - .reduce( - (acc: string[][], curr: Token, index: number, array: Token[]) => { - if (curr.typ == EnumToken.WhitespaceTokenType) { - if ( - trimWhiteSpace.includes(array[index - 1]?.typ) || - trimWhiteSpace.includes(array[index + 1]?.typ) || - combinators.includes((array[index - 1])?.val) || - combinators.includes((array[index + 1])?.val) - ) { - return acc; + const node = arena.allocate(); + + node.set( + node, + EnumToken.RuleNodeType, + arena.internString( + [ + ...all + .reduce( + (acc: string[][], curr: number, index: number, array: number[]) => { + if (arena.getType(curr) == EnumToken.WhitespaceTokenType) { + if ( + trimWhiteSpace.includes(arena.getType(array[index - 1])) || + trimWhiteSpace.includes(arena.getType(array[index + 1])) || + combinators.includes(arena.getValue(array[index - 1])) || + combinators.includes(arena.getValue(array[index + 1])) + ) { + return acc; + } } - } - let t: string = renderValue(curr, { minify: false }); + let t: string = renderValue(curr, { minify: false, arena }); - if (t == ",") { - acc.push([]); - } else { - acc[acc.length - 1].push(t); - } - return acc; - }, - [[]], - ) - .reduce((acc: Map, curr: string[]) => { - let i: number = 0; - - for (; i < curr.length; i++) { - if (i + 1 < curr.length && curr[i] == "*") { - if (curr[i] == "*") { - let index: number = curr[i + 1] == " " ? 2 : 1; - - if (![">", "~", "+"].includes(curr[index])) { - curr.splice(i, index); + if (t == ",") { + acc.push([]); + } else { + acc[acc.length - 1].push(t); + } + return acc; + }, + [[]], + ) + .reduce((acc: Map, curr: string[]) => { + let i: number = 0; + + for (; i < curr.length; i++) { + if (i + 1 < curr.length && curr[i] == "*") { + if (curr[i] == "*") { + let index: number = curr[i + 1] == " " ? 2 : 1; + + if (![">", "~", "+"].includes(curr[index])) { + curr.splice(i, index); + } } } } - } - acc.set(curr.join(""), curr); - return acc; - }, uniq) - .keys(), - ].join(","), - chi: [], - [LOCSRCID]: tokens[0][LOCSRCID], - [LOCSTA]: tokens[0][LOCSTA], - [LOCEND]: tokens[tokens.length - 1][LOCEND], - [TOKENS]: tokens, - [STATE]: EnumAstNodeStatus.Invalid, - [ERRORS]: [ - { - action: "drop", - node: token, - message: "invalid hash id", - }, - ], - } as AstRule; + acc.set(curr.join(""), curr); + return acc; + }, uniq) + .keys(), + ].join(","), + ), + ); + + arena.setTokens(node, all); + arena.setFlags(node, EnumAstNodeStatus.HasChildren | EnumAstNodeStatus.Invalid); + + arena.setErrors(node, [ + { + action: "drop", + node: node, + message: "invalid hash id", + }, + ]); + + return node; + + // return { + // typ: EnumToken.RuleNodeType, + // sel: [ + // ...tokens + // .reduce( + // (acc: string[][], curr: Token, index: number, array: Token[]) => { + // if (curr.typ == EnumToken.WhitespaceTokenType) { + // if ( + // trimWhiteSpace.includes(array[index - 1]?.typ) || + // trimWhiteSpace.includes(array[index + 1]?.typ) || + // combinators.includes((array[index - 1])?.val) || + // combinators.includes((array[index + 1])?.val) + // ) { + // return acc; + // } + // } + + // let t: string = renderValue(curr, { minify: false }); + + // if (t == ",") { + // acc.push([]); + // } else { + // acc[acc.length - 1].push(t); + // } + // return acc; + // }, + // [[]], + // ) + // .reduce((acc: Map, curr: string[]) => { + // let i: number = 0; + + // for (; i < curr.length; i++) { + // if (i + 1 < curr.length && curr[i] == "*") { + // if (curr[i] == "*") { + // let index: number = curr[i + 1] == " " ? 2 : 1; + + // if (![">", "~", "+"].includes(curr[index])) { + // curr.splice(i, index); + // } + // } + // } + // } + + // acc.set(curr.join(""), curr); + // return acc; + // }, uniq) + // .keys(), + // ].join(","), + // chi: [], + // [LOCSRCID]: tokens[0][LOCSRCID], + // [LOCSTA]: tokens[0][LOCSTA], + // [LOCEND]: tokens[tokens.length - 1][LOCEND], + // [TOKENS]: tokens, + // [STATE]: EnumAstNodeStatus.Invalid, + // [ERRORS]: [ + // { + // action: "drop", + // node: token, + // message: "invalid hash id", + // }, + // ], + // } as AstRule; } all[write++] = token; @@ -332,32 +450,42 @@ export function parseSelector( const result = matchSelectorSyntax(tokens, errors, options, nested); - trimArray(tokens); + for (const token of tokens) { + + + console.error(EnumToken[arena.getType(token)]); + } + + trimArray(tokens, options); if (result.success) { for (let i = 0; i < tokens.length; i++) { - const token = tokens[i]; + token = tokens[i]; + typ = arena.getType(token); - switch (token.typ) { + switch (token) { case EnumToken.AttrStartTokenType: stack.push(token); break; case EnumToken.AttrEndTokenType: - if (stack.at(-1)?.typ == EnumToken.AttrStartTokenType) { + if (arena.getType(stack.at(-1)) == EnumToken.AttrStartTokenType) { index = tokens.indexOf(stack.at(-1)!); - // @ts-expect-error - const { val, ...attr } = stack.at(-1) as AttrStartToken; - attr[LOCSRCID] = stack.at(-1)![LOCSRCID]; - attr[LOCSTA] = stack.at(-1)![LOCSTA]; - attr[LOCEND] = token[LOCEND]; + + // const { val, ...attr } = stack.at(-1) as AttrStartToken; + // attr[LOCSRCID] = stack.at(-1)![LOCSRCID]; + // attr[LOCSTA] = stack.at(-1)![LOCSTA]; + // attr[LOCEND] = token[LOCEND]; + + arena.setSpanEnd(tokens[index], arena.getSpanEnd(token)); + arena.setChildren(tokens[index], tokens.splice(index + 1, i - index - 1)); tokens.splice(i, 1); - Object.assign(attr, { - typ: EnumToken.AttrTokenType, - chi: tokens.splice(index + 1, i - index - 1), - }); + // Object.assign(attr, { + // typ: EnumToken.AttrTokenType, + // chi: tokens.splice(index + 1, i - index - 1), + // }); - tokens[index] = attr; + // tokens[index] = attr; i = index; stack.pop(); } @@ -370,237 +498,302 @@ export function parseSelector( break; case EnumToken.EndParensTokenType: - if (stack.at(-1)?.typ == EnumToken.PseudoClassFunctionTokenDefType) { - const func = stack.at(-1) as PseudoClassFunctionToken; + if (stack.length > 0 && arena.getType(stack.at(-1)) == EnumToken.PseudoClassFunctionTokenDefType) { + const func = stack.at(-1) as number; + const funcType = arena.getType(func) as EnumToken; index = tokens.indexOf(func); - (stack.at(-1) as AttrStartToken)[LOCEND] = token[LOCEND]; + + arena.setSpanEnd(tokens[index], arena.getSpanEnd(token)); + // (stack.at(-1) as AttrStartToken)[LOCEND] = token[LOCEND]; tokens.splice(i, 1); - if (tokensfuncDefMap.has(func.typ)) { - // @ts-expect-error - func.typ = tokensfuncDefMap.get(func.typ) as EnumToken; - func.chi = trimArray(tokens.splice(index + 1, i - index - 1)); + if (tokensfuncDefMap.has(funcType)) { + arena.setType(func, tokensfuncDefMap.get(funcType) as EnumToken); + arena.setChildren(func, trimArray(tokens.splice(index + 1, i - index - 1), options)); } if (result.success && options.minify) { + val = arena.getValue(func); + // parse an+b // an+b produces an ident such as 'n-0', a literal such as '+2n-0' or a list of tokens such as[2n\s?[+-]\s?3] if ( - func.val == ":nth-child" || - func.val == ":nth-last-child" || - func.val == ":nth-of-type" || - func.val == ":nth-last-of-type" + val == ":nth-child" || + val == ":nth-last-child" || + val == ":nth-of-type" || + val == ":nth-last-of-type" ) { - const list: Token[] = []; - let index: number; + const list: number[] = []; + let index: number = -1; + let child: number = arena.getFirstChild(func); - for (index = 0; index < func.chi.length; index++) { - if ( - func.chi[index].typ == EnumToken.CommentTokenType || - func.chi[index].typ == EnumToken.WhitespaceTokenType - ) { + while (child != 0) { + index++; + typ = arena.getType(child); + + if (typ == EnumToken.CommentTokenType || typ == EnumToken.WhitespaceTokenType) { continue; } - if ( - func.chi[index].typ == EnumToken.IdenTokenType && - equalsIgnoreCase("of", (func.chi[index] as IdentToken).val) - ) { + if (typ == EnumToken.IdenTokenType && arena.equalsIgnoreCase(child, "val", "of")) { index--; break; } - list.push(func.chi[index]); + list.push(child); + child = arena.getNextSibling(child); } if (list.length == 2) { - if (list[1].typ == EnumToken.NumberTokenType) { - if ((list[1] as NumberToken).val == 0) { + if (arena.getType(list[1]) == EnumToken.NumberTokenType) { + if (arena.getNumericValue(list[1]) == 0) { list.length = 1; if ( - list[0].typ == EnumToken.DimensionTokenType && - (list[0] as DimensionToken).val == -2 + arena.getType(list[0]) == EnumToken.DimensionTokenType && + arena.getNumericValue(list[0]) == -2 ) { - (list[0] as DimensionToken).val = 2; + arena.setNumericValue(list[0], 2); } } else { - const sign = Math.sign((list[1] as NumberToken).val as number); + const sign = Math.sign(arena.getNumericValue(list[1])); // @ts-ignore (list[1] as NumberToken).val *= sign; - list.splice(1, 0, { - typ: EnumToken.LiteralTokenType, - val: sign > 0 ? "+" : "-", - } as LiteralToken); + + const literal = arena.allocate(); + arena.setType( + literal, + EnumToken.LiteralTokenType, + arena.internString(sign < 0 ? "-" : "+"), + ); + + list.splice(1, 0, literal); } } if ( list.length == 3 && - list[2].typ == EnumToken.NumberTokenType && - list[0].typ == EnumToken.DimensionTokenType && - (((list[0] as DimensionToken).val as number) == 2 || - (list[0] as DimensionToken).val == -2) + arena.getType(list[2]) == EnumToken.NumberTokenType && + arena.getType(list[0]) == EnumToken.DimensionTokenType ) { - if (1 == (list[2] as NumberToken).val) { - list.splice(0, 3, { - typ: EnumToken.IdenTokenType, - val: "odd", - [LOCSRCID]: list[0][LOCSRCID], - [LOCSTA]: list[0][LOCSTA], - [LOCEND]: list[0][LOCEND], - } as IdentToken); - } else if (0 == (list[2] as NumberToken).val) { - list.splice(0, 3, { - typ: EnumToken.IdenTokenType, - val: "even", - [LOCSRCID]: list[0][LOCSRCID], - [LOCSTA]: list[0][LOCSTA], - [LOCEND]: list[0][LOCEND], - } as IdentToken); + const value = arena.getNumericValue(list[0]); + + if (value == 2 || value == -2) { + const value0 = arena.getNumericValue(list[2]); + + if (1 == value0) { + const literal = arena.allocate(); + arena.setType( + literal, + EnumToken.IdenTokenType, + arena.internString("odd"), + ); + arena.setSpan( + literal, + arena.getSourceId(list[0]), + arena.getSpanStart(list[0]), + arena.getSpanEnd(list[0]), + ); + + list.splice(0, 3, literal); + } else if (0 == value0) { + const literal = arena.allocate(); + arena.setType( + literal, + EnumToken.IdenTokenType, + arena.internString("even"), + ); + arena.setSpan( + literal, + arena.getSourceId(list[0]), + arena.getSpanStart(list[0]), + arena.getSpanEnd(list[0]), + ); + + list.splice(0, 3, literal); + } } } - func.chi.splice(0, index, ...list); + arena.setChildren(func, arena.children(func).splice(0, index, ...list)); } if (list.length == 1) { if ( - list[0].typ == EnumToken.IdenTokenType && - equalsIgnoreCase("-n", (list[0] as IdentToken).val) + arena.getType(list[0]) == EnumToken.IdenTokenType && + arena.equalsIgnoreCase(list[0], "val", "-n") ) { - (list[0] as IdentToken).val = "n"; + arena.setValue(list[0], arena.interString("n")); } } if (list.length == 3) { - if ( - list[0].typ == EnumToken.IdenTokenType && - ("n" == (list[0] as IdentToken).val || - "-n" == (list[0] as IdentToken).val || - "+n" == (list[0] as IdentToken).val) - ) { - if (list[1].typ == EnumToken.NextSiblingCombinatorTokenType) { - if ( - list[2].typ == EnumToken.NumberTokenType && - 0 == (list[2] as NumberToken).val - ) { - (list[0] as IdentToken).val = "n"; - func.chi.splice(0, index, list[0]); - break; + if (arena.getType(list[0]) == EnumToken.IdenTokenType) { + val = arena.getValue(list[0]); + + if ("n" == val || "-n" == val || "+n" == val) { + if (arena.getType(list[1]) == EnumToken.NextSiblingCombinatorTokenType) { + if ( + arena.getType(list[2]) == EnumToken.NumberTokenType && + 0 == arena.getNumericValue(list[2]) + ) { + arena.setValue(list[0], "n"); + arena.setChildren( + func, + arena.children(func).splice(0, index, list[0]), + ); + // func.chi.splice(0, index, list[0]); + break; + } } } } } - const token = func.chi.find( - (t) => - t.typ != EnumToken.WhitespaceTokenType && t.typ != EnumToken.CommentTokenType, - ) as Token; + const token = arena.findChild(func, (t: number) => { + typ = arena.getType(t); - if (token?.typ == EnumToken.IdenTokenType || token?.typ == EnumToken.LiteralTokenType) { - if ( - token.typ == EnumToken.IdenTokenType && - ((token as IdentToken).val == "odd" || (token as IdentToken).val == "even") - ) { - if ((token as IdentToken).val == "even") { - Object.assign(token, { - typ: EnumToken.DimensionTokenType, - val: 2, - unit: "n", - }); - } - } else { - const matches = /^(([+-]?[0-9]*)?n)?([+-]?[0-9]+)?$/.exec( - (token as IdentToken | LiteralToken).val, - ); + return typ != EnumToken.WhitespaceTokenType && typ != EnumToken.CommentTokenType; + }) as number; - if (matches != null) { - let [_ /* , an, a, b */]: string[] = matches; + if (token != null) { + typ = arena.getType(token); - const a1 = matches[2] === "" ? 1 : matches[2] === "-" ? -1 : +matches[2]; - const b1 = +matches[3]; + if (typ == EnumToken.IdenTokenType || typ == EnumToken.LiteralTokenType) { + val = arena.getValue(token); - if (b1 === 0) { - Object.assign( + if (typ == EnumToken.IdenTokenType) { + if (val == "even") { + arena.set( token, - Math.abs(a1) === 1 - ? { - typ: EnumToken.IdenTokenType, - val: "n", - } - : { - typ: EnumToken.DimensionTokenType, - val: a1 * Math.sign(a1), - unit: "n", - }, + EnumToken.DimensionTokenType, + 0, + 2, + arena.internString("n"), ); } + } else { + const matches = /^(([+-]?[0-9]*)?n)?([+-]?[0-9]+)?$/.exec( + val + ); + + if (matches != null) { + let [_ /* , an, a, b */]: string[] = matches; + + const a1 = + matches[2] === "" ? 1 : matches[2] === "-" ? -1 : +matches[2]; + const b1 = +matches[3]; + + if (b1 === 0) { + + if (Math.abs(a1) === 1) { + + arena.set(token, + EnumToken.IdenTokenType, + arena.internString("n") + ) + } + + else { + + arena.set(token, + EnumToken.DimensionTokenType, + 0, + a1 * Math.sign(a1), + arena.internString("n")); + } + + // Object.assign( + // token, + // Math.abs(a1) === 1 + // ? { + // typ: EnumToken.IdenTokenType, + // val: "n", + // } + // : { + // typ: EnumToken.DimensionTokenType, + // val: a1 * Math.sign(a1), + // unit: "n", + // }, + // ); + } + } } - } - } else if (token?.typ === EnumToken.DimensionTokenType) { - const index: number = func.chi.indexOf(token); + } else if (typ === EnumToken.DimensionTokenType) { - let sign: Token | null = null; - let num: NumberToken | null = null; - let i: number = index + 1; + const children = arena.children(func); + const index: number = children.indexOf(token); - for (; i < func.chi.length; i++) { - if ( - func.chi[i].typ == EnumToken.WhitespaceTokenType || - func.chi[i].typ == EnumToken.CommentTokenType - ) { - continue; - } + let sign: Token | null = null; + let num: NumberToken | null = null; + let i: number = index + 1; - if (func.chi[i].typ == EnumToken.NumberTokenType) { - num = func.chi[i] as NumberToken; - break; - } else { - sign = func.chi[i]; + for (; i < children.length; i++) { + + typ = arena.getType(children[i]); + if ( + typ == EnumToken.WhitespaceTokenType || + typ == EnumToken.CommentTokenType + ) { + continue; + } + + if (typ == EnumToken.NumberTokenType) { + num = children[i] as NumberToken; + break; + } else { + sign = children[i]; + } } - } - if (num != null) { - if (num.val === 0) { - func.chi.splice(index + 1, i - index); + if (num != null) { + if (arena.getNumericValue(num) === 0 ) { + children.splice(index + 1, i - index); + + if (arena.getNumericValue(token) < 0) { + arena.setNumericValue(token, arena.getNumericValue(token) *-1); + } - if (((token as DimensionToken).val as number) < 0) { - ((token as DimensionToken).val as number) *= -1; + arena.setChildren(func, children); + + break; + } else if ( + arena.getNumericValue(token) === 2 && + Math.abs(arena.getNumericValue(num)) === 1 + ) { + + arena.set(token, EnumToken.IdenTokenType, arena.internString("n")); + + // Object.assign(token, { + // typ: EnumToken.IdenTokenType, + // val: "odd", + // }); + + children.splice(index + 1, i - index); + arena.setChildren(func, children); + break; } - break; - } else if ( - ((token as DimensionToken).val as number) === 2 && - Math.abs(num.val as number) === 1 - ) { - Object.assign(token, { - typ: EnumToken.IdenTokenType, - val: "odd", - }); - func.chi.splice(index + 1, i - index); - break; + // if (((token as DimensionToken).val as number) === 1) { + // Object.assign(token, { + // typ: EnumToken.IdenTokenType, + // val: + // Math.sign((token as DimensionToken).val as number) === 1 + // ? "n" + // : "-n", + // }); + // } + + // if (sign == null) { + // func.chi.splice(index + 1, i - index - 1); + // if (Math.sign(num.val as number) === 1) { + // func.chi.splice(index + 1, 0, { + // typ: EnumToken.LiteralTokenType, + // val: "+", + // }); + // } + // } } - - // if (((token as DimensionToken).val as number) === 1) { - // Object.assign(token, { - // typ: EnumToken.IdenTokenType, - // val: - // Math.sign((token as DimensionToken).val as number) === 1 - // ? "n" - // : "-n", - // }); - // } - - // if (sign == null) { - // func.chi.splice(index + 1, i - index - 1); - // if (Math.sign(num.val as number) === 1) { - // func.chi.splice(index + 1, 0, { - // typ: EnumToken.LiteralTokenType, - // val: "+", - // }); - // } - // } } } } @@ -615,28 +808,28 @@ export function parseSelector( } } - return { - typ: EnumToken.RuleNodeType, - sel: [ - ...tokens + const node = arena.allocate(); + + arena.set(node, EnumToken.RuleNodeType, arena.internString([ + ...all .reduce( - (acc: string[][], curr: Token, index: number, array: Token[]) => { + (acc: string[][], curr: number, index: number, array: number[]) => { // if (curr.typ == EnumToken.CommentTokenType) { // return acc; // } - if (curr.typ == EnumToken.WhitespaceTokenType) { + if (arena.getType(curr) == EnumToken.WhitespaceTokenType) { if ( - trimWhiteSpace.includes(array[index - 1]?.typ) || - trimWhiteSpace.includes(array[index + 1]?.typ) || - combinators.includes((array[index - 1])?.val) || - combinators.includes((array[index + 1])?.val) + trimWhiteSpace.includes(arena.getType(array[index - 1])) || + trimWhiteSpace.includes(arena.getType(array[index + 1])) || + combinators.includes(arena.getValue(array[index - 1])) || + combinators.includes(arena.getValue(array[index + 1])) ) { return acc; } } - let t: string = renderValue(curr, { minify: false }); + let t: string = renderValue(curr, { minify: false, arena }); if (t == ",") { acc.push([]); @@ -666,18 +859,76 @@ export function parseSelector( return acc; }, uniq) .keys(), - ].join(","), - chi: [], - [LOCSRCID]: tokens[0][LOCSRCID], - [LOCSTA]: tokens[0][LOCSTA], - [LOCEND]: tokens[tokens.length - 1][LOCEND], - [TOKENS]: tokens, - [STATE]: - result.success && allowed - ? EnumAstNodeStatus.Validated - : allowed - ? EnumAstNodeStatus.Invalid - : EnumAstNodeStatus.Disallowed, - [ERRORS]: result.success ? [] : result.errors, - } as AstRule; + ].join(","))); + + arena.setSpan(node, arena.getSourceId(tokens[0]), arena.getSpanStart(tokens[0]), arena.getSpanEnd(tokens[tokens.length - 1])); + arena.setFlags(node, EnumAstNodeStatus.HasChildren | EnumAstNodeStatus.Validated ); + + return node; + + // return { + // typ: EnumToken.RuleNodeType, + // sel: [ + // ...tokens + // .reduce( + // (acc: string[][], curr: Token, index: number, array: Token[]) => { + // // if (curr.typ == EnumToken.CommentTokenType) { + // // return acc; + // // } + + // if (curr.typ == EnumToken.WhitespaceTokenType) { + // if ( + // trimWhiteSpace.includes(array[index - 1]?.typ) || + // trimWhiteSpace.includes(array[index + 1]?.typ) || + // combinators.includes((array[index - 1])?.val) || + // combinators.includes((array[index + 1])?.val) + // ) { + // return acc; + // } + // } + + // let t: string = renderValue(curr, { minify: false }); + + // if (t == ",") { + // acc.push([]); + // } else { + // acc[acc.length - 1].push(t); + // } + // return acc; + // }, + // [[]], + // ) + // .reduce((acc: Map, curr: string[]) => { + // let i: number = 0; + + // for (; i < curr.length; i++) { + // if (i + 1 < curr.length && curr[i] == "*") { + // if (curr[i] == "*") { + // let index: number = curr[i + 1] == " " ? 2 : 1; + + // if (![">", "~", "+"].includes(curr[index])) { + // curr.splice(i, index); + // } + // } + // } + // } + + // acc.set(curr.join(""), curr); + // return acc; + // }, uniq) + // .keys(), + // ].join(","), + // chi: [], + // [LOCSRCID]: tokens[0][LOCSRCID], + // [LOCSTA]: tokens[0][LOCSTA], + // [LOCEND]: tokens[tokens.length - 1][LOCEND], + // [TOKENS]: tokens, + // [STATE]: + // result.success && allowed + // ? EnumAstNodeStatus.Validated + // : allowed + // ? EnumAstNodeStatus.Invalid + // : EnumAstNodeStatus.Disallowed, + // [ERRORS]: result.success ? [] : result.errors, + // } as AstRule; } diff --git a/src/lib/printer/render.ts b/src/lib/printer/render.ts index 6e8d8777c..f0fc52a61 100644 --- a/src/lib/printer/render.ts +++ b/src/lib/printer/render.ts @@ -77,6 +77,7 @@ import { LineMap as LinesMap } from "../parser/linesmap.ts"; import { dirname } from "../fs/resolve.ts"; import { SourceFile } from "../parser/source.ts"; import { cloneNode } from "../ast/clone.ts"; +import { Arena } from "../parser/arena.ts"; /** * render ast @@ -637,17 +638,21 @@ function renderAstNode( * @private */ export function renderValue( - token: Token, - options: RenderOptions = {}, + token: number, + options: RenderOptions, cache: { [key: string]: any; } = Object.create(null), - reducer?: null | ((acc: string, curr: Token) => string), + reducer?: null | ((acc: string, curr: number) => string), errors?: ErrorDescription[], ): string { + + const arena = options.arena! as Arena; + const typ = arena.getType(token); + if ( - token.typ === EnumToken.WhenElseFunctionTokenType && - equalsIgnoreCase((token as FunctionToken).val, "supports") + typ === EnumToken.WhenElseFunctionTokenType && + arena.equalsIgnoreCase(token, 'val', "supports") ) { options = { ...options, minifyValues: false, minify: false, convertColor: false }; reducer = null; @@ -656,20 +661,20 @@ export function renderValue( options.minifyValues ??= options.minify; if (reducer == null) { - reducer = function (acc: string, curr: Token): string { - if (curr.typ == EnumToken.CommentTokenType && options.removeComments) { - if (!options.preserveLicense || !(curr as AstComment).val.startsWith("/*!")) { + reducer = function (acc: string, curr: number): string { + if (arena.getType(curr) == EnumToken.CommentTokenType && options.removeComments) { + if (!options.preserveLicense || !arena.startsWith(curr, 'val', "/*!")) { return acc; } - return acc + (curr as AstComment).val; + return acc + arena.getValue(curr); } return acc + renderValue(curr, options, cache, reducer, errors); }; } - switch (token.typ) { + switch (typ) { case EnumToken.UrlFunctionTokenDefType: case EnumToken.FunctionTokenDefType: case EnumToken.MathFunctionTokenDefType: @@ -686,7 +691,7 @@ export function renderValue( case EnumToken.GeneralEnclosedFunctionTokenDefType: case EnumToken.CustomFunctionTokenDefType: case EnumToken.WildCardFunctionTokenDefType: - return (token as FunctionDefToken).val + "("; + return arena.getValue(token) + "("; case EnumToken.ListToken: return (token as ListToken).chi.reduce( diff --git a/src/lib/syntax/syntax.ts b/src/lib/syntax/syntax.ts index 07f493b3f..6e613e8c2 100644 --- a/src/lib/syntax/syntax.ts +++ b/src/lib/syntax/syntax.ts @@ -37,6 +37,7 @@ import { systemColors, } from "./constants.ts"; import { getSyntaxConfig } from "../validation/config.ts"; +import { Arena } from "../parser/arena.ts"; // https://www.w3.org/TR/CSS21/syndata.html#syntax // https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#typedef-ident-token @@ -554,7 +555,7 @@ export function isFrequency(dimension: DimensionToken): boolean { */ // export function isColorspace(token: Token): boolean { // return ( -// token.typ === EnumToken.IdenTokenType && +// typ === EnumToken.IdenTokenType && // [ // "srgb", // "srgb-linear", @@ -813,7 +814,7 @@ export function reduceConicColorStops(stops: Token[]): Token[] { */ export function isRectangularOrthogonalColorspace(token: Token): boolean { return ( - token.typ === EnumToken.IdenTokenType && + typ === EnumToken.IdenTokenType && colorFuncColorSpace.some((t) => equalsIgnoreCase(t, (token as IdentToken).val)) ); } @@ -825,7 +826,7 @@ export function isRectangularOrthogonalColorspace(token: Token): boolean { */ export function isPolarColorspace(token: Token): boolean { return ( - token.typ === EnumToken.IdenTokenType && + typ === EnumToken.IdenTokenType && ["hsl", "hwb", "lch", "oklch"].some((t) => equalsIgnoreCase(t, (token as IdentToken).val)) ); } @@ -837,7 +838,7 @@ export function isPolarColorspace(token: Token): boolean { */ // export function isHueInterpolationMethod(token: Token | Token[]): boolean { // if (!Array.isArray(token)) { -// return token.typ == EnumToken.IdenTokenType && "hue" === (token as IdentToken).val?.toLowerCase?.(); +// return typ == EnumToken.IdenTokenType && "hue" === (token as IdentToken).val?.toLowerCase?.(); // } // // if (token.length != 2 || token[0].typ != EnumToken.IdenTokenType || token[1].typ != EnumToken.IdenTokenType) { @@ -858,7 +859,7 @@ export function isPolarColorspace(token: Token): boolean { */ export function isIdentColor(token: Token): boolean { return ( - token.typ == EnumToken.ColorTokenType && + typ == EnumToken.ColorTokenType && [ColorType.SYS, ColorType.DPSYS, ColorType.LIT].includes((token as ColorToken).kin) && isIdent((token as ColorToken).val) ); @@ -866,23 +867,25 @@ export function isIdentColor(token: Token): boolean { // export function isPercentageToken(token: Token): boolean { // return ( -// token.typ == EnumToken.PercentageTokenType || -// (token.typ == EnumToken.NumberTokenType && (token as NumberToken).val == 0) +// typ == EnumToken.PercentageTokenType || +// (typ == EnumToken.NumberTokenType && (token as NumberToken).val == 0) // ); // } -export function isColor(token: Token, errors?: ErrorDescription[]): boolean { - if (token.typ == EnumToken.WildCardFunctionTokenType) { +export function isColor(token: number, arena: Arena): boolean { + + const typ: EnumToken = arena.getType(token); + if (typ == EnumToken.WildCardFunctionTokenType) { return true; } - if (token.typ == EnumToken.ColorTokenType) { + if (typ == EnumToken.ColorTokenType) { if ("kin" in token && !("chi" in token)) { return true; } } - if (token.typ == EnumToken.IdenTokenType) { + if (typ == EnumToken.IdenTokenType) { const val = (token as IdentToken).val.toLowerCase(); if (systemColors.has(val) || deprecatedSystemColors.has(val) || nonStandardColors.has(val)) { @@ -895,7 +898,7 @@ export function isColor(token: Token, errors?: ErrorDescription[]): boolean { let isLegacySyntax: boolean = false; - if (token.typ === EnumToken.FunctionTokenType || token.typ === EnumToken.ColorTokenType) { + if (typ === EnumToken.FunctionTokenType || typ === EnumToken.ColorTokenType) { // if (!colorsFunc.includes((token as FunctionToken).val.toLowerCase())) { // return false; // } @@ -1237,8 +1240,10 @@ export function isColor(token: Token, errors?: ErrorDescription[]): boolean { return false; } -export function parseColor(token: Token) { - if (token.typ === EnumToken.IdenTokenType) { +export function parseColor(token: number, arena: Arena): number { + + const typ = arena.getType(token); + if (typ === EnumToken.IdenTokenType) { const val: string = (token as IdentToken).val.toLowerCase(); if (nonStandardColors.has(val)) { @@ -1275,7 +1280,7 @@ export function parseColor(token: Token) { } } - if (token.typ === EnumToken.ColorTokenType) { + if (typ === EnumToken.ColorTokenType) { if (!("kin" in token) && "val" in token) { // @ts-expect-error (token as ColorToken).kin = ColorType[(token as ColorToken).val.replaceAll("-", "_").toUpperCase()]; @@ -1680,24 +1685,24 @@ export function isWhiteSpace(codepoint: number): boolean { // } // // return ( -// token.typ === EnumToken.IdenTokenType || -// token.typ === EnumToken.DimensionTokenType || -// token.typ === EnumToken.LengthTokenType || -// token.typ === EnumToken.AngleTokenType || -// token.typ === EnumToken.FlexTokenType || -// token.typ === EnumToken.TimeTokenType || -// token.typ === EnumToken.ResolutionTokenType || -// token.typ === EnumToken.FrequencyTokenType || -// token.typ === EnumToken.NumberTokenType || -// token.typ === EnumToken.ColorTokenType || -// token.typ === EnumToken.FunctionTokenType || -// token.typ === EnumToken.UrlFunctionTokenType || -// token.typ === EnumToken.GridTemplateFuncTokenType || -// token.typ === EnumToken.ImageFunctionTokenType || -// token.typ === EnumToken.TimelineFunctionTokenType || -// token.typ === EnumToken.TimingFunctionTokenType || -// token.typ === EnumToken.MathFunctionTokenType || -// token.typ === EnumToken.TransformFunctionTokenType +// typ === EnumToken.IdenTokenType || +// typ === EnumToken.DimensionTokenType || +// typ === EnumToken.LengthTokenType || +// typ === EnumToken.AngleTokenType || +// typ === EnumToken.FlexTokenType || +// typ === EnumToken.TimeTokenType || +// typ === EnumToken.ResolutionTokenType || +// typ === EnumToken.FrequencyTokenType || +// typ === EnumToken.NumberTokenType || +// typ === EnumToken.ColorTokenType || +// typ === EnumToken.FunctionTokenType || +// typ === EnumToken.UrlFunctionTokenType || +// typ === EnumToken.GridTemplateFuncTokenType || +// typ === EnumToken.ImageFunctionTokenType || +// typ === EnumToken.TimelineFunctionTokenType || +// typ === EnumToken.TimingFunctionTokenType || +// typ === EnumToken.MathFunctionTokenType || +// typ === EnumToken.TransformFunctionTokenType // ); // } diff --git a/src/lib/validation/match.ts b/src/lib/validation/match.ts index 9cc221da0..76ad7727b 100644 --- a/src/lib/validation/match.ts +++ b/src/lib/validation/match.ts @@ -90,12 +90,13 @@ funcTypes.push(EnumToken.FunctionTokenType, EnumToken.PseudoClassFuncTokenType); * @param tokens * @returns */ -export function trimArray(tokens: Token[]): Token[] { - while (tokens[0]?.typ === EnumToken.WhitespaceTokenType) { +export function trimArray(tokens: number[], options: ParserOptions): number[] { + const arena = options.arena!; + while (arena.getType(tokens[0]) === EnumToken.WhitespaceTokenType) { tokens.shift(); } - while (tokens[tokens.length - 1]?.typ === EnumToken.WhitespaceTokenType) { + while (arena.getType(tokens[tokens.length - 1]) === EnumToken.WhitespaceTokenType) { tokens.pop(); } @@ -155,7 +156,7 @@ export function isMFValue( featureName = featureName.toLowerCase(); // @ts-expect-error - if ( config.mediaFeatures[featureName] == null) { + if (config.mediaFeatures[featureName] == null) { return { valid: false, success: false }; } @@ -212,8 +213,7 @@ export function isMFValue( return { valid: true, success: - (tokens.length == 1 && - tokens[0].typ == EnumToken.NumberTokenType) || + (tokens.length == 1 && tokens[0].typ == EnumToken.NumberTokenType) || (tokens.length === 3 && tokens[0].typ == EnumToken.NumberTokenType && typeof (tokens[0] as NumberToken).val === "number" && @@ -238,10 +238,11 @@ export function isMFValue( * @param tokens * @returns */ -export function createValidationContext(tokens: Token[]): ValidationContext { - tokens = trimArray(tokens.filter((t) => t.typ !== EnumToken.CommentTokenType)); +export function createValidationContext(tokens: number[], options: ParserOptions): ValidationContext { + const arena = options.arena!; + tokens = trimArray(tokens.filter((t) => arena.getType(t) !== EnumToken.CommentTokenType)); - if (tokens.at(-1)?.typ === EnumToken.ImportantTokenType) { + if (arena.getType(tokens.at(-1)) === EnumToken.ImportantTokenType) { tokens.pop(); trimArray(tokens); } @@ -258,7 +259,7 @@ export function createValidationContext(tokens: Token[]): ValidationContext { }, peek(offset: number = 0) { let index: number = this.index; - let token: Token = this.tokens[++index]; + let token: number = this.tokens[++index]; while (offset >= 0 && index < this.tokens.length) { while ( @@ -428,7 +429,7 @@ export function createValidationContext(tokens: Token[]): ValidationContext { * @returns */ export function matchSelectorSyntax( - stream: Token[], + stream: number[], errors: ErrorDescription[], options: ParserOptions | ValidationOptions, nested: boolean = true, @@ -436,22 +437,29 @@ export function matchSelectorSyntax( success: boolean; errors: ErrorDescription[]; } { - const stack: Token[] = []; - const tokens: Token[] = []; + const stack: number[] = []; + const tokens: number[] = []; + const arena = options.arena!; - let token: Token; + let token: number; let i: number = 0; let success: boolean = true; + let typ: EnumToken; + let val: string; + + while (i < stream.length) { + typ = arena.getType(stream[i]); + + if (typ !== EnumToken.WhitespaceTokenType && typ !== EnumToken.CommentTokenType) { + break; + } - while ( - i < stream.length && - (stream[i].typ === EnumToken.WhitespaceTokenType || stream[i].typ === EnumToken.CommentTokenType) - ) { i++; } if (i < stream.length) { - switch (stream[i].typ) { + typ = arena.getType(stream[i]); + switch (typ) { case EnumToken.Plus: case EnumToken.Tilda: case EnumToken.GtTokenType: @@ -465,10 +473,10 @@ export function matchSelectorSyntax( errors: [ { action: "drop", - message: `Unexpected token ${EnumToken[stream[i].typ]}`, + message: `Unexpected token ${EnumToken[typ]}`, node: stream[i], // @ts-expect-error - location: options.source!.getSourceLocation(stream[i][LOCSTA]), + location: options.source!.getSourceLocation(arena.getSpanStart(stream[i])), }, ], }; @@ -478,15 +486,16 @@ export function matchSelectorSyntax( for (; i < stream.length; i++) { token = stream[i]; + typ = arena.getType(token); - if (token.typ === EnumToken.Star) { - token.typ = EnumToken.UniversalSelectorTokenType; + if (typ === EnumToken.Star) { + typ = EnumToken.UniversalSelectorTokenType; } tokens.push(token); - if (tokensfuncDefMap.has(token.typ)) { - if (stack.length > 0 && selectorNodeTypes.includes(stack.at(-1)!.typ)) { + if (tokensfuncDefMap.has(typ)) { + if (stack.length > 0 && selectorNodeTypes.includes(arena.getType(stack.at(-1)))) { stack.pop(); } @@ -496,26 +505,27 @@ export function matchSelectorSyntax( if ( stack.length > 0 && - selectorNodeTypes.includes(stack.at(-1)!.typ) && - !selectorNodeTypes.includes(token.typ) && - token.typ !== EnumToken.WhitespaceTokenType && - token.typ !== EnumToken.CommentTokenType && - token.typ !== EnumToken.CDOCOMMTokenType + selectorNodeTypes.includes(arena.getType(stack.at(-1))) && + !selectorNodeTypes.includes(typ) && + typ !== EnumToken.WhitespaceTokenType && + typ !== EnumToken.CommentTokenType && + typ !== EnumToken.CDOCOMMTokenType ) { stack.pop(); } - switch (token.typ) { + switch (typ) { case EnumToken.PseudoClassFuncTokenType: { + val = arena.getValue(token); const result = matchAllSyntaxes( (( getParsedSyntax( ValidationSyntaxGroupEnum.Selectors, - (token as PseudoClassFunctionToken).val + "()", + val + "()", ) as ValidationFunctionToken[] )?.[0]?.chi as ValidationToken[]) ?? [], - createValidationContext((token as PseudoClassFunctionToken).chi), + createValidationContext(arena.children(token), options), options, ); @@ -547,7 +557,7 @@ export function matchSelectorSyntax( message: `Nesting selector is not allowed`, node: token, // @ts-expect-error - location: options.source!.getSourceLocation(token[LOCSTA]), + location: options.source!.getSourceLocation(arena.getSpanStart(token)), }, ], }; @@ -556,13 +566,15 @@ export function matchSelectorSyntax( break; case EnumToken.Plus: - Object.assign(token, { typ: EnumToken.NextSiblingCombinatorTokenType }); + arena.set(token, EnumToken.SubsequentSiblingCombinatorTokenType, 0); + // Object.assign(token, { typ: EnumToken.NextSiblingCombinatorTokenType }); stack.push(token); break; case EnumToken.Tilda: case EnumToken.GtTokenType: - Object.assign(token, { typ: selectorEnumMap.get(token.typ)! }); + arena.set(token, selectorEnumMap.get(typ)); + // Object.assign(token, { typ: selectorEnumMap.get(token.typ)! }); case EnumToken.ColumnCombinatorTokenType: case EnumToken.ChildCombinatorTokenType: @@ -570,24 +582,24 @@ export function matchSelectorSyntax( case EnumToken.DescendantCombinatorTokenType: case EnumToken.NextSiblingCombinatorTokenType: case EnumToken.SubsequentSiblingCombinatorTokenType: - if (stream[i + 1]?.typ === EnumToken.WhitespaceTokenType) { + if (arena.getType(stream[i + 1]) === EnumToken.WhitespaceTokenType) { i++; } - if (stack.length > 0 && stack.at(-1)?.typ === EnumToken.UniversalSelectorTokenType) { + if (stack.length > 0 && arena.getType(stack.at(-1)) === EnumToken.UniversalSelectorTokenType) { stack.pop(); } - if (stack.length > 0 && selectorNodeTypes.includes(stack.at(-1)?.typ)) { + if (stack.length > 0 && selectorNodeTypes.includes(arena.getType(stack.at(-1)))) { return { success: false, errors: [ { action: "drop", - message: `Unexpected combinator ${EnumToken[token.typ]}`, + message: `Unexpected combinator ${EnumToken[typ]}`, node: token, // @ts-expect-error - location: options.source!.getSourceLocation(token[LOCSTA]), + location: options.source!.getSourceLocation(arena.getSpanStart(token)), }, ], }; @@ -597,7 +609,7 @@ export function matchSelectorSyntax( break; case EnumToken.CommaTokenType: - if (stack.length > 0 && stack.at(-1)!.typ === EnumToken.UniversalSelectorTokenType) { + if (stack.length > 0 && arena.getType(stack.at(-1)) === EnumToken.UniversalSelectorTokenType) { stack.pop(); } @@ -606,11 +618,13 @@ export function matchSelectorSyntax( if (!nested) { let k: number = i + 1; - while ( - k < stream.length && - (stream[k].typ === EnumToken.WhitespaceTokenType || - stream[k].typ === EnumToken.CommentTokenType) - ) { + while (k < stream.length) { + typ = arena.getType(stream[k]); + + if (typ !== EnumToken.WhitespaceTokenType && typ !== EnumToken.CommentTokenType) { + break; + } + k++; } } @@ -639,20 +653,20 @@ export function matchSelectorSyntax( break; case EnumToken.AttrEndTokenType: - if (stack.length > 0 && stack.at(-1)!.typ === EnumToken.UniversalSelectorTokenType) { + if (stack.length > 0 && arena.getType(stack.at(-1)) === EnumToken.UniversalSelectorTokenType) { stack.pop(); } - if (stack.at(-1)?.typ !== EnumToken.AttrStartTokenType) { + if (arena.getType(stack.at(-1)) !== EnumToken.AttrStartTokenType) { return { success: false, errors: [ { action: "drop", - message: `Unexpected token ${EnumToken[token.typ]}`, + message: `Unexpected token ${EnumToken[typ]}`, node: token, // @ts-expect-error - location: options.source!.getSourceLocation(token[LOCSTA]), + location: options.source!.getSourceLocation(arena.getSpanStart(token)), }, ], }; @@ -660,14 +674,16 @@ export function matchSelectorSyntax( { const k: number = tokens.length - 1; - let index: number = tokens.indexOf(stack.at(-1) as Token); - const slice: Token[] = []; + let index: number = tokens.indexOf(stack.at(-1)!); + const slice: number[] = []; for (let n = index + 1; n < k; n++) { + + typ = arena.getType(tokens[n]); if ( - tokens[n].typ === EnumToken.WhitespaceTokenType || - tokens[n].typ === EnumToken.CommentTokenType || - tokens[n].typ === EnumToken.CDOCOMMTokenType + typ === EnumToken.WhitespaceTokenType || + typ === EnumToken.CommentTokenType || + typ === EnumToken.CDOCOMMTokenType ) { continue; } @@ -675,11 +691,11 @@ export function matchSelectorSyntax( slice.push(tokens[n]); } - if (slice[1]?.typ === EnumToken.Pipe) { + if (arena.getType(slice[1]) === EnumToken.Pipe) { slice.shift(); } - if (slice[0].typ === EnumToken.Pipe) { + if (arena.getType(slice[0]) === EnumToken.Pipe) { slice.shift(); } @@ -692,13 +708,16 @@ export function matchSelectorSyntax( slice.shift(); if ( - slice[0]?.typ === EnumToken.StringTokenType && - /^[a-zA-Z0-9_-]+$/.test((slice[0] as StringToken).val.slice(1, -1)) + arena.getType(slice[0]) === EnumToken.StringTokenType + ) { - Object.assign(slice[0], { - typ: EnumToken.IdenTokenType, - val: (slice[0] as StringToken).val.slice(1, -1), - }); + + val = arena.getValue(slice[0]); + + if (/^[a-zA-Z0-9_-]+$/.test(val.slice(1, -1))) { + + arena.set(slice[0], EnumToken.IdenTokenType, val.slice(1, -1)); + } } slice.shift(); @@ -708,19 +727,21 @@ export function matchSelectorSyntax( break; } + val = arena.getValue(slice[0]); + if ( - slice[0].typ !== EnumToken.IdenTokenType || - ((slice[0] as IdentToken).val != "i" && (slice[0] as IdentToken).val != "s") + arena.getType(slice[0]) !== EnumToken.IdenTokenType || + (val != "i" &&val != "s") ) { return { success: false, errors: [ { action: "drop", - message: `Unexpected token ${EnumToken[slice[0].typ]}`, + message: `Unexpected token ${EnumToken[arena.getType(slice[0])]}`, node: slice[0], // @ts-expect-error - location: options.source!.getSourceLocation(slice[0][LOCSTA]), + location: options.source!.getSourceLocation(arena.getSpanStart(slice[0])), }, ], }; @@ -732,28 +753,27 @@ export function matchSelectorSyntax( } case EnumToken.StartParensTokenType: - return { success: false, errors: [ { action: "drop", - message: `Unexpected token ${EnumToken[token.typ]}`, + message: `Unexpected token ${EnumToken[typ]}`, node: token, // @ts-expect-error - location: options.source!.getSourceLocation(token[LOCSTA]), + location: options.source!.getSourceLocation(arena.getSpanStart(token)), }, ], }; case EnumToken.EndParensTokenType: - if (stack.length > 0 && stack.at(-1)?.typ === EnumToken.UniversalSelectorTokenType) { + if (stack.length > 0 && arena.getType(stack.at(-1)) === EnumToken.UniversalSelectorTokenType) { stack.pop(); } if ( - stack.at(-1)?.typ === EnumToken.PseudoClassFunctionTokenDefType || - stack.at(-1)?.typ === EnumToken.PseudoElementTokenType + arena.getType(stack.at(-1))=== EnumToken.PseudoClassFunctionTokenDefType || + arena.getType(stack.at(-1)) === EnumToken.PseudoElementTokenType ) { const token = stack.at(-1) as PseudoClassFunctionToken | PseudoElementToken; @@ -788,10 +808,10 @@ export function matchSelectorSyntax( errors: [ { action: "drop", - message: `Unexpected token ${EnumToken[token.typ]}`, + message: `Unexpected token ${EnumToken[typ]}`, node: token, // @ts-expect-error - location: options.source!.getSourceLocation(token[LOCSTA]), + location: options.source!.getSourceLocation(arena.getSpanStart(token)), }, ], }; @@ -803,7 +823,7 @@ export function matchSelectorSyntax( // stack.pop(); // } - if (stack.at(-1)?.typ === EnumToken.PseudoClassFunctionTokenDefType) { + if (arena.getType(stack.at(-1)) === EnumToken.PseudoClassFunctionTokenDefType) { break; } @@ -813,10 +833,10 @@ export function matchSelectorSyntax( errors: [ { action: "drop", - message: `Unsupported selector token ${EnumToken[token.typ]}`, + message: `Unsupported selector token ${EnumToken[typ]}`, node: token, // @ts-expect-error - location: options.source!.getSourceLocation(token[LOCSTA]), + location: options.source!.getSourceLocation(arena.getSpanStart(token)), }, ], }; @@ -833,7 +853,7 @@ export function matchSelectorSyntax( } } - if (stack.length > 0 && stack.at(-1)!.typ === EnumToken.UniversalSelectorTokenType) { + if (stack.length > 0 && arena.getType(stack.at(-1)) === EnumToken.UniversalSelectorTokenType) { stack.pop(); } @@ -843,10 +863,10 @@ export function matchSelectorSyntax( errors: [ { action: "drop", - message: `Unmatched token ${EnumToken[stack.at(-1)!.typ]}`, - node: stack.at(-1)! as Token, + message: `Unmatched token ${EnumToken[arena.getType(stack.at(-1))]}`, + node: stack.at(-1)!, // @ts-expect-error - location: options.source!.getSourceLocation(stack.at(-1)![LOCSTA]), + location: options.source!.getSourceLocation(arena.getSpanStart(stack.at(-1)!)), }, ], }; @@ -883,7 +903,6 @@ export function matchAllSyntaxes( if (index != -1) { for (let i = index; i < syntaxes.length; i++) { - if ( syntaxes[i].typ == ValidationTokenEnum.Whitespace || syntaxes[i].isOptional || @@ -1100,7 +1119,6 @@ function matchSyntax( } while (++i < syntaxes.length) { - if (syntaxes[i].typ == ValidationTokenEnum.Whitespace) { continue; } @@ -2023,7 +2041,7 @@ function matchProperty( const syntax = ( getParsedSyntax( ValidationSyntaxGroupEnum.Syntaxes, - (token as FunctionToken).val + "()", + (token as FunctionToken).val + "()", ) as ValidationFunctionToken[] )?.[0]?.chi as ValidationToken[]; @@ -2285,24 +2303,22 @@ function matchProperty( case "display-internal": case "display-outside": case "display-legacy": - case "content-position": - { - let values = propertyKeywordValues.get(property.val); + case "content-position": { + let values = propertyKeywordValues.get(property.val); - if (values == null) { - values = new Set( - (config.syntaxes as unknown as Record)[property.val].syntax.split( - /[\s|]+/, - ), - ); - propertyKeywordValues.set(property.val, values); - } - - success = - token?.typ == EnumToken.IdenTokenType && values.has((token as IdentToken).val.toLowerCase()); - break; + if (values == null) { + values = new Set( + (config.syntaxes as unknown as Record)[property.val].syntax.split( + /[\s|]+/, + ), + ); + propertyKeywordValues.set(property.val, values); } + success = token?.typ == EnumToken.IdenTokenType && values.has((token as IdentToken).val.toLowerCase()); + break; + } + case "mf-name": { const token = context.peek(); @@ -2325,8 +2341,7 @@ function matchProperty( case "custom-ident": case "counter-name": case "counter-style-name": - success = - token?.typ == EnumToken.IdenTokenType || token?.typ == EnumToken.DashedIdenTokenType; + success = token?.typ == EnumToken.IdenTokenType || token?.typ == EnumToken.DashedIdenTokenType; if (success && token?.typ === EnumToken.IdenTokenType) { const val: string = (token as IdentToken).val.toLowerCase(); diff --git a/src/lib/validation/types.d.ts b/src/lib/validation/types.d.ts index ee07e7c21..ee5b255bf 100644 --- a/src/lib/validation/types.d.ts +++ b/src/lib/validation/types.d.ts @@ -8,7 +8,7 @@ export interface ValidationContext { /** * the tokens */ - tokens: Token[]; + tokens: number[]; /** * current index */ @@ -26,28 +26,28 @@ export interface ValidationContext { * @param close * @param counter */ - peekRange(open?: EnumToken, close?: EnumToken, counter?: number): Token[]; + peekRange(open?: EnumToken, close?: EnumToken, counter?: number): number[]; /** * * @param split */ - split(split?: EnumToken): Token[][]; + split(split?: EnumToken): number[][]; /** * return the current token that is not consumed yet */ - current(): Token | null; + current(): number | null; /** * consume the current token and move to the next */ - next(): Token | null; + next(): number | null; /** * return tokens that are not consumed */ - getRemainingTokens(): Token[]; + getRemainingTokens(): number[]; /** * */ @@ -62,7 +62,7 @@ export interface ValidationContext { * consume all tokens up to the specified token * @param token */ - update(token: Token): ValidationContext; + update(token: number): ValidationContext; /** * returns true if all tokens are consumed. returns false otherwise diff --git a/src/lib/validation/utils/list.ts b/src/lib/validation/utils/list.ts index 4fa0a1edc..af9d636e2 100644 --- a/src/lib/validation/utils/list.ts +++ b/src/lib/validation/utils/list.ts @@ -1,5 +1,5 @@ import { EnumToken } from "../../ast/types.ts"; -import type { Token } from "../../../@types/index.d.ts"; +import type { ParserOptions, Token } from "../../../@types/index.d.ts"; export function stripCommaToken(tokenList: Token[]): Token[] | null { let result: Token[] = []; @@ -20,10 +20,12 @@ export function stripCommaToken(tokenList: Token[]): Token[] | null { return result; } -export function splitTokenList(tokenList: Token[], split: EnumToken[] = [EnumToken.CommaTokenType], includeSplitToken: boolean = false): Token[][] { +export function splitTokenList(tokenList: number[], options: ParserOptions, split: EnumToken[] = [EnumToken.CommaTokenType], includeSplitToken: boolean = false): number[][] { + + const arena = options.arena!; return tokenList.reduce( - (acc: Token[][], curr: Token): Token[][] => { - if (split.includes(curr.typ)) { + (acc: number[][], curr: number): number[][] => { + if (split.includes(arena.getType(curr))) { if (includeSplitToken && Array.isArray(acc[acc.length - 1])) { @@ -37,6 +39,6 @@ export function splitTokenList(tokenList: Token[], split: EnumToken[] = [EnumTok return acc; }, - [[]] as Token[][], + [[]] as number[][], ); } diff --git a/src/node.ts b/src/node.ts index af0b7b690..07f0190b7 100644 --- a/src/node.ts +++ b/src/node.ts @@ -60,7 +60,6 @@ export { replaceNodeOrValue } from "./lib/parser/utils/token.ts"; export { EnumToken, ColorType, - ValidationLevel, EnumAstNodeStatus, ModuleScopeEnumOptions, ModuleCaseTransformEnum, @@ -295,7 +294,7 @@ export function parseSync( options.src ??= ""; options.sourcesMap ??= new Map(); - options.arena ??= new Arena(); + options.arena ??= new Arena(2 << Math.ceil(2 + Math.log2((stream as string).length))); Object.assign(options, { resolve, @@ -647,6 +646,9 @@ export async function parse( options ??= {}; options.src ??= ""; options.sourcesMap ??= new Map(); + options.arena ??= new Arena( + typeof stream == "string" ? 2 << Math.ceil(2 + Math.log2((stream as string).length)) : 4096, + ); Object.assign(options, { load, diff --git a/src/web.ts b/src/web.ts index 6fd15498e..849d59e3c 100644 --- a/src/web.ts +++ b/src/web.ts @@ -23,6 +23,7 @@ import { dirname, matchUrl, resolve } from "./lib/fs/resolve.ts"; import { ResponseType } from "./types.ts"; import { SourceFile } from "./lib/parser/source.ts"; import { parseResult, validateSyncArguments } from "./utils/sync.ts"; +import { Arena } from "./lib/parser/arena.ts"; export type * from "./@types/index.d.ts"; export type * from "./@types/ast.d.ts"; @@ -310,7 +311,8 @@ export function parseSync( options ??= {}; options.src ??= ""; - options.sourcesMap ??= new Map(); + options.sourcesMap ??= new Map(); + options.arena ??= new Arena(2 << Math.ceil(2 + Math.log2((stream as string).length))); Object.assign(options, { resolve, @@ -322,6 +324,9 @@ export function parseSync( }); options.src = resolve(options.src!, options.cwd).relative; + options.arena ??= new Arena( + typeof stream == "string" ? 2 << Math.ceil(2 + Math.log2((stream as string).length)) : 4096, + ); if (options.source == null) { const source = new SourceFile(typeof stream == "string" ? stream : "", [], options.src); From 5e39cf222c93101f80a7c555fe9b446c0edb1d51 Mon Sep 17 00:00:00 2001 From: Thierry Bela Nanga Date: Sun, 11 Oct 2026 13:39:14 -0400 Subject: [PATCH 03/24] work in progress --- src/@types/index.d.ts | 12 +- src/@types/token.d.ts | 2 +- src/@types/walker.d.ts | 5 +- src/lib/ast/expand.ts | 3 +- src/lib/ast/features/if.ts | 5 +- src/lib/ast/features/inlinecssvariables.ts | 3 +- src/lib/ast/features/prefix.ts | 4 +- src/lib/ast/features/transform.ts | 6 +- src/lib/ast/math/expression.ts | 6 +- src/lib/ast/minify.ts | 10 +- src/lib/ast/types.ts | 11 +- src/lib/ast/walk.ts | 24 +- src/lib/parser/arena.ts | 110 +- src/lib/parser/declaration/map.ts | 11 +- src/lib/parser/parse.ts | 44 +- src/lib/parser/tokenize.ts | 1 - src/lib/parser/utils/declaration.ts | 455 +++-- src/lib/parser/utils/selector.ts | 35 +- src/lib/printer/print.ts | 1958 ++++++++++++++++++++ src/lib/printer/render.ts | 1444 +-------------- src/lib/validation/utils/list.ts | 14 +- src/node.ts | 2 +- src/web.ts | 6 +- 23 files changed, 2478 insertions(+), 1693 deletions(-) create mode 100644 src/lib/printer/print.ts diff --git a/src/@types/index.d.ts b/src/@types/index.d.ts index f21195cf6..67175951a 100644 --- a/src/@types/index.d.ts +++ b/src/@types/index.d.ts @@ -13,6 +13,7 @@ import { FeatureWalkMode } from "../lib/ast/features/type.ts"; import { ValidationToken } from "../lib/validation/parser/types"; import { SourceFile } from "../lib/parser/source.ts"; import { ResponseType } from "../types.ts"; +import { Arena } from "../lib/parser/arena.ts"; export * from "./ast.d.ts"; export * from "./token.d.ts"; @@ -717,11 +718,6 @@ export declare interface ResolvedPath { */ export declare interface RenderOptions { - /** - * Arena - */ - arena: Arena; - /** * Source file to be used as CSS input file for sourcemap resolution */ @@ -884,6 +880,12 @@ export declare interface ParseResultStats { * Parse result object */ export declare interface ParseResult { + + /** + * Arena memory + */ + arena: Arena; + /** * Parsed ast tree */ diff --git a/src/@types/token.d.ts b/src/@types/token.d.ts index d26d469b6..6dbafb500 100644 --- a/src/@types/token.d.ts +++ b/src/@types/token.d.ts @@ -1432,7 +1432,7 @@ export declare interface ListToken extends BaseToken { /** * @inheritdoc */ - typ: EnumToken.ListToken; + typ: EnumToken.ListTokenType; /** * Children */ diff --git a/src/@types/walker.d.ts b/src/@types/walker.d.ts index a108b6d8e..4783bc579 100644 --- a/src/@types/walker.d.ts +++ b/src/@types/walker.d.ts @@ -50,10 +50,7 @@ export declare type WalkerFilter = (node: AstNode) => WalkerOption; * - {@link AstNode}: * - {@link Token}: */ -export declare type WalkerValueFilter = ( - node: number, - event?: WalkerEvent, -) => WalkerOption | number[]; +export declare type WalkerValueFilter = (node: number, event?: WalkerEvent) => WalkerOption | number[]; /** * walker result diff --git a/src/lib/ast/expand.ts b/src/lib/ast/expand.ts index 59f5f2032..4d860f86e 100644 --- a/src/lib/ast/expand.ts +++ b/src/lib/ast/expand.ts @@ -2,7 +2,6 @@ import {splitRule} from "./minify.ts"; import {combinators, PARENT, RAW, STATE} from "../syntax/constants.ts"; import {parseString} from "../parser/parse.ts"; import {walkValues} from "./walk.ts"; -import {renderValue} from "../printer/render.ts"; import type {AstAtRule, AstNode, AstRule, AstStyleSheet, LiteralToken, Token} from "../../@types/index.d.ts"; import {EnumAstNodeStatus, EnumToken} from "./types.ts"; import {cloneNode} from "./clone.ts"; @@ -355,7 +354,7 @@ export function replaceCompound(input: string, replace: string): string { } } - return tokens.reduce((acc: string, curr: Token) => acc + renderValue(curr), ""); + return tokens.reduce((acc: string, curr: Token) => acc + printValue(curr), ""); } function replaceCompoundLiteral(selector: string, replace: string) { diff --git a/src/lib/ast/features/if.ts b/src/lib/ast/features/if.ts index b54d7f361..986f109ae 100644 --- a/src/lib/ast/features/if.ts +++ b/src/lib/ast/features/if.ts @@ -11,7 +11,6 @@ import type { Token, } from "../../../@types/index.d.ts"; import { EnumToken } from "../types.ts"; -import { renderValue } from "../../printer/render.ts"; import { FeatureWalkMode } from "./type.ts"; import { LOCEND, LOCSRCID, LOCSTA, PARENT, TOKENS } from "../../syntax/constants.ts"; import { equalsIgnoreCase } from "../../parser/utils/text.ts"; @@ -169,7 +168,7 @@ function substituteIfElseNode( convertColor: minify, }; - atRule.val = atRule[TOKENS]!.reduce((acc: string, curr: Token) => acc + renderValue(curr, options), ""); + atRule.val = atRule[TOKENS]!.reduce((acc: string, curr: Token) => acc + printValue(curr, options), ""); clonedDeclaration = cloneNode(declaration, true, nodeMap) as AstDeclaration; @@ -192,7 +191,7 @@ function substituteIfElseNode( }) as AstAtRule; atRule[TOKENS] = [left]; - atRule.val = atRule[TOKENS]!.reduce((acc: string, curr: Token) => acc + renderValue(curr), ""); + atRule.val = atRule[TOKENS]!.reduce((acc: string, curr: Token) => acc + printValue(curr), ""); if (declaration[PARENT] != null) { atRule[LOCSRCID] = declaration[PARENT][LOCSRCID]!; diff --git a/src/lib/ast/features/inlinecssvariables.ts b/src/lib/ast/features/inlinecssvariables.ts index 4ae954704..253fca6d8 100644 --- a/src/lib/ast/features/inlinecssvariables.ts +++ b/src/lib/ast/features/inlinecssvariables.ts @@ -17,7 +17,6 @@ import type { } from "../../../@types/index.d.ts"; import { EnumToken } from "../types.ts"; import { walkValues } from "../walk.ts"; -import { renderValue } from "../../printer/render.ts"; import { splitRule } from "../minify.ts"; import { FeatureWalkMode } from "./type.ts"; import { mathFuncs, RAW } from "../../syntax/constants.ts"; @@ -198,7 +197,7 @@ export class InlineCssVariablesFeature { // @ts-ignore (parent.chi).splice(i, 1, { typ: EnumToken.CommentTokenType, - val: `/* ${info.node.nam}: ${info.values.reduce((acc: string, curr: Token): string => acc + renderValue(curr, { convertColor: false }), "")} */`, + val: `/* ${info.node.nam}: ${info.values.reduce((acc: string, curr: Token): string => acc + printNode(curr, { convertColor: false }), "")} */`, } as CommentToken); break; } diff --git a/src/lib/ast/features/prefix.ts b/src/lib/ast/features/prefix.ts index e5dbf1235..64cf20d87 100644 --- a/src/lib/ast/features/prefix.ts +++ b/src/lib/ast/features/prefix.ts @@ -18,7 +18,6 @@ import {walkValues} from "../walk.ts"; import {pseudoAliasMap} from "../../syntax/syntax.ts"; import {splitRule} from "../minify.ts"; import type {ValidationConfiguration} from "../../../@types/validation.d.ts"; -import {renderValue} from "../../printer/render.ts"; import {funcLike, regMatchLinearGradient, regMatchRadialGradient, STATE, TOKENS} from "../../syntax/constants.ts"; import {FeatureWalkMode} from "./type.ts"; import {ValidationSyntaxGroupEnum} from "../../validation/parser/typedef.ts"; @@ -26,6 +25,7 @@ import {getSyntaxConfig} from "../../validation/config.ts"; import {splitTokenList} from "../../validation/utils/list.ts"; import {equalsIgnoreCase} from "../../parser/utils/text.ts"; import {toDegrees} from "../../parser/utils/angle.ts"; +import { printValue } from "../../printer/print.ts"; const config: ValidationConfiguration = getSyntaxConfig(); @@ -257,7 +257,7 @@ export class ComputePrefixFeature { if (node.typ == EnumToken.AtRuleNodeType && (node as AstAtRule).val !== "") { if (replaceAstNodes((node as AstAtRule)[TOKENS] as Token[])) { (node as AstAtRule).val = ((node as AstAtRule)[TOKENS] as Token[]).reduce( - (acc, curr) => acc + renderValue(curr), + (acc, curr) => acc + printValue(curr), "", ); } diff --git a/src/lib/ast/features/transform.ts b/src/lib/ast/features/transform.ts index 3bea03fa8..e98b1de04 100644 --- a/src/lib/ast/features/transform.ts +++ b/src/lib/ast/features/transform.ts @@ -9,10 +9,10 @@ import type { } from "../../../@types/index.d.ts"; import { EnumAstNodeStatus, EnumToken } from "../types.ts"; import { compute } from "../transform/compute.ts"; -import { filterValues, renderValue } from "../../printer/render.ts"; import { eqMatrix, minifyTransformFunctions } from "../transform/minify.ts"; import { FeatureWalkMode } from "./type.ts"; import { STATE } from "../../syntax/constants.ts"; +import { filterValues, printValue } from "../../printer/print.ts"; export class TransformCssFeature { public accept: Set = new Set([ @@ -93,11 +93,11 @@ export class TransformCssFeature { r.push(minified); } - const l: number = renderValue(matrix).length; + const l: number = printValue(matrix).length; (node as AstDeclaration).val = r.reduce( (acc: Token[], curr: Token[]): Token[] => { - if (curr.reduce((acc: string, t: Token) => acc + renderValue(t), "").length < l) { + if (curr.reduce((acc: string, t: Token) => acc + printValue(t), "").length < l) { return curr; } diff --git a/src/lib/ast/math/expression.ts b/src/lib/ast/math/expression.ts index c911f2a24..07b81e226 100644 --- a/src/lib/ast/math/expression.ts +++ b/src/lib/ast/math/expression.ts @@ -129,7 +129,7 @@ export function evaluate(tokens: Token[]): Token[] { if (token.typ == EnumToken.Sub) { if (!isScalarToken(nodes[i + 1])) { token = { - typ: EnumToken.ListToken, + typ: EnumToken.ListTokenType, chi: [nodes[i], nodes[i + 1]], [LOCSRCID]: nodes[i][LOCSRCID], [LOCSTA]: nodes[i][LOCSTA], @@ -187,7 +187,7 @@ export function evaluate(tokens: Token[]): Token[] { } } - if (acc.length > 0 && curr[0] != EnumToken.ListToken) { + if (acc.length > 0 && curr[0] != EnumToken.ListTokenType) { acc.push({ typ: EnumToken.Add, [LOCSRCID]: token[LOCSRCID], @@ -1083,7 +1083,7 @@ function factor(tokens: Array, ops: Array<"+" | " } for (let i = 0; i < tokens.length; i++) { - if (tokens[i].typ == EnumToken.ListToken) { + if (tokens[i].typ == EnumToken.ListTokenType) { // @ts-ignore tokens.splice(i, 1, ...tokens[i].chi); } diff --git a/src/lib/ast/minify.ts b/src/lib/ast/minify.ts index 63493bd33..8dc339e7b 100644 --- a/src/lib/ast/minify.ts +++ b/src/lib/ast/minify.ts @@ -1,5 +1,5 @@ import { eq } from "../parser/utils/eq.ts"; -import { doRender, renderValue } from "../printer/render.ts"; +import { doRender } from "../printer/render.ts"; import * as allFeatures from "./features/index.ts"; import { walkValues } from "./walk.ts"; import type { @@ -406,7 +406,7 @@ function minifyAtRuleMedia(tokens: Token[]): Token[] { } acc.set( - values.reduce((acc, t) => acc + renderValue(t), ""), + values.reduce((acc, t) => acc + printValue(t), ""), result.values, ); @@ -610,7 +610,7 @@ function doMinify( (index + 3 < arr.length || arr[index + 2]?.typ === EnumToken.WhitespaceTokenType)) ? "" - : renderValue(curr)), + : printValue(curr)), "", ); } @@ -648,7 +648,7 @@ function doMinify( arr[index + 1]?.typ === EnumToken.CommentTokenType && (index + 3 < arr.length || arr[index + 2].typ === EnumToken.WhitespaceTokenType)) ? "" - : renderValue(curr)), + : printValue(curr)), "", ); } @@ -1648,7 +1648,7 @@ function fixSelector(node: AstRule): void { } } - node.sel = attributes.reduce((acc: string, curr: Token) => acc + renderValue(curr), ""); + node.sel = attributes.reduce((acc: string, curr: Token) => acc + printValue(curr), ""); node[TOKENS] = null; } } diff --git a/src/lib/ast/types.ts b/src/lib/ast/types.ts index 24f74bb7f..671bd6902 100644 --- a/src/lib/ast/types.ts +++ b/src/lib/ast/types.ts @@ -52,7 +52,15 @@ export enum EnumAstNodeStatus { */ HasChildren = 1 << 8, + /** + * Node has values + */ HasValues = 1 << 9, + + /** + * Node has fraction value + */ + HasFractionValue = 1 << 10, } export declare type AstNodePropertyType = "state" | "errors" | "location" | "tokens" | "parent" | "selector"; @@ -300,7 +308,8 @@ export enum EnumToken { /** * token list token type */ - ListToken, + ListTokenType, + /* arithmetic tokens */ /** * addition token type diff --git a/src/lib/ast/walk.ts b/src/lib/ast/walk.ts index 103c437ec..fff88231c 100644 --- a/src/lib/ast/walk.ts +++ b/src/lib/ast/walk.ts @@ -492,18 +492,18 @@ export function* walkValues( // @ts-expect-error option = filter.fn( value, - value[PARENT] ?? root, - WalkerEvent.Enter, - // @ts-expect-error - function* () { - // @ts-expect-error - let parent = node[PARENT]; - - while (parent != null) { - yield parent; - parent = parent[PARENT] ?? null; - } - }, + // value[PARENT] ?? root, + // WalkerEvent.Enter, + // // @ts-expect-error + // function* () { + // // @ts-expect-error + // let parent = node[PARENT]; + + // while (parent != null) { + // yield parent; + // parent = parent[PARENT] ?? null; + // } + // }, ); isNumeric = typeof option == "number"; diff --git a/src/lib/parser/arena.ts b/src/lib/parser/arena.ts index c802075bc..16bb9d8b2 100644 --- a/src/lib/parser/arena.ts +++ b/src/lib/parser/arena.ts @@ -21,7 +21,7 @@ import { equalsIgnoreCase } from "./utils/text.ts"; export class Arena { public stringIds: Map = new Map(); public strings: string[] = []; - public root: number = 1; + public readonly root: number = 1; public current: number = 1; public dataTypes: Uint8Array; @@ -75,6 +75,8 @@ export class Arena { this.renderedTokens = new Uint32Array(size); this.colorType = new Uint8Array(size); this.spans = new BigUint64Array(size); + + this.dataTypes[this.root] = EnumToken.StyleSheetNodeType; } public allocate(): number { @@ -176,7 +178,7 @@ export class Arena { return this.strings[this.stringValue[id]] ?? null; } - setNumbericValue(id: number, numericValue: number): void { + setNumericValue(id: number, numericValue: number): void { this.numericValue[id] = numericValue; } @@ -188,24 +190,94 @@ export class Arena { this.parents[id] = parentId; } + getFirstChild(id: number): number { + return this.firstChild[id]; + } + setFirstChild(id: number, firstChildId: number): void { + this.detach(firstChildId); + + const firstChild = this.firstChild[id]; + + if (this.firstChild[id] != 0) { + const nextSibling = this.nextSibling[firstChild]; + + if (nextSibling != 0) { + this.nextSibling[firstChildId] = nextSibling; + this.previousSibling[nextSibling] = firstChildId; + } + + this.detach(firstChild); + } + this.firstChild[id] = firstChildId; + this.parents[firstChild] = id; + } + + getLastChild(id: number): number { + return this.lastChild[id]; } setLastChild(id: number, lastChildId: number): void { + this.detach(lastChildId); + + const lastChild = this.lastChild[id]; + + if (lastChild != 0) { + const previousSibling = this.previousSibling[lastChild]; + + if (previousSibling != 0) { + this.previousSibling[lastChildId] = previousSibling; + this.nextSibling[previousSibling] = lastChildId; + } + + this.detach(lastChild); + } + this.lastChild[id] = lastChildId; + this.nextSibling[lastChildId] = 0; + this.parents[lastChildId] = id; } setNextSibling(id: number, nextSiblingId: number): void { + this.detach(nextSiblingId); + + this.nextSibling[nextSiblingId] = this.nextSibling[id]; this.nextSibling[id] = nextSiblingId; + this.previousSibling[nextSiblingId] = id; + this.parents[nextSiblingId] = this.parents[id]; } setPreviousSibling(id: number, previousSiblingId: number): void { + this.detach(previousSiblingId); + + if (this.previousSibling[id] != 0) { + const previousSibling = this.previousSibling[id]; + + if (previousSibling != 0) { + this.nextSibling[previousSibling] = previousSiblingId; + this.previousSibling[previousSiblingId] = previousSibling; + } + + this.nextSibling[previousSiblingId] = this.nextSibling[id]; + } + this.previousSibling[id] = previousSiblingId; + this.parents[previousSiblingId] = this.parents[id]; } setFirstToken(id: number, firstTokenId: number): void { + this.detach(firstTokenId); + + const firstToken = this.firstToken[id]; + + this.previousSibling[firstTokenId] = 0; + this.nextSibling[firstTokenId] = this.nextSibling[firstTokenId]; + this.parents[firstTokenId] = id; + this.firstToken[id] = firstTokenId; + + this.detach(firstToken); } setColorType(id: number, colorType: number): void { @@ -282,22 +354,43 @@ export class Arena { this.parents[child] = id; } + /** + * let child should be of type EnumToken.ListToken or EnumToken.ParensToken + * @param id + * @param child + */ setLeft(id: number, child: number): void { this.setChildAt(id, 0, child); } + getLeft(id: number): number { + return this.getChildAt(id, 0 ) as number; + } + setRight(id: number, rightId: number): void { this.setChildAt(id, 1, rightId); } + getRight(id: number): number { + return this.getChildAt(id, 1) as number; + } + setOp(id: number, opId: number): void { this.setChildAt(id, 2, opId); } + getOp(id: number): number { + return this.getChildAt(id, 2) as number; + } + setOp2(id: number, op2Id: number): void { this.setChildAt(id, 3, op2Id); } + getOp2(id: number): number { + return this.getChildAt(id, 3) as number; + } + setRenderedTokens(id: number, renderedTokensId: number): void { this.renderedTokens[id] = renderedTokensId; } @@ -448,7 +541,6 @@ export class Arena { *walkValues( values: number[], - root: number | null = null, filter?: | WalkerValueFilter | null @@ -460,7 +552,6 @@ export class Arena { reverse?: boolean, ): Generator { const stack: number[] = values.slice(); - let previous: number | null = null; if (filter != null && typeof filter == "function") { filter = { @@ -476,7 +567,6 @@ export class Arena { let isNumeric: boolean = false; let value: number; let option: WalkerOption | number[]; - let node: number | null; let i: number = -1; const eventType = filter.event ?? WalkerEvent.Enter; // @ts-ignore @@ -534,17 +624,15 @@ export class Arena { if (!isNumeric || ((option as number) & WalkerOptionEnum.IgnoreChildren) === 0) { if (this.flags[value] & EnumAstNodeStatus.HasChildren) { - for (const child of this.children(value)) { - if (reverse) { stack.unshift(child); } else { stack.push(child); } } - } - + } + // else { // const values: Token[] = []; @@ -616,7 +704,7 @@ export class Arena { option = filter.fn(value, value[PARENT], WalkerEvent.Leave); // @ts-ignore - if (option != null && (Array.isArray(option))) { + if (option != null && Array.isArray(option)) { // const op = Array.isArray(option) ? option : [option]; stack.splice(i, 0, ...(reverse ? option.toReversed() : option)); @@ -628,8 +716,6 @@ export class Arena { } } } - - previous = value; } } diff --git a/src/lib/parser/declaration/map.ts b/src/lib/parser/declaration/map.ts index 6053b75ad..1cdd125f3 100644 --- a/src/lib/parser/declaration/map.ts +++ b/src/lib/parser/declaration/map.ts @@ -13,12 +13,11 @@ import { eq } from "../utils/eq.ts"; import { getConfig } from "../utils/config.ts"; import { matchType } from "../utils/type.ts"; import { EnumToken } from "../../ast/types.ts"; -import { renderValue } from "../../printer/render.ts"; import { parseString } from "../parse.ts"; import { PropertySet } from "./set.ts"; import { PROPERTYNAME } from "../../syntax/constants.ts"; import { cloneNode } from "../../ast/clone.ts"; -import { parse } from "node:path"; +import { printValue } from "../../printer/print.ts"; const propertiesConfig: PropertiesConfig = getConfig(); @@ -352,7 +351,7 @@ export class PropertyMap { } as Token); // @ts-ignore const separator: string = this.config.separator - ? renderValue({ + ? printValue({ ...this.config.separator, typ: EnumToken[this.config.separator.typ as keyof typeof EnumToken] as EnumToken, } as Token) @@ -366,7 +365,7 @@ export class PropertyMap { t = declaration.val[i]; if (!cache.has(t)) { - cache.set(t, renderValue(t, { minify: true })); + cache.set(t, printValue(t, { minify: true })); } if (t.typ == typ && separator == cache.get(t)) { @@ -764,7 +763,7 @@ export class PropertyMap { props.default.includes( curr[1][i] .reduce( - (acc: string, curr: Token): string => acc + renderValue(curr) + " ", + (acc: string, curr: Token): string => acc + printNode(curr) + " ", "", ) .trimEnd(), @@ -907,7 +906,7 @@ export class PropertyMap { const val: string = values.reduce( (acc: string, curr: Token): string => acc + - renderValue(curr, { + printNode(curr, { removeComments: true, minify: true, }), diff --git a/src/lib/parser/parse.ts b/src/lib/parser/parse.ts index 5028bcfee..f207a1b51 100644 --- a/src/lib/parser/parse.ts +++ b/src/lib/parser/parse.ts @@ -1,6 +1,5 @@ import { isColor, isIdentColor, parseColor } from "../syntax/syntax.ts"; import { camelize, dasherize, equalsIgnoreCase } from "./utils/text.ts"; -import { renderValue } from "../printer/render.ts"; import { EnumAstNodeStatus, EnumToken, ModuleCaseTransformEnum, ModuleScopeEnumOptions } from "../ast/types.ts"; import { minify } from "../ast/minify.ts"; import { expand } from "../ast/expand.ts"; @@ -80,12 +79,12 @@ import { matchGenericSyntax } from "./utils/at-rule-generic.ts"; import { memoize } from "./utils/cache.ts"; import { SourceFile } from "./source.ts"; import { dirname } from "../fs/resolve.ts"; -import { Arena, ARENA_ROOT } from "./arena.ts"; +import { Arena } from "./arena.ts"; function renderTokens(tokens: Token[] | null | undefined, options?: any): string { if (tokens == null || tokens.length === 0) return ""; - if (options != null) return tokens.map((t) => renderValue(t, options)).join(""); - return tokens.map((t) => renderValue(t)).join(""); + if (options != null) return tokens.map((t) => printValue(t, options)).join(""); + return tokens.map((t) => printValue(t)).join(""); } declare type T = AstDeclaration | AstAtRule | AstRule | AstKeyframesRule | AstKeyframesAtRule; @@ -725,7 +724,7 @@ export function doParseSync(tokenizer: Tokenizer, options: ParserSyncOptions = { if (tokenizer.unit != null) { - arena.set(tokenId, tokenizer.typ, 0, tokenizer.val, arena.internString(tokenizer.unit)); + arena.set(tokenId, tokenizer.typ as EnumToken, 0, tokenizer.val as number, arena.internString(tokenizer.unit)); // item = { // typ: tokenizer.typ as EnumToken, @@ -734,20 +733,20 @@ export function doParseSync(tokenizer: Tokenizer, options: ParserSyncOptions = { // } as DimensionToken; } else if (tokenizer.nam != null) { - arena.set(tokenId, tokenizer.typ); - arena.setName(tokenId, tokenizer.nam); + arena.set(tokenId, tokenizer.typ as EnumToken); + arena.setName(tokenId, arena.internString(tokenizer.nam as string)); // item = { // typ: tokenizer.typ as EnumToken, // nam: tokenizer.nam, // } as Token; } else if (tokenizer.val === null) { - arena.set(tokenId, tokenizer.typ); + arena.set(tokenId, tokenizer.typ as EnumToken); // item = { // typ: tokenizer.typ as EnumToken, // } as Token; } else if (tokenizer.kin != null) { - arena.set(tokenId, tokenizer.typ, arena.internString(tokenizer.val)); + arena.set(tokenId, tokenizer.typ as EnumToken, arena.internString(tokenizer.val as string)); arena.setColorType(tokenId, tokenizer.kin); // item = { @@ -756,14 +755,14 @@ export function doParseSync(tokenizer: Tokenizer, options: ParserSyncOptions = { // kin: tokenizer.kin, // } as ColorToken; } else { - arena.set(tokenId, tokenizer.typ, arena.internString(tokenizer.val)); + arena.set(tokenId, tokenizer.typ as EnumToken, arena.internString(tokenizer.val as string)); // item = { // typ: tokenizer.typ as EnumToken, // val: tokenizer.val, // } as Token; } - arena.setSpan(tokenId, tokenizer.srcId, tokenizer.sta, tokenizer.end); + arena.setSpan(tokenId, tokenizer.srcId as number, tokenizer.sta as number, tokenizer.end as number); // item[LOCSRCID] = tokenizer.srcId as number; // item[LOCSTA] = tokenizer.sta as number; @@ -826,7 +825,7 @@ export function doParseSync(tokenizer: Tokenizer, options: ParserSyncOptions = { if (tokenizer.unit != null) { - arena.set(tokenId, tokenizer.typ, 0, tokenizer.val, arena.internString(tokenizer.unit)); + arena.set(tokenId, tokenizer.typ, 0, tokenizer.val as number, arena.internString(tokenizer.unit)); // item = { // typ: tokenizer.typ as EnumToken, // val: tokenizer.val, @@ -835,7 +834,7 @@ export function doParseSync(tokenizer: Tokenizer, options: ParserSyncOptions = { } else if (tokenizer.nam != null) { arena.set(tokenId, tokenizer.typ); - arena.setName(tokenId, tokenizer.nam); + arena.setName(tokenId, arena.internString(tokenizer.nam as string)); // item = { // typ: tokenizer.typ as EnumToken, // nam: tokenizer.nam, @@ -847,7 +846,7 @@ export function doParseSync(tokenizer: Tokenizer, options: ParserSyncOptions = { // } as Token; } else if (tokenizer.kin != null) { - arena.set(tokenId, tokenizer.typ, arena.internString(tokenizer.val)); + arena.set(tokenId, tokenizer.typ, arena.internString(tokenizer.val as string)); arena.setColorType(tokenId, tokenizer.kin); // item = { // typ: tokenizer.typ as EnumToken, @@ -856,14 +855,14 @@ export function doParseSync(tokenizer: Tokenizer, options: ParserSyncOptions = { // } as ColorToken; } else { - arena.set(tokenId, tokenizer.typ, arena.internString(tokenizer.val)); + arena.set(tokenId, tokenizer.typ, arena.internString(tokenizer.val as string)); // item = { // typ: tokenizer.typ as EnumToken, // val: tokenizer.val, // } as Token; } - arena.setSpan(tokenId, tokenizer.srcId, tokenizer.sta, tokenizer.end); + arena.setSpan(tokenId, tokenizer.srcId as number, tokenizer.sta as number, tokenizer.end as number); // item[LOCSRCID] = tokenizer.srcId as number; // item[LOCSTA] = tokenizer.sta as number; @@ -900,8 +899,8 @@ export function doParseSync(tokenizer: Tokenizer, options: ParserSyncOptions = { if ( options.removeEmpty && previousNode != null && - arena.firstChild(previousNode) == 0 && - arena.lastChild(context) == previousNode + arena.getFirstChild(previousNode) == 0 && + arena.getLastChild(context) == previousNode ) { arena.detach(previousNode); @@ -1170,6 +1169,7 @@ export function doParseSync(tokenizer: Tokenizer, options: ParserSyncOptions = { let endTime: number = performance.now(); const result = { + arena, ast, errors, stats: { @@ -1734,7 +1734,7 @@ export function doParseSync(tokenizer: Tokenizer, options: ParserSyncOptions = { node.sel = ""; for (const token of node[TOKENS]! as Token[]) { - node.sel += renderValue(token); + node.sel += printValue(token); } } else if (node.typ == EnumToken.AtRuleNodeType || node.typ == EnumToken.KeyframesAtRuleNodeType) { const val: string = node.nam.toLowerCase(); @@ -3130,7 +3130,7 @@ export async function doParse(iter: Tokenizer | Promise, options: Par node.sel = ""; for (const token of node[TOKENS]! as Token[]) { - node.sel += renderValue(token); + node.sel += printValue(token); } } else if (node.typ == EnumToken.AtRuleNodeType || node.typ == EnumToken.KeyframesAtRuleNodeType) { const val: string = node.nam.toLowerCase(); @@ -3870,7 +3870,7 @@ export function parseAtRule( stream[index + 1]?.typ === EnumToken.CommentTokenType && (stream.length < index + 3 || stream[index + 2]?.typ === EnumToken.WhitespaceTokenType)) ? "" - : renderValue(t, options)), + : printValue(t, options)), "", ), ...(parseAsBlock ? { chi: [] } : {}), @@ -3911,7 +3911,7 @@ export function parseAtRule( stream[index + 1]?.typ === EnumToken.CommentTokenType && (stream.length < index + 3 || stream[index + 2]?.typ === EnumToken.WhitespaceTokenType)) ? "" - : renderValue(t, options)), + : printValue(t, options)), "", ), }) as AstAtRule; diff --git a/src/lib/parser/tokenize.ts b/src/lib/parser/tokenize.ts index 099b10dfa..56dd77214 100644 --- a/src/lib/parser/tokenize.ts +++ b/src/lib/parser/tokenize.ts @@ -1958,7 +1958,6 @@ export class Tokenizer { break; case TokenMap.REVERSE_SOLIDUS: - this.advance(parseInfo); // EOF diff --git a/src/lib/parser/utils/declaration.ts b/src/lib/parser/utils/declaration.ts index 36f66fb07..64923e018 100644 --- a/src/lib/parser/utils/declaration.ts +++ b/src/lib/parser/utils/declaration.ts @@ -43,6 +43,7 @@ import type { ValidationPropertyToken } from "../../validation/parser/types.d.ts import { equalsIgnoreCase } from "./text.ts"; import { buildExpression } from "../../ast/math/expression.ts"; import { splitTokenList } from "../../validation/utils/list.ts"; +import { Arena } from "../arena.ts"; /** * @@ -92,12 +93,12 @@ function parseGridTemplate(template: string): string { export function parseDeclaration( tokens: number[], - parent: number| null, + parent: number | null, options: ParserOptions, errors: ErrorDescription[], ): number { const name = tokens.shift() as number; - const arena = options.arena!; + const arena = options.arena! as Arena; let i: number; let rules: ValidationSyntaxRule | null = null; let syntaxRules = null; @@ -114,8 +115,8 @@ export function parseDeclaration( } for (i = 0; i < tokens.length; i++) { - token = tokens[i]; - typ = arena.getType(token); + token = tokens[i]; + typ = arena.getType(token); if ( typ == EnumToken.WhitespaceTokenType || @@ -143,7 +144,6 @@ export function parseDeclaration( arena.setType(node, EnumToken.RawNodeTokenType); arena.setFlags(node, EnumAstNodeStatus.Unparsed); arena.setErrors(node, [ - { action: "drop", node: name, @@ -152,7 +152,7 @@ export function parseDeclaration( } as ErrorDescription, ]); - arena.setTokens(node, tokens); + arena.setTokens(node, ...tokens); arena.setSpan(node, arena.getSourceId(name), arena.getSpanStart(name), arena.getSpanEnd(tokens.at(-1) ?? name)); return node; @@ -176,16 +176,15 @@ export function parseDeclaration( tokens = trimArray(tokens.slice(i + 1), options); if (validate && typ === EnumToken.IdenTokenType) { - - typ = arena.getType(parent); - val = arena.getValue(parent).toLowerCase(); + typ = arena.getType(parent as number); + val = arena.getValue(parent as number).toLowerCase(); if ( parent != null && typ === EnumToken.AtRuleNodeType && val != "import" && val != "supports" && - val!= "media" + val != "media" ) { // check @at-rule description // pick up the syntax or reject immediately @@ -218,23 +217,22 @@ export function parseDeclaration( ((syntaxRules[0] as ValidationPropertyToken).val === "declaration-list" || (syntaxRules[0] as ValidationPropertyToken).val === "declaration-rule-list")) ) { - - typ = arena.getType(parent); - val = arena.getValue(parent).toLowerCase(); + typ = arena.getType(parent as number); + val = arena.getValue(parent as number).toLowerCase(); if ( parent != null && - typ === EnumToken.AtRuleNodeType && + typ === EnumToken.AtRuleNodeType && (val === "swash" || - val=== "annotation" || + val === "annotation" || val === "ornaments" || - val === "stylistic" || + val === "stylistic" || val === "historical-forms") ) { syntaxRules = getParsedSyntax(ValidationSyntaxGroupEnum.Syntaxes, "font-feature-custom-ident"); } else if (typ === EnumToken.AtRuleNodeType && val === "styleset") { syntaxRules = getParsedSyntax(ValidationSyntaxGroupEnum.Syntaxes, "font-feature-custom-ident-list"); - } else if (typ === EnumToken.AtRuleNodeType && val=== "character-variant") { + } else if (typ === EnumToken.AtRuleNodeType && val === "character-variant") { syntaxRules = getParsedSyntax( ValidationSyntaxGroupEnum.Syntaxes, "font-feature-custom-ident-character-variant", @@ -261,9 +259,9 @@ export function parseDeclaration( arena.setFlags(name, EnumAstNodeStatus.Invalid | EnumAstNodeStatus.HasValues); arena.setErrors(name, [errors[errors.length - 1]]); - arena.setName(name, arena.getValue(name)); + arena.setName(name, arena.internString(arena.getValue(name as number))); arena.set(name, EnumToken.DeclarationNodeType, 0); - arena.setChildren(name, tokens); + arena.setChildren(name, ...tokens); return name; @@ -285,16 +283,19 @@ export function parseDeclaration( if (syntaxRules != null) { const doNotValidate: boolean = options.validation === false; - result = doNotValidate ? null : matchAllSyntaxes(syntaxRules, createValidationContext(tokens, options), options); + result = doNotValidate + ? null + : matchAllSyntaxes(syntaxRules, createValidationContext(tokens, options), options); if (doNotValidate || result != null) { success = doNotValidate || (result?.success as boolean); if (success) { for (const value of arena.walkValues(tokens)) { - if (arena.getType(value) === EnumToken.IdenTokenType && isColor(value, arena)) { + typ = arena.getType(value); + if (typ === EnumToken.IdenTokenType && isColor(value, arena)) { parseColor(value, arena); - } else if (value.typ === EnumToken.ColorTokenType) { + } else if (typ === EnumToken.ColorTokenType) { if (isColor(value, arena)) { parseColor(value, arena); } @@ -334,14 +335,13 @@ export function parseDeclaration( switch (typ) { case EnumToken.Literal: - if (arena.equalsIgnoreCase(token, 'val', '/') && stackType == EnumToken.MathFunctionTokenDefType) { - + if (arena.equalsIgnoreCase(token, "val", "/") && stackType == EnumToken.MathFunctionTokenDefType) { arena.setType(token, EnumToken.Div); // Object.assign(token, { // typ: EnumToken.Div, // }); } else if ( - arena.equalsIgnoreCase(token, 'val', '-')&& + arena.equalsIgnoreCase(token, "val", "-") && stackType == EnumToken.MathFunctionTokenDefType ) { arena.setType(token, EnumToken.Sub); @@ -354,8 +354,7 @@ export function parseDeclaration( case EnumToken.Star: case EnumToken.Plus: - - arena.setType(token, tokensMap.get(typ)); + arena.setType(token, tokensMap.get(typ) as EnumToken); // Object.assign(token, { // typ: tokensMap.get(typ), // }); @@ -369,7 +368,7 @@ export function parseDeclaration( tokens.splice(i, 1); arena.setType(tokens[index], EnumToken.WrappedValuesTokenType); - arena.setChildren(tokens[index], trimArray(tokens.splice(index + 1, i - index - 1), options)); + arena.setChildren(tokens[index], ...trimArray(tokens.splice(index + 1, i - index - 1), options)); // Object.assign(tokens[index], { // typ: EnumToken.WrappedValuesTokenType, @@ -388,11 +387,14 @@ export function parseDeclaration( tokens.splice(i, 1); - arena.setType(tokens[index], stackType === EnumToken.StartParensTokenType - ? EnumToken.ParensTokenType - : (tokensfuncDefMap.get(stackType) as EnumToken)); + arena.setType( + tokens[index], + stackType === EnumToken.StartParensTokenType + ? EnumToken.ParensTokenType + : (tokensfuncDefMap.get(stackType) as EnumToken), + ); - arena.setChildren(tokens[index], trimArray(tokens.splice(index + 1, i - index - 1), options)); + arena.setChildren(tokens[index], ...trimArray(tokens.splice(index + 1, i - index - 1), options)); // Object.assign(tokens[index], { // typ: @@ -404,12 +406,11 @@ export function parseDeclaration( if ( arena.getType(tokens[index]) === EnumToken.WildCardFunctionTokenType && - arena.equalsIgnoreCase(tokens[index] , 'val' , 'if') + arena.equalsIgnoreCase(tokens[index], "val", "if") ) { const lastChild = arena.getLastChild(tokens[index]); if (lastChild != 0 && arena.getType(lastChild) === EnumToken.SemiColonTokenType) { - arena.detach(lastChild); } @@ -419,10 +420,9 @@ export function parseDeclaration( let foundElse: boolean = false; - const children = arena.children(tokens[index]); - - (tokens[index] as FunctionToken).chi = splitTokenList( - (tokens[index] as FunctionToken).chi, + const children = splitTokenList( + arena.children(tokens[index]), + options, [EnumToken.SemiColonTokenType], true, ).reduce((acc, curr) => { @@ -434,85 +434,142 @@ export function parseDeclaration( } let index = -1; + let typ: EnumToken = arena.getType(curr[index]); while (++index < curr.length) { if ( - curr[index].typ == EnumToken.IdenTokenType && - equalsIgnoreCase("else", (curr[index] as IdentToken).val) + typ == EnumToken.IdenTokenType && + arena.equalsIgnoreCase(curr[index], "val", "else") ) { foundElse = true; } - if (curr[index].typ === EnumToken.ColonTokenType) { + if (typ === EnumToken.ColonTokenType) { break; } } if (foundElse && acc.length > 0) { - acc[acc.length - 1] = { - typ: EnumToken.IfElseConditionTokenType, - l: acc[acc.length - 1] as IfConditionToken, - r: { - typ: EnumToken.IfConditionTokenType, - l: trimArray(curr.slice(0, index)), - r: trimArray( - curr.at(-1)?.typ === EnumToken.SemiColonTokenType - ? curr.slice(index + 1, -1) - : curr.slice(index + 1), - ), - } as IfConditionToken, - } as IfElseConditionToken; + const leftChild = arena.allocate(); + + arena.setType(leftChild, EnumToken.ListTokenType); + arena.setChildren(leftChild, ...trimArray(curr.slice(0, index), options)); + + const rightChild = arena.allocate(); + + arena.setType(rightChild, EnumToken.ListTokenType); + arena.setChildren( + rightChild, + ...trimArray( + arena.getType(curr.at(-1)!) === EnumToken.SemiColonTokenType + ? curr.slice(index + 1, -1) + : curr.slice(index + 1), + options, + ), + ); + + const ifCondition = arena.allocate(); + + arena.setType(ifCondition, EnumToken.IfConditionTokenType); + arena.setLeft(ifCondition, leftChild); + arena.setRight(ifCondition, rightChild); + + const ifElseLeftCondition = arena.allocate(); + + arena.setType(ifElseLeftCondition, EnumToken.IfElseConditionTokenType); + arena.setLeft(ifElseLeftCondition, acc[acc.length - 1] as number); + arena.setRight(ifElseLeftCondition, ifCondition); + + acc[acc.length - 1] = ifElseLeftCondition; + + // acc[acc.length - 1] = { + // typ: EnumToken.IfElseConditionTokenType, + // l: acc[acc.length - 1] as IfConditionToken, + // r: { + // typ: EnumToken.IfConditionTokenType, + // l: trimArray(curr.slice(0, index)), + // r: trimArray( + // curr.at(-1)?.typ === EnumToken.SemiColonTokenType + // ? curr.slice(index + 1, -1) + // : curr.slice(index + 1), + // ), + // } as IfConditionToken, + // } as IfElseConditionToken; } else { - acc.push({ - typ: EnumToken.IfConditionTokenType, - l: trimArray(curr.slice(0, index)), - r: trimArray(curr.slice(index + 1)), - } as IfConditionToken); + const ifCondition = arena.allocate(); + + arena.setType(ifCondition, EnumToken.IfConditionTokenType); + + const leftChild = arena.allocate(); + + arena.setType(leftChild, EnumToken.ListTokenType); + arena.setChildren(leftChild, ...trimArray(curr.slice(0, index), options)); + + const rightChild = arena.allocate(); + + arena.setType(rightChild, EnumToken.ListTokenType); + arena.setChildren(rightChild, ...trimArray(curr.slice(index + 1), options)); + + arena.setLeft(ifCondition, leftChild); + arena.setRight(ifCondition, rightChild); + + acc.push(ifCondition); + + // acc.push({ + // typ: EnumToken.IfConditionTokenType, + // l: trimArray(curr.slice(0, index)), + // r: trimArray(curr.slice(index + 1)), + // } as IfConditionToken); } return acc; - }, [] as Token[]); - } else if (tokens[index].typ === EnumToken.UrlFunctionTokenType) { + }, [] as number[]); + } else if (arena.getType(tokens[index]) === EnumToken.UrlFunctionTokenType) { let l: number = -1; - while (++l < (tokens[index] as FunctionToken).chi.length) { - if ((tokens[index] as FunctionToken).chi[l].typ === EnumToken.StringTokenType) { + const children = arena.children(tokens[index]); + + while (++l < children.length) { + if (arena.getType(children[l]) === EnumToken.StringTokenType) { break; } } - if ( - (tokens[index] as FunctionToken).chi[l]?.typ === EnumToken.StringTokenType && - /^[a-zA-Z0-0/_.-]+$/.test( - ((tokens[index] as FunctionToken).chi[l] as StringToken).val.slice(1, -1), - ) - ) { - Object.assign((tokens[index] as FunctionToken).chi[l], { - typ: EnumToken.UrlTokenTokenType, - val: ((tokens[index] as FunctionToken).chi[l] as StringToken).val.slice(1, -1), - }); + if (l < children.length && arena.getType(children[l]) === EnumToken.StringTokenType) { + const val = arena.getValue(children[l]).slice(1, -1); + + if (/^[a-zA-Z0-0/_.-]+$/.test(val)) { + arena.setValue(children[l], arena.internString(val)); + arena.setType(children[l], EnumToken.UrlTokenTokenType); + + // Object.assign((tokens[index] as FunctionToken).chi[l], { + // typ: EnumToken.UrlTokenTokenType, + // val: ((tokens[index] as FunctionToken).chi[l] as StringToken).val.slice(1, -1), + // }); + } } } i = index; if ( - tokens[index].typ === EnumToken.MathFunctionTokenType && - equalsIgnoreCase("calc", (tokens[index] as FunctionToken).val) + arena.getType(tokens[index]) === EnumToken.MathFunctionTokenType && + arena.equalsIgnoreCase(tokens[index], "val", "calc") ) { - (tokens[index] as FunctionToken).chi = [buildExpression((tokens[index] as FunctionToken).chi)]; - } else if (tokens[index].typ === EnumToken.ColorTokenType) { - if (isColor(tokens[index])) { - parseColor(tokens[index]); + arena.setChildren(tokens[index], ...arena.children(tokens[index])); + // (tokens[index] as FunctionToken).chi = [buildExpression((tokens[index] as FunctionToken).chi)]; + } else if (arena.getType(tokens[index]) === EnumToken.ColorTokenType) { + if (isColor(tokens[index], arena)) { + parseColor(tokens[index], arena); } else { success = false; - tokens[index].typ = EnumToken.FunctionTokenType; + arena.setType(tokens[index], EnumToken.FunctionTokenType); errors.push({ action: "drop", message: `invalid color`, node: tokens[index], - location: options.source!.getSourceLocation(tokens[index][LOCSTA]!), + location: options.source!.getSourceLocation(arena.getSpanStart(tokens[index])), }); } } @@ -524,37 +581,47 @@ export function parseDeclaration( case EnumToken.IdenTokenType: { - const val: string = (token as IdentToken).val.toLowerCase(); - - if (val in COLORS_NAMES || val === "currentcolor") { - Object.assign(token, { - val, - typ: EnumToken.ColorTokenType, - kin: ColorType.LIT, - }); + const val: string = arena.getValue(token).toLowerCase(); + + if (COLORS_NAMES[val] != null || val === "currentcolor") { + arena.setType(token, EnumToken.ColorTokenType); + arena.setColorType(token, ColorType.LIT); + // Object.assign(token, { + // val, + // typ: EnumToken.ColorTokenType, + // kin: ColorType.LIT, + // }); } else if (nonStandardColors.has(val)) { - Object.assign(token, { - typ: EnumToken.ColorTokenType, - kin: ColorType.NON_STD, - }); + arena.setType(token, EnumToken.ColorTokenType); + arena.setColorType(token, ColorType.NON_STD); + // Object.assign(token, { + // typ: EnumToken.ColorTokenType, + // kin: ColorType.NON_STD, + // }); } else if (systemColors.has(val)) { - Object.assign(token, { - typ: EnumToken.ColorTokenType, - kin: ColorType.SYS, - }); + arena.setType(token, EnumToken.ColorTokenType); + arena.setColorType(token, ColorType.SYS); + // Object.assign(token, { + // typ: EnumToken.ColorTokenType, + // kin: ColorType.SYS, + // }); } else if (deprecatedSystemColors.has(val)) { - Object.assign(token, { - typ: EnumToken.ColorTokenType, - kin: ColorType.DPSYS, - }); + arena.setType(token, EnumToken.ColorTokenType); + arena.setColorType(token, ColorType.DPSYS); + // Object.assign(token, { + // typ: EnumToken.ColorTokenType, + // kin: ColorType.DPSYS, + // }); } } break; } - if (trimTokenSpace.has(token.typ)) { - if (tokens[i + 1]?.typ === EnumToken.WhitespaceTokenType) { + typ = arena.getType(token); + + if (trimTokenSpace.has(typ)) { + if (tokens.length > i + 1 && arena.getType(tokens[i + 1]) === EnumToken.WhitespaceTokenType) { for (let index = i + 1; index < tokens.length - 1; index++) { tokens[index] = tokens[index + 1]; } @@ -564,7 +631,7 @@ export function parseDeclaration( continue; } - if (tokens[i - 1]?.typ == EnumToken.WhitespaceTokenType) { + if (tokens.length >= i && arena.getType(tokens[i - 1]) == EnumToken.WhitespaceTokenType) { for (let index = i - 1; index < tokens.length - 1; index++) { tokens[index] = tokens[index + 1]; } @@ -580,47 +647,62 @@ export function parseDeclaration( action: "drop", message: "unbalanced token", node: stack[stack.length - 1], - location: options.source!.getSourceLocation(stack[stack.length - 1][LOCSTA]!), + location: options.source!.getSourceLocation(arena.getSpanStart(stack[stack.length - 1])), }); - if (tokens[tokens.length - 1][LOCEND] != null) { - name[LOCEND] = tokens[tokens.length - 1][LOCEND]; + if (tokens.length > 0) { + arena.setSpanEnd(name, arena.getSpanEnd(tokens[tokens.length - 1])); + // name[LOCEND] = tokens[tokens.length - 1][LOCEND]; } - name[STATE] = EnumAstNodeStatus.Invalid; - name[ERRORS] = result?.errors ?? []; + arena.setName(name, arena.internString(arena.getValue(name))); + arena.set(name, EnumToken.DeclarationNodeType, 0); + arena.setFlags(name, EnumAstNodeStatus.Invalid | EnumAstNodeStatus.HasValues); + arena.setErrors(name, result?.errors ?? []); + arena.setChildren(name, ...tokens); + + return name; + + // name[STATE] = EnumAstNodeStatus.Invalid; + // name[ERRORS] = result?.errors ?? []; - //@ts-expect-error - return Object.assign(name, { - typ: EnumToken.DeclarationNodeType, - nam: name.val, - val: tokens, - }) as AstDeclaration; + // return Object.assign(name, { + // typ: EnumToken.DeclarationNodeType, + // nam: name.val, + // val: tokens, + // }) as AstDeclaration; } - for (const value of arena.walk(tokens)) { - if (value.typ === EnumToken.IdenTokenType && isColor(value, arena)) { + for (const value of arena.walkValues(tokens)) { + if (arena.getType(value) === EnumToken.IdenTokenType && isColor(value, arena)) { parseColor(value, arena); } } + val = arena.getValue(name); + if ( - name.val === "grid" || - name.val === "grid-template-areas" || - name.val === "grid-template-rows" || - name.val === "grid-template-columns" + val === "grid" || + val === "grid-template-areas" || + val === "grid-template-rows" || + val === "grid-template-columns" ) { let i: number = 0; for (; i < tokens.length; i++) { - if (tokens[i].typ === EnumToken.StringTokenType) { - if (/[\s.]/.test((tokens[i] as StringToken).val)) { - (tokens[i] as StringToken).val = parseGridTemplate((tokens[i] as StringToken).val); + typ = arena.getType(tokens[i]); + + if (typ === EnumToken.StringTokenType) { + val = arena.getValue(tokens[i]); + if (/[\s.]/.test(val)) { + arena.setValue(tokens[i], arena.internString(parseGridTemplate(val))); + + // (tokens[i] as StringToken).val = parseGridTemplate(val); } if ( - tokens[i + 1]?.typ === EnumToken.WhitespaceTokenType && - tokens[i + 2]?.typ === EnumToken.StringTokenType + arena.getType(tokens[i + 1]) === EnumToken.WhitespaceTokenType && + arena.getType(tokens[i + 2]) === EnumToken.StringTokenType ) { tokens.splice(i + 1, 1); } @@ -628,27 +710,39 @@ export function parseDeclaration( } } - if (validate && syntaxRules == null && name.typ === EnumToken.IdenTokenType) { - if (tokens[tokens.length - 1]?.[LOCEND] != null) { - name[LOCEND] = tokens[tokens.length - 1][LOCEND]; + if (validate && syntaxRules == null && arena.getType(name) === EnumToken.IdenTokenType) { + if (tokens.length > 0 != null) { + arena.setSpanEnd(name, arena.getSpanEnd(tokens[tokens.length - 1])); + // name[LOCEND] = tokens[tokens.length - 1][LOCEND]; } - name[STATE] = EnumAstNodeStatus.Unknown; - name[ERRORS] = result?.errors ?? []; + arena.setName(name, arena.internString(arena.getValue(name))); + arena.set(name, EnumToken.DeclarationNodeType, 0); + arena.setFlags(name, EnumAstNodeStatus.Unknown); + arena.setErrors(name, result?.errors ?? []); + arena.setChildren(name, ...tokens); + + return name; + + // name[STATE] = EnumAstNodeStatus.Unknown; + // name[ERRORS] = result?.errors ?? []; - // @ts-expect-error - return Object.assign(name, { - typ: EnumToken.DeclarationNodeType, - nam: name.val, - val: tokens, - }) as AstDeclaration; + // // @ts-expect-error + // return Object.assign(name, { + // typ: EnumToken.DeclarationNodeType, + // nam: name.val, + // val: tokens, + // }) as AstDeclaration; } - if (equalsIgnoreCase("composes", name.val)) { + if (arena.equalsIgnoreCase(name, "val", "composes")) { let index: number = -1; for (let i = 0; i < tokens.length; i++) { - if (tokens[i].typ === EnumToken.IdenTokenType && equalsIgnoreCase("from", (tokens[i] as IdentToken).val)) { + if ( + arena.getType(tokens[i]) === EnumToken.IdenTokenType && + arena.equalsIgnoreCase(tokens[i], "val", "from") + ) { index = i; break; } @@ -656,33 +750,72 @@ export function parseDeclaration( const left = trimArray(index == -1 ? tokens : tokens.slice(0, index), options); const right = index == -1 ? null : trimArray(tokens.slice(index + 1), options); - tokens = [ - { - typ: EnumToken.ComposesSelectorNodeType, - l: left, - r: right?.[0] ?? null, - [LOCSRCID]: tokens[0][LOCSRCID], - [LOCSTA]: tokens[0][LOCSTA], - [LOCEND]: index != -1 ? right![right!.length - 1]?.[LOCEND] : left[left.length - 1][LOCEND], - } as ComposesSelectorToken, - ]; + + const composeSelector = arena.allocate(); + + arena.set(composeSelector, EnumToken.ComposesSelectorNodeType); + arena.setSpan(composeSelector, arena.getSourceId(tokens[0]), arena.getSpanStart(tokens[0]), arena.getSpanEnd(right?.[right.length - 1] ?? left[left.length - 1])); + + const leftSide = arena.allocate(); + + arena.set(leftSide, EnumToken.ListTokenType); + arena.setChildren(leftSide, ...left); + + arena.setLeft(composeSelector, leftSide); + + if (right != null) { + + const rightSide = arena.allocate(); + + arena.set(rightSide, EnumToken.ListTokenType); + arena.setChildren(rightSide, ...right!); + + arena.setRight(composeSelector, rightSide); + } + + tokens.length = 0; + tokens.push(composeSelector); + + // tokens = [ + // { + // typ: EnumToken.ComposesSelectorNodeType, + // l: left, + // r: right?.[0] ?? null, + // [LOCSRCID]: tokens[0][LOCSRCID], + // [LOCSTA]: tokens[0][LOCSTA], + // [LOCEND]: index != -1 ? right![right!.length - 1]?.[LOCEND] : left[left.length - 1][LOCEND], + // } as ComposesSelectorToken, + // ]; } - if (tokens[tokens.length - 1]?.[LOCEND] != null) { - name[LOCEND] = tokens[tokens.length - 1][LOCEND]; + if (tokens.length > 0) { + + arena.setSpanEnd(name, arena.getSpanEnd(tokens[tokens.length - 1])); + // name[LOCEND] = tokens[tokens.length - 1][LOCEND]; } - name[STATE] = success + arena.setName(name, arena.internString(arena.getValue(name))); + arena.set(name, EnumToken.DeclarationNodeType, 0); + arena.setFlags(name, EnumAstNodeStatus.HasValues | (success ? result == null ? EnumAstNodeStatus.Unvalidated : EnumAstNodeStatus.Validated - : EnumAstNodeStatus.ValidationFailed; - name[ERRORS] = result?.errors ?? []; - - // @ts-expect-error - return Object.assign(name, { - typ: EnumToken.DeclarationNodeType, - nam: name.val, - val: tokens, - }) as AstDeclaration; + : EnumAstNodeStatus.ValidationFailed)); + arena.setErrors(name, result?.errors ?? []); + arena.setChildren(name, ...tokens); + + return name; + + // name[STATE] = success + // ? result == null + // ? EnumAstNodeStatus.Unvalidated + // : EnumAstNodeStatus.Validated + // : EnumAstNodeStatus.ValidationFailed; + // name[ERRORS] = result?.errors ?? []; + + // return Object.assign(name, { + // typ: EnumToken.DeclarationNodeType, + // nam: name.val, + // val: tokens, + // }) as AstDeclaration; } diff --git a/src/lib/parser/utils/selector.ts b/src/lib/parser/utils/selector.ts index 73cb68564..d74ff6383 100644 --- a/src/lib/parser/utils/selector.ts +++ b/src/lib/parser/utils/selector.ts @@ -19,7 +19,7 @@ import type { Token, } from "../../../@types/index.d.ts"; import { EnumAstNodeStatus, EnumToken } from "../../ast/types.ts"; -import { renderValue } from "../../printer/render.ts"; +import { printValue } from "../../printer/print.ts"; import { combinators, ERRORS, @@ -39,6 +39,7 @@ import { createValidationContext, matchAllSyntaxes, matchSelectorSyntax, trimArr import { ValidationSyntaxGroupEnum, ValidationTokenEnum } from "../../validation/parser/typedef.ts"; import type { ValidationPropertyToken } from "../../validation/parser/types.d.ts"; import { splitTokenList } from "../../validation/utils/list.ts"; +import { Arena } from "../arena.ts"; import { trimWhiteSpace } from "../parse.ts"; import { equalsIgnoreCase } from "./text.ts"; @@ -52,11 +53,11 @@ export function parseSelector( options: ParserOptions, errors: ErrorDescription[], ): number { - const arena = options.arena!; + const arena = options.arena as Arena; const contextTyp = arena.getType(context!); let typ: EnumToken; - if (arena.getType(context) === EnumToken.KeyframesAtRuleNodeType) { + if (arena.getType(context as number) === EnumToken.KeyframesAtRuleNodeType) { const result = matchAllSyntaxes( getParsedSyntax(ValidationSyntaxGroupEnum.Syntaxes, "keyframe-selectors"), createValidationContext(tokens, options), @@ -127,7 +128,7 @@ export function parseSelector( typ: EnumToken.KeyframesRuleNodeType, sel: [ ...splitTokenList(trimArray(tokens, options), options).reduce((acc, curr: number[]) => { - acc.add(curr.reduce((acc, curr) => acc + renderValue(curr, { minify: false }), "")); + acc.add(curr.reduce((acc, curr) => acc + printValue(curr, { minify: false }), "")); return acc; }, new Set()), ].join(), @@ -326,7 +327,7 @@ export function parseSelector( } } - let t: string = renderValue(curr, { minify: false, arena }); + let t: string = printValue(curr, { minify: false, arena }); if (t == ",") { acc.push([]); @@ -390,7 +391,7 @@ export function parseSelector( // } // } - // let t: string = renderValue(curr, { minify: false }); + // let t: string = printValue(curr, { minify: false }); // if (t == ",") { // acc.push([]); @@ -468,7 +469,7 @@ export function parseSelector( stack.push(token); break; case EnumToken.AttrEndTokenType: - if (arena.getType(stack.at(-1)) == EnumToken.AttrStartTokenType) { + if (arena.getType(stack.at(-1) as number) == EnumToken.AttrStartTokenType) { index = tokens.indexOf(stack.at(-1)!); // const { val, ...attr } = stack.at(-1) as AttrStartToken; @@ -477,7 +478,7 @@ export function parseSelector( // attr[LOCEND] = token[LOCEND]; arena.setSpanEnd(tokens[index], arena.getSpanEnd(token)); - arena.setChildren(tokens[index], tokens.splice(index + 1, i - index - 1)); + arena.setChildren(tokens[index], ...tokens.splice(index + 1, i - index - 1) as number[]); tokens.splice(i, 1); // Object.assign(attr, { @@ -498,7 +499,7 @@ export function parseSelector( break; case EnumToken.EndParensTokenType: - if (stack.length > 0 && arena.getType(stack.at(-1)) == EnumToken.PseudoClassFunctionTokenDefType) { + if (stack.length > 0 && arena.getType(stack.at(-1) as number) == EnumToken.PseudoClassFunctionTokenDefType) { const func = stack.at(-1) as number; const funcType = arena.getType(func) as EnumToken; index = tokens.indexOf(func); @@ -509,7 +510,7 @@ export function parseSelector( if (tokensfuncDefMap.has(funcType)) { arena.setType(func, tokensfuncDefMap.get(funcType) as EnumToken); - arena.setChildren(func, trimArray(tokens.splice(index + 1, i - index - 1), options)); + arena.setChildren(func, ...trimArray(tokens.splice(index + 1, i - index - 1), options)); } if (result.success && options.minify) { @@ -524,11 +525,12 @@ export function parseSelector( val == ":nth-last-of-type" ) { const list: number[] = []; - let index: number = -1; - let child: number = arena.getFirstChild(func); + const children = arena.children(func); + let index: number = 0; + let child: number = 0; - while (child != 0) { - index++; + for (index = 0; index < children.length; index++) { + child = children[index]; typ = arena.getType(child); if (typ == EnumToken.CommentTokenType || typ == EnumToken.WhitespaceTokenType) { @@ -541,7 +543,6 @@ export function parseSelector( } list.push(child); - child = arena.getNextSibling(child); } if (list.length == 2) { @@ -829,7 +830,7 @@ export function parseSelector( } } - let t: string = renderValue(curr, { minify: false, arena }); + let t: string = printValue(curr, arena, { minify: false, arena }); if (t == ",") { acc.push([]); @@ -887,7 +888,7 @@ export function parseSelector( // } // } - // let t: string = renderValue(curr, { minify: false }); + // let t: string = printValue(curr, { minify: false }); // if (t == ",") { // acc.push([]); diff --git a/src/lib/printer/print.ts b/src/lib/printer/print.ts new file mode 100644 index 000000000..1cdafa4f0 --- /dev/null +++ b/src/lib/printer/print.ts @@ -0,0 +1,1958 @@ +import type { + AngleToken, + AstAtRule, + AstComment, + AstDeclaration, + AstNode, + AstRule, + AstStyleSheet, + AtRuleToken, + AttrToken, + BinaryExpressionToken, + ClassSelectorToken, + ColorToken, + CommentToken, + DashedIdentToken, + ErrorDescription, + FractionToken, + FunctionToken, + HashToken, + IdentListToken, + IdentToken, + IfConditionToken, + IfElseConditionToken, + InvalidAttrToken, + InvalidClassSelectorToken, + LengthToken, + ListToken, + LiteralToken, + MediaFeatureToken, + MediaQueryConditionToken, + MediaQueryUnaryFeatureToken, + MediaRangeQueryToken, + NumberToken, + PercentageToken, + PseudoElementToken, + PseudoPageToken, + RenderOptions, + SourceLocation, + StringToken, + SupportsQueryConditionToken, + SupportsQueryUnaryConditionToken, + Token, + WhenElseQueryConditionToken, + WhenElseUnaryConditionToken, + WrappedValuesToken, +} from "../../@types/index.d.ts"; +import { convertColor, EnumAstNodeStatus, expand, RenderResult, SourceMap } from "../../node.ts"; +import { ColorType, EnumToken } from "../ast/types.ts"; +import { Arena } from "../parser/arena.ts"; +import { LineMap } from "../parser/linesmap.ts"; +import { SourceFile } from "../parser/source.ts"; +import { toDegrees } from "../parser/utils/angle.ts"; +import { equalsIgnoreCase } from "../parser/utils/text.ts"; +import { getAngle } from "../syntax/color/color.ts"; +import { reduceHexValue } from "../syntax/color/hex.ts"; +import { anglePrecision, pseudoElements, tokensfuncSet, urlTokenMatcher } from "../syntax/constants.ts"; +import { + minifyNumber, + parseColor, + reduceColorStops, + reduceConicColorStops, + reducegradientBackgroundPosition, + toPrecisionAngle, + toPrecisionValue, +} from "../syntax/syntax.ts"; + +export function print(node: number, arena: Arena, options: RenderOptions = {}): RenderResult { + const minify: boolean = options.minify ?? true; + const beautify: boolean = options.beautify ?? !minify; + + options = { + ...(beautify + ? { + indent: " ", + newLine: "\n", + } + : { + indent: "", + newLine: "", + }), + ...(minify + ? { + removeEmpty: true, + removeComments: true, + + minify: true, + } + : { + removeEmpty: false, + removeComments: false, + }), + minifyValues: true, + sourcemap: false, + convertColor: true, + expandNestingRules: false, + preserveLicense: false, + ...options, + }; + + const startTime: number = performance.now(); + const errors: ErrorDescription[] = []; + const sourcemap: SourceMap | null = options.sourcemap ? new SourceMap() : null; + const sourcemaps: { sources: number[]; maps: Array<[number, number, number, number, number]> } | null = + options.sourcemap ? { sources: [], maps: [] } : null; + const cache: { + [key: string]: any; + } = Object.create(null); + + const sourceLocation = { + srcId: 0, + sta: 0, + end: 0, + } as SourceLocation; + const lineMap: LineMap | null = options.sourcemap ? new LineMap() : null; + + let code: string = ""; + if (options.output != null) { + // @ts-ignore + options.output = options.resolve(options.output, options.cwd).absolute; + } + + const result: RenderResult = { + code: + code + + printNode( + // options.expandNestingRules && + // [EnumToken.StyleSheetNodeType, EnumToken.AtRuleNodeType, EnumToken.RuleNodeType].includes( + // data.typ, + // ) && + // "chi" in data + // ? expand(data as AstStyleSheet | AstAtRule | AstRule) + // : + node, + arena, + options, + sourcemaps, + sourceLocation, + lineMap, + errors, + function reducer(acc: string, curr: number): string { + if (arena.getType(curr) == EnumToken.CommentTokenType && options.removeComments) { + const val = arena.getValue(curr); + + if (!options.preserveLicense || !val.startsWith("/*!")) { + return acc; + } + + return acc + val; + } + + return acc + printValue(curr, arena, options, cache, reducer, errors); + }, + cache, + ), + errors, + stats: { + total: `${(performance.now() - startTime).toFixed(2)}ms`, + }, + }; + + if (sourcemap != null) { + let source: SourceFile; + for (const sourceId of sourcemaps!.sources) { + source = options!.sourcesMap!.get(sourceId)! as SourceFile; + sourcemap.addSourceContent(source.id, source.getFileName(), source.getContent()); + } + + sourcemap.add(sourcemaps!.maps!); + result.map = sourcemap; + + if (options.sourcemap === "inline") { + result.code += `\n/*# sourceMappingURL=${result.map.toUrl()} */`; + } + } + + return result; +} + +function printNode( + data: number, + arena: Arena, + options: RenderOptions, + sourcemaps: { sources: number[]; maps: Array<[number, number, number, number, number]> } | null, + sourceLocation: SourceLocation, + lineMap: LineMap | null, + errors: ErrorDescription[], + reducer: (acc: string, curr: number) => string, + cache: { + [key: string]: any; + }, + level: number = 0, + indents: string[] = [], +): string { + if (indents.length < level + 1) { + indents.push((options.indent).repeat(level)); + } + + if (indents.length < level + 2) { + indents.push((options.indent).repeat(level + 1)); + } + + // @ts-ignore + let children: string = ""; + let str: string = ""; + let val: string; + + let typ: number = arena.getType(data); + const indent: string = indents[level]; + const indentSub: string = indents[level + 1]; + + switch (typ) { + case EnumToken.DeclarationNodeType: + return `${arena.getName(data)}:${options.indent}${(options.minifyValues + ? filterValues(arena.children(data), arena) + : arena.children(data) + ).reduce(reducer, "")}`; + + case EnumToken.CommentNodeType: + case EnumToken.CDOCOMMNodeType: + str = arena.getValue(data); + + if (str.startsWith("/*# sourceMappingURL=")) { + // ignore sourcemap comment + return ""; + } + + return !options.removeComments || (options.preserveLicense && str.startsWith("/*!")) ? str : ""; + + case EnumToken.StyleSheetNodeType: + for (const node of arena.children(data)) { + str = printNode( + node, + arena, + options, + sourcemaps, + sourceLocation, + lineMap, + errors, + reducer, + cache, + level, + indents, + ); + + if (str === "") { + continue; + } + + if (children.length > 0) { + str = options.newLine + str; + } + + children += str; + + if (sourcemaps != null && str !== "" && options.newLine) { + move(sourceLocation, lineMap!, options.newLine as string); + } + } + + return children; + + case EnumToken.AtRuleNodeType: + case EnumToken.RuleNodeType: + case EnumToken.KeyframesRuleNodeType: + case EnumToken.KeyframesAtRuleNodeType: + if ( + (typ == EnumToken.AtRuleNodeType || EnumToken.KeyframesAtRuleNodeType == typ) && + !arena.hasFlags(data, EnumAstNodeStatus.HasChildren) + ) { + str = arena.getValue(data); + return `${indent}@${arena.getName(data)}${str === "" ? "" : options.indent || " "}${str};`; + } + + str = arena.getValue(data); + const lineMapLength = lineMap ? lineMap.getLineStarts().length : 0; + const prelude = + (indent.length > 0 ? options.newLine : "") + + indent + + (typ === EnumToken.AtRuleNodeType || typ === EnumToken.KeyframesAtRuleNodeType + ? `@${arena.getName(data)}${str === "" ? "" : options.indent || " "}${str}${options.indent}{` + : str + `${options.indent}{`); + + if (sourcemaps != null) { + updateSourceMap(data, options, cache, sourcemaps, sourceLocation, lineMap!, prelude); + } + + let node: number; + let recordDeclarationSourceMap: boolean = typ == EnumToken.AtRuleNodeType; + const kids = arena.children(data); + + for (let i = 0; i < kids.length; i++) { + node = kids[i]; + typ = arena.getType(node); + if (typ == EnumToken.CommentNodeType) { + val = arena.getValue(node); + str = options.removeComments && (!options.preserveLicense || !val.startsWith("/*!")) ? "" : val; + } else if (typ == EnumToken.DeclarationNodeType) { + str = `${arena.getName(node)}:${options.indent}${(options.minifyValues + ? filterValues(arena.children(node), arena) + : arena.children(node) + ) + .reduce(reducer, "") + .trimEnd()};`; + } else { + str = printNode( + node, + arena, + options, + sourcemaps, + sourceLocation, + lineMap, + errors, + reducer, + cache, + level + 1, + indents, + ); + + if (str === "") { + continue; + } + + children += str; + str = ""; + continue; + } + + if (str === "") { + continue; + } + + str = options.newLine + indentSub + str; + children += str; + + if (sourcemaps != null && str !== "") { + if (arena.getType(node) == EnumToken.DeclarationNodeType && recordDeclarationSourceMap) { + // if declaration is child of at-rule, then record it + // .rule { + // @media screen { + // color: red; + // } + // } + // @ts-ignore + updateSourceMap(node, options, cache, sourcemaps, sourceLocation, lineMap!, str); + } else { + move(sourceLocation, lineMap!, str); + } + } + } + + if (children.endsWith(";")) { + children = children.slice(0, -1); + sourceLocation.end--; + } + + if (options.removeEmpty && children === "") { + if (sourcemaps != null) { + sourceLocation.end -= prelude.length; + lineMap!.getLineStarts().length = lineMapLength; + } + return ""; + } + + const end: string = options.newLine + indent + `}`; + + if (sourcemaps != null) { + move(sourceLocation, lineMap!, end); + } + + return prelude + children + end; + + default: + return ""; + } +} + +/** + * render ast token + * @param token + * @param options + * @param cache + * @param reducer + * @param errors + * @private + */ +export function printValue( + token: number, + arena: Arena, + options: RenderOptions, + cache: { + [key: string]: any; + } = Object.create(null), + reducer?: null | ((acc: string, curr: number) => string), + errors?: ErrorDescription[], +): string { + const typ = arena.getType(token); + + if (typ === EnumToken.WhenElseFunctionTokenType && arena.equalsIgnoreCase(token, "val", "supports")) { + options = { ...options, minifyValues: false, minify: false, convertColor: false }; + reducer = null; + } + + options.minifyValues ??= options.minify; + + if (reducer == null) { + reducer = function (acc: string, curr: number): string { + if (arena.getType(curr) == EnumToken.CommentTokenType && options.removeComments) { + if (!options.preserveLicense || !arena.startsWith(curr, "val", "/*!")) { + return acc; + } + + return acc + arena.getValue(curr); + } + + return acc + printValue(curr, arena, options, cache, reducer, errors); + }; + } + + switch (typ) { + case EnumToken.UrlFunctionTokenDefType: + case EnumToken.FunctionTokenDefType: + case EnumToken.MathFunctionTokenDefType: + case EnumToken.ImageFunctionTokenDefType: + case EnumToken.ColorFunctionTokenDefType: + case EnumToken.TimingFunctionTokenDefType: + case EnumToken.WhenElseFunctionTokenDefType: + case EnumToken.SupportsFunctionTokenDefType: + case EnumToken.TimelineFunctionTokenDefType: + case EnumToken.GridTemplateFuncTokenDefType: + case EnumToken.TransformFunctionTokenDefType: + case EnumToken.ContainerFunctionTokenDefType: + case EnumToken.PseudoClassFunctionTokenDefType: + case EnumToken.GeneralEnclosedFunctionTokenDefType: + case EnumToken.CustomFunctionTokenDefType: + case EnumToken.WildCardFunctionTokenDefType: + return arena.getValue(token) + "("; + + case EnumToken.ListTokenType: + return arena + .children(token) + .reduce((acc: string, curr: number) => acc + printValue(curr, arena, options, cache), ""); + + case EnumToken.BinaryExpressionTokenType: + if ( + EnumToken.Mul === (token as BinaryExpressionToken).op || + EnumToken.Div === (token as BinaryExpressionToken).op + ) { + let result: string = ""; + + if ( + (token as BinaryExpressionToken).l.typ == EnumToken.BinaryExpressionTokenType && + [EnumToken.Add, EnumToken.Sub].includes( + ((token as BinaryExpressionToken).l as BinaryExpressionToken).op, + ) + ) { + result = "(" + printValue((token as BinaryExpressionToken).l, arena, options, cache) + ")"; + } else { + result = printValue((token as BinaryExpressionToken).l, options, cache); + } + + result += (token as BinaryExpressionToken).op == EnumToken.Mul ? "*" : "/"; + + if ( + (token as BinaryExpressionToken).r.typ == EnumToken.BinaryExpressionTokenType && + [EnumToken.Add, EnumToken.Sub].includes( + ((token as BinaryExpressionToken).r as BinaryExpressionToken).op, + ) + ) { + result += "(" + printValue((token as BinaryExpressionToken).r, options, cache) + ")"; + } else { + result += printValue((token as BinaryExpressionToken).r, options, cache); + } + + return result; + } + + return ( + printValue((token as BinaryExpressionToken).l, options, cache) + + ((token as BinaryExpressionToken).op == EnumToken.Add ? " + " : " - ") + + printValue((token as BinaryExpressionToken).r, options, cache) + ); + + case EnumToken.FractionTokenType: + const fraction: string = + printValue((token as FractionToken).l) + "/" + printValue((token as FractionToken).r); + + if (+(token as FractionToken).r.val != 0) { + const value: string = minifyNumber(+(token as FractionToken).l.val / +(token as FractionToken).r.val); + + if (value.length <= fraction.length) { + return value; + } + } + + return fraction; + + case EnumToken.Add: + return " + "; + + case EnumToken.Sub: + return " - "; + + case EnumToken.UniversalSelectorTokenType: + case EnumToken.Star: + case EnumToken.Mul: + return "*"; + + case EnumToken.Div: + return "/"; + + case EnumToken.WrappedValuesTokenType: + return ( + "{" + + (token as WrappedValuesToken).chi.reduce( + (acc: string, curr: Token) => acc + printValue(curr, options, cache), + "", + ) + + "}" + ); + + case EnumToken.ColorTokenType: + if ( + (token as ColorToken).kin == ColorType.LIGHT_DARK || + ("chi" in token && options.convertColor === false && ((token as ColorToken).chi as Token[]).length == 2) + ) { + return ( + (token as ColorToken).val + + "(" + + ((token as ColorToken).chi as Token[]).reduce((acc: string, curr: Token) => { + if (curr.typ === EnumToken.IdenTokenType) { + parseColor(curr as IdentToken); + } + + return acc + printValue(curr, options, cache); + }, "") + + ")" + ); + } + + if (options.convertColor !== false) { + const value: ColorToken | null = convertColor( + token as ColorToken, + typeof options.convertColor === "boolean" + ? ColorType.HEX + : (ColorType[ + (ColorType[options.convertColor ?? "HEX"] as string) + ?.toUpperCase?.() + .replaceAll?.("-", "_") as keyof typeof ColorType + ] ?? ColorType.HEX), + ); + + if (value != null) { + token = value; + } + + if ((token as ColorToken).kin == ColorType.HEX || (token as ColorToken).kin == ColorType.LIT) { + return reduceHexValue((token as ColorToken).val); + } + } + + if ( + (token as ColorToken).kin === ColorType.SYS || + (token as ColorToken).kin === ColorType.DPSYS || + (token as ColorToken).kin === ColorType.NON_STD + ) { + return (token as ColorToken).val; + } + + if ((token as ColorToken).kin == ColorType.HEX || (token as ColorToken).kin == ColorType.LIT) { + return (token as ColorToken).val; + } + + if (Array.isArray((token as ColorToken).chi)) { + const fnName: string = (token as ColorToken).val; + const isLegacy: boolean = ["rgb", "rgba", "hsl", "hsla"].includes((token as ColorToken).val); + const hasAlpha: boolean = [ + "rgb", + "rgba", + "hsl", + "hsla", + "hwb", + "oklab", + "oklch", + "lab", + "lch", + ].includes(fnName); + const useAlpha: boolean = + (hasAlpha && (token as ColorToken).chi!.length == (isLegacy ? 7 : 4)) || + ("color" == (token as ColorToken).val && (token as ColorToken).chi!.length == 5); + + return ( + (hasAlpha && (token as ColorToken).val.endsWith("a") ? fnName.slice(0, -1) : fnName) + + "(" + + (token as ColorToken) + .chi!.reduce((acc: string, curr: Token, index: number, array: Token[]) => { + if (/[,/]\s*$/.test(acc)) { + if (curr.typ == EnumToken.WhitespaceTokenType) { + return acc.trimEnd(); + } + + return acc.trimStart() + printValue(curr, options, cache); + } + + if (isLegacy && curr.typ == EnumToken.CommaTokenType) { + return acc.trimEnd() + " "; + } + + if (curr.typ == EnumToken.WhitespaceTokenType) { + return acc.trimEnd() + " "; + } + + if ( + curr.typ == EnumToken.CommaTokenType || + (curr.typ == EnumToken.LiteralTokenType && (curr as LiteralToken).val == "/") + ) { + return acc.trimEnd() + (curr.typ == EnumToken.CommaTokenType ? "," : "/"); + } + + return ( + acc.trimEnd() + + (useAlpha && index == array.length - 1 ? "/" : " ") + + printValue(curr, options, cache) + ); + }, "") + .trimStart() + + ")" + ); + } + + case EnumToken.UrlFunctionTokenType: + if (options.minifyValues && token.typ === EnumToken.UrlFunctionTokenType) { + if ((token as FunctionToken).chi[0]?.typ === EnumToken.BadUrlTokenType) { + return "url()"; + } + + for (const child of (token as FunctionToken).chi) { + if (child.typ === EnumToken.StringTokenType) { + if ( + (child as StringToken).val.slice(1, 5) !== "data:" && + urlTokenMatcher.test((child as StringToken).val) + ) { + Object.assign(child, { + typ: EnumToken.UrlTokenTokenType, + val: (child as StringToken).val.slice(1, -1), + }); + } + + break; + } + } + } + + case EnumToken.ParensTokenType: + case EnumToken.FunctionTokenType: + case EnumToken.MathFunctionTokenType: + case EnumToken.ImageFunctionTokenType: + if (options.minifyValues && token.typ === EnumToken.ImageFunctionTokenType) { + const slice = (token as FunctionToken).chi.slice() as Token[]; + + switch ((token as FunctionToken).val) { + case "linear-gradient": + case "repeating-linear-gradient": + { + let i: number; + let k: number; + let j: number; + let key: string; + + for (i = 0; i < slice.length; i++) { + if ( + slice[i].typ == EnumToken.ColorTokenType || + slice[i].typ === EnumToken.CommaTokenType + ) { + break; + } + + if ( + slice[i].typ == EnumToken.IdenTokenType && + equalsIgnoreCase((slice[i] as IdentToken).val, "to") + ) { + k = i + 1; + while ( + k < slice.length && + (slice[k].typ === EnumToken.WhitespaceTokenType || + slice[k].typ === EnumToken.CommentTokenType) + ) { + k++; + } + + if (k < slice.length && slice[k].typ === EnumToken.IdenTokenType) { + // convert keyword to angle + key = (slice[k] as IdentToken).val.toLowerCase(); + switch (key) { + case "right": + slice.splice(i, k - i + 1, { + typ: EnumToken.AngleTokenType, + val: 90, + } as AngleToken); + break; + case "left": + slice.splice(i, k - i + 1, { + typ: EnumToken.AngleTokenType, + val: 270, + } as AngleToken); + break; + case "top": + case "bottom": + j = k + 1; + + while ( + j < slice.length && + (slice[j].typ === EnumToken.WhitespaceTokenType || + slice[j].typ === EnumToken.CommentTokenType) + ) { + j++; + } + + if (j < slice.length && slice[j].typ !== EnumToken.CommaTokenType) { + i = j - 1; + break; + } + + // https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/gradient/linear-gradient#:~:text=If%20unspecified%2C%20it%20defaults%20to%20to%20bottom + if (key === "bottom") { + // to bottom -> 180deg is the default angle and will be removed + while ( + k < slice.length && + slice[k].typ != EnumToken.CommaTokenType + ) { + k++; + } + + slice.splice(i, k - i + 1); + i--; + continue; + } + + slice.splice(i, k - i + 1, { + typ: EnumToken.AngleTokenType, + val: key === "top" ? 0 : 180, + } as AngleToken); + i--; + break; + } + } + } else if (slice[i].typ == EnumToken.AngleTokenType) { + if ((180 - (toDegrees(slice[i] as AngleToken).val as number)) % 360 === 0) { + k = i + 1; + + // while (k < slice.length && slice[k].typ !== EnumToken.CommaTokenType) { + // k++; + // } + + slice.splice(i, k - i + 1); + } + } + } + + if (slice[i]?.typ === EnumToken.CommaTokenType) { + i++; + // while ( + // i < slice.length && + // (slice[i].typ === EnumToken.WhitespaceTokenType || + // slice[i].typ === EnumToken.CommentTokenType) + // ) { + // i++; + // } + } + + if (slice[i]?.typ === EnumToken.ColorTokenType) { + for (const token of reduceColorStops(slice.splice(i, slice.length - i))) { + slice.push(token); + } + } + } + + break; + + case "radial-gradient": + case "repeating-radial-gradient": + { + let i: number = 0; + + // while ( + // i < slice.length && + // (slice[i].typ === EnumToken.WhitespaceTokenType || + // slice[i].typ === EnumToken.CommentTokenType) + // ) { + // i++; + // } + + const positions: Token[] = []; + const form: Token[] = []; + const size: Token[] = []; + const colorSpaceDef: Token[] = []; + + if (slice[i]?.typ === EnumToken.IdenTokenType) { + if ( + equalsIgnoreCase((slice[i] as IdentToken).val, "farthest-corner") || + equalsIgnoreCase((slice[i] as IdentToken).val, "ellipse") + ) { + i++; + } else if ( + equalsIgnoreCase((slice[i] as IdentToken).val, "closest-side") || + equalsIgnoreCase((slice[i] as IdentToken).val, "closest-corner") || + equalsIgnoreCase((slice[i] as IdentToken).val, "farthest-side") + ) { + size.push(slice[i++]); + } else if (equalsIgnoreCase((slice[i] as IdentToken).val, "circle")) { + form.push(slice[i++]); + } + + while ( + slice[i]?.typ === EnumToken.WhitespaceTokenType || + slice[i]?.typ === EnumToken.CommentTokenType + ) { + i++; + } + } + + if (slice[i]?.typ === EnumToken.IdenTokenType) { + if ( + equalsIgnoreCase((slice[i] as IdentToken).val, "farthest-corner") || + equalsIgnoreCase((slice[i] as IdentToken).val, "ellipse") + ) { + i++; + } else if ( + equalsIgnoreCase((slice[i] as IdentToken).val, "closest-side") || + equalsIgnoreCase((slice[i] as IdentToken).val, "closest-corner") || + equalsIgnoreCase((slice[i] as IdentToken).val, "farthest-side") + ) { + size.push(slice[i++]); + } else if (equalsIgnoreCase((slice[i] as IdentToken).val, "circle")) { + form.push(slice[i++]); + } + + while ( + slice[i]?.typ === EnumToken.WhitespaceTokenType || + slice[i]?.typ === EnumToken.CommentTokenType + ) { + i++; + } + } + + if ( + slice[i]?.typ === EnumToken.IdenTokenType && + equalsIgnoreCase((slice[i] as IdentToken).val, "at") + ) { + i++; + } + + while ( + slice[i]?.typ === EnumToken.WhitespaceTokenType || + slice[i]?.typ === EnumToken.CommentTokenType + ) { + i++; + } + + if ( + slice[i]?.typ === EnumToken.PercentageTokenType || + (slice[i]?.typ === EnumToken.NumberTokenType && 0 === (slice[i] as NumberToken).val) || + (slice[i]?.typ === EnumToken.IdenTokenType && + (equalsIgnoreCase((slice[i] as IdentToken).val, "center") || + equalsIgnoreCase((slice[i] as IdentToken).val, "top") || + equalsIgnoreCase((slice[i] as IdentToken).val, "bottom") || + equalsIgnoreCase((slice[i] as IdentToken).val, "left") || + equalsIgnoreCase((slice[i] as IdentToken).val, "right"))) + ) { + do { + if ( + slice[i]?.typ === EnumToken.IdenTokenType && + "in" === (slice[i] as IdentToken).val + ) { + break; + } + + positions.push(slice[i]); + i++; + } while (slice[i]?.typ !== EnumToken.CommaTokenType); + } + + while (slice[i]?.typ !== EnumToken.CommaTokenType) { + colorSpaceDef.push(slice[i++]); + } + + if (slice[i]?.typ === EnumToken.CommaTokenType) { + i++; + } + + if (positions.length > 0) { + let k: number; + let position1: string = ""; + let position2: string = ""; + + for (k = 0; k < positions.length; k++) { + if (positions[k].typ === EnumToken.IdenTokenType) { + if (position1.length === 0) { + position1 = (positions[k] as IdentToken).val; + } else { + position2 = (positions[k] as IdentToken).val; + } + } else if ( + positions[k].typ === EnumToken.PercentageTokenType || + positions[k].typ === EnumToken.NumberTokenType + ) { + if (position1.length === 0) { + position1 = (positions[k] as PercentageToken | NumberToken).val + "%"; + } else { + position2 = (positions[k] as PercentageToken | NumberToken).val + "%"; + } + } + + if (position2.length > 0) { + break; + } + } + + if (position1.length > 0) { + let key = `${position1} ${position2}`.trim(); + + if ( + key === "50% 50%" || + key === "center center" || + key === "50%" || + key === "center" + ) { + positions.length = 0; + } else if ( + key === "0% 0%" || + key === "0 0" || + key === "top left" || + key === "left top" + ) { + positions.splice(0, positions.length, { + typ: EnumToken.PercentageTokenType, + val: 0, + }); + } else if ( + key === "50% 0%" || + key === "50% 0" || + key === "center top" || + key === "top center" + ) { + positions.splice(0, positions.length, { + typ: EnumToken.IdenTokenType, + val: "top", + }); + } else if (key === "top right" || key === "right top") { + positions.splice( + 0, + positions.length, + { + typ: EnumToken.PercentageTokenType, + val: 100, + }, + { typ: EnumToken.WhitespaceTokenType }, + { typ: EnumToken.PercentageTokenType, val: 0 }, + ); + } else if ( + key === "0 50%" || + key === "0% 50%" || + key === "left center" || + key === "center left" + ) { + positions.splice(0, positions.length, { + typ: EnumToken.IdenTokenType, + val: "left", + }); + } else if (key === "100% 50%" || key === "right center" || key === "center right") { + positions.splice(0, positions.length, { + typ: EnumToken.IdenTokenType, + val: "right", + }); + } else if ( + key === "0 100%" || + key === "0% 100%" || + key === "left bottom" || + key === "bottom left" + ) { + positions.splice(0, positions.length, { + typ: EnumToken.IdenTokenType, + val: "bottom", + }); + } else if ( + key === "50% 100%" || + key === "center bottom" || + key === "bottom center" + ) { + positions.splice(0, positions.length, { + typ: EnumToken.IdenTokenType, + val: "bottom", + }); + } else if ( + key === "100% 100%" || + key === "right bottom" || + key === "bottom right" + ) { + positions.splice(0, positions.length, { + typ: EnumToken.PercentageTokenType, + val: 100, + }); + } else if (position1 === position2) { + positions.splice(0, positions.length, positions[k]); + } + } + } + + const result: Token[] = []; + + if (form.length > 0) { + for (const token of form) { + result.push(token); + } + } + + if (size.length > 0) { + if (result.length > 0) { + result.push({ typ: EnumToken.WhitespaceTokenType }); + } + + for (const token of size) { + result.push(token); + } + } + + if (positions.length > 0) { + if (result.length > 0) { + result.push({ typ: EnumToken.WhitespaceTokenType }); + } + + result.push( + { typ: EnumToken.IdenTokenType, val: "at" }, + { typ: EnumToken.WhitespaceTokenType }, + ); + + for (const token of positions) { + result.push(token); + } + } + + if (colorSpaceDef.length > 0) { + if (result.length > 0) { + result.push({ typ: EnumToken.WhitespaceTokenType }); + } + + for (const token of colorSpaceDef) { + result.push(token); + } + } + + if (result.length > 0) { + result.push({ typ: EnumToken.CommaTokenType }); + } + + for (const token of reduceColorStops(slice.slice(i))) { + result.push(token); + } + + slice.length = 0; + + for (const token of result) { + slice.push(token); + } + } + break; + + case "conic-gradient": + case "repeating-conic-gradient": + { + let i: number = 0; + + const angles: Token[] = []; + const positions: Token[] = []; + const colorSpaceDef: Token[] = []; + + // while ( + // i < slice.length && + // (slice[i].typ === EnumToken.WhitespaceTokenType || + // slice[i].typ === EnumToken.CommentTokenType) + // ) { + // i++; + // } + + if ( + slice[i]?.typ === EnumToken.IdenTokenType && + equalsIgnoreCase((slice[i] as IdentToken).val, "from") + ) { + angles.push(slice[i++]); + + while ( + i < slice.length && + (slice[i].typ === EnumToken.WhitespaceTokenType || + slice[i].typ === EnumToken.CommentTokenType) + ) { + angles.push(slice[i++]); + } + + if ( + (slice[i]?.typ === EnumToken.NumberTokenType || + slice[i]?.typ === EnumToken.AngleTokenType) && + 0 === toDegrees(slice[i] as AngleToken).val + ) { + angles.length = 0; + i++; + } else if ( + slice[i]?.typ !== EnumToken.CommaTokenType && + slice[i].typ != EnumToken.IdenTokenType + ) { + angles.push(slice[i++]); + } + + while ( + i < slice.length && + (slice[i].typ === EnumToken.WhitespaceTokenType || + slice[i].typ === EnumToken.CommentTokenType) + ) { + i++; + } + } + + if ( + slice[i]?.typ === EnumToken.IdenTokenType && + equalsIgnoreCase((slice[i] as IdentToken).val, "at") + ) { + i++; + + while ( + i < slice.length && + (slice[i].typ === EnumToken.WhitespaceTokenType || + slice[i].typ === EnumToken.CommentTokenType) + ) { + i++; + } + + let position1: string = ""; + let position2: string = ""; + + if ( + slice[i]?.typ === EnumToken.IdenTokenType && + !equalsIgnoreCase("in", (slice[i] as IdentToken).val) + ) { + position1 = (slice[i] as IdentToken).val; + positions.push(slice[i++]); + } else if ( + slice[i]?.typ === EnumToken.PercentageTokenType || + slice[i]?.typ === EnumToken.NumberTokenType + ) { + position1 = (slice[i] as IdentToken).val + "%"; + positions.push(slice[i++]); + } + + while ( + i < slice.length && + (slice[i].typ === EnumToken.WhitespaceTokenType || + slice[i].typ === EnumToken.CommentTokenType) + ) { + positions.push(slice[i++]); + } + + if ( + slice[i]?.typ === EnumToken.IdenTokenType && + !equalsIgnoreCase("in", (slice[i] as IdentToken).val) + ) { + position2 = (slice[i] as IdentToken).val; + positions.push(slice[i++]); + } else if ( + slice[i]?.typ === EnumToken.PercentageTokenType || + slice[i]?.typ === EnumToken.NumberTokenType + ) { + position2 = (slice[i] as IdentToken).val + "%"; + positions.push(slice[i++]); + } + + while ( + i < slice.length && + (slice[i].typ === EnumToken.WhitespaceTokenType || + slice[i].typ === EnumToken.CommentTokenType) + ) { + i++; + } + + if (position1.length > 0) { + reducegradientBackgroundPosition(positions, `${position1} ${position2}`.trim()); + } + } + + while ( + i < slice.length && + (slice[i].typ === EnumToken.WhitespaceTokenType || + slice[i].typ === EnumToken.CommentTokenType) + ) { + i++; + } + + if ( + slice[i]?.typ === EnumToken.IdenTokenType && + equalsIgnoreCase("in", (slice[i] as IdentToken).val) + ) { + while (i < slice.length && slice[i].typ !== EnumToken.CommaTokenType) { + colorSpaceDef.push(slice[i++]); + } + } + + if (slice[i]?.typ === EnumToken.CommaTokenType) { + i++; + } + + const result: Token[] = []; + + if (positions.length > 0) { + if (positions.length > 0) { + if (angles.length > 0) { + angles.push({ typ: EnumToken.WhitespaceTokenType }); + } + + angles.push( + { typ: EnumToken.IdenTokenType, val: "at" }, + { typ: EnumToken.WhitespaceTokenType }, + ); + + for (const position of positions) { + angles.push(position); + } + } + } + + if (angles.length > 0) { + for (const angle of angles) { + result.push(angle); + } + result.push({ typ: EnumToken.CommaTokenType }); + } + + if (colorSpaceDef.length > 0) { + if (colorSpaceDef.length > 0) { + if (result.length > 0) { + result.push({ typ: EnumToken.WhitespaceTokenType }); + } + + for (const token of colorSpaceDef) { + result.push(token); + } + } + + result.push({ typ: EnumToken.CommaTokenType }); + } + + for (const token of reduceConicColorStops(slice.slice(i))) { + result.push(token); + } + + slice.length = 0; + + for (let j = 0; j < result.length; j++) { + slice.push(result[j]); + } + } + break; + } + + return (token as FunctionToken).val + "(" + slice.reduce(reducer, "") + ")"; + } + + case EnumToken.TimingFunctionTokenType: + case EnumToken.PseudoClassFuncTokenType: + case EnumToken.WhenElseFunctionTokenType: + case EnumToken.TimelineFunctionTokenType: + case EnumToken.GridTemplateFuncTokenType: + case EnumToken.SupportsFunctionTokenType: + case EnumToken.ContainerFunctionTokenType: + case EnumToken.TransformFunctionTokenType: + case EnumToken.GeneralEnclosedFunctionTokenType: + case EnumToken.CustomFunctionTokenType: + case EnumToken.WildCardFunctionTokenType: + if ( + token.typ == EnumToken.MathFunctionTokenType && + (token as FunctionToken).chi.length == 1 && + ![EnumToken.BinaryExpressionTokenType, EnumToken.FractionTokenType, EnumToken.IdenTokenType].includes( + (token as FunctionToken).chi[0].typ, + ) && + // @ts-ignore + (((((token as FunctionToken).chi[0]) as NumberToken).val) as FractionToken) + ?.typ != EnumToken.FractionTokenType + ) { + return ( + (token as FunctionToken).val + + "(" + + (token as FunctionToken).chi.reduce( + (acc: string, curr: Token) => + acc + + printValue( + curr, + token.typ == EnumToken.FunctionTokenType ? { minify: false } : options, + cache, + reducer, + ), + "", + ) + + ")" + ); + } + + return ((token as FunctionToken).val ?? "") + "(" + (token as FunctionToken).chi.reduce(reducer, "") + ")"; + + case EnumToken.BlockStartTokenType: + return "{"; + + case EnumToken.BlockEndTokenType: + return "}"; + + case EnumToken.StartParensTokenType: + return "("; + + case EnumToken.DelimTokenType: + case EnumToken.EqualMatchTokenType: + return "="; + + case EnumToken.IncludeMatchTokenType: + return "~="; + + case EnumToken.DashMatchTokenType: + return "|="; + + case EnumToken.StartMatchTokenType: + return "^="; + + case EnumToken.EndMatchTokenType: + return "$="; + + case EnumToken.ContainMatchTokenType: + return "*="; + + case EnumToken.LtTokenType: + return "<"; + + case EnumToken.LteTokenType: + return "<="; + + case EnumToken.Tilda: + case EnumToken.SubsequentSiblingCombinatorTokenType: + return "~"; + + case EnumToken.Plus: + case EnumToken.NextSiblingCombinatorTokenType: + return "+"; + + case EnumToken.GtTokenType: + case EnumToken.ChildCombinatorTokenType: + return ">"; + + case EnumToken.GteTokenType: + return ">="; + + case EnumToken.ColumnCombinatorTokenType: + return "||"; + + case EnumToken.EndParensTokenType: + return ")"; + + case EnumToken.AttrStartTokenType: + return "["; + + case EnumToken.AttrEndTokenType: + return "]"; + + case EnumToken.DescendantCombinatorTokenType: + case EnumToken.WhitespaceTokenType: + return " "; + + case EnumToken.ColonTokenType: + return ":"; + + case EnumToken.DoubleColonTokenType: + return "::"; + + case EnumToken.SemiColonTokenType: + return ";"; + + case EnumToken.CommaTokenType: + return ","; + + case EnumToken.ImportantTokenType: + return "!important"; + + case EnumToken.Pipe: + return "|"; + case EnumToken.AttrTokenType: + case EnumToken.IdenListTokenType: + return "[" + ((token as AttrToken | IdentListToken)).chi.reduce(reducer, "") + "]"; + + case EnumToken.TimeTokenType: + case EnumToken.AngleTokenType: + case EnumToken.LengthTokenType: + case EnumToken.DimensionTokenType: + case EnumToken.FrequencyTokenType: + case EnumToken.ResolutionTokenType: + let val: string = + ((token as LengthToken).val).typ == EnumToken.FractionTokenType + ? printValue((token as LengthToken).val, options, cache) + : minifyNumber((token as LengthToken).val); + let unit: string = (token as LengthToken).unit; + + if (token.typ == EnumToken.AngleTokenType && !val.includes("/")) { + const angle: number = getAngle(token); + + let v: string; + let value: string = val + unit; + + for (const u of ["deg", "turn", "rad", "grad"]) { + if ((token as AngleToken).unit == u) { + continue; + } + + switch (u) { + case "deg": + v = minifyNumber( + toPrecisionAngle(angle * 360, anglePrecision, false).toFixed(anglePrecision), + ); + + if (v.length + 3 < value.length) { + val = v; + unit = u; + value = v + u; + } + + break; + + case "turn": + v = minifyNumber(toPrecisionAngle(angle, anglePrecision, false).toFixed(anglePrecision)); + + if (v.length + 4 < value.length) { + val = v; + unit = u; + value = v + u; + } + + break; + + case "rad": + v = minifyNumber( + toPrecisionAngle(angle * (2 * Math.PI), anglePrecision, false).toFixed(anglePrecision), + ); + + if (v.length + 3 < value.length) { + val = v; + unit = u; + value = v + u; + } + + break; + + case "grad": + v = minifyNumber( + toPrecisionAngle(angle * 400, anglePrecision, false).toFixed(anglePrecision), + ); + + if (v.length + 4 < value.length) { + val = v; + unit = u; + value = v + u; + } + + break; + } + } + } + + if (val === "0") { + if (token.typ == EnumToken.TimeTokenType) { + return "0s"; + } + + if (token.typ == EnumToken.FrequencyTokenType) { + return "0Hz"; + } + + // @ts-ignore + if (token.typ == EnumToken.ResolutionTokenType) { + return "0x"; + } + + return "0"; + } + + if (token.typ == EnumToken.TimeTokenType) { + if (unit == "ms") { + // @ts-ignore + const v: string = minifyNumber(val / 1000); + + if (v.length + 1 <= val.length) { + return v + "s"; + } + + return val + "ms"; + } + + return val + "s"; + } + + if (token.typ == EnumToken.ResolutionTokenType && unit == "dppx") { + unit = "x"; + } + + return val.includes("/") ? val.replace("/", unit + "/") : minifyNumber(toPrecisionValue(val)) + unit; + + case EnumToken.FlexTokenType: + case EnumToken.PercentageTokenType: + const uni: string = token.typ == EnumToken.PercentageTokenType ? "%" : "fr"; + const perc: string = arena.hasFlags(token, EnumAstNodeStatus.HasFractionValue) + ? printValue(arena.getNumericValue(token), arena, options, cache) + : minifyNumber(arena.getNumericValue(token)); + + return options.minifyValues && perc == "0" + ? "0" + : perc.includes("/") + ? perc.replace("/", uni + "/") + : perc + uni; + + case EnumToken.NumberTokenType: + return arena.hasFlags(token, EnumAstNodeStatus.HasFractionValue) + ? printValue(arena.getNumericValue(token), arena, options, cache) + : minifyNumber(arena.getNumericValue(token)); + + case EnumToken.InfinityTokenType: + return "1/0"; + case EnumToken.NegativeInfinityTokenType: + return "-1/0"; + + case EnumToken.NaNTokenType: + return "NaN"; + + case EnumToken.AtRuleTokenType: + return "@" + arena.getName(token); + + case EnumToken.CommentTokenType: + case EnumToken.CDOCOMMNodeType: + if (options.removeComments && (!options.preserveLicense || !arena.startsWith(token, "val", "/*!"))) { + return ""; + } + + case EnumToken.PseudoClassTokenType: + case EnumToken.PseudoElementTokenType: + // https://www.w3.org/TR/selectors-4/#single-colon-pseudos + if (typ == EnumToken.PseudoElementTokenType) { + let val = arena.getValue(token); + let slice = val.slice(1); + + if (pseudoElements.includes(slice)) { + return slice; + } + } + + case EnumToken.UrlTokenTokenType: + + case EnumToken.HashTokenType: + case EnumToken.IdenTokenType: + case EnumToken.StringTokenType: + case EnumToken.LiteralTokenType: + case EnumToken.DashedIdenTokenType: + case EnumToken.PseudoPageTokenType: + case EnumToken.ClassSelectorTokenType: + return arena.getValue( + token + ); + + case EnumToken.NestingSelectorTokenType: + return "&"; + + case EnumToken.InvalidAttrTokenType: + return ( + "[" + + arena.children(token).reduce( + (acc: string, curr: number): string => acc + printValue(curr, arena, options, cache), + "", + ) + ); + + case EnumToken.InvalidClassSelectorTokenType: + return arena.getValue(token ); + + case EnumToken.SupportsQueryUnaryConditionTokenType: + case EnumToken.WhenElseUnaryConditionTokenType: + return ( + printValue( + arena.getLeft(token ) as number, + arena, + options, + cache, + reducer, + errors, + ) + + " " + + + printValue( + arena.getRight(token ) as number, + arena, + options, + cache, + reducer, + errors, + ) + ); + + case EnumToken.SupportsQueryConditionTokenType: + case EnumToken.WhenElseQueryConditionTokenType: + return ( + + printValue( + arena.getLeft(token ) as number, + arena, + options, + cache, + reducer, + errors, + ) + + " " + + + printValue( + arena.getOp(token ) as number, + arena, + options, + cache, + reducer, + errors, + ) + + " " + + + printValue( + arena.getRight(token ) as number, + arena, + options, + cache, + reducer, + errors, + ) + ); + + case EnumToken.IfConditionTokenType: + return (token as IfConditionToken).l.length == 0 + ? "" + : (token as SupportsQueryConditionToken | WhenElseQueryConditionToken).l.reduce( + (acc, curr) => acc + printValue(curr, options, cache, reducer, errors), + "", + ) + + ":" + + (options.indent ?? "") + + (token as SupportsQueryConditionToken).r.reduce( + (acc, curr) => acc + printValue(curr, options, cache, reducer, errors), + "", + ); + + case EnumToken.IfElseConditionTokenType: + return ( + printValue((token as IfElseConditionToken).l, options, cache, reducer, errors) + + (options.indent ?? "") + + printValue((token as IfElseConditionToken).r, options, cache, reducer, errors) + ); + + case EnumToken.DeclarationNodeType: + return ( + arena.getName(token) + + ":" + + (options.minifyValues ? filterValues(arena.children(token), arena) : arena.children(token)).reduce( + (acc: string, curr: number): string => acc + printValue(curr, arena, options, cache, reducer, errors), + "", + ) + ); + + case EnumToken.MediaQueryUnaryFeatureTokenType: + return ( + printValue( + arena.getLeft(token ) as number, + arena, + options, + cache, + reducer, + errors, + ) + + " " + + printValue( + arena.getRight(token ) as number, + arena, + options, + cache, + reducer, + errors, + ) + ); + + case EnumToken.MediaQueryConditionTokenType: { + const indent = + (token as MediaQueryConditionToken).op.typ == EnumToken.LtTokenType || + (token as MediaQueryConditionToken).op.typ == EnumToken.GtTokenType || + (token as MediaQueryConditionToken).op.typ == EnumToken.ColonTokenType || + (token as MediaQueryConditionToken).op.typ == EnumToken.DelimTokenType || + (token as MediaQueryConditionToken).op.typ == EnumToken.LteTokenType || + (token as MediaQueryConditionToken).op.typ == EnumToken.GteTokenType + ? "" + : " "; + return ( + (token as MediaQueryConditionToken).l.reduce( + (acc, curr) => acc + printValue(curr, options, cache, reducer, errors), + "", + ) + + indent + + printValue((token as MediaQueryConditionToken).op, options, cache, reducer, errors) + + indent + + (token as MediaQueryConditionToken).r.reduce( + (acc: string, curr: Token): string => acc + printValue(curr, options, cache, reducer, errors), + "", + ) + ); + } + + case EnumToken.MediaRangeQueryTokenType: + return ( + (token as MediaRangeQueryToken).l.reduce( + (acc: string, curr: Token): string => acc + printValue(curr, options, cache, reducer, errors), + "", + ) + + printValue((token as MediaRangeQueryToken).op1, options, cache, reducer, errors) + + (token as MediaRangeQueryToken).val.reduce( + (acc: string, curr: Token): string => acc + printValue(curr, options, cache, reducer, errors), + "", + ) + + printValue((token as MediaRangeQueryToken).op2, options, cache, reducer, errors) + + (token as MediaRangeQueryToken).r.reduce( + (acc: string, curr: Token): string => acc + printValue(curr, options, cache, reducer, errors), + "", + ) + ); + + case EnumToken.MediaFeatureTokenType: + return arena.getValue(token ); + + case EnumToken.NotTokenType: + return "not"; + + case EnumToken.OnlyTokenType: + return "only"; + case EnumToken.AndTokenType: + return "and"; + + case EnumToken.OrTokenType: + return "or"; + + case EnumToken.InvalidMediaQueryTokenType: + case EnumToken.InvalidCommentTokenType: + case EnumToken.BadCommentTokenType: + case EnumToken.BadCdoTokenType: + case EnumToken.BadStringTokenType: + case EnumToken.BadUrlTokenType: + case EnumToken.EOFTokenType: + return ""; + + default: + console.debug({ token }); + throw new Error(`Unsupported token type for ${EnumToken[typ]}`); + } + + // errors?.push({ action: "ignore", message: `render: unexpected token ${JSON.stringify(token, null, 1)}` }); + // return ""; +} + +/** + * Remove whitespace tokens that are not needed + * @param values + * + * @internal + */ +export function filterValues(values: number[], arena: Arena): number[] { + let i: number = 0; + let typ: EnumToken; + + for (; i < values.length; i++) { + typ = arena.getType(values[i]); + + if (typ == EnumToken.ImportantTokenType && arena.getType(values[i - 1]) === EnumToken.WhitespaceTokenType) { + values.splice(i - 1, 1); + } else if ( + tokensfuncSet.has(typ) && + arena.hasFlags(values[i], EnumAstNodeStatus.HasChildren) && + typ != EnumToken.WildCardFunctionTokenType && + arena.getType(values[i + 1]) == EnumToken.WhitespaceTokenType + ) { + values.splice(i + 1, 1); + } + } + + return values; +} + +/** + * Update source map + * @param node + * @param options + * @param cache + * @param sourcemaps + * @param sourceLocation + * @param lineMap + * @param str + * + * @internal + */ +function updateSourceMap( + node: AstRuleList | AstComment, + options: RenderOptions, + cache: { + [p: string]: any; + }, + sourcemaps: { sources: number[]; maps: Array<[number, number, number, number, number]> }, + sourceLocation: SourceLocation, + lineMap: LineMap, + str: string, +) { + let offset: number = 0; + + // eat leanding whitespace + while (offset < str.length && isWhiteSpace(str.charCodeAt(offset))) { + offset++; + } + + if (offset > 0) { + move(sourceLocation, lineMap, str, 0, offset + 1); + } + + if (node[LOCSTA] != null) { + const source = options.sourcesMap!.get(node[LOCSRCID]!) as SourceFile; + const inputSourceMap = source.getInputSourceMap(); + const offsets: [number, number] = source.getOffsets(node[LOCSTA]) as [number, number]; + const [newLine, newColumn] = lineMap.getOffsets(sourceLocation.end); + let records: Array<[string | null, number, number, string | null]> | null = null; + let srcId: number = node[LOCSRCID]!; + let sourceFileName: string | null = (source.getFileName() as string) || null; + let sourceContent: string | null; // = (source.getContent() as string) || null; + + if (inputSourceMap != null && (records = inputSourceMap.find(offsets[0], offsets[1])) != null) { + let newId: number | null = null; + + for (const record of records) { + newId = null; + + // @ts-ignore + sourceFileName = (record[0] as string) || null; + // @ts-ignore + offsets[0] = record[1] as number; + // @ts-ignore + offsets[1] = record[2] as number; + + sourceContent = (record[3] as string) || null; + + if (sourceFileName != null && options.output != null && !sourceFileName.startsWith("data:")) { + if (cache[sourceFileName] == null) { + const absolute = options.resolve!(dirname(options.output as string), options.cwd as string) + .absolute as string; + const absoluteSourcePath = options.resolve!( + dirname(options.src! || ("" as string)), + options.cwd as string, + ).absolute; + // resolution is relative to the source file + const absoluteSourceFileName = options.resolve!(sourceFileName, absoluteSourcePath as string) + .absolute as string; + + cache[sourceFileName] = options.resolve!(absoluteSourceFileName, absolute).relative as string; + } + + sourceFileName = cache[sourceFileName] as string; + } + + for (const [id, file] of options.sourcesMap!.entries()) { + if (file.getFileName() === sourceFileName) { + newId = id; + break; + } + + if (sourceFileName == null && file.getContent() === sourceContent) { + newId = id; + break; + } + } + + if (newId == null) { + const source = new SourceFile(sourceContent as string, [], sourceFileName); + + options.sourcesMap!.set(source.id, source); + newId = source.id; + } + + srcId = newId as number; + + if (!sourcemaps.sources.includes(srcId)) { + sourcemaps.sources.push(srcId); + } + + sourcemaps.maps.push([newLine, newColumn, srcId, offsets[0], offsets[1]]); + } + } else { + // if (sourceFileName != null && options.output != null && !sourceFileName.startsWith("data:")) { + // if (cache[sourceFileName] == null) { + // const absolute = options.resolve!(dirname(options.output as string), options.cwd as string) + // .absolute as string; + // const absoluteSourceFileName = options.resolve!(sourceFileName, options.cwd as string) + // .absolute as string; + + // cache[sourceFileName] = options.resolve!(absoluteSourceFileName, absolute).relative as string; + // } + + // sourceFileName = cache[sourceFileName] as string; + // } + + if (!sourcemaps.sources.includes(srcId)) { + sourcemaps.sources.push(srcId); + } + + sourcemaps.maps.push([newLine, newColumn, srcId, offsets[0], offsets[1]]); + } + } + + move(sourceLocation, lineMap, str, offset); +} + +/** + * Update position + * @param sourceLocation + * @param lineMap + * @param str + * @param start + * @param end + */ +export function move(sourceLocation: SourceLocation, lineMap: LineMap, str: string, start?: number, end?: number) { + let i: number = start ?? 0; + let j: number = end ?? str.length; + let codepoint: number; + let char: string; + + for (; i < j; i++) { + char = str.charAt(i); + codepoint = char.charCodeAt(0); + sourceLocation.end += char.length; + + if ( + codepoint == 0xa || // \n + codepoint == 0xb || // \v + codepoint == 0xc || // \f + codepoint == 0xd || // \r + codepoint == 0x2028 || // \u2028 + codepoint == 0x2029 // \u2029 + ) { + if (codepoint == 0xd && i + 1 < str.length && str.charCodeAt(i + 1) == 0xa) { + // defer CRLF to the LF step so the next line starts after both characters + } else if (codepoint == 0xa && i > 0 && str.charCodeAt(i - 1) == 0xd) { + // nope + } else { + lineMap.addLineStart(sourceLocation.end); + } + } + } +} diff --git a/src/lib/printer/render.ts b/src/lib/printer/render.ts index f0fc52a61..bee79a336 100644 --- a/src/lib/printer/render.ts +++ b/src/lib/printer/render.ts @@ -73,7 +73,7 @@ import { } from "../syntax/syntax.ts"; import { equalsIgnoreCase } from "../parser/utils/text.ts"; import { toDegrees } from "../parser/utils/angle.ts"; -import { LineMap as LinesMap } from "../parser/linesmap.ts"; +import { LineMap } from "../parser/linesmap.ts"; import { dirname } from "../fs/resolve.ts"; import { SourceFile } from "../parser/source.ts"; import { cloneNode } from "../ast/clone.ts"; @@ -159,7 +159,7 @@ export function doRender( sta: 0, end: 0, } as SourceLocation; - const linesMap: LinesMap | null = options.sourcemap ? new LinesMap() : null; + const lineMap: LineMap | null = options.sourcemap ? new LineMap() : null; let code: string = ""; @@ -192,7 +192,7 @@ export function doRender( )}${options.newLine}}${options.newLine}`; if (sourcemap != null) { - move(sourceLocation, linesMap!, code); + move(sourceLocation, lineMap!, code); } } @@ -215,7 +215,7 @@ export function doRender( options, sourcemaps, sourceLocation, - linesMap, + lineMap, errors, function reducer(acc: string, curr: Token): string { if (curr.typ == EnumToken.CommentTokenType && options.removeComments) { @@ -261,7 +261,7 @@ export function doRender( * @param cache * @param sourcemaps * @param sourceLocation - * @param linesMap + * @param lineMap * @param str * * @internal @@ -274,7 +274,7 @@ function updateSourceMap( }, sourcemaps: { sources: number[]; maps: Array<[number, number, number, number, number]> }, sourceLocation: SourceLocation, - linesMap: LinesMap, + lineMap: LineMap, str: string, ) { let offset: number = 0; @@ -285,14 +285,14 @@ function updateSourceMap( } if (offset > 0) { - move(sourceLocation, linesMap, str, 0, offset + 1); + move(sourceLocation, lineMap, str, 0, offset + 1); } if (node[LOCSTA] != null) { const source = options.sourcesMap!.get(node[LOCSRCID]!) as SourceFile; const inputSourceMap = source.getInputSourceMap(); const offsets: [number, number] = source.getOffsets(node[LOCSTA]) as [number, number]; - const [newLine, newColumn] = linesMap.getOffsets(sourceLocation.end); + const [newLine, newColumn] = lineMap.getOffsets(sourceLocation.end); let records: Array<[string | null, number, number, string | null]> | null = null; let srcId: number = node[LOCSRCID]!; let sourceFileName: string | null = (source.getFileName() as string) || null; @@ -380,18 +380,18 @@ function updateSourceMap( } } - move(sourceLocation, linesMap, str, offset); + move(sourceLocation, lineMap, str, offset); } /** * Update position * @param sourceLocation - * @param linesMap + * @param lineMap * @param str * @param start * @param end */ -export function move(sourceLocation: SourceLocation, linesMap: LinesMap, str: string, start?: number, end?: number) { +export function move(sourceLocation: SourceLocation, lineMap: LineMap, str: string, start?: number, end?: number) { let i: number = start ?? 0; let j: number = end ?? str.length; let codepoint: number; @@ -415,7 +415,7 @@ export function move(sourceLocation: SourceLocation, linesMap: LinesMap, str: st } else if (codepoint == 0xa && i > 0 && str.charCodeAt(i - 1) == 0xd) { // nope } else { - linesMap.addLineStart(sourceLocation.end); + lineMap.addLineStart(sourceLocation.end); } } } @@ -427,7 +427,7 @@ export function move(sourceLocation: SourceLocation, linesMap: LinesMap, str: st * @param options * @param sourcemaps * @param sourceLocation - * @param linesMap + * @param lineMap * @param errors * @param reducer * @param cache @@ -441,7 +441,7 @@ function renderAstNode( options: RenderOptions, sourcemaps: { sources: number[]; maps: Array<[number, number, number, number, number]> } | null, sourceLocation: SourceLocation, - linesMap: LinesMap | null, + lineMap: LineMap | null, errors: ErrorDescription[], reducer: (acc: string, curr: Token) => string, cache: { @@ -490,7 +490,7 @@ function renderAstNode( options, sourcemaps, sourceLocation, - linesMap, + lineMap, errors, reducer, cache, @@ -509,7 +509,7 @@ function renderAstNode( children += str; if (sourcemaps != null && str !== "" && options.newLine) { - move(sourceLocation, linesMap!, options.newLine as string); + move(sourceLocation, lineMap!, options.newLine as string); } } @@ -525,7 +525,7 @@ function renderAstNode( };`; } - const lineMapLength = linesMap ? linesMap.getLineStarts().length : 0; + const lineMapLength = lineMap ? lineMap.getLineStarts().length : 0; const prelude = (indent.length > 0 ? options.newLine : "") + indent + @@ -536,7 +536,7 @@ function renderAstNode( : (data).sel + `${options.indent}{`); if (sourcemaps != null) { - updateSourceMap(data, options, cache, sourcemaps, sourceLocation, linesMap!, prelude); + updateSourceMap(data, options, cache, sourcemaps, sourceLocation, lineMap!, prelude); } let node: AstNode; @@ -562,7 +562,7 @@ function renderAstNode( options, sourcemaps, sourceLocation, - linesMap, + lineMap, errors, reducer, cache, @@ -595,9 +595,9 @@ function renderAstNode( // } // } // @ts-ignore - updateSourceMap(node, options, cache, sourcemaps, sourceLocation, linesMap!, str); + updateSourceMap(node, options, cache, sourcemaps, sourceLocation, lineMap!, str); } else { - move(sourceLocation, linesMap!, str); + move(sourceLocation, lineMap!, str); } } } @@ -610,7 +610,7 @@ function renderAstNode( if (options.removeEmpty && children === "") { if (sourcemaps != null) { sourceLocation.end -= prelude.length; - linesMap!.getLineStarts().length = lineMapLength; + lineMap!.getLineStarts().length = lineMapLength; } return ""; } @@ -618,7 +618,7 @@ function renderAstNode( const end: string = options.newLine + indent + `}`; if (sourcemaps != null) { - move(sourceLocation, linesMap!, end); + move(sourceLocation, lineMap!, end); } return prelude + children + end; @@ -627,1401 +627,3 @@ function renderAstNode( return ""; } } - -/** - * render ast token - * @param token - * @param options - * @param cache - * @param reducer - * @param errors - * @private - */ -export function renderValue( - token: number, - options: RenderOptions, - cache: { - [key: string]: any; - } = Object.create(null), - reducer?: null | ((acc: string, curr: number) => string), - errors?: ErrorDescription[], -): string { - - const arena = options.arena! as Arena; - const typ = arena.getType(token); - - if ( - typ === EnumToken.WhenElseFunctionTokenType && - arena.equalsIgnoreCase(token, 'val', "supports") - ) { - options = { ...options, minifyValues: false, minify: false, convertColor: false }; - reducer = null; - } - - options.minifyValues ??= options.minify; - - if (reducer == null) { - reducer = function (acc: string, curr: number): string { - if (arena.getType(curr) == EnumToken.CommentTokenType && options.removeComments) { - if (!options.preserveLicense || !arena.startsWith(curr, 'val', "/*!")) { - return acc; - } - - return acc + arena.getValue(curr); - } - - return acc + renderValue(curr, options, cache, reducer, errors); - }; - } - - switch (typ) { - case EnumToken.UrlFunctionTokenDefType: - case EnumToken.FunctionTokenDefType: - case EnumToken.MathFunctionTokenDefType: - case EnumToken.ImageFunctionTokenDefType: - case EnumToken.ColorFunctionTokenDefType: - case EnumToken.TimingFunctionTokenDefType: - case EnumToken.WhenElseFunctionTokenDefType: - case EnumToken.SupportsFunctionTokenDefType: - case EnumToken.TimelineFunctionTokenDefType: - case EnumToken.GridTemplateFuncTokenDefType: - case EnumToken.TransformFunctionTokenDefType: - case EnumToken.ContainerFunctionTokenDefType: - case EnumToken.PseudoClassFunctionTokenDefType: - case EnumToken.GeneralEnclosedFunctionTokenDefType: - case EnumToken.CustomFunctionTokenDefType: - case EnumToken.WildCardFunctionTokenDefType: - return arena.getValue(token) + "("; - - case EnumToken.ListToken: - return (token as ListToken).chi.reduce( - (acc: string, curr: Token) => acc + renderValue(curr, options, cache), - "", - ); - - case EnumToken.BinaryExpressionTokenType: - if ( - EnumToken.Mul === (token as BinaryExpressionToken).op || - EnumToken.Div === (token as BinaryExpressionToken).op - ) { - let result: string = ""; - - if ( - (token as BinaryExpressionToken).l.typ == EnumToken.BinaryExpressionTokenType && - [EnumToken.Add, EnumToken.Sub].includes( - ((token as BinaryExpressionToken).l as BinaryExpressionToken).op, - ) - ) { - result = "(" + renderValue((token as BinaryExpressionToken).l, options, cache) + ")"; - } else { - result = renderValue((token as BinaryExpressionToken).l, options, cache); - } - - result += (token as BinaryExpressionToken).op == EnumToken.Mul ? "*" : "/"; - - if ( - (token as BinaryExpressionToken).r.typ == EnumToken.BinaryExpressionTokenType && - [EnumToken.Add, EnumToken.Sub].includes( - ((token as BinaryExpressionToken).r as BinaryExpressionToken).op, - ) - ) { - result += "(" + renderValue((token as BinaryExpressionToken).r, options, cache) + ")"; - } else { - result += renderValue((token as BinaryExpressionToken).r, options, cache); - } - - return result; - } - - return ( - renderValue((token as BinaryExpressionToken).l, options, cache) + - ((token as BinaryExpressionToken).op == EnumToken.Add ? " + " : " - ") + - renderValue((token as BinaryExpressionToken).r, options, cache) - ); - - case EnumToken.FractionTokenType: - const fraction: string = - renderValue((token as FractionToken).l) + "/" + renderValue((token as FractionToken).r); - - if (+(token as FractionToken).r.val != 0) { - const value: string = minifyNumber(+(token as FractionToken).l.val / +(token as FractionToken).r.val); - - if (value.length <= fraction.length) { - return value; - } - } - - return fraction; - - case EnumToken.Add: - return " + "; - - case EnumToken.Sub: - return " - "; - - case EnumToken.UniversalSelectorTokenType: - case EnumToken.Star: - case EnumToken.Mul: - return "*"; - - case EnumToken.Div: - return "/"; - - case EnumToken.WrappedValuesTokenType: - return ( - "{" + - (token as WrappedValuesToken).chi.reduce( - (acc: string, curr: Token) => acc + renderValue(curr, options, cache), - "", - ) + - "}" - ); - - case EnumToken.ColorTokenType: - if ( - (token as ColorToken).kin == ColorType.LIGHT_DARK || - ("chi" in token && options.convertColor === false && ((token as ColorToken).chi as Token[]).length == 2) - ) { - return ( - (token as ColorToken).val + - "(" + - ((token as ColorToken).chi as Token[]).reduce((acc: string, curr: Token) => { - if (curr.typ === EnumToken.IdenTokenType) { - parseColor(curr as IdentToken); - } - - return acc + renderValue(curr, options, cache); - }, "") + - ")" - ); - } - - if (options.convertColor !== false) { - const value: ColorToken | null = convertColor( - token as ColorToken, - typeof options.convertColor === "boolean" - ? ColorType.HEX - : (ColorType[ - (ColorType[options.convertColor ?? "HEX"] as string) - ?.toUpperCase?.() - .replaceAll?.("-", "_") as keyof typeof ColorType - ] ?? ColorType.HEX), - ); - - if (value != null) { - token = value; - } - - if ((token as ColorToken).kin == ColorType.HEX || (token as ColorToken).kin == ColorType.LIT) { - return reduceHexValue((token as ColorToken).val); - } - } - - if ( - (token as ColorToken).kin === ColorType.SYS || - (token as ColorToken).kin === ColorType.DPSYS || - (token as ColorToken).kin === ColorType.NON_STD - ) { - return (token as ColorToken).val; - } - - if ((token as ColorToken).kin == ColorType.HEX || (token as ColorToken).kin == ColorType.LIT) { - return (token as ColorToken).val; - } - - if (Array.isArray((token as ColorToken).chi)) { - const fnName: string = (token as ColorToken).val; - const isLegacy: boolean = ["rgb", "rgba", "hsl", "hsla"].includes((token as ColorToken).val); - const hasAlpha: boolean = [ - "rgb", - "rgba", - "hsl", - "hsla", - "hwb", - "oklab", - "oklch", - "lab", - "lch", - ].includes(fnName); - const useAlpha: boolean = - (hasAlpha && (token as ColorToken).chi!.length == (isLegacy ? 7 : 4)) || - ("color" == (token as ColorToken).val && (token as ColorToken).chi!.length == 5); - - return ( - (hasAlpha && (token as ColorToken).val.endsWith("a") ? fnName.slice(0, -1) : fnName) + - "(" + - (token as ColorToken) - .chi!.reduce((acc: string, curr: Token, index: number, array: Token[]) => { - if (/[,/]\s*$/.test(acc)) { - if (curr.typ == EnumToken.WhitespaceTokenType) { - return acc.trimEnd(); - } - - return acc.trimStart() + renderValue(curr, options, cache); - } - - if (isLegacy && curr.typ == EnumToken.CommaTokenType) { - return acc.trimEnd() + " "; - } - - if (curr.typ == EnumToken.WhitespaceTokenType) { - return acc.trimEnd() + " "; - } - - if ( - curr.typ == EnumToken.CommaTokenType || - (curr.typ == EnumToken.LiteralTokenType && (curr as LiteralToken).val == "/") - ) { - return acc.trimEnd() + (curr.typ == EnumToken.CommaTokenType ? "," : "/"); - } - - return ( - acc.trimEnd() + - (useAlpha && index == array.length - 1 ? "/" : " ") + - renderValue(curr, options, cache) - ); - }, "") - .trimStart() + - ")" - ); - } - - case EnumToken.UrlFunctionTokenType: - if (options.minifyValues && token.typ === EnumToken.UrlFunctionTokenType) { - if ((token as FunctionToken).chi[0]?.typ === EnumToken.BadUrlTokenType) { - return "url()"; - } - - for (const child of (token as FunctionToken).chi) { - if (child.typ === EnumToken.StringTokenType) { - if ( - (child as StringToken).val.slice(1, 5) !== "data:" && - urlTokenMatcher.test((child as StringToken).val) - ) { - Object.assign(child, { - typ: EnumToken.UrlTokenTokenType, - val: (child as StringToken).val.slice(1, -1), - }); - } - - break; - } - } - } - - case EnumToken.ParensTokenType: - case EnumToken.FunctionTokenType: - case EnumToken.MathFunctionTokenType: - case EnumToken.ImageFunctionTokenType: - if (options.minifyValues && token.typ === EnumToken.ImageFunctionTokenType) { - const slice = (token as FunctionToken).chi.slice() as Token[]; - - switch ((token as FunctionToken).val) { - case "linear-gradient": - case "repeating-linear-gradient": - { - let i: number; - let k: number; - let j: number; - let key: string; - - for (i = 0; i < slice.length; i++) { - if ( - slice[i].typ == EnumToken.ColorTokenType || - slice[i].typ === EnumToken.CommaTokenType - ) { - break; - } - - if ( - slice[i].typ == EnumToken.IdenTokenType && - equalsIgnoreCase((slice[i] as IdentToken).val, "to") - ) { - k = i + 1; - while ( - k < slice.length && - (slice[k].typ === EnumToken.WhitespaceTokenType || - slice[k].typ === EnumToken.CommentTokenType) - ) { - k++; - } - - if (k < slice.length && slice[k].typ === EnumToken.IdenTokenType) { - // convert keyword to angle - key = (slice[k] as IdentToken).val.toLowerCase(); - switch (key) { - case "right": - slice.splice(i, k - i + 1, { - typ: EnumToken.AngleTokenType, - val: 90, - } as AngleToken); - break; - case "left": - slice.splice(i, k - i + 1, { - typ: EnumToken.AngleTokenType, - val: 270, - } as AngleToken); - break; - case "top": - case "bottom": - j = k + 1; - - while ( - j < slice.length && - (slice[j].typ === EnumToken.WhitespaceTokenType || - slice[j].typ === EnumToken.CommentTokenType) - ) { - j++; - } - - if (j < slice.length && slice[j].typ !== EnumToken.CommaTokenType) { - i = j - 1; - break; - } - - // https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/gradient/linear-gradient#:~:text=If%20unspecified%2C%20it%20defaults%20to%20to%20bottom - if (key === "bottom") { - // to bottom -> 180deg is the default angle and will be removed - while ( - k < slice.length && - slice[k].typ != EnumToken.CommaTokenType - ) { - k++; - } - - slice.splice(i, k - i + 1); - i--; - continue; - } - - slice.splice(i, k - i + 1, { - typ: EnumToken.AngleTokenType, - val: key === "top" ? 0 : 180, - } as AngleToken); - i--; - break; - } - } - } else if (slice[i].typ == EnumToken.AngleTokenType) { - if ((180 - (toDegrees(slice[i] as AngleToken).val as number)) % 360 === 0) { - k = i + 1; - - // while (k < slice.length && slice[k].typ !== EnumToken.CommaTokenType) { - // k++; - // } - - slice.splice(i, k - i + 1); - } - } - } - - if (slice[i]?.typ === EnumToken.CommaTokenType) { - i++; - // while ( - // i < slice.length && - // (slice[i].typ === EnumToken.WhitespaceTokenType || - // slice[i].typ === EnumToken.CommentTokenType) - // ) { - // i++; - // } - } - - if (slice[i]?.typ === EnumToken.ColorTokenType) { - for (const token of reduceColorStops(slice.splice(i, slice.length - i))) { - slice.push(token); - } - } - } - - break; - - case "radial-gradient": - case "repeating-radial-gradient": - { - let i: number = 0; - - // while ( - // i < slice.length && - // (slice[i].typ === EnumToken.WhitespaceTokenType || - // slice[i].typ === EnumToken.CommentTokenType) - // ) { - // i++; - // } - - const positions: Token[] = []; - const form: Token[] = []; - const size: Token[] = []; - const colorSpaceDef: Token[] = []; - - if (slice[i]?.typ === EnumToken.IdenTokenType) { - if ( - equalsIgnoreCase((slice[i] as IdentToken).val, "farthest-corner") || - equalsIgnoreCase((slice[i] as IdentToken).val, "ellipse") - ) { - i++; - } else if ( - equalsIgnoreCase((slice[i] as IdentToken).val, "closest-side") || - equalsIgnoreCase((slice[i] as IdentToken).val, "closest-corner") || - equalsIgnoreCase((slice[i] as IdentToken).val, "farthest-side") - ) { - size.push(slice[i++]); - } else if (equalsIgnoreCase((slice[i] as IdentToken).val, "circle")) { - form.push(slice[i++]); - } - - while ( - slice[i]?.typ === EnumToken.WhitespaceTokenType || - slice[i]?.typ === EnumToken.CommentTokenType - ) { - i++; - } - } - - if (slice[i]?.typ === EnumToken.IdenTokenType) { - if ( - equalsIgnoreCase((slice[i] as IdentToken).val, "farthest-corner") || - equalsIgnoreCase((slice[i] as IdentToken).val, "ellipse") - ) { - i++; - } else if ( - equalsIgnoreCase((slice[i] as IdentToken).val, "closest-side") || - equalsIgnoreCase((slice[i] as IdentToken).val, "closest-corner") || - equalsIgnoreCase((slice[i] as IdentToken).val, "farthest-side") - ) { - size.push(slice[i++]); - } else if (equalsIgnoreCase((slice[i] as IdentToken).val, "circle")) { - form.push(slice[i++]); - } - - while ( - slice[i]?.typ === EnumToken.WhitespaceTokenType || - slice[i]?.typ === EnumToken.CommentTokenType - ) { - i++; - } - } - - if ( - slice[i]?.typ === EnumToken.IdenTokenType && - equalsIgnoreCase((slice[i] as IdentToken).val, "at") - ) { - i++; - } - - while ( - slice[i]?.typ === EnumToken.WhitespaceTokenType || - slice[i]?.typ === EnumToken.CommentTokenType - ) { - i++; - } - - if ( - slice[i]?.typ === EnumToken.PercentageTokenType || - (slice[i]?.typ === EnumToken.NumberTokenType && 0 === (slice[i] as NumberToken).val) || - (slice[i]?.typ === EnumToken.IdenTokenType && - (equalsIgnoreCase((slice[i] as IdentToken).val, "center") || - equalsIgnoreCase((slice[i] as IdentToken).val, "top") || - equalsIgnoreCase((slice[i] as IdentToken).val, "bottom") || - equalsIgnoreCase((slice[i] as IdentToken).val, "left") || - equalsIgnoreCase((slice[i] as IdentToken).val, "right"))) - ) { - do { - if ( - slice[i]?.typ === EnumToken.IdenTokenType && - "in" === (slice[i] as IdentToken).val - ) { - break; - } - - positions.push(slice[i]); - i++; - } while (slice[i]?.typ !== EnumToken.CommaTokenType); - } - - while (slice[i]?.typ !== EnumToken.CommaTokenType) { - colorSpaceDef.push(slice[i++]); - } - - if (slice[i]?.typ === EnumToken.CommaTokenType) { - i++; - } - - if (positions.length > 0) { - let k: number; - let position1: string = ""; - let position2: string = ""; - - for (k = 0; k < positions.length; k++) { - if (positions[k].typ === EnumToken.IdenTokenType) { - if (position1.length === 0) { - position1 = (positions[k] as IdentToken).val; - } else { - position2 = (positions[k] as IdentToken).val; - } - } else if ( - positions[k].typ === EnumToken.PercentageTokenType || - positions[k].typ === EnumToken.NumberTokenType - ) { - if (position1.length === 0) { - position1 = (positions[k] as PercentageToken | NumberToken).val + "%"; - } else { - position2 = (positions[k] as PercentageToken | NumberToken).val + "%"; - } - } - - if (position2.length > 0) { - break; - } - } - - if (position1.length > 0) { - let key = `${position1} ${position2}`.trim(); - - if ( - key === "50% 50%" || - key === "center center" || - key === "50%" || - key === "center" - ) { - positions.length = 0; - } else if ( - key === "0% 0%" || - key === "0 0" || - key === "top left" || - key === "left top" - ) { - positions.splice(0, positions.length, { - typ: EnumToken.PercentageTokenType, - val: 0, - }); - } else if ( - key === "50% 0%" || - key === "50% 0" || - key === "center top" || - key === "top center" - ) { - positions.splice(0, positions.length, { - typ: EnumToken.IdenTokenType, - val: "top", - }); - } else if (key === "top right" || key === "right top") { - positions.splice( - 0, - positions.length, - { - typ: EnumToken.PercentageTokenType, - val: 100, - }, - { typ: EnumToken.WhitespaceTokenType }, - { typ: EnumToken.PercentageTokenType, val: 0 }, - ); - } else if ( - key === "0 50%" || - key === "0% 50%" || - key === "left center" || - key === "center left" - ) { - positions.splice(0, positions.length, { - typ: EnumToken.IdenTokenType, - val: "left", - }); - } else if (key === "100% 50%" || key === "right center" || key === "center right") { - positions.splice(0, positions.length, { - typ: EnumToken.IdenTokenType, - val: "right", - }); - } else if ( - key === "0 100%" || - key === "0% 100%" || - key === "left bottom" || - key === "bottom left" - ) { - positions.splice(0, positions.length, { - typ: EnumToken.IdenTokenType, - val: "bottom", - }); - } else if ( - key === "50% 100%" || - key === "center bottom" || - key === "bottom center" - ) { - positions.splice(0, positions.length, { - typ: EnumToken.IdenTokenType, - val: "bottom", - }); - } else if ( - key === "100% 100%" || - key === "right bottom" || - key === "bottom right" - ) { - positions.splice(0, positions.length, { - typ: EnumToken.PercentageTokenType, - val: 100, - }); - } else if (position1 === position2) { - positions.splice(0, positions.length, positions[k]); - } - } - } - - const result: Token[] = []; - - if (form.length > 0) { - for (const token of form) { - result.push(token); - } - } - - if (size.length > 0) { - if (result.length > 0) { - result.push({ typ: EnumToken.WhitespaceTokenType }); - } - - for (const token of size) { - result.push(token); - } - } - - if (positions.length > 0) { - if (result.length > 0) { - result.push({ typ: EnumToken.WhitespaceTokenType }); - } - - result.push( - { typ: EnumToken.IdenTokenType, val: "at" }, - { typ: EnumToken.WhitespaceTokenType }, - ); - - for (const token of positions) { - result.push(token); - } - } - - if (colorSpaceDef.length > 0) { - if (result.length > 0) { - result.push({ typ: EnumToken.WhitespaceTokenType }); - } - - for (const token of colorSpaceDef) { - result.push(token); - } - } - - if (result.length > 0) { - result.push({ typ: EnumToken.CommaTokenType }); - } - - for (const token of reduceColorStops(slice.slice(i))) { - result.push(token); - } - - slice.length = 0; - - for (const token of result) { - slice.push(token); - } - } - break; - - case "conic-gradient": - case "repeating-conic-gradient": - { - let i: number = 0; - - const angles: Token[] = []; - const positions: Token[] = []; - const colorSpaceDef: Token[] = []; - - // while ( - // i < slice.length && - // (slice[i].typ === EnumToken.WhitespaceTokenType || - // slice[i].typ === EnumToken.CommentTokenType) - // ) { - // i++; - // } - - if ( - slice[i]?.typ === EnumToken.IdenTokenType && - equalsIgnoreCase((slice[i] as IdentToken).val, "from") - ) { - angles.push(slice[i++]); - - while ( - i < slice.length && - (slice[i].typ === EnumToken.WhitespaceTokenType || - slice[i].typ === EnumToken.CommentTokenType) - ) { - angles.push(slice[i++]); - } - - if ( - (slice[i]?.typ === EnumToken.NumberTokenType || - slice[i]?.typ === EnumToken.AngleTokenType) && - 0 === toDegrees(slice[i] as AngleToken).val - ) { - angles.length = 0; - i++; - } else if ( - slice[i]?.typ !== EnumToken.CommaTokenType && - slice[i].typ != EnumToken.IdenTokenType - ) { - angles.push(slice[i++]); - } - - while ( - i < slice.length && - (slice[i].typ === EnumToken.WhitespaceTokenType || - slice[i].typ === EnumToken.CommentTokenType) - ) { - i++; - } - } - - if ( - slice[i]?.typ === EnumToken.IdenTokenType && - equalsIgnoreCase((slice[i] as IdentToken).val, "at") - ) { - i++; - - while ( - i < slice.length && - (slice[i].typ === EnumToken.WhitespaceTokenType || - slice[i].typ === EnumToken.CommentTokenType) - ) { - i++; - } - - let position1: string = ""; - let position2: string = ""; - - if ( - slice[i]?.typ === EnumToken.IdenTokenType && - !equalsIgnoreCase("in", (slice[i] as IdentToken).val) - ) { - position1 = (slice[i] as IdentToken).val; - positions.push(slice[i++]); - } else if ( - slice[i]?.typ === EnumToken.PercentageTokenType || - slice[i]?.typ === EnumToken.NumberTokenType - ) { - position1 = (slice[i] as IdentToken).val + "%"; - positions.push(slice[i++]); - } - - while ( - i < slice.length && - (slice[i].typ === EnumToken.WhitespaceTokenType || - slice[i].typ === EnumToken.CommentTokenType) - ) { - positions.push(slice[i++]); - } - - if ( - slice[i]?.typ === EnumToken.IdenTokenType && - !equalsIgnoreCase("in", (slice[i] as IdentToken).val) - ) { - position2 = (slice[i] as IdentToken).val; - positions.push(slice[i++]); - } else if ( - slice[i]?.typ === EnumToken.PercentageTokenType || - slice[i]?.typ === EnumToken.NumberTokenType - ) { - position2 = (slice[i] as IdentToken).val + "%"; - positions.push(slice[i++]); - } - - while ( - i < slice.length && - (slice[i].typ === EnumToken.WhitespaceTokenType || - slice[i].typ === EnumToken.CommentTokenType) - ) { - i++; - } - - if (position1.length > 0) { - reducegradientBackgroundPosition(positions, `${position1} ${position2}`.trim()); - } - } - - while ( - i < slice.length && - (slice[i].typ === EnumToken.WhitespaceTokenType || - slice[i].typ === EnumToken.CommentTokenType) - ) { - i++; - } - - if ( - slice[i]?.typ === EnumToken.IdenTokenType && - equalsIgnoreCase("in", (slice[i] as IdentToken).val) - ) { - while (i < slice.length && slice[i].typ !== EnumToken.CommaTokenType) { - colorSpaceDef.push(slice[i++]); - } - } - - if (slice[i]?.typ === EnumToken.CommaTokenType) { - i++; - } - - const result: Token[] = []; - - if (positions.length > 0) { - if (positions.length > 0) { - if (angles.length > 0) { - angles.push({ typ: EnumToken.WhitespaceTokenType }); - } - - angles.push( - { typ: EnumToken.IdenTokenType, val: "at" }, - { typ: EnumToken.WhitespaceTokenType }, - ); - - for (const position of positions) { - angles.push(position); - } - } - } - - if (angles.length > 0) { - for (const angle of angles) { - result.push(angle); - } - result.push({ typ: EnumToken.CommaTokenType }); - } - - if (colorSpaceDef.length > 0) { - if (colorSpaceDef.length > 0) { - if (result.length > 0) { - result.push({ typ: EnumToken.WhitespaceTokenType }); - } - - for (const token of colorSpaceDef) { - result.push(token); - } - } - - result.push({ typ: EnumToken.CommaTokenType }); - } - - for (const token of reduceConicColorStops(slice.slice(i))) { - result.push(token); - } - - slice.length = 0; - - for (let j = 0; j < result.length; j++) { - slice.push(result[j]); - } - } - break; - } - - return (token as FunctionToken).val + "(" + slice.reduce(reducer, "") + ")"; - } - - case EnumToken.TimingFunctionTokenType: - case EnumToken.PseudoClassFuncTokenType: - case EnumToken.WhenElseFunctionTokenType: - case EnumToken.TimelineFunctionTokenType: - case EnumToken.GridTemplateFuncTokenType: - case EnumToken.SupportsFunctionTokenType: - case EnumToken.ContainerFunctionTokenType: - case EnumToken.TransformFunctionTokenType: - case EnumToken.GeneralEnclosedFunctionTokenType: - case EnumToken.CustomFunctionTokenType: - case EnumToken.WildCardFunctionTokenType: - if ( - token.typ == EnumToken.MathFunctionTokenType && - (token as FunctionToken).chi.length == 1 && - ![EnumToken.BinaryExpressionTokenType, EnumToken.FractionTokenType, EnumToken.IdenTokenType].includes( - (token as FunctionToken).chi[0].typ, - ) && - // @ts-ignore - (((((token as FunctionToken).chi[0]) as NumberToken).val) as FractionToken) - ?.typ != EnumToken.FractionTokenType - ) { - return ( - (token as FunctionToken).val + - "(" + - (token as FunctionToken).chi.reduce( - (acc: string, curr: Token) => - acc + - renderValue( - curr, - token.typ == EnumToken.FunctionTokenType ? { minify: false } : options, - cache, - reducer, - ), - "", - ) + - ")" - ); - } - - return ((token as FunctionToken).val ?? "") + "(" + (token as FunctionToken).chi.reduce(reducer, "") + ")"; - - case EnumToken.BlockStartTokenType: - return "{"; - - case EnumToken.BlockEndTokenType: - return "}"; - - case EnumToken.StartParensTokenType: - return "("; - - case EnumToken.DelimTokenType: - case EnumToken.EqualMatchTokenType: - return "="; - - case EnumToken.IncludeMatchTokenType: - return "~="; - - case EnumToken.DashMatchTokenType: - return "|="; - - case EnumToken.StartMatchTokenType: - return "^="; - - case EnumToken.EndMatchTokenType: - return "$="; - - case EnumToken.ContainMatchTokenType: - return "*="; - - case EnumToken.LtTokenType: - return "<"; - - case EnumToken.LteTokenType: - return "<="; - - case EnumToken.Tilda: - case EnumToken.SubsequentSiblingCombinatorTokenType: - return "~"; - - case EnumToken.Plus: - case EnumToken.NextSiblingCombinatorTokenType: - return "+"; - - case EnumToken.GtTokenType: - case EnumToken.ChildCombinatorTokenType: - return ">"; - - case EnumToken.GteTokenType: - return ">="; - - case EnumToken.ColumnCombinatorTokenType: - return "||"; - - case EnumToken.EndParensTokenType: - return ")"; - - case EnumToken.AttrStartTokenType: - return "["; - - case EnumToken.AttrEndTokenType: - return "]"; - - case EnumToken.DescendantCombinatorTokenType: - case EnumToken.WhitespaceTokenType: - return " "; - - case EnumToken.ColonTokenType: - return ":"; - - case EnumToken.DoubleColonTokenType: - return "::"; - - case EnumToken.SemiColonTokenType: - return ";"; - - case EnumToken.CommaTokenType: - return ","; - - case EnumToken.ImportantTokenType: - return "!important"; - - case EnumToken.Pipe: - return "|"; - case EnumToken.AttrTokenType: - case EnumToken.IdenListTokenType: - return "[" + ((token as AttrToken | IdentListToken)).chi.reduce(reducer, "") + "]"; - - case EnumToken.TimeTokenType: - case EnumToken.AngleTokenType: - case EnumToken.LengthTokenType: - case EnumToken.DimensionTokenType: - case EnumToken.FrequencyTokenType: - case EnumToken.ResolutionTokenType: - let val: string = - ((token as LengthToken).val).typ == EnumToken.FractionTokenType - ? renderValue((token as LengthToken).val, options, cache) - : minifyNumber((token as LengthToken).val); - let unit: string = (token as LengthToken).unit; - - if (token.typ == EnumToken.AngleTokenType && !val.includes("/")) { - const angle: number = getAngle(token); - - let v: string; - let value: string = val + unit; - - for (const u of ["deg", "turn", "rad", "grad"]) { - if ((token as AngleToken).unit == u) { - continue; - } - - switch (u) { - case "deg": - v = minifyNumber( - toPrecisionAngle(angle * 360, anglePrecision, false).toFixed(anglePrecision), - ); - - if (v.length + 3 < value.length) { - val = v; - unit = u; - value = v + u; - } - - break; - - case "turn": - v = minifyNumber(toPrecisionAngle(angle, anglePrecision, false).toFixed(anglePrecision)); - - if (v.length + 4 < value.length) { - val = v; - unit = u; - value = v + u; - } - - break; - - case "rad": - v = minifyNumber( - toPrecisionAngle(angle * (2 * Math.PI), anglePrecision, false).toFixed(anglePrecision), - ); - - if (v.length + 3 < value.length) { - val = v; - unit = u; - value = v + u; - } - - break; - - case "grad": - v = minifyNumber( - toPrecisionAngle(angle * 400, anglePrecision, false).toFixed(anglePrecision), - ); - - if (v.length + 4 < value.length) { - val = v; - unit = u; - value = v + u; - } - - break; - } - } - } - - if (val === "0") { - if (token.typ == EnumToken.TimeTokenType) { - return "0s"; - } - - if (token.typ == EnumToken.FrequencyTokenType) { - return "0Hz"; - } - - // @ts-ignore - if (token.typ == EnumToken.ResolutionTokenType) { - return "0x"; - } - - return "0"; - } - - if (token.typ == EnumToken.TimeTokenType) { - if (unit == "ms") { - // @ts-ignore - const v: string = minifyNumber(val / 1000); - - if (v.length + 1 <= val.length) { - return v + "s"; - } - - return val + "ms"; - } - - return val + "s"; - } - - if (token.typ == EnumToken.ResolutionTokenType && unit == "dppx") { - unit = "x"; - } - - return val.includes("/") ? val.replace("/", unit + "/") : minifyNumber(toPrecisionValue(val)) + unit; - - case EnumToken.FlexTokenType: - case EnumToken.PercentageTokenType: - const uni: string = token.typ == EnumToken.PercentageTokenType ? "%" : "fr"; - const perc: string = - ((token as PercentageToken).val).typ == EnumToken.FractionTokenType - ? renderValue((token as PercentageToken).val, options, cache) - : minifyNumber((token as PercentageToken).val as number); - - return options.minifyValues && perc == "0" - ? "0" - : perc.includes("/") - ? perc.replace("/", uni + "/") - : perc + uni; - - case EnumToken.NumberTokenType: - return ((token as NumberToken).val).typ == EnumToken.FractionTokenType - ? renderValue((token as NumberToken).val, options, cache) - : minifyNumber((token as NumberToken).val as number); - - case EnumToken.InfinityTokenType: - return "1/0"; - case EnumToken.NegativeInfinityTokenType: - return "-1/0"; - - case EnumToken.NaNTokenType: - return "NaN"; - - case EnumToken.AtRuleTokenType: - return "@" + (token as AtRuleToken).nam; - - case EnumToken.CommentTokenType: - case EnumToken.CDOCOMMNodeType: - if ( - options.removeComments && - (!options.preserveLicense || !(token as CommentToken).val.startsWith("/*!")) - ) { - return ""; - } - - case EnumToken.PseudoClassTokenType: - case EnumToken.PseudoElementTokenType: - // https://www.w3.org/TR/selectors-4/#single-colon-pseudos - if ( - token.typ == EnumToken.PseudoElementTokenType && - pseudoElements.includes((token as PseudoElementToken).val.slice(1)) - ) { - return (token as PseudoElementToken).val.slice(1); - } - - case EnumToken.UrlTokenTokenType: - - case EnumToken.HashTokenType: - case EnumToken.IdenTokenType: - case EnumToken.StringTokenType: - case EnumToken.LiteralTokenType: - case EnumToken.DashedIdenTokenType: - case EnumToken.PseudoPageTokenType: - case EnumToken.ClassSelectorTokenType: - return ( - token as - | ClassSelectorToken - | StringToken - | LiteralToken - | HashToken - | DashedIdentToken - | PseudoPageToken - | IdentToken - ).val; - - case EnumToken.NestingSelectorTokenType: - return "&"; - - case EnumToken.InvalidAttrTokenType: - return ( - "[" + - (token).chi.reduce( - (acc: string, curr: Token): string => acc + renderValue(curr, options, cache), - "", - ) - ); - - case EnumToken.InvalidClassSelectorTokenType: - return (token as InvalidClassSelectorToken).val; - - case EnumToken.SupportsQueryUnaryConditionTokenType: - case EnumToken.WhenElseUnaryConditionTokenType: - return ( - renderValue( - (token as SupportsQueryUnaryConditionToken | WhenElseUnaryConditionToken).l, - options, - cache, - reducer, - errors, - ) + - " " + - (token as SupportsQueryUnaryConditionToken | WhenElseUnaryConditionToken).r.reduce( - (acc, curr) => acc + renderValue(curr, options, cache, reducer, errors), - "", - ) - ); - - case EnumToken.SupportsQueryConditionTokenType: - case EnumToken.WhenElseQueryConditionTokenType: - return ( - (token as SupportsQueryConditionToken | WhenElseQueryConditionToken).l.reduce( - (acc, curr) => acc + renderValue(curr, options, cache, reducer, errors), - "", - ) + - " " + - renderValue( - (token as SupportsQueryConditionToken | WhenElseQueryConditionToken).op, - options, - cache, - reducer, - errors, - ) + - " " + - (token as SupportsQueryConditionToken).r.reduce( - (acc, curr) => acc + renderValue(curr, options, cache, reducer, errors), - "", - ) - ); - - case EnumToken.IfConditionTokenType: - return (token as IfConditionToken).l.length == 0 - ? "" - : (token as SupportsQueryConditionToken | WhenElseQueryConditionToken).l.reduce( - (acc, curr) => acc + renderValue(curr, options, cache, reducer, errors), - "", - ) + - ":" + - (options.indent ?? "") + - (token as SupportsQueryConditionToken).r.reduce( - (acc, curr) => acc + renderValue(curr, options, cache, reducer, errors), - "", - ); - - case EnumToken.IfElseConditionTokenType: - return ( - renderValue((token as IfElseConditionToken).l, options, cache, reducer, errors) + - (options.indent ?? "") + - renderValue((token as IfElseConditionToken).r, options, cache, reducer, errors) - ); - - case EnumToken.DeclarationNodeType: - return ( - (token).nam + - ":" + - (options.minifyValues ? filterValues((token).val) : (token).val).reduce( - (acc: string, curr: Token): string => acc + renderValue(curr, options, cache, reducer, errors), - "", - ) - ); - - case EnumToken.MediaQueryUnaryFeatureTokenType: - return ( - renderValue((token as MediaQueryUnaryFeatureToken).l, options, cache, reducer, errors) + - " " + - (token as MediaQueryUnaryFeatureToken).r.reduce( - (acc: string, curr: Token): string => acc + renderValue(curr, options, cache, reducer, errors), - "", - ) - ); - - case EnumToken.MediaQueryConditionTokenType: { - const indent = - (token as MediaQueryConditionToken).op.typ == EnumToken.LtTokenType || - (token as MediaQueryConditionToken).op.typ == EnumToken.GtTokenType || - (token as MediaQueryConditionToken).op.typ == EnumToken.ColonTokenType || - (token as MediaQueryConditionToken).op.typ == EnumToken.DelimTokenType || - (token as MediaQueryConditionToken).op.typ == EnumToken.LteTokenType || - (token as MediaQueryConditionToken).op.typ == EnumToken.GteTokenType - ? "" - : " "; - return ( - (token as MediaQueryConditionToken).l.reduce( - (acc, curr) => acc + renderValue(curr, options, cache, reducer, errors), - "", - ) + - indent + - renderValue((token as MediaQueryConditionToken).op, options, cache, reducer, errors) + - indent + - (token as MediaQueryConditionToken).r.reduce( - (acc: string, curr: Token): string => acc + renderValue(curr, options, cache, reducer, errors), - "", - ) - ); - } - - case EnumToken.MediaRangeQueryTokenType: - return ( - (token as MediaRangeQueryToken).l.reduce( - (acc: string, curr: Token): string => acc + renderValue(curr, options, cache, reducer, errors), - "", - ) + - renderValue((token as MediaRangeQueryToken).op1, options, cache, reducer, errors) + - (token as MediaRangeQueryToken).val.reduce( - (acc: string, curr: Token): string => acc + renderValue(curr, options, cache, reducer, errors), - "", - ) + - renderValue((token as MediaRangeQueryToken).op2, options, cache, reducer, errors) + - (token as MediaRangeQueryToken).r.reduce( - (acc: string, curr: Token): string => acc + renderValue(curr, options, cache, reducer, errors), - "", - ) - ); - - case EnumToken.MediaFeatureTokenType: - return (token as MediaFeatureToken).val; - - case EnumToken.NotTokenType: - return "not"; - - case EnumToken.OnlyTokenType: - return "only"; - case EnumToken.AndTokenType: - return "and"; - - case EnumToken.OrTokenType: - return "or"; - - case EnumToken.InvalidMediaQueryTokenType: - case EnumToken.InvalidCommentTokenType: - case EnumToken.BadCommentTokenType: - case EnumToken.BadCdoTokenType: - case EnumToken.BadStringTokenType: - case EnumToken.BadUrlTokenType: - case EnumToken.EOFTokenType: - return ""; - - default: - console.debug({ token }); - throw new Error(`Unsupported token type for ${EnumToken[token.typ]}`); - } - - // errors?.push({ action: "ignore", message: `render: unexpected token ${JSON.stringify(token, null, 1)}` }); - // return ""; -} - -/** - * Remove whitespace tokens that are not needed - * @param values - * - * @internal - */ -export function filterValues(values: Token[]): Token[] { - let i: number = 0; - - for (; i < values.length; i++) { - if (values[i].typ == EnumToken.ImportantTokenType && values[i - 1]?.typ === EnumToken.WhitespaceTokenType) { - values.splice(i - 1, 1); - } else if ( - tokensfuncSet.has(values[i].typ) && - "chi" in values[i] && - (values[i] as FunctionToken).typ != EnumToken.WildCardFunctionTokenType && - values[i + 1]?.typ == EnumToken.WhitespaceTokenType - ) { - values.splice(i + 1, 1); - } - } - - return values; -} diff --git a/src/lib/validation/utils/list.ts b/src/lib/validation/utils/list.ts index af9d636e2..287db7585 100644 --- a/src/lib/validation/utils/list.ts +++ b/src/lib/validation/utils/list.ts @@ -1,16 +1,20 @@ import { EnumToken } from "../../ast/types.ts"; import type { ParserOptions, Token } from "../../../@types/index.d.ts"; -export function stripCommaToken(tokenList: Token[]): Token[] | null { - let result: Token[] = []; - let last: Token | null = null; +export function stripCommaToken(tokenList: number[], options: ParserOptions): number[] | null { + let result: number[] = []; + let typ: EnumToken; + let last: number; + const arena = options.arena!; for (let i = 0; i < tokenList.length; i++) { - if (tokenList[i].typ != EnumToken.WhitespaceTokenType) { + + typ = arena.getType(tokenList[i]); + if (typ != EnumToken.WhitespaceTokenType) { last = tokenList[i]; } - if (tokenList[i].typ == EnumToken.CommentTokenType || tokenList[i].typ == EnumToken.CommaTokenType) { + if (typ == EnumToken.CommentTokenType || typ == EnumToken.CommaTokenType) { continue; } diff --git a/src/node.ts b/src/node.ts index 07f0190b7..ef4fdb31b 100644 --- a/src/node.ts +++ b/src/node.ts @@ -50,13 +50,13 @@ export { minify } from "./lib/ast/minify.ts"; export { expand } from "./lib/ast/expand.ts"; export { walk, walkValues, WalkerEvent, WalkerOptionEnum } from "./lib/ast/walk.ts"; export { parseString } from "./lib/parser/parse.ts"; -export { renderValue as renderToken } from "./lib/printer/render.ts"; export { convertColor } from "./lib/syntax/color/color.ts"; export { isOkLabClose, okLabDistance } from "./lib/syntax/color/utils/distance.ts"; export { parseDeclarations } from "./lib/parser/parse.ts"; export { find, findLast, findByValue, findAll } from "./lib/ast/find.ts"; export { cloneNode } from "./lib/ast/clone.ts"; export { replaceNodeOrValue } from "./lib/parser/utils/token.ts"; +export { printValue } from "./lib/printer/print.ts"; export { EnumToken, ColorType, diff --git a/src/web.ts b/src/web.ts index 849d59e3c..72fa81d60 100644 --- a/src/web.ts +++ b/src/web.ts @@ -45,18 +45,16 @@ export { minify } from "./lib/ast/minify.ts"; export { expand } from "./lib/ast/expand.ts"; export { walk, walkValues, WalkerEvent, WalkerOptionEnum } from "./lib/ast/walk.ts"; export { parseString } from "./lib/parser/parse.ts"; -export { renderValue as renderToken } from "./lib/printer/render.ts"; export { convertColor } from "./lib/syntax/color/color.ts"; export { isOkLabClose, okLabDistance } from "./lib/syntax/color/utils/distance.ts"; export { parseDeclarations } from "./lib/parser/parse.ts"; export { find, findLast, findByValue, findAll } from "./lib/ast/find.ts"; export { cloneNode } from "./lib/ast/clone.ts"; export { replaceNodeOrValue } from "./lib/parser/utils/token.ts"; -export { PropertyList } from "./lib/parser/declaration/list.ts"; +export { printValue } from "./lib/printer/print.ts"; export { EnumToken, ColorType, - ValidationLevel, EnumAstNodeStatus, ModuleScopeEnumOptions, ModuleCaseTransformEnum, @@ -311,7 +309,7 @@ export function parseSync( options ??= {}; options.src ??= ""; - options.sourcesMap ??= new Map(); + options.sourcesMap ??= new Map(); options.arena ??= new Arena(2 << Math.ceil(2 + Math.log2((stream as string).length))); Object.assign(options, { From 3274cb07f4d69ef85dd03d41b218575ecd964adb Mon Sep 17 00:00:00 2001 From: Thierry Bela Nanga Date: Wed, 14 Oct 2026 17:47:45 -0400 Subject: [PATCH 04/24] work in progress --- src/lib/parser/arena.ts | 14 +++- src/lib/parser/parse.ts | 120 ++++++++++++++++--------------- src/lib/parser/utils/selector.ts | 14 ++-- src/lib/printer/print.ts | 34 ++++----- src/lib/syntax/color/color.ts | 31 ++++---- 5 files changed, 113 insertions(+), 100 deletions(-) diff --git a/src/lib/parser/arena.ts b/src/lib/parser/arena.ts index 235c895d4..0e5f3c227 100644 --- a/src/lib/parser/arena.ts +++ b/src/lib/parser/arena.ts @@ -94,9 +94,10 @@ export class Arena { return id; } - this.stringIds.set(str, this.strings.length); + id = this.strings.length; + this.stringIds.set(str, id); this.strings.push(str); - return this.strings.length; + return id; } /** @@ -175,6 +176,7 @@ export class Arena { } getValue(id: number): string { + return this.strings[this.stringValue[id]] ?? null; } @@ -182,6 +184,14 @@ export class Arena { this.numericValue[id] = numericValue; } + setUnit(id: number, unit: number): void { + this.unit[id] = unit; + } + + getUnit(id: number): string { + return this.strings[this.unit[id]] ?? null; + } + getNumericValue(id: number): number { return this.numericValue[id]; } diff --git a/src/lib/parser/parse.ts b/src/lib/parser/parse.ts index f996b44d9..ea36656ec 100644 --- a/src/lib/parser/parse.ts +++ b/src/lib/parser/parse.ts @@ -723,8 +723,13 @@ export function doParseSync(tokenizer: Tokenizer, options: ParserSyncOptions = { tokenId = arena.allocate(); if (tokenizer.unit != null) { - - arena.set(tokenId, tokenizer.typ as EnumToken, 0, tokenizer.val as number, arena.internString(tokenizer.unit)); + arena.set( + tokenId, + tokenizer.typ as EnumToken, + 0, + tokenizer.val as number, + arena.internString(tokenizer.unit), + ); // item = { // typ: tokenizer.typ as EnumToken, @@ -732,7 +737,6 @@ export function doParseSync(tokenizer: Tokenizer, options: ParserSyncOptions = { // unit: tokenizer.unit, // } as DimensionToken; } else if (tokenizer.nam != null) { - arena.set(tokenId, tokenizer.typ as EnumToken); arena.setName(tokenId, arena.internString(tokenizer.nam as string)); // item = { @@ -745,7 +749,6 @@ export function doParseSync(tokenizer: Tokenizer, options: ParserSyncOptions = { // typ: tokenizer.typ as EnumToken, // } as Token; } else if (tokenizer.kin != null) { - arena.set(tokenId, tokenizer.typ as EnumToken, arena.internString(tokenizer.val as string)); arena.setColorType(tokenId, tokenizer.kin); @@ -755,7 +758,12 @@ export function doParseSync(tokenizer: Tokenizer, options: ParserSyncOptions = { // kin: tokenizer.kin, // } as ColorToken; } else { - arena.set(tokenId, tokenizer.typ as EnumToken, arena.internString(tokenizer.val as string)); + arena.set( + tokenId, + tokenizer.typ as EnumToken, + typeof tokenizer.val === "string" ? arena.internString(tokenizer.val as string) : 0, + typeof tokenizer.val === "number" ? tokenizer.val : 0, + ); // item = { // typ: tokenizer.typ as EnumToken, // val: tokenizer.val, @@ -809,7 +817,7 @@ export function doParseSync(tokenizer: Tokenizer, options: ParserSyncOptions = { if (node != null) { if (arena.hasFlags(node, EnumAstNodeStatus.HasChildren)) { - stack.push(node ); + stack.push(node); context = node; } } else if (tokenizer.typ == EnumToken.BlockStartTokenType) { @@ -824,15 +832,19 @@ export function doParseSync(tokenizer: Tokenizer, options: ParserSyncOptions = { tokenId = arena.allocate(); if (tokenizer.unit != null) { - - arena.set(tokenId, tokenizer.typ, 0, tokenizer.val as number, arena.internString(tokenizer.unit)); + arena.set( + tokenId, + tokenizer.typ, + 0, + tokenizer.val as number, + arena.internString(tokenizer.unit), + ); // item = { // typ: tokenizer.typ as EnumToken, // val: tokenizer.val, // unit: tokenizer.unit, // } as DimensionToken; } else if (tokenizer.nam != null) { - arena.set(tokenId, tokenizer.typ); arena.setName(tokenId, arena.internString(tokenizer.nam as string)); // item = { @@ -845,7 +857,6 @@ export function doParseSync(tokenizer: Tokenizer, options: ParserSyncOptions = { // typ: tokenizer.typ as EnumToken, // } as Token; } else if (tokenizer.kin != null) { - arena.set(tokenId, tokenizer.typ, arena.internString(tokenizer.val as string)); arena.setColorType(tokenId, tokenizer.kin); // item = { @@ -854,7 +865,6 @@ export function doParseSync(tokenizer: Tokenizer, options: ParserSyncOptions = { // kin: tokenizer.kin, // } as ColorToken; } else { - arena.set(tokenId, tokenizer.typ, arena.internString(tokenizer.val as string)); // item = { // typ: tokenizer.typ as EnumToken, @@ -890,11 +900,10 @@ export function doParseSync(tokenizer: Tokenizer, options: ParserSyncOptions = { } else if ((parensMatch === 0 || curlyBracketMatch === 0) && tokenizer.typ === EnumToken.BlockEndTokenType) { parseNode(tokens, context, options as ParserOptions, errors, stats, invalidNodes); - // context[LOCEND] = tokenizer.end as number; const previousNode = stack.pop() as number; - context = (stack[stack.length - 1] ?? arena.root); + context = stack[stack.length - 1] ?? arena.root; if ( options.removeEmpty && @@ -902,7 +911,6 @@ export function doParseSync(tokenizer: Tokenizer, options: ParserSyncOptions = { arena.getFirstChild(previousNode) == 0 && arena.getLastChild(context) == previousNode ) { - arena.detach(previousNode); } @@ -3219,7 +3227,7 @@ function parseNode( errors: ErrorDescription[], stats: ParseResultStats, invalidNodes: number[], -): number| null { +): number | null { let i: number = 0; const arena: Arena = options.arena!; @@ -3234,8 +3242,8 @@ function parseNode( let matchCount: number = 0; for (let i = 0; i < tokens.length; i++) { - token = tokens[i]; - typ = arena.getType(token); + token = tokens[i]; + typ = arena.getType(token); if (typ === EnumToken.StartParensTokenType || tokensfuncDefMap.has(typ)) { matchCount++; @@ -3248,42 +3256,35 @@ function parseNode( let k: number = tokens.length; if (k > 0) { - - typ = arena.getType(tokens[k - 1]) ; - - while ( - k-- > 0 && - typ === EnumToken.WhitespaceTokenType || typ === EnumToken.CommentTokenType - ) { - typ = arena.getType(tokens[k - 1]); - } - if (typ == EnumToken.SemiColonTokenType) { - matchCount = 0; - } + while ((k-- > 0 && typ === EnumToken.WhitespaceTokenType) || typ === EnumToken.CommentTokenType) { + typ = arena.getType(tokens[k - 1]); + } - let tokenId: number; - let sourceId: number = arena.getSourceId(tokens[k]); - let spanStart: number = arena.getSpanStart(tokens[k]); - let spanEnd: number = arena.getSpanEnd(tokens[k]); + if (typ == EnumToken.SemiColonTokenType) { + matchCount = 0; + } - while (matchCount > 0) { + let tokenId: number; + let sourceId: number = arena.getSourceId(tokens[k]); + let spanStart: number = arena.getSpanStart(tokens[k]); + let spanEnd: number = arena.getSpanEnd(tokens[k]); - tokenId = arena.allocate(); + while (matchCount > 0) { + tokenId = arena.allocate(); - arena.set(tokenId, EnumToken.EndParensTokenType); - arena.setSpan(tokenId, sourceId, spanStart, spanEnd); - // tokens.push({ - // typ: EnumToken.EndParensTokenType, - // [LOCSRCID]: tokens[k]?.[LOCSRCID], - // [LOCSTA]: tokens[k]?.[LOCSTA], - // [LOCEND]: tokens[k]?.[LOCEND], - // }); - matchCount--; - } + arena.set(tokenId, EnumToken.EndParensTokenType); + arena.setSpan(tokenId, sourceId, spanStart, spanEnd); + // tokens.push({ + // typ: EnumToken.EndParensTokenType, + // [LOCSRCID]: tokens[k]?.[LOCSRCID], + // [LOCSTA]: tokens[k]?.[LOCSTA], + // [LOCEND]: tokens[k]?.[LOCEND], + // }); + matchCount--; + } } - } } @@ -3314,16 +3315,14 @@ function parseNode( } if (i > 0) { - arena.appendChildren( - context, - ...(tokens.splice(0, i) ).filter((n: number) => arena.getType(n) !== EnumToken.WhitespaceTokenType) + context, + ...tokens.splice(0, i).filter((n: number) => arena.getType(n) !== EnumToken.WhitespaceTokenType), ); i = 0; } for (; i < tokens.length; i++) { - typ = arena.getType(tokens[i]); if (typ == EnumToken.CommentTokenType || typ == EnumToken.CDOCOMMTokenType) { if (typ == EnumToken.CDOCOMMTokenType && contextType != EnumToken.StyleSheetNodeType) { @@ -3334,7 +3333,7 @@ function parseNode( location: options.source!.getSourceLocation(arena.getSpanStart(tokens[i])), }); - arena.setType(tokens[i],EnumToken.InvalidCommentTokenType); + arena.setType(tokens[i], EnumToken.InvalidCommentTokenType); continue; } @@ -3352,7 +3351,7 @@ function parseNode( } // for (const token of tokens.slice(1)) { - + // console.error(EnumToken[arena.getType(token)]); // } @@ -3380,14 +3379,13 @@ function parseNode( let node; while (parent != 0) { - typ = arena.getType(parent); if (typ == EnumToken.RuleNodeType) { nestedRule = true; break; } - parent = arena.getParent(parent) ; + parent = arena.getParent(parent); } node = parseAtRule( @@ -3428,7 +3426,7 @@ function parseNode( // rule if (arena.getType(delim) == EnumToken.BlockStartTokenType) { - const node = parseSelector(tokens, context , options, errors); + const node = parseSelector(tokens, context, options, errors); arena.appendChildren(context, node); // context.chi!.push(node); @@ -3436,15 +3434,21 @@ function parseNode( // node[ROOT] = context[ROOT]; if ( - arena.hasFlags(node, EnumAstNodeStatus.Invalid | EnumAstNodeStatus.Disallowed | EnumAstNodeStatus.Unknown | EnumAstNodeStatus.Unparsed | EnumAstNodeStatus.Malformed) - + arena.hasFlags( + node, + EnumAstNodeStatus.Invalid | + EnumAstNodeStatus.Disallowed | + EnumAstNodeStatus.Unknown | + EnumAstNodeStatus.Unparsed | + EnumAstNodeStatus.Malformed, + ) ) { invalidNodes.push(node); } return node; } else { - const node = parseDeclaration(tokens, context , options, errors) as AstDeclaration; + const node = parseDeclaration(tokens, context, options, errors) as AstDeclaration; // node[PARENT] = context; // node[ROOT] = context[ROOT]; diff --git a/src/lib/parser/utils/selector.ts b/src/lib/parser/utils/selector.ts index d74ff6383..b3664a3cf 100644 --- a/src/lib/parser/utils/selector.ts +++ b/src/lib/parser/utils/selector.ts @@ -451,15 +451,9 @@ export function parseSelector( const result = matchSelectorSyntax(tokens, errors, options, nested); - for (const token of tokens) { - - - console.error(EnumToken[arena.getType(token)]); - } - trimArray(tokens, options); - if (result.success) { + // if (result.success) { for (let i = 0; i < tokens.length; i++) { token = tokens[i]; typ = arena.getType(token); @@ -807,12 +801,12 @@ export function parseSelector( break; } } - } + // } const node = arena.allocate(); arena.set(node, EnumToken.RuleNodeType, arena.internString([ - ...all + ...trimArray(all, options) .reduce( (acc: string[][], curr: number, index: number, array: number[]) => { // if (curr.typ == EnumToken.CommentTokenType) { @@ -861,7 +855,7 @@ export function parseSelector( }, uniq) .keys(), ].join(","))); - + arena.setSpan(node, arena.getSourceId(tokens[0]), arena.getSpanStart(tokens[0]), arena.getSpanEnd(tokens[tokens.length - 1])); arena.setFlags(node, EnumAstNodeStatus.HasChildren | EnumAstNodeStatus.Validated ); diff --git a/src/lib/printer/print.ts b/src/lib/printer/print.ts index 1cdafa4f0..c78b6c560 100644 --- a/src/lib/printer/print.ts +++ b/src/lib/printer/print.ts @@ -395,6 +395,8 @@ export function printValue( ): string { const typ = arena.getType(token); + // console.error(EnumToken[typ]); + if (typ === EnumToken.WhenElseFunctionTokenType && arena.equalsIgnoreCase(token, "val", "supports")) { options = { ...options, minifyValues: false, minify: false, convertColor: false }; reducer = null; @@ -1300,7 +1302,7 @@ export function printValue( ); } - return ((token as FunctionToken).val ?? "") + "(" + (token as FunctionToken).chi.reduce(reducer, "") + ")"; + return (arena.getValue(token) ?? "") + "(" + arena.children(token).reduce(reducer, "") + ")"; case EnumToken.BlockStartTokenType: return "{"; @@ -1386,7 +1388,7 @@ export function printValue( return "|"; case EnumToken.AttrTokenType: case EnumToken.IdenListTokenType: - return "[" + ((token as AttrToken | IdentListToken)).chi.reduce(reducer, "") + "]"; + return "[" + arena.children(token).reduce(reducer, "") + "]"; case EnumToken.TimeTokenType: case EnumToken.AngleTokenType: @@ -1394,20 +1396,20 @@ export function printValue( case EnumToken.DimensionTokenType: case EnumToken.FrequencyTokenType: case EnumToken.ResolutionTokenType: - let val: string = - ((token as LengthToken).val).typ == EnumToken.FractionTokenType - ? printValue((token as LengthToken).val, options, cache) - : minifyNumber((token as LengthToken).val); - let unit: string = (token as LengthToken).unit; + let val: string = arena.hasFlags(token, EnumAstNodeStatus.HasFractionValue) ? + + printValue(arena.getNumericValue(token), arena, options, cache) + : minifyNumber(arena.getNumericValue(token)); + let unit: string = arena.getUnit(token); - if (token.typ == EnumToken.AngleTokenType && !val.includes("/")) { - const angle: number = getAngle(token); + if (typ == EnumToken.AngleTokenType && !val.includes("/")) { + const angle: number = getAngle(token, arena); let v: string; let value: string = val + unit; for (const u of ["deg", "turn", "rad", "grad"]) { - if ((token as AngleToken).unit == u) { + if (unit == u) { continue; } @@ -1466,23 +1468,23 @@ export function printValue( } if (val === "0") { - if (token.typ == EnumToken.TimeTokenType) { + if (typ == EnumToken.TimeTokenType) { return "0s"; } - if (token.typ == EnumToken.FrequencyTokenType) { + if (typ == EnumToken.FrequencyTokenType) { return "0Hz"; } // @ts-ignore - if (token.typ == EnumToken.ResolutionTokenType) { + if (typ == EnumToken.ResolutionTokenType) { return "0x"; } return "0"; } - if (token.typ == EnumToken.TimeTokenType) { + if (typ == EnumToken.TimeTokenType) { if (unit == "ms") { // @ts-ignore const v: string = minifyNumber(val / 1000); @@ -1497,7 +1499,7 @@ export function printValue( return val + "s"; } - if (token.typ == EnumToken.ResolutionTokenType && unit == "dppx") { + if (typ == EnumToken.ResolutionTokenType && unit == "dppx") { unit = "x"; } @@ -1505,7 +1507,7 @@ export function printValue( case EnumToken.FlexTokenType: case EnumToken.PercentageTokenType: - const uni: string = token.typ == EnumToken.PercentageTokenType ? "%" : "fr"; + const uni: string = typ == EnumToken.PercentageTokenType ? "%" : "fr"; const perc: string = arena.hasFlags(token, EnumAstNodeStatus.HasFractionValue) ? printValue(arena.getNumericValue(token), arena, options, cache) : minifyNumber(arena.getNumericValue(token)); diff --git a/src/lib/syntax/color/color.ts b/src/lib/syntax/color/color.ts index b901ca718..57c1b221d 100644 --- a/src/lib/syntax/color/color.ts +++ b/src/lib/syntax/color/color.ts @@ -137,6 +137,7 @@ import { colorFuncColorSpace } from "../constants.ts"; import { trimArray } from "../../validation/match.ts"; import { alpha } from "./alpha.ts"; import { equalsIgnoreCase } from "../../parser/utils/text.ts"; +import { Arena } from "../../parser/arena.ts"; /** * Converts a color to another color space @@ -887,32 +888,34 @@ export function getNumber(token: NumberToken | PercentageToken | IdentToken | Fr * convert angle to turn * @param token */ -export function getAngle(token: NumberToken | AngleToken | IdentToken): number { - if (token.typ == EnumToken.IdenTokenType) { - if (token.val == "none") { +export function getAngle(token: number, arena: Arena): number { + + const typ = arena.getType(token); + + if (typ == EnumToken.IdenTokenType) { + if (arena.equalsIgnoreCase(token, 'val', "none")) { return 0; } } - if (token.typ == EnumToken.AngleTokenType) { - switch ((token as AngleToken).unit) { + const val = arena.getNumericValue(token); + + if (typ == EnumToken.AngleTokenType) { + switch (arena.getUnit(token)) { case "deg": - // @ts-ignore - return token.val / 360; + return val / 360; case "rad": - // @ts-ignore - return token.val / (2 * Math.PI); + return val / (2 * Math.PI); case "grad": - // @ts-ignore - return token.val / 400; + return val / 400; case "turn": - // @ts-ignore - return +token.val; + // @ts-ign + return +val; } } - return (token.val as number) / 360; + return val / 360; } From a9c6413a39f14a2905144460ac146b7fd9218a1a Mon Sep 17 00:00:00 2001 From: Thierry Bela Nanga Date: Sat, 17 Oct 2026 11:55:24 -0400 Subject: [PATCH 05/24] work in progress --- dist/index-umd-web.js | 25275 +++++++++------- dist/index.cjs | 22156 ++++++++------ dist/index.d.ts | 301 +- dist/package.json.js | 4 +- dist/src/data/properties.json.js | 2 +- dist/src/data/validation.json.js | 2 +- dist/src/lib/ast/clone.js | 2 +- dist/src/lib/ast/expand.js | 5 +- dist/src/lib/ast/features/calc.js | 2 +- dist/src/lib/ast/features/if.js | 7 +- dist/src/lib/ast/features/index.js | 2 +- .../lib/ast/features/inlinecssvariables.js | 5 +- dist/src/lib/ast/features/prefix.js | 6 +- dist/src/lib/ast/features/shorthand.js | 2 +- dist/src/lib/ast/features/transform.js | 8 +- dist/src/lib/ast/features/type.js | 2 +- dist/src/lib/ast/find.js | 2 +- dist/src/lib/ast/math/expression.js | 8 +- dist/src/lib/ast/math/math.js | 2 +- dist/src/lib/ast/minify.js | 12 +- dist/src/lib/ast/node.js | 2 +- dist/src/lib/ast/transform/compute.js | 2 +- dist/src/lib/ast/transform/matrix.js | 2 +- dist/src/lib/ast/transform/minify.js | 2 +- dist/src/lib/ast/transform/perspective.js | 2 +- dist/src/lib/ast/transform/rotate.js | 2 +- dist/src/lib/ast/transform/scale.js | 2 +- dist/src/lib/ast/transform/skew.js | 2 +- dist/src/lib/ast/transform/translate.js | 2 +- dist/src/lib/ast/transform/utils.js | 2 +- dist/src/lib/ast/types.js | 117 +- dist/src/lib/ast/walk.js | 14 +- dist/src/lib/fs/resolve.js | 2 +- dist/src/lib/parser/arena.js | 695 +- dist/src/lib/parser/declaration/list.js | 2 +- dist/src/lib/parser/declaration/map.js | 12 +- dist/src/lib/parser/declaration/set.js | 2 +- dist/src/lib/parser/linesmap.js | 2 +- dist/src/lib/parser/parse.js | 339 +- dist/src/lib/parser/source.js | 2 +- dist/src/lib/parser/tokenize.js | 2 +- dist/src/lib/parser/utils/angle.js | 2 +- .../src/lib/parser/utils/at-rule-container.js | 2 +- .../utils/at-rule-font-feature-values.js | 2 +- dist/src/lib/parser/utils/at-rule-generic.js | 2 +- dist/src/lib/parser/utils/at-rule-import.js | 2 +- dist/src/lib/parser/utils/at-rule-media.js | 2 +- dist/src/lib/parser/utils/at-rule-support.js | 2 +- .../src/lib/parser/utils/at-rule-when-else.js | 2 +- dist/src/lib/parser/utils/at-rule.js | 2 +- dist/src/lib/parser/utils/base64.js | 2 +- dist/src/lib/parser/utils/cache.js | 2 +- dist/src/lib/parser/utils/config.js | 2 +- dist/src/lib/parser/utils/declaration.js | 591 +- dist/src/lib/parser/utils/eq.js | 2 +- dist/src/lib/parser/utils/hash.js | 2 +- dist/src/lib/parser/utils/selector.js | 818 +- dist/src/lib/parser/utils/text.js | 2 +- dist/src/lib/parser/utils/token.js | 2 +- dist/src/lib/parser/utils/type.js | 2 +- dist/src/lib/printer/render.js | 54 +- dist/src/lib/printer/sourcemap/lib/codec.js | 2 +- dist/src/lib/printer/sourcemap/sourcemap.js | 2 +- dist/src/lib/syntax/color/a98rgb.js | 2 +- dist/src/lib/syntax/color/alpha.js | 2 +- dist/src/lib/syntax/color/cmyk.js | 2 +- dist/src/lib/syntax/color/color-mix.js | 2 +- dist/src/lib/syntax/color/color.js | 31 +- dist/src/lib/syntax/color/hex.js | 2 +- dist/src/lib/syntax/color/hsl.js | 2 +- dist/src/lib/syntax/color/hwb.js | 2 +- dist/src/lib/syntax/color/lab.js | 2 +- dist/src/lib/syntax/color/lch.js | 2 +- dist/src/lib/syntax/color/oklab.js | 2 +- dist/src/lib/syntax/color/oklch.js | 2 +- dist/src/lib/syntax/color/p3.js | 2 +- dist/src/lib/syntax/color/prophotorgb.js | 2 +- dist/src/lib/syntax/color/rec2020.js | 2 +- dist/src/lib/syntax/color/relative-color.js | 2 +- dist/src/lib/syntax/color/rgb.js | 2 +- dist/src/lib/syntax/color/srgb.js | 2 +- dist/src/lib/syntax/color/utils/color-type.js | 2 +- dist/src/lib/syntax/color/utils/colorspace.js | 2 +- dist/src/lib/syntax/color/utils/components.js | 2 +- dist/src/lib/syntax/color/utils/distance.js | 2 +- dist/src/lib/syntax/color/utils/make-color.js | 2 +- dist/src/lib/syntax/color/utils/matrix.js | 2 +- dist/src/lib/syntax/color/xyz.js | 2 +- dist/src/lib/syntax/color/xyzd50.js | 2 +- dist/src/lib/syntax/constants.js | 5 +- dist/src/lib/syntax/syntax.js | 72 +- dist/src/lib/validation/config.js | 2 +- dist/src/lib/validation/match.js | 178 +- dist/src/lib/validation/parser/parse.js | 2 +- dist/src/lib/validation/parser/typedef.js | 2 +- dist/src/lib/validation/utils/list.js | 14 +- dist/src/node.js | 9 +- dist/src/types.js | 2 +- dist/src/utils/sync.js | 2 +- dist/src/web.js | 10 +- src/@types/index.d.ts | 7 +- src/lib/parser/arena.ts | 21 +- src/lib/parser/utils/declaration.ts | 2 +- test/specs/code/color-a98-rgb.js | 1193 +- test/specs/code/color-cmyk.js | 635 +- test/specs/code/color-display-p3.js | 1163 +- test/specs/code/color-hex.js | 319 +- test/specs/code/color-hsl.js | 628 +- test/specs/code/color-hwb.js | 676 +- test/specs/code/color-lab.js | 624 +- test/specs/code/color-lch.js | 609 +- test/specs/code/color-linear-srgb.js | 1209 +- test/specs/code/color-oklab.js | 607 +- test/specs/code/color-oklch.js | 626 +- test/specs/code/color-prophoto-rgb.js | 1156 +- test/specs/code/color-rec2020.js | 1126 +- test/specs/code/color-rgb.js | 582 +- test/specs/code/color-srgb.js | 1103 +- test/specs/code/color-xyz-d50.js | 1140 +- test/specs/code/color-xyz.js | 1140 +- test/specs/code/modules.js | 2 - test/specs/code/sourcemaps.js | 1 - test/specs/code/visitors.js | 51 +- test/specs/code/walk.js | 1 - test/specs/node.spec.js | 35 +- test/specs/web.spec.js | 39 +- 126 files changed, 36139 insertions(+), 29450 deletions(-) diff --git a/dist/index-umd-web.js b/dist/index-umd-web.js index 0625dade7..99fe9ac4b 100644 --- a/dist/index-umd-web.js +++ b/dist/index-umd-web.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ (function (global, factory) { @@ -61,49 +61,26 @@ */ EnumAstNodeStatus[EnumAstNodeStatus["Malformed"] = 128] = "Malformed"; /** - * Important declaration - */ - EnumAstNodeStatus[EnumAstNodeStatus["ImportantDeclaration"] = 256] = "ImportantDeclaration"; - /** - * Color component without unit - */ - EnumAstNodeStatus[EnumAstNodeStatus["PowerlessColorComponent"] = 512] = "PowerlessColorComponent"; - })(exports.EnumAstNodeStatus || (exports.EnumAstNodeStatus = {})); - /** - * Enum of validation levels - * @deprecated - */ - exports.ValidationLevel = void 0; - (function (ValidationLevel) { - /** - * disable validation - */ - ValidationLevel[ValidationLevel["None"] = 0] = "None"; - /** - * validate selectors + * Node has children */ - ValidationLevel[ValidationLevel["Selector"] = 1] = "Selector"; + EnumAstNodeStatus[EnumAstNodeStatus["HasChildren"] = 256] = "HasChildren"; /** - * validate at-rules + * Node has values */ - ValidationLevel[ValidationLevel["AtRule"] = 2] = "AtRule"; + EnumAstNodeStatus[EnumAstNodeStatus["HasValues"] = 512] = "HasValues"; /** - * validate declarations + * Node has fraction value */ - ValidationLevel[ValidationLevel["Declaration"] = 4] = "Declaration"; + EnumAstNodeStatus[EnumAstNodeStatus["HasFractionValue"] = 1024] = "HasFractionValue"; /** - * validate selectors and at-rules - */ - ValidationLevel[ValidationLevel["Default"] = 3] = "Default"; - /** - * validate selectors, at-rules and declarations + * Important declaration */ - ValidationLevel[ValidationLevel["All"] = 7] = "All"; + EnumAstNodeStatus[EnumAstNodeStatus["ImportantDeclaration"] = 2048] = "ImportantDeclaration"; /** - * Report only. Apply validation and report nodes that are marked as invalid + * Color component without unit */ - ValidationLevel[ValidationLevel["ReportOnly"] = 8] = "ReportOnly"; - })(exports.ValidationLevel || (exports.ValidationLevel = {})); + EnumAstNodeStatus[EnumAstNodeStatus["PowerlessColorComponent"] = 4096] = "PowerlessColorComponent"; + })(exports.EnumAstNodeStatus || (exports.EnumAstNodeStatus = {})); /** * Enum of all token types */ @@ -348,7 +325,7 @@ /** * token list token type */ - EnumToken[EnumToken["ListToken"] = 59] = "ListToken"; + EnumToken[EnumToken["ListTokenType"] = 59] = "ListTokenType"; /* arithmetic tokens */ /** * addition token type @@ -811,135 +788,135 @@ /** * deprecated system colors */ - ColorType[ColorType["SYS"] = 0] = "SYS"; + ColorType[ColorType["SYS"] = 1] = "SYS"; /** * deprecated system colors */ - ColorType[ColorType["DPSYS"] = 1] = "DPSYS"; + ColorType[ColorType["DPSYS"] = 2] = "DPSYS"; /** * named colors */ - ColorType[ColorType["LIT"] = 2] = "LIT"; + ColorType[ColorType["LIT"] = 3] = "LIT"; /** * colors as hex values */ - ColorType[ColorType["HEX"] = 3] = "HEX"; + ColorType[ColorType["HEX"] = 4] = "HEX"; /** * colors as rgb values */ - ColorType[ColorType["RGBA"] = 4] = "RGBA"; + ColorType[ColorType["RGBA"] = 5] = "RGBA"; /** * colors using rgb */ - ColorType[ColorType["HSLA"] = 5] = "HSLA"; + ColorType[ColorType["HSLA"] = 6] = "HSLA"; /** * colors using hwb */ - ColorType[ColorType["HWB"] = 6] = "HWB"; + ColorType[ColorType["HWB"] = 7] = "HWB"; /** * colors using cmyk */ - ColorType[ColorType["CMYK"] = 7] = "CMYK"; + ColorType[ColorType["CMYK"] = 8] = "CMYK"; /** * colors using oklab * */ - ColorType[ColorType["OKLAB"] = 8] = "OKLAB"; + ColorType[ColorType["OKLAB"] = 9] = "OKLAB"; /** * colors using oklch * */ - ColorType[ColorType["OKLCH"] = 9] = "OKLCH"; + ColorType[ColorType["OKLCH"] = 10] = "OKLCH"; /** * colors using lab */ - ColorType[ColorType["LAB"] = 10] = "LAB"; + ColorType[ColorType["LAB"] = 11] = "LAB"; /** * colors using lch */ - ColorType[ColorType["LCH"] = 11] = "LCH"; + ColorType[ColorType["LCH"] = 12] = "LCH"; /** * colors using color() function */ - ColorType[ColorType["COLOR"] = 12] = "COLOR"; + ColorType[ColorType["COLOR"] = 13] = "COLOR"; /** * color using srgb */ - ColorType[ColorType["SRGB"] = 13] = "SRGB"; + ColorType[ColorType["SRGB"] = 14] = "SRGB"; /** * color using prophoto-rgb */ - ColorType[ColorType["PROPHOTO_RGB"] = 14] = "PROPHOTO_RGB"; + ColorType[ColorType["PROPHOTO_RGB"] = 15] = "PROPHOTO_RGB"; /** * color using a98-rgb */ - ColorType[ColorType["A98_RGB"] = 15] = "A98_RGB"; + ColorType[ColorType["A98_RGB"] = 16] = "A98_RGB"; /** * color using rec2020 */ - ColorType[ColorType["REC2020"] = 16] = "REC2020"; + ColorType[ColorType["REC2020"] = 17] = "REC2020"; /** * color using display-p3 */ - ColorType[ColorType["DISPLAY_P3"] = 17] = "DISPLAY_P3"; + ColorType[ColorType["DISPLAY_P3"] = 18] = "DISPLAY_P3"; /** * color using srgb-linear */ - ColorType[ColorType["SRGB_LINEAR"] = 18] = "SRGB_LINEAR"; + ColorType[ColorType["SRGB_LINEAR"] = 19] = "SRGB_LINEAR"; /** * color using xyz-d50 */ - ColorType[ColorType["XYZ_D50"] = 19] = "XYZ_D50"; + ColorType[ColorType["XYZ_D50"] = 20] = "XYZ_D50"; /** * color using xyz-d65 */ - ColorType[ColorType["XYZ_D65"] = 20] = "XYZ_D65"; + ColorType[ColorType["XYZ_D65"] = 21] = "XYZ_D65"; /** * light-dark() color function */ - ColorType[ColorType["LIGHT_DARK"] = 21] = "LIGHT_DARK"; + ColorType[ColorType["LIGHT_DARK"] = 22] = "LIGHT_DARK"; /** * color-mix() color function */ - ColorType[ColorType["COLOR_MIX"] = 22] = "COLOR_MIX"; + ColorType[ColorType["COLOR_MIX"] = 23] = "COLOR_MIX"; /** * non-standard color */ - ColorType[ColorType["NON_STD"] = 23] = "NON_STD"; + ColorType[ColorType["NON_STD"] = 24] = "NON_STD"; /** * custom color */ - ColorType[ColorType["CUSTOM_COLOR"] = 24] = "CUSTOM_COLOR"; + ColorType[ColorType["CUSTOM_COLOR"] = 25] = "CUSTOM_COLOR"; /** * alpha() color function */ - ColorType[ColorType["ALPHA"] = 25] = "ALPHA"; + ColorType[ColorType["ALPHA"] = 26] = "ALPHA"; /** * color using display-p3-linear */ - ColorType[ColorType["DISPLAY_P3_LINEAR"] = 26] = "DISPLAY_P3_LINEAR"; + ColorType[ColorType["DISPLAY_P3_LINEAR"] = 27] = "DISPLAY_P3_LINEAR"; /** * Contrast color */ - ColorType[ColorType["CONTRAST_COLOR"] = 27] = "CONTRAST_COLOR"; + ColorType[ColorType["CONTRAST_COLOR"] = 28] = "CONTRAST_COLOR"; /** * color layers */ - ColorType[ColorType["COLOR_LAYERS"] = 28] = "COLOR_LAYERS"; + ColorType[ColorType["COLOR_LAYERS"] = 29] = "COLOR_LAYERS"; /** * alias for rgba */ - ColorType[ColorType["RGB"] = 4] = "RGB"; + ColorType[ColorType["RGB"] = 5] = "RGB"; /** * alias for hsl */ - ColorType[ColorType["HSL"] = 5] = "HSL"; + ColorType[ColorType["HSL"] = 6] = "HSL"; /** * alias for xyz-d65 */ - ColorType[ColorType["XYZ"] = 20] = "XYZ"; + ColorType[ColorType["XYZ"] = 21] = "XYZ"; /** * alias for cmyk */ - ColorType[ColorType["DEVICE_CMYK"] = 7] = "DEVICE_CMYK"; + ColorType[ColorType["DEVICE_CMYK"] = 8] = "DEVICE_CMYK"; })(exports.ColorType || (exports.ColorType = {})); /** * Supported module case transform @@ -6393,7 +6370,6 @@ const LOC = Symbol.for("loc"); const RAW = Symbol.for("raw"); const STATE = Symbol.for("state"); - const ROOT = Symbol.for("root"); const ERRORS = Symbol.for("errors"); const TOKENS = Symbol.for("tokens"); const PARENT = Symbol.for("parent"); @@ -9911,7 +9887,6 @@ let isNumeric = false; let value; let option; - let node; let i = -1; const eventType = filter.event ?? exports.WalkerEvent.Enter; // @ts-ignore @@ -9945,16 +9920,7 @@ (typeof filter.type == "function" && filter.type(value)); if (isValid) { // @ts-expect-error - option = filter.fn(value, value[PARENT] ?? root, exports.WalkerEvent.Enter, - // @ts-expect-error - function* () { - // @ts-expect-error - let parent = node[PARENT]; - while (parent != null) { - yield parent; - parent = parent[PARENT] ?? null; - } - }); + option = filter.fn(value); isNumeric = typeof option == "number"; if (isNumeric && option & exports.WalkerOptionEnum.Stop) { return; @@ -10257,7 +10223,7 @@ if (token.typ == exports.EnumToken.Sub) { if (!isScalarToken(nodes[i + 1])) { token = { - typ: exports.EnumToken.ListToken, + typ: exports.EnumToken.ListTokenType, chi: [nodes[i], nodes[i + 1]], [LOCSRCID]: nodes[i][LOCSRCID], [LOCSTA]: nodes[i][LOCSTA], @@ -10305,7 +10271,7 @@ return acc; } } - if (acc.length > 0 && curr[0] != exports.EnumToken.ListToken) { + if (acc.length > 0 && curr[0] != exports.EnumToken.ListTokenType) { acc.push({ typ: exports.EnumToken.Add, [LOCSRCID]: token[LOCSRCID], @@ -10996,7 +10962,7 @@ return [factorToken(tokens[0])]; } for (let i = 0; i < tokens.length; i++) { - if (tokens[i].typ == exports.EnumToken.ListToken) { + if (tokens[i].typ == exports.EnumToken.ListTokenType) { // @ts-ignore tokens.splice(i, 1, ...tokens[i].chi); } @@ -12400,11 +12366,12 @@ * @param tokens * @returns */ - function trimArray(tokens) { - while (tokens[0]?.typ === exports.EnumToken.WhitespaceTokenType) { + function trimArray(tokens, options) { + const arena = options.arena; + while (arena.getType(tokens[0]) === exports.EnumToken.WhitespaceTokenType) { tokens.shift(); } - while (tokens[tokens.length - 1]?.typ === exports.EnumToken.WhitespaceTokenType) { + while (arena.getType(tokens[tokens.length - 1]) === exports.EnumToken.WhitespaceTokenType) { tokens.pop(); } return tokens; @@ -12492,8 +12459,7 @@ case MediaFeatureType.RatioType: return { valid: true, - success: (tokens.length == 1 && - tokens[0].typ == exports.EnumToken.NumberTokenType) || + success: (tokens.length == 1 && tokens[0].typ == exports.EnumToken.NumberTokenType) || (tokens.length === 3 && tokens[0].typ == exports.EnumToken.NumberTokenType && typeof tokens[0].val === "number" && @@ -12515,9 +12481,10 @@ * @param tokens * @returns */ - function createValidationContext(tokens) { - tokens = trimArray(tokens.filter((t) => t.typ !== exports.EnumToken.CommentTokenType)); - if (tokens.at(-1)?.typ === exports.EnumToken.ImportantTokenType) { + function createValidationContext(tokens, options) { + const arena = options.arena; + tokens = trimArray(tokens.filter((t) => arena.getType(t) !== exports.EnumToken.CommentTokenType)); + if (arena.getType(tokens.at(-1)) === exports.EnumToken.ImportantTokenType) { tokens.pop(); trimArray(tokens); } @@ -12672,15 +12639,22 @@ function matchSelectorSyntax(stream, errors, options, nested = true) { const stack = []; const tokens = []; + const arena = options.arena; let token; let i = 0; let success = true; - while (i < stream.length && - (stream[i].typ === exports.EnumToken.WhitespaceTokenType || stream[i].typ === exports.EnumToken.CommentTokenType)) { + let typ; + let val; + while (i < stream.length) { + typ = arena.getType(stream[i]); + if (typ !== exports.EnumToken.WhitespaceTokenType && typ !== exports.EnumToken.CommentTokenType) { + break; + } i++; } if (i < stream.length) { - switch (stream[i].typ) { + typ = arena.getType(stream[i]); + switch (typ) { case exports.EnumToken.Plus: case exports.EnumToken.Tilda: case exports.EnumToken.GtTokenType: @@ -12694,10 +12668,10 @@ errors: [ { action: "drop", - message: `Unexpected token ${exports.EnumToken[stream[i].typ]}`, + message: `Unexpected token ${exports.EnumToken[typ]}`, node: stream[i], // @ts-expect-error - location: options.source.getSourceLocation(stream[i][LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(stream[i])), }, ], }; @@ -12706,29 +12680,31 @@ } for (; i < stream.length; i++) { token = stream[i]; - if (token.typ === exports.EnumToken.Star) { - token.typ = exports.EnumToken.UniversalSelectorTokenType; + typ = arena.getType(token); + if (typ === exports.EnumToken.Star) { + typ = exports.EnumToken.UniversalSelectorTokenType; } tokens.push(token); - if (tokensfuncDefMap.has(token.typ)) { - if (stack.length > 0 && selectorNodeTypes.includes(stack.at(-1).typ)) { + if (tokensfuncDefMap.has(typ)) { + if (stack.length > 0 && selectorNodeTypes.includes(arena.getType(stack.at(-1)))) { stack.pop(); } stack.push(token); continue; } if (stack.length > 0 && - selectorNodeTypes.includes(stack.at(-1).typ) && - !selectorNodeTypes.includes(token.typ) && - token.typ !== exports.EnumToken.WhitespaceTokenType && - token.typ !== exports.EnumToken.CommentTokenType && - token.typ !== exports.EnumToken.CDOCOMMTokenType) { + selectorNodeTypes.includes(arena.getType(stack.at(-1))) && + !selectorNodeTypes.includes(typ) && + typ !== exports.EnumToken.WhitespaceTokenType && + typ !== exports.EnumToken.CommentTokenType && + typ !== exports.EnumToken.CDOCOMMTokenType) { stack.pop(); } - switch (token.typ) { + switch (typ) { case exports.EnumToken.PseudoClassFuncTokenType: { - const result = matchAllSyntaxes(getParsedSyntax(ValidationSyntaxGroupEnum.Selectors, token.val + "()")?.[0]?.chi ?? [], createValidationContext(token.chi), options); + val = arena.getValue(token); + const result = matchAllSyntaxes(getParsedSyntax(ValidationSyntaxGroupEnum.Selectors, val + "()")?.[0]?.chi ?? [], createValidationContext(arena.children(token), options), options); if (!result.success) { success = false; if (result.errors.length > 0) { @@ -12753,41 +12729,43 @@ message: `Nesting selector is not allowed`, node: token, // @ts-expect-error - location: options.source.getSourceLocation(token[LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(token)), }, ], }; } break; case exports.EnumToken.Plus: - Object.assign(token, { typ: exports.EnumToken.NextSiblingCombinatorTokenType }); + arena.set(token, exports.EnumToken.SubsequentSiblingCombinatorTokenType, 0); + // Object.assign(token, { typ: EnumToken.NextSiblingCombinatorTokenType }); stack.push(token); break; case exports.EnumToken.Tilda: case exports.EnumToken.GtTokenType: - Object.assign(token, { typ: selectorEnumMap.get(token.typ) }); + arena.set(token, selectorEnumMap.get(typ)); + // Object.assign(token, { typ: selectorEnumMap.get(token.typ)! }); case exports.EnumToken.ColumnCombinatorTokenType: case exports.EnumToken.ChildCombinatorTokenType: case exports.EnumToken.UniversalSelectorTokenType: case exports.EnumToken.DescendantCombinatorTokenType: case exports.EnumToken.NextSiblingCombinatorTokenType: case exports.EnumToken.SubsequentSiblingCombinatorTokenType: - if (stream[i + 1]?.typ === exports.EnumToken.WhitespaceTokenType) { + if (arena.getType(stream[i + 1]) === exports.EnumToken.WhitespaceTokenType) { i++; } - if (stack.length > 0 && stack.at(-1)?.typ === exports.EnumToken.UniversalSelectorTokenType) { + if (stack.length > 0 && arena.getType(stack.at(-1)) === exports.EnumToken.UniversalSelectorTokenType) { stack.pop(); } - if (stack.length > 0 && selectorNodeTypes.includes(stack.at(-1)?.typ)) { + if (stack.length > 0 && selectorNodeTypes.includes(arena.getType(stack.at(-1)))) { return { success: false, errors: [ { action: "drop", - message: `Unexpected combinator ${exports.EnumToken[token.typ]}`, + message: `Unexpected combinator ${exports.EnumToken[typ]}`, node: token, // @ts-expect-error - location: options.source.getSourceLocation(token[LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(token)), }, ], }; @@ -12795,10 +12773,20 @@ stack.push(token); break; case exports.EnumToken.CommaTokenType: - if (stack.length > 0 && stack.at(-1).typ === exports.EnumToken.UniversalSelectorTokenType) { + if (stack.length > 0 && arena.getType(stack.at(-1)) === exports.EnumToken.UniversalSelectorTokenType) { stack.pop(); } stack.push(token); + if (!nested) { + let k = i + 1; + while (k < stream.length) { + typ = arena.getType(stream[k]); + if (typ !== exports.EnumToken.WhitespaceTokenType && typ !== exports.EnumToken.CommentTokenType) { + break; + } + k++; + } + } break; case exports.EnumToken.Pipe: case exports.EnumToken.DelimTokenType: @@ -12819,19 +12807,19 @@ stack.push(token); break; case exports.EnumToken.AttrEndTokenType: - if (stack.length > 0 && stack.at(-1).typ === exports.EnumToken.UniversalSelectorTokenType) { + if (stack.length > 0 && arena.getType(stack.at(-1)) === exports.EnumToken.UniversalSelectorTokenType) { stack.pop(); } - if (stack.at(-1)?.typ !== exports.EnumToken.AttrStartTokenType) { + if (arena.getType(stack.at(-1)) !== exports.EnumToken.AttrStartTokenType) { return { success: false, errors: [ { action: "drop", - message: `Unexpected token ${exports.EnumToken[token.typ]}`, + message: `Unexpected token ${exports.EnumToken[typ]}`, node: token, // @ts-expect-error - location: options.source.getSourceLocation(token[LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(token)), }, ], }; @@ -12841,17 +12829,18 @@ let index = tokens.indexOf(stack.at(-1)); const slice = []; for (let n = index + 1; n < k; n++) { - if (tokens[n].typ === exports.EnumToken.WhitespaceTokenType || - tokens[n].typ === exports.EnumToken.CommentTokenType || - tokens[n].typ === exports.EnumToken.CDOCOMMTokenType) { + typ = arena.getType(tokens[n]); + if (typ === exports.EnumToken.WhitespaceTokenType || + typ === exports.EnumToken.CommentTokenType || + typ === exports.EnumToken.CDOCOMMTokenType) { continue; } slice.push(tokens[n]); } - if (slice[1]?.typ === exports.EnumToken.Pipe) { + if (arena.getType(slice[1]) === exports.EnumToken.Pipe) { slice.shift(); } - if (slice[0].typ === exports.EnumToken.Pipe) { + if (arena.getType(slice[0]) === exports.EnumToken.Pipe) { slice.shift(); } if (slice.length === 1) { @@ -12860,29 +12849,29 @@ } slice.shift(); slice.shift(); - if (slice[0]?.typ === exports.EnumToken.StringTokenType && - /^[a-zA-Z0-9_-]+$/.test(slice[0].val.slice(1, -1))) { - Object.assign(slice[0], { - typ: exports.EnumToken.IdenTokenType, - val: slice[0].val.slice(1, -1), - }); + if (arena.getType(slice[0]) === exports.EnumToken.StringTokenType) { + val = arena.getValue(slice[0]); + if (/^[a-zA-Z0-9_-]+$/.test(val.slice(1, -1))) { + arena.set(slice[0], exports.EnumToken.IdenTokenType, val.slice(1, -1)); + } } slice.shift(); if (slice.length === 0) { stack.pop(); break; } - if (slice[0].typ !== exports.EnumToken.IdenTokenType || - (slice[0].val != "i" && slice[0].val != "s")) { + val = arena.getValue(slice[0]); + if (arena.getType(slice[0]) !== exports.EnumToken.IdenTokenType || + (val != "i" && val != "s")) { return { success: false, errors: [ { action: "drop", - message: `Unexpected token ${exports.EnumToken[slice[0].typ]}`, + message: `Unexpected token ${exports.EnumToken[arena.getType(slice[0])]}`, node: slice[0], // @ts-expect-error - location: options.source.getSourceLocation(slice[0][LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(slice[0])), }, ], }; @@ -12897,19 +12886,19 @@ errors: [ { action: "drop", - message: `Unexpected token ${exports.EnumToken[token.typ]}`, + message: `Unexpected token ${exports.EnumToken[typ]}`, node: token, // @ts-expect-error - location: options.source.getSourceLocation(token[LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(token)), }, ], }; case exports.EnumToken.EndParensTokenType: - if (stack.length > 0 && stack.at(-1)?.typ === exports.EnumToken.UniversalSelectorTokenType) { + if (stack.length > 0 && arena.getType(stack.at(-1)) === exports.EnumToken.UniversalSelectorTokenType) { stack.pop(); } - if (stack.at(-1)?.typ === exports.EnumToken.PseudoClassFunctionTokenDefType || - stack.at(-1)?.typ === exports.EnumToken.PseudoElementTokenType) { + if (arena.getType(stack.at(-1)) === exports.EnumToken.PseudoClassFunctionTokenDefType || + arena.getType(stack.at(-1)) === exports.EnumToken.PseudoElementTokenType) { const token = stack.at(-1); const index = tokens.indexOf(token); const result = matchAllSyntaxes(getParsedSyntax(ValidationSyntaxGroupEnum.Selectors, token.val + "()")?.[0]?.chi ?? [], createValidationContext(tokens.slice(index + 1, tokens.length - 1)), options); @@ -12929,10 +12918,10 @@ errors: [ { action: "drop", - message: `Unexpected token ${exports.EnumToken[token.typ]}`, + message: `Unexpected token ${exports.EnumToken[typ]}`, node: token, // @ts-expect-error - location: options.source.getSourceLocation(token[LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(token)), }, ], }; @@ -12942,7 +12931,7 @@ // if (stack.at(-1)?.typ === EnumToken.CommaTokenType) { // stack.pop(); // } - if (stack.at(-1)?.typ === exports.EnumToken.PseudoClassFunctionTokenDefType) { + if (arena.getType(stack.at(-1)) === exports.EnumToken.PseudoClassFunctionTokenDefType) { break; } default: @@ -12951,10 +12940,10 @@ errors: [ { action: "drop", - message: `Unsupported selector token ${exports.EnumToken[token.typ]}`, + message: `Unsupported selector token ${exports.EnumToken[typ]}`, node: token, // @ts-expect-error - location: options.source.getSourceLocation(token[LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(token)), }, ], }; @@ -12968,7 +12957,7 @@ tokens.splice(tokens.length - 2, 1); } } - if (stack.length > 0 && stack.at(-1).typ === exports.EnumToken.UniversalSelectorTokenType) { + if (stack.length > 0 && arena.getType(stack.at(-1)) === exports.EnumToken.UniversalSelectorTokenType) { stack.pop(); } if (stack.length > 0) { @@ -12977,10 +12966,10 @@ errors: [ { action: "drop", - message: `Unmatched token ${exports.EnumToken[stack.at(-1).typ]}`, + message: `Unmatched token ${exports.EnumToken[arena.getType(stack.at(-1))]}`, node: stack.at(-1), // @ts-expect-error - location: options.source.getSourceLocation(stack.at(-1)[LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(stack.at(-1))), }, ], }; @@ -14061,17 +14050,15 @@ case "display-internal": case "display-outside": case "display-legacy": - case "content-position": - { - let values = propertyKeywordValues.get(property.val); - if (values == null) { - values = new Set(config$3.syntaxes[property.val].syntax.split(/[\s|]+/)); - propertyKeywordValues.set(property.val, values); - } - success = - token?.typ == exports.EnumToken.IdenTokenType && values.has(token.val.toLowerCase()); - break; + case "content-position": { + let values = propertyKeywordValues.get(property.val); + if (values == null) { + values = new Set(config$3.syntaxes[property.val].syntax.split(/[\s|]+/)); + propertyKeywordValues.set(property.val, values); } + success = token?.typ == exports.EnumToken.IdenTokenType && values.has(token.val.toLowerCase()); + break; + } case "mf-name": { const token = context.peek(); @@ -14091,8 +14078,7 @@ case "custom-ident": case "counter-name": case "counter-style-name": - success = - token?.typ == exports.EnumToken.IdenTokenType || token?.typ == exports.EnumToken.DashedIdenTokenType; + success = token?.typ == exports.EnumToken.IdenTokenType || token?.typ == exports.EnumToken.DashedIdenTokenType; if (success && token?.typ === exports.EnumToken.IdenTokenType) { const val = token.val.toLowerCase(); success = "none" !== val && !allValues.includes(val); @@ -15319,29 +15305,28 @@ * convert angle to turn * @param token */ - function getAngle(token) { - if (token.typ == exports.EnumToken.IdenTokenType) { - if (token.val == "none") { + function getAngle(token, arena) { + const typ = arena.getType(token); + if (typ == exports.EnumToken.IdenTokenType) { + if (arena.equalsIgnoreCase(token, 'val', "none")) { return 0; } } - if (token.typ == exports.EnumToken.AngleTokenType) { - switch (token.unit) { + const val = arena.getNumericValue(token); + if (typ == exports.EnumToken.AngleTokenType) { + switch (arena.getUnit(token)) { case "deg": - // @ts-ignore - return token.val / 360; + return val / 360; case "rad": - // @ts-ignore - return token.val / (2 * Math.PI); + return val / (2 * Math.PI); case "grad": - // @ts-ignore - return token.val / 400; + return val / 400; case "turn": - // @ts-ignore - return +token.val; + // @ts-ign + return +val; } } - return token.val / 360; + return val / 360; } /** @@ -15425,19 +15410,23 @@ return angle; } - function stripCommaToken(tokenList) { + function stripCommaToken(tokenList, options) { let result = []; + let typ; + const arena = options.arena; for (let i = 0; i < tokenList.length; i++) { - if (tokenList[i].typ == exports.EnumToken.CommentTokenType || tokenList[i].typ == exports.EnumToken.CommaTokenType) { + typ = arena.getType(tokenList[i]); + if (typ == exports.EnumToken.CommentTokenType || typ == exports.EnumToken.CommaTokenType) { continue; } result.push(tokenList[i]); } return result; } - function splitTokenList(tokenList, split = [exports.EnumToken.CommaTokenType], includeSplitToken = false) { + function splitTokenList(tokenList, options, split = [exports.EnumToken.CommaTokenType], includeSplitToken = false) { + const arena = options.arena; return tokenList.reduce((acc, curr) => { - if (split.includes(curr.typ)) { + if (split.includes(arena.getType(curr))) { if (includeSplitToken && Array.isArray(acc[acc.length - 1])) { acc[acc.length - 1].push(curr); } @@ -15997,7 +15986,7 @@ */ // export function isColorspace(token: Token): boolean { // return ( - // token.typ === EnumToken.IdenTokenType && + // typ === EnumToken.IdenTokenType && // [ // "srgb", // "srgb-linear", @@ -16203,7 +16192,7 @@ * @returns */ function isRectangularOrthogonalColorspace(token) { - return (token.typ === exports.EnumToken.IdenTokenType && + return (typ === exports.EnumToken.IdenTokenType && colorFuncColorSpace.some((t) => equalsIgnoreCase(t, token.val))); } /** @@ -16212,7 +16201,7 @@ * @returns */ function isPolarColorspace(token) { - return (token.typ === exports.EnumToken.IdenTokenType && + return (typ === exports.EnumToken.IdenTokenType && ["hsl", "hwb", "lch", "oklch"].some((t) => equalsIgnoreCase(t, token.val))); } /** @@ -16222,7 +16211,7 @@ */ // export function isHueInterpolationMethod(token: Token | Token[]): boolean { // if (!Array.isArray(token)) { - // return token.typ == EnumToken.IdenTokenType && "hue" === (token as IdentToken).val?.toLowerCase?.(); + // return typ == EnumToken.IdenTokenType && "hue" === (token as IdentToken).val?.toLowerCase?.(); // } // // if (token.length != 2 || token[0].typ != EnumToken.IdenTokenType || token[1].typ != EnumToken.IdenTokenType) { @@ -16241,26 +16230,27 @@ * @returns */ function isIdentColor(token) { - return (token.typ == exports.EnumToken.ColorTokenType && + return (typ == exports.EnumToken.ColorTokenType && [exports.ColorType.SYS, exports.ColorType.DPSYS, exports.ColorType.LIT].includes(token.kin) && isIdent(token.val)); } // export function isPercentageToken(token: Token): boolean { // return ( - // token.typ == EnumToken.PercentageTokenType || - // (token.typ == EnumToken.NumberTokenType && (token as NumberToken).val == 0) + // typ == EnumToken.PercentageTokenType || + // (typ == EnumToken.NumberTokenType && (token as NumberToken).val == 0) // ); // } - function isColor(token, errors) { - if (token.typ == exports.EnumToken.WildCardFunctionTokenType) { + function isColor(token, arena) { + const typ = arena.getType(token); + if (typ == exports.EnumToken.WildCardFunctionTokenType) { return true; } - if (token.typ == exports.EnumToken.ColorTokenType) { + if (typ == exports.EnumToken.ColorTokenType) { if ("kin" in token && !("chi" in token)) { return true; } } - if (token.typ == exports.EnumToken.IdenTokenType) { + if (typ == exports.EnumToken.IdenTokenType) { const val = token.val.toLowerCase(); if (systemColors.has(val) || deprecatedSystemColors.has(val) || nonStandardColors.has(val)) { return true; @@ -16268,7 +16258,7 @@ // named color return val in COLORS_NAMES || "currentcolor" === val || "transparent" === val; } - if (token.typ === exports.EnumToken.FunctionTokenType || token.typ === exports.EnumToken.ColorTokenType) { + if (typ === exports.EnumToken.FunctionTokenType || typ === exports.EnumToken.ColorTokenType) { // if (!colorsFunc.includes((token as FunctionToken).val.toLowerCase())) { // return false; // } @@ -16548,8 +16538,9 @@ } return false; } - function parseColor(token) { - if (token.typ === exports.EnumToken.IdenTokenType) { + function parseColor(token, arena) { + const typ = arena.getType(token); + if (typ === exports.EnumToken.IdenTokenType) { const val = token.val.toLowerCase(); if (nonStandardColors.has(val)) { Object.assign(token, { @@ -16581,7 +16572,7 @@ return token; } } - if (token.typ === exports.EnumToken.ColorTokenType) { + if (typ === exports.EnumToken.ColorTokenType) { if (!("kin" in token) && "val" in token) { // @ts-expect-error token.kin = exports.ColorType[token.val.replaceAll("-", "_").toUpperCase()]; @@ -16927,24 +16918,24 @@ // } // // return ( - // token.typ === EnumToken.IdenTokenType || - // token.typ === EnumToken.DimensionTokenType || - // token.typ === EnumToken.LengthTokenType || - // token.typ === EnumToken.AngleTokenType || - // token.typ === EnumToken.FlexTokenType || - // token.typ === EnumToken.TimeTokenType || - // token.typ === EnumToken.ResolutionTokenType || - // token.typ === EnumToken.FrequencyTokenType || - // token.typ === EnumToken.NumberTokenType || - // token.typ === EnumToken.ColorTokenType || - // token.typ === EnumToken.FunctionTokenType || - // token.typ === EnumToken.UrlFunctionTokenType || - // token.typ === EnumToken.GridTemplateFuncTokenType || - // token.typ === EnumToken.ImageFunctionTokenType || - // token.typ === EnumToken.TimelineFunctionTokenType || - // token.typ === EnumToken.TimingFunctionTokenType || - // token.typ === EnumToken.MathFunctionTokenType || - // token.typ === EnumToken.TransformFunctionTokenType + // typ === EnumToken.IdenTokenType || + // typ === EnumToken.DimensionTokenType || + // typ === EnumToken.LengthTokenType || + // typ === EnumToken.AngleTokenType || + // typ === EnumToken.FlexTokenType || + // typ === EnumToken.TimeTokenType || + // typ === EnumToken.ResolutionTokenType || + // typ === EnumToken.FrequencyTokenType || + // typ === EnumToken.NumberTokenType || + // typ === EnumToken.ColorTokenType || + // typ === EnumToken.FunctionTokenType || + // typ === EnumToken.UrlFunctionTokenType || + // typ === EnumToken.GridTemplateFuncTokenType || + // typ === EnumToken.ImageFunctionTokenType || + // typ === EnumToken.TimelineFunctionTokenType || + // typ === EnumToken.TimingFunctionTokenType || + // typ === EnumToken.MathFunctionTokenType || + // typ === EnumToken.TransformFunctionTokenType // ); // } // https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Values_and_units#absolute_length_units @@ -17072,5541 +17063,4842 @@ } /** - * feature walk mode + * expand css nesting ast nodes + * @param ast * * @private */ - exports.FeatureWalkMode = void 0; - (function (FeatureWalkMode) { - /** - * pre process - */ - FeatureWalkMode[FeatureWalkMode["Pre"] = 1] = "Pre"; - /** - * post process - */ - FeatureWalkMode[FeatureWalkMode["Post"] = 2] = "Post"; - })(exports.FeatureWalkMode || (exports.FeatureWalkMode = {})); - - const config$2 = getSyntaxConfig(); - function replacePseudo(tokens) { - return tokens.map((raw) => raw.map((r) => { - if (r.includes("(")) { - const index = r.indexOf("("); - const name = r.slice(0, index) + "()"; - if (name in pseudoAliasMap) { - return pseudoAliasMap[name] + r.slice(index); + function expand(ast) { + if (ast[STATE] & + (exports.EnumAstNodeStatus.Invalid | + exports.EnumAstNodeStatus.Disallowed | + exports.EnumAstNodeStatus.Unknown | + exports.EnumAstNodeStatus.Unparsed | + exports.EnumAstNodeStatus.Malformed)) { + return ast; + } + const result = Object.assign(cloneNode(ast), { chi: [] }); + let children; + for (let i = 0; i < ast.chi.length; i++) { + let node = ast.chi[i]; + if (node.typ === exports.EnumToken.RuleNodeType) { + children = expandRule(node); + for (const child of children) { + child[PARENT] = result; + result.chi.push(child); } - return r; } - return r in pseudoAliasMap && pseudoAliasMap[r] in config$2[ValidationSyntaxGroupEnum.Selectors] - ? pseudoAliasMap[r] - : r; - })); - } - function replaceAstNodes(tokens, root) { - let result = false; - for (const { value, parent } of walkValues(tokens, root)) { - if (value.typ == exports.EnumToken.MediaQueryConditionTokenType) { - const token = value.l.find((t) => t.typ == exports.EnumToken.IdenTokenType); - if (token != null) { - if (token.val in pseudoAliasMap) { - token.val = pseudoAliasMap[token.val]; - if ((equalsIgnoreCase(token.val, "min-resolution") || - equalsIgnoreCase(token.val, "max-resolution")) && - value.r?.[0]?.typ == exports.EnumToken.NumberTokenType) { - Object.assign(value.r?.[0], { - typ: exports.EnumToken.ResolutionTokenType, - unit: "x", - }); - result = true; - } - let isMin = token.val.startsWith("min-"); - let isMax = token.val.startsWith("max-"); - if (isMin || isMax) { - token.val = token.val.slice(4); - value.op.typ = isMax - ? exports.EnumToken.LteTokenType - : exports.EnumToken.GteTokenType; - } + else if (node.typ == exports.EnumToken.AtRuleNodeType && "chi" in node) { + let hasRule = false; + let j = node.chi.length; + while (j--) { + // @ts-ignore + if (node.chi[j].typ == exports.EnumToken.RuleNodeType || node.chi[j].typ == exports.EnumToken.AtRuleNodeType) { + hasRule = true; + break; } } - } - else if (value.typ == exports.EnumToken.IdenTokenType || - value.typ == exports.EnumToken.PseudoClassFuncTokenType || - value.typ == exports.EnumToken.PseudoClassTokenType || - value.typ == exports.EnumToken.PseudoElementTokenType) { - let key = value.val + - (value.typ == exports.EnumToken.PseudoClassFuncTokenType ? "()" : ""); - if (key in pseudoAliasMap) { - const isPseudClass = pseudoAliasMap[key].startsWith("::"); - value.val = pseudoAliasMap[key]; - // if ( - // value.typ == EnumToken.IdenTokenType && - // ["min-resolution", "max-resolution"].includes((value as IdentToken).val) && - // parent?.typ == EnumToken.MediaQueryConditionTokenType && - // (parent as MediaQueryConditionToken).r?.[0]?.typ == EnumToken.NumberTokenType - // ) { - // Object.assign((parent as MediaQueryConditionToken).r?.[0], { - // typ: EnumToken.ResolutionTokenType, - // unit: "x", - // }); - // } - // else - if (isPseudClass && value.typ == exports.EnumToken.PseudoElementTokenType) { - // @ts-ignore - value.typ = exports.EnumToken.PseudoClassTokenType; + if (hasRule) { + node = expand(node); + for (const child of node.chi) { + child[PARENT] = result; } - result = true; - } - } - } - // remove duplicate tokens - if (tokens.find((t) => t.typ == exports.EnumToken.CommaTokenType) != null) { - const set = new Set(); - const split = splitTokenList(tokens, [exports.EnumToken.CommaTokenType]); - tokens.length = 0; - for (const token of split.reduce((acc, curr) => { - const str = curr.reduce((acc, curr) => acc + renderValue(curr), ""); - if (set.has(str)) { - return acc; + node[PARENT] = result; + // @ts-ignore + result.chi.push(node); } - set.add(str); - if (acc.length > 0) { - acc.push({ - typ: exports.EnumToken.CommaTokenType, - }); + else { + node[PARENT] = result; + // @ts-ignore + result.chi.push(node); } - return acc.concat(curr); - }, [])) { - tokens.push(token); + } + else { + node[PARENT] = result; + // @ts-ignore + result.chi.push(node); } } return result; } - class ComputePrefixFeature { - get ordering() { - return 0; - } - get processMode() { - return exports.FeatureWalkMode.Pre; - } - static register(options) { - if (options.removePrefix) { - // @ts-ignore - options.features.push(new ComputePrefixFeature(options)); - } + function expandRule(node) { + if (node[STATE] & + (exports.EnumAstNodeStatus.Invalid | + exports.EnumAstNodeStatus.Disallowed | + exports.EnumAstNodeStatus.Unknown | + exports.EnumAstNodeStatus.Unparsed | + exports.EnumAstNodeStatus.Malformed)) { + return [node]; } - run(node) { - if (node[STATE] & (exports.EnumAstNodeStatus.Disallowed | exports.EnumAstNodeStatus.Invalid | exports.EnumAstNodeStatus.Malformed)) { - return null; - } - if (node.typ == exports.EnumToken.RuleNodeType) { - node.sel = replacePseudo(splitRule(node.sel)).reduce((acc, curr, index) => acc + (index > 0 ? "," : "") + curr.join(""), ""); - if (node[TOKENS] != null) { - replaceAstNodes(node[TOKENS]); - } - } - else if (node.typ == exports.EnumToken.DeclarationNodeType) { - if (node.nam.charAt(0) == "-") { - const match = node.nam.match(/^-([^-]+)-(.+)$/); - if (match != null) { - let nam = match[2]; - if (!(nam in config$2.declarations)) { - if (node.nam in pseudoAliasMap) { - nam = pseudoAliasMap[node.nam]; - } + const ast = Object.assign(cloneNode(node), { chi: node.chi.slice() }); + const result = []; + if (ast.typ == exports.EnumToken.RuleNodeType) { + let i = 0; + for (; i < ast.chi.length; i++) { + if (ast.chi[i].typ == exports.EnumToken.RuleNodeType) { + const rule = ast.chi[i]; + if (!rule.sel.includes("&")) { + const selRule = splitRule(rule.sel); + const arSelf = splitRule(ast.sel) + .filter((r) => r.every((t) => t != ":before" && t != ":after" && !t.startsWith("::"))) + .reduce((acc, curr) => acc.concat([curr.join("")]), []) + .join(","); + if (arSelf.length == 0) { + ast.chi.splice(i--, 1); + continue; } - if (nam in config$2.declarations) { - node.nam = nam; + for (let i1 = 0; i1 < selRule.length; i1++) { + const arr = selRule[i1]; + combinators.includes(arr[0].charAt(0)) ? arr.unshift(arSelf) : arr.unshift(arSelf, " "); } + rule.sel = selRule + .reduce((acc, curr) => { + acc.push(curr.join("")); + return acc; + }, []) + .join(","); } - } - let hasPrefix = false; - for (const { value } of walkValues(node.val)) { - if ((value.typ == exports.EnumToken.IdenTokenType || - (value.typ != exports.EnumToken.ParensTokenType && funcLike.includes(value.typ))) && - value.val.match(/^-([^-]+)-(.+)$/) != null) { - if (value.val.endsWith("-gradient")) { - if (regMatchLinearGradient.test(value.val)) { - this.webkitLinearToLinearGradient(value); + else { + let childSelectorCompound = []; + let withCompound = []; + let withoutCompound = []; + // pseudo elements cannot be used with '&' + // https://www.w3.org/TR/css-nesting-1/#example-7145ff1e + const rules = splitRule(ast.sel).filter((r) => r.every((t) => t != ":before" && t != ":after" && !t.startsWith("::"))); + const parentSelector = !node.sel.includes("&"); + if (rules.length == 0) { + ast.chi.splice(i--, 1); + continue; + } + for (const sel of rule[RAW] ?? splitRule(rule.sel)) { + const s = sel.join(""); + if (s.includes("&") || parentSelector) { + if (s.indexOf("&", 1) == -1) { + if (s.at(0) == "&") { + if (s.at(1) == " ") { + childSelectorCompound.push(s.slice(2)); + } + else { + if (s == "&" || parentSelector) { + withCompound.push(s); + } + } + } + else { + withoutCompound.push(s); + } + } + else { + withCompound.push(s); + } } - else if (regMatchRadialGradient.test(value.val)) { - this.webkitRadialToRadialGradient(value); + } + const selectors = []; + const selector = rules.length > 1 ? ":is(" + rules.map((a) => a.join("")).join(",") + ")" : rules[0].join(""); + if (childSelectorCompound.length > 0) { + if (childSelectorCompound.length == 1) { + selectors.push(replaceCompound("& " + childSelectorCompound[0].trim(), selector)); } - else if (equalsIgnoreCase(value.val, "-webkit-gradient")) { - this.webkitGradientToGradient(value); + else { + selectors.push(replaceCompound("& :is(" + + childSelectorCompound.reduce((acc, curr) => acc + (acc.length > 0 ? "," : "") + curr.trim(), "") + + ")", selector)); } - // not supported yet - break; } - hasPrefix = true; - break; - } - } - if (hasPrefix) { - const nodes = structuredClone(node.val); - for (const { value } of walkValues(nodes)) { - if (value.typ == exports.EnumToken.IdenTokenType || funcLike.includes(value.typ)) { - const match = value.val.match(/^-([^-]+)-(.+)$/); - if (match != null) { - value.val = match[2]; + if (withCompound.length > 0) { + if (withCompound.every((t) => t[0] == "&" && t.indexOf("&", 1) == -1)) { + // for (const w of withCompound) { + // for (let m = 0; m < w.length; m++) { + // // for (let n = 0; n < w[m].length; n++) { + // withoutCompound.push(w[m].slice(1)); + // // } + // } + // } + withoutCompound.push(...withCompound.map((t) => t.slice(1))); + withCompound.length = 0; + } + } + if (withoutCompound.length > 0) { + if (withoutCompound.length == 1) { + const useIs = rules.length == 1 && + selector.match(/^[a-zA-Z.:]/) != null && + selector.includes(" ") && + withoutCompound.length == 1 && + withoutCompound[0].match(/^[a-zA-Z]+$/) != null; + const compound = useIs ? ":is(&)" : "&"; + selectors.push(replaceCompound(rules.length == 1 + ? useIs + ? withoutCompound[0] + ":is(&)" + : selector.match(/^[.:]/) && withoutCompound[0].match(/^[a-zA-Z]+$/) + ? withoutCompound[0] + compound + : compound + withoutCompound[0] + : withoutCompound[0].match(/^[a-zA-Z:]+$/) + ? withoutCompound[0].trim() + compound + : "&" + + (withoutCompound[0].match(/^\S+$/) + ? withoutCompound[0].trim() + : ":is(" + withoutCompound[0].trim() + ")"), selector)); + } + else { + selectors.push(replaceCompound("&:is(" + + withoutCompound.reduce((acc, curr) => acc + (acc.length > 0 ? "," : "") + curr.trim(), "") + + ")", selector)); + } + } + if (withCompound.length > 0) { + if (withCompound.length == 1) { + selectors.push(replaceCompound(withCompound[0], selector)); } } + rule.sel = selectors.reduce((acc, curr) => (curr.length == 0 ? acc : acc + (acc.length > 0 ? "," : "") + curr), ""); } - node.val = nodes; - } - } - else if (node.typ == exports.EnumToken.AtRuleNodeType || node.typ == exports.EnumToken.KeyframesAtRuleNodeType) { - if (node.nam.startsWith("-")) { - const match = node.nam.match(/^-([^-]+)-(.+)$/); - if (match != null && "@" + match[2] in config$2.atRules) { - node.nam = match[2]; + ast.chi.splice(i--, 1); + for (const s of expandRule(rule)) { + result.push(s); } } - if (node.typ == exports.EnumToken.AtRuleNodeType && node.val !== "") { - if (replaceAstNodes(node[TOKENS])) { - node.val = node[TOKENS].reduce((acc, curr) => acc + renderValue(curr), ""); + else if (ast.chi[i].typ == exports.EnumToken.AtRuleNodeType) { + let astAtRule = ast.chi[i]; + const values = []; + if (astAtRule.nam === "scope") { + if (astAtRule.val.includes("&")) { + astAtRule.val = replaceCompound(astAtRule.val, ast.sel); + } + const slice = astAtRule.chi + .slice() + .filter((t) => t.typ == exports.EnumToken.RuleNodeType && t.sel.includes("&")); + if (slice.length > 0) { + expandRule({ ...node, chi: astAtRule.chi.slice() }); + } } - } - } - return node; - } - /** - * Convert -webkit-linear-gradient to linear-gradient syntax - * @param tokens - * @returns - */ - webkitLinearToLinearGradient(token) { - let i; - let k; - let to; - let val; - let key; - const tokens = token.chi; - // conversion rules - // translation: - // - left -> to right - // - right -> to left - // - top -> to bottom - // - top right -> to bottom left - // - top left -> to bottom right - // - bottom -> to top - // - bottom right -> to top left - // - bottom left -> to top right - // https://drafts.csswg.org/css-images-3/#linear-gradients - // final angle: - // - to top -> 0deg - // - to right -> 90deg - // - to bottom -> 180deg - // - to left -> 270deg - for (i = 0; i < tokens.length; i++) { - if (tokens[i].typ == exports.EnumToken.AngleTokenType || - (tokens[i].typ == exports.EnumToken.NumberTokenType && tokens[i].val == 0)) { - tokens[i].val = - (90 - - // @ts-expect-error - (tokens[i].typ == exports.EnumToken.NumberTokenType - ? tokens[i] - : toDegrees(tokens[i]).val)) % - 360; - } - else if (tokens[i].typ == exports.EnumToken.IdenTokenType) { - key = tokens[i].val.toLowerCase(); - switch (key) { - case "right": - case "left": - tokens.splice(i, 1, { typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { - typ: exports.EnumToken.IdenTokenType, - val: key == "right" ? "left" : "right", - }); - i += 2; - break; - case "top": - case "bottom": - k = i + 1; - to = key == "top" ? "bottom" : "top"; - while (k < tokens.length && - (tokens[k].typ === exports.EnumToken.WhitespaceTokenType || - tokens[k].typ === exports.EnumToken.CommentTokenType)) { - k++; - } - if (tokens[k]?.typ === exports.EnumToken.IdenTokenType) { - val = ""; - if (equalsIgnoreCase(tokens[k].val, "left")) { - val = "right"; - } - if (equalsIgnoreCase(tokens[k].val, "right")) { - val = "left"; + else { + // @ts-ignore + const clone = { ...ast, chi: astAtRule.chi.slice() }; + // @ts-ignore + astAtRule.chi.length = 0; + for (const r of expandRule(clone)) { + if (r.typ == exports.EnumToken.AtRuleNodeType && "chi" in r) { + if (astAtRule.val !== "" && r.val !== "") { + if (astAtRule.nam === "media" && r.nam === "media") { + r.val = astAtRule.val + " and " + r.val; + } + else if (astAtRule.nam == "layer" && r.nam == "layer") { + r.val = astAtRule.val + "." + r.val; + } } - if (val !== "") { - tokens.splice(i, k - i + 1, { typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: to }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val }); - i += 4; - break; + // @ts-ignore + values.push(r); + } + else if (r.typ == exports.EnumToken.RuleNodeType) { + for (const rule of expandRule(r)) { + // @ts-ignore + astAtRule.chi.push(rule); } } - tokens.splice(i, 1, { typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: to }); - i += 2; - break; + } + } + if (astAtRule.chi.length > 0) { + result.push(astAtRule); + } + for (const r of values) { + result.push(r); } + ast.chi.splice(i--, 1); } } - token.val = equalsIgnoreCase(token.val, "-webkit-repeating-linear-gradient") - ? "repeating-linear-gradient" - : "linear-gradient"; } - /** - * Convert -webkit-gradient(linear) to linear-gradient or linear-gradient syntax - * @param token - * @returns - */ - webkitGradientToGradient(token) { - let i = 0; - let k; - let key = ""; - let commaCount; - let type = ""; - let tokens = token.chi.slice(); - // linear or radial - if (equalsIgnoreCase(tokens[i].val, "linear")) { - type = "linear-gradient"; - i++; - } - tokens.splice(0, i + 1); - commaCount = 0; - for (i = 0; i < tokens.length; i++) { - if (tokens[i].typ === exports.EnumToken.CommaTokenType) { - commaCount++; - if (commaCount > 1) { - break; + // @ts-ignore + return ast.chi.length > 0 ? [ast].concat(result) : result; + } + /** + * replace compound selector + * @param input + * @param replace + */ + function replaceCompound(input, replace) { + const tokens = parseString(input); + let replacement = null; + for (const t of walkValues(tokens)) { + if (t.value.typ == exports.EnumToken.NestingSelectorTokenType) { + if (tokens.length == 2) { + if (replacement == null) { + replacement = parseString(replace); } + Object.assign(t.value, { + typ: exports.EnumToken.LiteralTokenType, + val: replaceCompoundLiteral(t.value.val, replace), + }); + continue; } - if (tokens[i].typ === exports.EnumToken.IdenTokenType) { - key += (key.length > 0 ? " " : "") + tokens[i].val; - } - } - // mapping keywords - // left top → left bottom to bottom - // left bottom → left top to top - // left top → right top to right - // right top → left top to left - // left top → right bottom to bottom right - // right top → left bottom to bottom left - // left bottom → right top to top right - // right bottom → left top to top left - const replacements = []; - if (key === "left top left bottom") { - replacements.push({ typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "bottom" }); - } - else if (key === "left bottom left top") { - replacements.push({ typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "top" }); - } - else if (key === "left top right top") { - replacements.push({ typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "right" }); + const rule = splitRule(replace); + Object.assign(t.value, { + typ: exports.EnumToken.LiteralTokenType, + val: rule.length > 1 ? ":is(" + replace + ")" : replace, + }); } - else if (key === "right top left top") { - replacements.push({ typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "left" }); + } + return tokens.reduce((acc, curr) => acc + printValue(curr), ""); + } + function replaceCompoundLiteral(selector, replace) { + const tokens = [""]; + let i = 0; + for (; i < selector.length; i++) { + if (selector.charAt(i) == "&") { + tokens.push("&", ""); } - else if (key === "left top right bottom") { - replacements.push({ typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "bottom" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "right" }); + } + return tokens + .sort((a, b) => { + if (a == "&") { + return 1; } - else if (key === "right top left bottom") { - replacements.push({ typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "bottom" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "left" }); + return b == "&" ? -1 : 0; + }) + .reduce((acc, curr) => acc + (curr == "&" ? replace : curr), ""); + } + + /** + * + * @param value + * @returns + */ + function toBase64(value) { + let result = ''; + for (const c of new TextEncoder().encode(value)) { + result += String.fromCharCode(c); + } + return btoa(result); + } + + // from https://github.com/Rich-Harris/vlq/tree/master + // credit: Rich Harris + const integer_to_char = {}; + const char_to_integer = {}; + let i = 0; + for (const char of 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=') { + char_to_integer[char] = i; + integer_to_char[i++] = char; + } + /** + * @param {string} str + */ + function decode(str) { + /** @type {number[]} */ + let result = []; + let shift = 0; + let value = 0; + for (let i = 0; i < str.length; i += 1) { + let integer = char_to_integer[str[i]]; + // if (integer === undefined) { + // throw new Error('Invalid character (' + str[i] + ')'); + // } + const has_continuation_bit = integer & 32; + integer &= 31; + value += integer << shift; + if (has_continuation_bit) { + shift += 5; } - else if (key === "left bottom right top") { - replacements.push({ typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "top" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "right" }); + else { + const should_negate = value & 1; + value >>>= 1; + if (should_negate) { + result.push(value === 0 ? -2147483648 : -value); + } + else { + result.push(value); + } + // reset + value = shift = 0; } - else if (key === "right bottom left top") { - replacements.push({ typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "top" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "left" }); + } + return result; + } + /** + * + * @param value + * @returns + */ + function encode(value) { + // if (typeof value === 'number') { + // return encode_integer(value); + // } + let result = ''; + for (let i = 0; i < value.length; i += 1) { + result += encode_integer(value[i]); + } + return result; + } + /** + * + * @param num + * @returns + */ + function encode_integer(num) { + let result = ''; + if (num < 0) { + num = (-num << 1) | 1; + } + else { + num <<= 1; + } + do { + let clamped = num & 31; + num >>>= 5; + if (num > 0) { + clamped |= 32; } - tokens.splice(0, i, ...replacements); - let checkStop = true; - let checkStopIndex = replacements.length + 1; - let colorStop = []; - for (i = replacements.length + 1; i < tokens.length; i++) { - if (tokens[i].typ === exports.EnumToken.CommaTokenType && checkStop) { - checkStopIndex = i; - continue; - } - if (tokens[i].typ === exports.EnumToken.FunctionTokenType) { - if (equalsIgnoreCase(tokens[i].val, "to")) { - colorStop.push(tokens[checkStopIndex]); - for (const token of tokens[i].chi) { - colorStop.push(token); - } - tokens.splice(checkStopIndex, i - checkStopIndex + 1); - i = checkStopIndex; - checkStop = false; + result += integer_to_char[clamped]; + } while (num > 0); + return result; + } + + /** + * Generate and parse source map + * + * ```ts + * const sourcemap = new SourceMap(); + * + * // you can also pass an input sourcemap + * // const sourcemap = new SourceMap(sourcemapObjectOrJSONString); + * + * sourcemap.addSourceContent(0, '/css/styles.css', null); + * sourcemap.addSourceContent(1, '/css/typography.css', null); + * + * const maps = []; + * + * maps.add([ + 1, 1, 0, 1, 1]); + * maps.add([ + 2, 1, 1, 1, 1 + ]); + + sourcemap.add(maps); + + // convert to inline sourcemap + sourcemap.toUrl(); + + // convert to JSON + sourcemap.toJSON(); + + // find the original file, line and column + sourcemap.find(2, 1); + ``` + */ + class SourceMap { + /** + * + * @private + */ + keys = new Set(); + /** + * Last location + */ + lastLocation = null; + /** + * Version + * @private + */ + version = 3; + /** + * Sources map + * @private + */ + sourcesMap = []; + /** + * Sources content + * @private + */ + sourcesContent = []; + /** + * Sources + * @private + */ + sources = []; + /** + * Map + * @private + * + */ + map = new Map(); + /** + * Map + * @private + * + */ + reverseMap = new Map(); + /** + * Line + * @private + */ + line = -1; + /** + * + * @param sourcemaps input sourcemap + */ + constructor(sourcemaps) { + if (typeof sourcemaps === "string") { + if (sourcemaps.startsWith("data:")) { + let encoding = ""; + let offset = sourcemaps.indexOf(",") + 1; + if (offset == 0) { + offset = sourcemaps.lastIndexOf(";") + 1; } - else if (equalsIgnoreCase(tokens[i].val, "from")) { - k = tokens[i].chi.length + 1; - tokens.splice(i, 1, ...tokens[i].chi, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.PercentageTokenType, val: 0 }); - i += k; + else { + encoding = sourcemaps.slice(sourcemaps.lastIndexOf(";") + 1, offset - 1); } - else if (equalsIgnoreCase(tokens[i].val, "color-stop")) { - k = tokens[i].chi.length - 1; - tokens.splice(i, 1, - // @ts-expect-error - ...tokens[i].chi.reverse().map((t) => t.typ === exports.EnumToken.CommaTokenType - ? { typ: exports.EnumToken.WhitespaceTokenType } - : t.typ === exports.EnumToken.NumberTokenType - ? { - typ: exports.EnumToken.PercentageTokenType, - // @ts-expect-error - val: t.val * 100, - } - : t)); - i += k; + if (encoding == "base64") { + sourcemaps = atob(sourcemaps.slice(offset)); + } + else { + sourcemaps = decodeURIComponent(sourcemaps.slice(offset)); } } + sourcemaps = JSON.parse(sourcemaps); } - if (colorStop.length > 0) { - for (const t of colorStop) { - tokens.push(t); - } - } - if (type !== "") { - token.val = type; - token.chi.length = 0; - for (const t of tokens) { - token.chi.push(t); + if (sourcemaps != null) { + this.sources = sourcemaps.sources?.slice() ?? []; + this.sourcesContent = sourcemaps.sourcesContent?.slice() ?? []; + const decodedMappings = sourcemaps.mappings + .split(";") + .map((mapping) => mapping.split(",").map((mapping) => decode(mapping))); + this.line = decodedMappings.length - 1; + for (let index = 0; index < decodedMappings.length; index++) { + if (decodedMappings[index].length == 0 || + (decodedMappings[index].length == 1 && decodedMappings[index][0].length == 0)) { + continue; + } + this.map.set(index, decodedMappings[index]); } + this.computePositions(); } } /** - * Convert -webkit-radial-gradient to radial-gradient syntax - * @param tokens + * add source + * @param id source id + * @param fileName source file + * @param content source content * @returns */ - webkitRadialToRadialGradient(token) { - let i = 0; - const tokens = token.chi; - while (i < tokens.length && - (tokens[i].typ === exports.EnumToken.WhitespaceTokenType || tokens[i].typ === exports.EnumToken.CommentTokenType)) { - i++; - } - const positions = []; - const form = []; - const size = []; - const colorStops = []; - if (tokens[i]?.typ === exports.EnumToken.PercentageTokenType || - (tokens[i]?.typ === exports.EnumToken.NumberTokenType && 0 === tokens[i].val) || - (tokens[i]?.typ === exports.EnumToken.IdenTokenType && - (equalsIgnoreCase(tokens[i].val, "center") || - equalsIgnoreCase(tokens[i].val, "top") || - equalsIgnoreCase(tokens[i].val, "bottom") || - equalsIgnoreCase(tokens[i].val, "left") || - equalsIgnoreCase(tokens[i].val, "right")))) { - do { - positions.push(tokens[i]); - i++; - } while (tokens[i]?.typ !== exports.EnumToken.CommaTokenType); - if (tokens[i]?.typ === exports.EnumToken.CommaTokenType) { - i++; - } + addSourceContent(id, fileName, content) { + if (this.sourcesMap.includes(id)) { + return; } - if (tokens[i]?.typ === exports.EnumToken.IdenTokenType) { - if (equalsIgnoreCase(tokens[i].val, "closest-side") || - equalsIgnoreCase(tokens[i].val, "closest-corner") || - equalsIgnoreCase(tokens[i].val, "farthest-side") || - equalsIgnoreCase(tokens[i].val, "farthest-corner")) { - size.push(tokens[i++]); - } - else if (equalsIgnoreCase(tokens[i].val, "circle") || - equalsIgnoreCase(tokens[i].val, "ellipse")) { - form.push(tokens[i++]); - } - while (tokens[i]?.typ === exports.EnumToken.WhitespaceTokenType || tokens[i]?.typ === exports.EnumToken.CommentTokenType) { - i++; + this.sourcesMap[this.sourcesMap.length] = id; + this.sources[this.sources.length] = fileName || null; + this.sourcesContent[this.sourcesContent.length] = content || null; + } + /** + * Add multiple sourcemaps + * @param maps + * @throws + */ + add(maps) { + let srcIndex; + for (let [newLine, newColumn, srcId, ln, col] of maps) { + const key = `${srcId}:${ln}:${col}:${newLine}:${newColumn}`; + if (this.keys.has(key)) { + continue; } - if (tokens[i]?.typ === exports.EnumToken.CommaTokenType) { - i++; + this.keys.add(key); + const line = newLine - 1; + let record; + if (line > this.line) { + this.line = line; } - } - if (tokens[i]?.typ === exports.EnumToken.IdenTokenType) { - if (equalsIgnoreCase(tokens[i].val, "closest-side") || - equalsIgnoreCase(tokens[i].val, "closest-corner") || - equalsIgnoreCase(tokens[i].val, "farthest-side") || - equalsIgnoreCase(tokens[i].val, "farthest-corner")) { - size.push(tokens[i++]); + srcIndex = this.sourcesMap.indexOf(srcId); + if (srcIndex == -1) { + throw new Error(`Source file ${srcId} not added to sourcemap`); } - else if (equalsIgnoreCase(tokens[i].val, "circle") || - equalsIgnoreCase(tokens[i].val, "ellipse")) { - form.push(tokens[i++]); + if (!this.map.has(line)) { + record = [Math.max(0, newColumn - 1), srcIndex, ln - 1, col - 1]; + this.map.set(line, [record]); } - while (tokens[i]?.typ === exports.EnumToken.WhitespaceTokenType || tokens[i]?.typ === exports.EnumToken.CommentTokenType) { - i++; + else { + const arr = this.map.get(line); + record = [Math.max(0, newColumn - 1) - arr[0][0], srcIndex - arr[0][1], ln - 1, col - 1]; + arr.push(record); } - if (tokens[i]?.typ === exports.EnumToken.CommaTokenType) { - i++; + if (this.lastLocation != null) { + record[2] -= this.lastLocation.ln - 1; + record[3] -= this.lastLocation.col - 1; } + this.lastLocation ??= { ln, col }; + this.lastLocation.ln = ln; + this.lastLocation.col = col; } - for (let m = i; m < tokens.length; m++) { - colorStops.push(tokens[m]); - } - tokens.length = 0; - if (form.length > 0 || size.length > 0) { - if (form.length === 0) { - form.push({ typ: exports.EnumToken.IdenTokenType, val: "ellipse" }); + } + /** + * compute original positions + */ + computePositions() { + this.reverseMap.clear(); + let sourceFileIndex = 0; // second field + let sourceCodeLine = 0; // third field + let sourceCodeColumn = 0; // fourth field + // let nameIndex: number = 0; // fifth field + let generatedCodeColumn; + let result; + // mappings to original source + for (let [i, line] of this.map.entries()) { + if (line.length === 0 || (line.length === 1 && line[0].length === 0)) { + continue; } - if (size.length > 0) { - form.push({ typ: exports.EnumToken.WhitespaceTokenType }); - for (const token of size) { - form.push(token); + generatedCodeColumn = line[0][0]; // first field - reset each time + line = line + .map((segment, index, array) => { + if (segment.length === 0) { + return []; } - } - if (positions.length > 0) { - form.push({ typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "at" }, { typ: exports.EnumToken.WhitespaceTokenType }); - for (const position of positions) { - form.push(position); + generatedCodeColumn = index == 0 ? segment[0] : segment[0] + array[0][0]; + result = [generatedCodeColumn]; + if (segment.length <= 1) { + return result; } + sourceFileIndex = index == 0 ? segment[1] : segment[1] + array[0][1]; + sourceCodeLine += segment[2]; + sourceCodeColumn += segment[3]; + result.push(sourceFileIndex, sourceCodeLine, sourceCodeColumn); + // nameIndex not needed + // if (segment.length === 5) { + // nameIndex += segment[4]; + // result.push(nameIndex); + // } + return result; + }) + .sort((a, b) => { + if (a[1] !== b[1]) { + return a[1] - b[1]; + } + return a[0] - b[0]; + }); + if (line.length == 0 || (line.length == 1 && line[0].length == 0)) { + continue; } - for (const token of form) { - tokens.push(token); - } - tokens.push({ typ: exports.EnumToken.CommaTokenType }); - } - token.val = equalsIgnoreCase(token.val, "-webkit-repeating-radial-gradient") - ? "repeating-radial-gradient" - : "radial-gradient"; - for (const colorStop of colorStops) { - tokens.push(colorStop); + this.reverseMap.set(i, line); } - return tokens; } - } - - function inlineExpression(token) { - const result = []; - if (token.typ == exports.EnumToken.BinaryExpressionTokenType) { - const chi = inlineExpression(token.l); - chi.push({ typ: token.op }); - for (const child of inlineExpression(token.r)) { - chi.push(child); + /** + * retrieve original sources, lines and columns + * @param line generated line + * @param column generated column + */ + find(line, column) { + if (this.reverseMap.size == 0) { + this.computePositions(); } - result.push({ - typ: exports.EnumToken.ParensTokenType, - chi, - }); - } - else { - result.push(token); - } - return result; - } - function replace(node, variableScope) { - for (const { value, parent: parentValue } of walkValues(node.val)) { - if (value.typ == exports.EnumToken.BinaryExpressionTokenType && parentValue != null && "chi" in parentValue) { - // @ts-ignore - parentValue.chi.splice(parentValue.chi.indexOf(value), 1, ...inlineExpression(value)); + if (!this.reverseMap.has(--line)) { + return null; } - } - for (const { value, parent: parentValue } of walkValues(node.val)) { - if (value.typ == exports.EnumToken.MathFunctionTokenDefType || - (value.typ == exports.EnumToken.WildCardFunctionTokenType && value.val == "var")) { - if (value.chi.length == 1 && - value.chi[0].typ == exports.EnumToken.DashedIdenTokenType) { - const info = (variableScope.get(value.chi[0].val)); - if (info?.replaceable) { - if (parentValue != null) { - let i = 0; - for (; i < parentValue.chi.length; i++) { - if (parentValue.chi[i] == value) { - parentValue.chi.splice(i, 1, ...info.node.val); - break; - } - } - } - else { - node.val = info.node.val.slice(); - } - } + column--; + const result = []; + for (const record of this.reverseMap.get(line)) { + if (record.length == 0 || record[0] < column) { + continue; } + if (record[0] > column) { + break; + } + result.push([ + this.sources?.[record[1]] ?? null, + record[2] + 1, + record[3] + 1, + this.sourcesContent?.[record[1]] ?? null, + ]); } + return result.length == 0 ? null : result; } - } - class InlineCssVariablesFeature { - accept = new Set([exports.EnumToken.RuleNodeType, exports.EnumToken.AtRuleNodeType]); - get ordering() { - return 2; - } - get processMode() { - return exports.FeatureWalkMode.Post; - } - static register(options) { - if (options.inlineCssVariables) { - // @ts-ignore - options.features.push(new InlineCssVariablesFeature()); - } + /** + * Convert to URL encoded string + */ + toUrl() { + // /*# sourceMappingURL = ${url} */ + return `data:application/json;charset=utf-8;base64,${toBase64(JSON.stringify(this.toJSON()))}`; } - run(ast, options = {}, parent, context) { - if (!("chi" in ast)) { - return null; - } - if (!("variableScope" in context)) { - context.variableScope = new Map(); - } - // [':root', 'html'] - const isRoot = parent.typ == exports.EnumToken.StyleSheetNodeType && - ast.typ == exports.EnumToken.RuleNodeType && - (ast[RAW] ?? splitRule(ast.sel)).some((segment) => segment.some((s) => s == ":root" || s == "html")); - const variableScope = context.variableScope; - // @ts-ignore - for (const node of ast.chi) { - if (node.typ !== exports.EnumToken.DeclarationNodeType) { - continue; - } - // css variable - if (node.nam.startsWith("--")) { - if (!variableScope.has(node.val)) { - const info = { - globalScope: isRoot, - // @ts-ignore - parent: new Set(), - declarationCount: 1, - replaceable: isRoot, - node: node, - values: structuredClone(node.val), - }; - info.parent.add(ast); - variableScope.set(node.nam, info); - let recursive = false; - for (const { value } of walkValues(node.val)) { - if (value?.typ == exports.EnumToken.MathFunctionTokenDefType || - (value?.typ == exports.EnumToken.WildCardFunctionTokenType && - (mathFuncs.includes(value.val) || - value.val === "var"))) { - recursive = true; - break; - } - } - if (recursive) { - replace(node, variableScope); - } - } + /** + * Convert to JSON object + */ + toJSON() { + const mappings = []; + let i = 0; + for (; i <= this.line; i++) { + if (!this.map.has(i)) { + mappings.push(""); } else { - replace(node, variableScope); + mappings.push(this.map.get(i).reduce((acc, curr) => acc + (acc === "" ? "" : ",") + encode(curr), "")); } } - return null; + return { + version: this.version, + sources: this.sources.slice(), + sourcesContent: this.sourcesContent?.slice(), + mappings: mappings.join(";"), + }; } - cleanup(ast, options = {}, context) { - const variableScope = context.variableScope; - if (variableScope == null) { - return; + } + + /** + * Compute line and column of the offset + */ + class LineMap { + /** + * line starts + */ + lineStarts; + /** + * Constructor + * @param lines + */ + constructor(lines = []) { + if (lines.length === 0) { + lines.push(0); } - for (const info of variableScope.values()) { - if (info.replaceable) { - let i; - // drop declarations from :root{} - for (const parent of info.parent) { - i = parent.chi?.length ?? 0; - while (i--) { - if (parent.chi[i] == info.node) { - // @ts-ignore - parent.chi.splice(i, 1, { - typ: exports.EnumToken.CommentTokenType, - val: `/* ${info.node.nam}: ${info.values.reduce((acc, curr) => acc + renderValue(curr, { convertColor: false }), "")} */`, - }); - break; - } - } - } + this.lineStarts = lines; + } + /** + * Compute line and column of the offset + * @param offset + * @returns + */ + getOffsets(offset) { + const line = this.search(offset); + const column = offset - this.lineStarts[line]; + // [line, column] + return [line + 1, line == 0 ? column + 1 : column]; + } + /** + * search the greatest index of the value less than or equal to offset + * @param offset + * @returns + */ + search(offset) { + // search lineStarts using binary search + let start = 0; + let end = this.lineStarts.length - 1; + let mid = 0; + let result = -1; + while (start <= end) { + mid = start + ((end - start) >>> 1); + if (this.lineStarts[mid] <= offset) { + result = mid; + start = mid + 1; + } + else if (this.lineStarts[mid] > offset) { + end = mid - 1; } } + return result; + } + /** + * get line starts + * @returns + */ + getLineStarts() { + return this.lineStarts; + } + /** + * add line start + */ + addLineStart(lineStart) { + this.lineStarts.push(lineStart); } } - function dedup(values) { - for (const value of values) { - let i = value.length; - while (i-- > 1) { - const t = value[i]; - const k = value[i == 1 ? 0 : i % 2]; - if (t.val == k.val && t.val == 0) { - if ((t.typ == exports.EnumToken.NumberTokenType && isLength(k)) || - (k.typ == exports.EnumToken.NumberTokenType && isLength(t)) || - isLength(k) || - isLength(t)) { - value.splice(i, 1); - continue; - } - } - if (eq(t, k)) { - value.splice(i, 1); - continue; - } - break; + /** + * match url + */ + const matchUrl = /^(https?:)?\/\//; + const windowsPathnameRegexp = /^\/?[a-zA-Z]:/; + /** + * return the directory name of a path + * @param path + * + * @private + */ + function dirname(path) { + if (path === "") { + return ""; + } + if (path.startsWith("data:")) { + return path; + } + let i = 0; + let parts = [""]; + for (; i < path.length; i++) { + const chr = path.charAt(i); + if (chr == "/") { + parts.push(""); + } + else { + parts[parts.length - 1] += chr; } } - return values; + parts.pop(); + return parts.join("/"); } - class PropertySet { - config; - declarations; - constructor(config) { - this.config = config; - this.declarations = new Map(); + /** + * split path + * @param result + * @private + */ + function splitPath(result) { + if (result.length == 0) { + return { parts: [], i: 0 }; } - add(declaration) { - if (declaration.nam == this.config.shorthand) { - this.declarations = new Map(); + const parts = result == "/" ? [] : [""]; + let i = 0; + for (; i < result.length; i++) { + const chr = result.charAt(i); + if (chr == "/") { + parts.push(""); } + // else if (chr == "?" || chr == "#") { + // break; + // } else { - // expand shorthand - if (this.declarations.has(this.config.shorthand)) { - let isValid = true; - let current = -1; - const tokens = []; - // @ts-ignore - for (let token of this.declarations.get(this.config.shorthand).val) { - if ( - // @ts-expect-error - this.config.types.some((t) => token.typ == exports.EnumToken[t]) || - (token.typ == exports.EnumToken.NumberTokenType && - token.val == 0 && - (this.config.types.includes("Length") || - this.config.types.includes("Angle") || - this.config.types.includes("Dimension")))) { - if (tokens.length == 0) { - tokens.push([]); - current++; - } - tokens[current].push(token); - continue; - } - if (token.typ != exports.EnumToken.WhitespaceTokenType && token.typ != exports.EnumToken.CommentTokenType) { - if (token.typ == exports.EnumToken.IdenTokenType && - this.config.keywords.includes(token.val)) { - if (tokens.length == 0) { - tokens.push([]); - current++; - } - tokens[current].push(token); - } - // @ts-ignore - if (token.typ == exports.EnumToken.LiteralTokenType && token.val == this.config.separator?.val) { - tokens.push([]); - current++; - continue; - } - isValid = false; - break; - } - } - if (isValid && tokens.length > 0) { - this.declarations.delete(this.config.shorthand); - for (const values of tokens) { - this.config.properties.forEach((property, index) => { - if (!this.declarations.has(property)) { - this.declarations.set(property, { - typ: exports.EnumToken.DeclarationNodeType, - nam: property, - val: [], - }); - } - while (index > 0 && index >= values.length) { - if (index > 1) { - index %= 2; - } - else { - index = 0; - break; - } - } - // @ts-ignore - const val = this.declarations.get(property).val; - if (val.length > 0) { - val.push({ typ: exports.EnumToken.WhitespaceTokenType }); - } - val.push({ ...values[index] }); - }); - } - } - this.declarations.set(declaration.nam, declaration); - return this; - } + parts[parts.length - 1] += chr; } - this.declarations.set(declaration.nam, declaration); - return this; } - isShortHand() { - if (this.declarations.has(this.config.shorthand)) { - return this.declarations.size == 1; - } - return this.config.properties.length == this.declarations.size; + // let k: number = -1; + // while (++k < parts.length) { + // if (parts[k] == ".") { + // parts.splice(k--, 1); + // } else if (parts[k] == "..") { + // parts.splice(k - 1, 2); + // k -= 2; + // } + // } + return { parts, i }; + } + /** + * Nomalize path + * @param path + * @private + */ + const normalize$1 = memoize(function (path) { + let parts = []; + let i = 0; + if (path.includes("\\")) { + path = path.replace(/(\\)/g, "/"); } - [Symbol.iterator]() { - let iterator; - const declarations = this.declarations; - if (declarations.size < this.config.properties.length) { - const values = [...declarations.values()]; - if (this.isShortHand()) { - const val = values[0].val.reduce((acc, curr) => { - if (![exports.EnumToken.WhitespaceTokenType, exports.EnumToken.CommentTokenType].includes(curr.typ)) { - acc.push(curr); - } - return acc; - }, []); - values[0].val = val.reduce((acc, curr) => { - if (acc.length > 0) { - acc.push({ typ: exports.EnumToken.WhitespaceTokenType }); - } - acc.push(curr); - return acc; - }, []); + if (windowsPathnameRegexp.test(path)) { + path = path.replace(windowsPathnameRegexp, ""); + } + for (; i < path.length; i++) { + const chr = path.charAt(i); + if (chr == "/") { + if (parts.length == 0 || parts[parts.length - 1] !== "") { + parts.push(""); } - return values[Symbol.iterator](); + } + else if (chr == "?" || chr == "#") { + break; } else { - const values = []; - this.config.properties.forEach((property) => { - let index = 0; - // @ts-ignore - for (const token of this.declarations.get(property)?.val ?? []) { - if (token == null) { - return this.declarations.values()[Symbol.iterator](); - } - if (token.typ == exports.EnumToken.WhitespaceTokenType) { - continue; - } - if (values.length == index) { - values.push([]); - } - values[index].push(token); - index++; - } - }); - if (this.config.valueSeparator == null) { - dedup(values); + if (parts.length == 0) { + parts.push(""); } - iterator = [ - { - typ: exports.EnumToken.DeclarationNodeType, - nam: this.config.shorthand, - val: values.reduce((acc, curr) => { - if (curr.length > 1) { - const k = curr.length * 2 - 1; - let i = 1; - while (i < k) { - curr.splice(i, 0, { typ: exports.EnumToken.WhitespaceTokenType }); - i += 2; - } - } - if (acc.length > 0) { - // @ts-ignore - acc.push({ ...this.config.separator, typ: exports.EnumToken.LiteralTokenType }); - } - for (const token of curr) { - acc.push(token); - } - return acc; - }, []), - }, - ][Symbol.iterator](); + parts[parts.length - 1] += chr; } - return iterator; } - } - - var properties = { - gap: { - shorthand: "gap", - properties: [ - "row-gap", - "column-gap" - ], - types: [ - "Length", - "Perc" - ], - multiple: false, - separator: null, - keywords: [ - "normal" - ] - }, - "row-gap": { - shorthand: "gap" - }, - "column-gap": { - shorthand: "gap" - }, - inset: { - shorthand: "inset", - properties: [ - "top", - "right", - "bottom", - "left" - ], - types: [ - "Length", - "Perc" - ], - multiple: false, - separator: null, - keywords: [ - "auto" - ] - }, - top: { - shorthand: "inset" - }, - right: { - shorthand: "inset" - }, - bottom: { - shorthand: "inset" - }, - left: { - shorthand: "inset" - }, - margin: { - shorthand: "margin", - properties: [ - "margin-top", - "margin-right", - "margin-bottom", - "margin-left" - ], - types: [ - "Length", - "Perc" - ], - multiple: false, - separator: null, - keywords: [ - "auto" - ] - }, - "margin-top": { - shorthand: "margin" - }, - "margin-right": { - shorthand: "margin" - }, - "margin-bottom": { - shorthand: "margin" - }, - "margin-left": { - shorthand: "margin" - }, - padding: { - shorthand: "padding", - properties: [ - "padding-top", - "padding-right", - "padding-bottom", - "padding-left" - ], - types: [ - "Length", - "Perc" - ], - keywords: [ - ] - }, - "padding-top": { - shorthand: "padding" - }, - "padding-right": { - shorthand: "padding" - }, - "padding-bottom": { - shorthand: "padding" - }, - "padding-left": { - shorthand: "padding" - }, - "scroll-margin": { - shorthand: "scroll-margin", - properties: [ - "scroll-margin-top", - "scroll-margin-right", - "scroll-margin-bottom", - "scroll-margin-left" - ], - types: [ - "Length", - "Perc" - ], - "default": [ - "0" - ], - keywords: [ - "auto" - ] - }, - "scroll-margin-top": { - shorthand: "scroll-margin" - }, - "scroll-margin-right": { - shorthand: "scroll-margin" - }, - "scroll-margin-bottom": { - shorthand: "scroll-margin" - }, - "scroll-margin-left": { - shorthand: "scroll-margin" - }, - "scroll-padding": { - shorthand: "scroll-padding", - properties: [ - "scroll-padding-top", - "scroll-padding-right", - "scroll-padding-bottom", - "scroll-padding-left" - ], - types: [ - "Length", - "Perc" - ], - keywords: [ - "auto" - ] - }, - "scroll-padding-top": { - shorthand: "scroll-padding" - }, - "scroll-padding-right": { - shorthand: "scroll-padding" - }, - "scroll-padding-bottom": { - shorthand: "scroll-padding" - }, - "scroll-padding-left": { - shorthand: "scroll-padding" - }, - "border-radius": { - shorthand: "border-radius", - properties: [ - "border-top-left-radius", - "border-top-right-radius", - "border-bottom-right-radius", - "border-bottom-left-radius" - ], - types: [ - "Length", - "Perc" - ], - multiple: true, - separator: { - typ: "Literal", - val: "/" - }, - keywords: [ - ] - }, - "border-top-left-radius": { - shorthand: "border-radius" - }, - "border-top-right-radius": { - shorthand: "border-radius" - }, - "border-bottom-right-radius": { - shorthand: "border-radius" - }, - "border-bottom-left-radius": { - shorthand: "border-radius" - }, - "border-width": { - shorthand: "border-width", - map: "border", - properties: [ - "border-top-width", - "border-right-width", - "border-bottom-width", - "border-left-width" - ], - types: [ - "Length", - "Perc" - ], - "default": [ - "medium" - ], - keywords: [ - "thin", - "medium", - "thick" - ] - }, - "border-top-width": { - map: "border", - shorthand: "border-width" - }, - "border-right-width": { - map: "border", - shorthand: "border-width" - }, - "border-bottom-width": { - map: "border", - shorthand: "border-width" - }, - "border-left-width": { - map: "border", - shorthand: "border-width" - }, - "border-style": { - shorthand: "border-style", - map: "border", - properties: [ - "border-top-style", - "border-right-style", - "border-bottom-style", - "border-left-style" - ], - types: [ - ], - "default": [ - "none" - ], - keywords: [ - "none", - "hidden", - "dotted", - "dashed", - "solid", - "double", - "groove", - "ridge", - "inset", - "outset" - ] - }, - "border-top-style": { - map: "border", - shorthand: "border-style" - }, - "border-right-style": { - map: "border", - shorthand: "border-style" - }, - "border-bottom-style": { - map: "border", - shorthand: "border-style" - }, - "border-left-style": { - map: "border", - shorthand: "border-style" - }, - "border-color": { - shorthand: "border-color", - map: "border", - properties: [ - "border-top-color", - "border-right-color", - "border-bottom-color", - "border-left-color" - ], - types: [ - "Color" - ], - "default": [ - "currentcolor" - ], - keywords: [ - ] - }, - "border-top-color": { - map: "border", - shorthand: "border-color" - }, - "border-right-color": { - map: "border", - shorthand: "border-color" - }, - "border-bottom-color": { - map: "border", - shorthand: "border-color" - }, - "border-left-color": { - map: "border", - shorthand: "border-color" - } - }; - var map = { - "flex-flow": { - shorthand: "flex-flow", - pattern: "flex-direction flex-wrap", - keywords: [ - ], - "default": [ - "row", - "nowrap" - ], - properties: { - "flex-direction": { - keywords: [ - "row", - "row-reverse", - "column", - "column-reverse" - ], - "default": [ - "row" - ], - types: [ - ] - }, - "flex-wrap": { - keywords: [ - "wrap", - "nowrap", - "wrap-reverse" - ], - "default": [ - "nowrap" - ], - types: [ - ] - } - } - }, - "flex-direction": { - shorthand: "flex-flow" - }, - "flex-wrap": { - shorthand: "flex-flow" - }, - "scroll-timeline": { - shorthand: "scroll-timeline", - pattern: "scroll-timeline-name scroll-timeline-axis", - keywords: [ - "none", - "block", - "inline", - "x", - "y" - ], - "default": [ - "none", - "block" - ], - properties: { - "scroll-timeline-name": { - keywords: [ - "none" - ], - "default": [ - "none" - ], - types: [ - "DashedIden" - ] - }, - "scroll-timeline-axis": { - keywords: [ - "block", - "inline", - "x", - "y" - ], - "default": [ - "block" - ], - types: [ - ] - } - } - }, - "scroll-timeline-name": { - shorthand: "scroll-timeline" - }, - "scroll-timeline-axis": { - shorthand: "scroll-timeline" - }, - container: { - shorthand: "container", - pattern: "container-name container-type", - keywords: [ - ], - "default": [ - ], - properties: { - "container-name": { - required: true, - multiple: true, - keywords: [ - "none" - ], - "default": [ - "none" - ], - types: [ - "Iden", - "DashedIden" - ] - }, - "container-type": { - previous: "container-name", - prefix: { - typ: "Literal", - val: "/" - }, - keywords: [ - "size", - "inline-size", - "normal" - ], - "default": [ - "normal" - ], - types: [ - ] - } - } - }, - "container-name": { - shorthand: "container" - }, - "container-type": { - shorthand: "container" - }, - flex: { - shorthand: "flex", - pattern: "flex-grow flex-shrink flex-basis", - keywords: [ - "auto", - "none", - "initial" - ], - "default": [ - ], - mapping: { - "0 1 auto": "initial", - "0 0 auto": "none", - "1 1 auto": "auto" - }, - properties: { - "flex-grow": { - required: true, - keywords: [ - ], - "default": [ - ], - types: [ - "Number" - ] - }, - "flex-shrink": { - keywords: [ - ], - "default": [ - ], - types: [ - "Number" - ] - }, - "flex-basis": { - keywords: [ - "max-content", - "min-content", - "fit-content", - "fit-content", - "content", - "auto" - ], - "default": [ - ], - types: [ - "Length", - "Perc" - ] - } - } - }, - "flex-grow": { - shorthand: "flex" - }, - "flex-shrink": { - shorthand: "flex" - }, - "flex-basis": { - shorthand: "flex" - }, - columns: { - shorthand: "columns", - pattern: "column-count column-width", - keywords: [ - "auto" - ], - "default": [ - "auto", - "auto auto" - ], - properties: { - "column-count": { - keywords: [ - "auto" - ], - "default": [ - "auto" - ], - types: [ - "Number" - ] - }, - "column-width": { - keywords: [ - "auto" - ], - "default": [ - "auto" - ], - types: [ - "Length" - ] - } - } - }, - "column-count": { - shorthand: "columns" - }, - "column-width": { - shorthand: "columns" - }, - transition: { - shorthand: "transition", - multiple: true, - separator: { - typ: "Comma" - }, - pattern: "transition-property transition-duration transition-timing-function transition-delay transition-behavior", - keywords: [ - "none", - "all" - ], - "default": [ - "0s", - "0ms", - "all", - "ease", - "none", - "normal" - ], - mapping: { - "cubic-bezier(.25,.1,.25,1)": "ease", - "cubic-bezier(0,0,1,1)": "linear", - "cubic-bezier(.42,0,1,1)": "ease-in", - "cubic-bezier(0,0,.58,1)": "ease-out", - "cubic-bezier(.42,0,.58,.42)": "ease-in-out" - }, - properties: { - "transition-property": { - keywords: [ - "none", - "all" - ], - "default": [ - "all" - ], - types: [ - "Iden" - ] - }, - "transition-duration": { - keywords: [ - ], - "default": [ - "0s", - "0ms", - "normal" - ], - types: [ - "Time" - ] - }, - "transition-timing-function": { - keywords: [ - "ease", - "ease-in", - "ease-out", - "ease-in-out", - "linear", - "step-start", - "step-end" - ], - "default": [ - "ease" - ], - types: [ - "TimingFunction" - ], - mapping: { - "cubic-bezier(.25,.1,.25,1)": "ease", - "cubic-bezier(0,0,1,1)": "linear", - "cubic-bezier(.42,0,1,1)": "ease-in", - "cubic-bezier(0,0,.58,1)": "ease-out", - "cubic-bezier(.42,0,.58,.42)": "ease-in-out" - } - }, - "transition-delay": { - keywords: [ - ], - "default": [ - "0s" - ], - types: [ - "Time" - ] - }, - "transition-behavior": { - keywords: [ - "normal", - "allow-discrete" - ], - "default": [ - "normal" - ], - types: [ - ] - } - } - }, - "transition-property": { - shorthand: "transition" - }, - "transition-duration": { - shorthand: "transition" - }, - "transition-timing-function": { - shorthand: "transition" - }, - "transition-delay": { - shorthand: "transition" - }, - "transition-behavior": { - shorthand: "transition" - }, - animation: { - shorthand: "animation", - separator: { - typ: "Comma" - }, - pattern: "animation-name animation-duration animation-timing-function animation-delay animation-iteration-count animation-direction animation-fill-mode animation-play-state animation-timeline", - "default": [ - "1", - "0s", - "0ms", - "none", - "ease", - "normal", - "running", - "auto" - ], - properties: { - "animation-name": { - keywords: [ - "none" - ], - "default": [ - "none" - ], - types: [ - "Iden" - ] - }, - "animation-duration": { - keywords: [ - "auto" - ], - "default": [ - "0s", - "0ms", - "auto" - ], - types: [ - "Time" - ], - mapping: { - auto: "0s" - } - }, - "animation-timing-function": { - keywords: [ - "ease", - "ease-in", - "ease-out", - "ease-in-out", - "linear", - "step-start", - "step-end" - ], - "default": [ - "ease" - ], - types: [ - "TimingFunction" - ], - mapping: { - "cubic-bezier(.25,.1,.25,1)": "ease", - "cubic-bezier(0,0,1,1)": "linear", - "cubic-bezier(.42,0,1,1)": "ease-in", - "cubic-bezier(0,0,.58,1)": "ease-out", - "cubic-bezier(.42,0,.58,.42)": "ease-in-out" - } - }, - "animation-delay": { - keywords: [ - ], - "default": [ - "0s", - "0ms" - ], - types: [ - "Time" - ] - }, - "animation-iteration-count": { - keywords: [ - "infinite" - ], - "default": [ - "1" - ], - types: [ - "Number" - ] - }, - "animation-direction": { - keywords: [ - "normal", - "reverse", - "alternate", - "alternate-reverse" - ], - "default": [ - "normal" - ], - types: [ - ] - }, - "animation-fill-mode": { - keywords: [ - "none", - "forwards", - "backwards", - "both" - ], - "default": [ - "none" - ], - types: [ - ] - }, - "animation-play-state": { - keywords: [ - "running", - "paused" - ], - "default": [ - "running" - ], - types: [ - ] - }, - "animation-timeline": { - keywords: [ - "none", - "auto" - ], - "default": [ - "auto" - ], - types: [ - "DashedIden", - "TimelineFunction" - ] - } - } - }, - "animation-name": { - shorthand: "animation" - }, - "animation-duration": { - shorthand: "animation" - }, - "animation-timing-function": { - shorthand: "animation" - }, - "animation-delay": { - shorthand: "animation" - }, - "animation-iteration-count": { - shorthand: "animation" - }, - "animation-direction": { - shorthand: "animation" - }, - "animation-fill-mode": { - shorthand: "animation" - }, - "animation-play-state": { - shorthand: "animation" - }, - "animation-timeline": { - shorthand: "animation" - }, - "text-emphasis": { - shorthand: "text-emphasis", - pattern: "text-emphasis-color text-emphasis-style", - "default": [ - "none", - "currentcolor" - ], - properties: { - "text-emphasis-style": { - keywords: [ - "none", - "filled", - "open", - "dot", - "circle", - "double-circle", - "triangle", - "sesame" - ], - "default": [ - "none" - ], - types: [ - "String" - ] - }, - "text-emphasis-color": { - "default": [ - "currentcolor" - ], - types: [ - "Color" - ] - } - } - }, - "text-emphasis-style": { - shorthand: "text-emphasis" - }, - "text-emphasis-color": { - shorthand: "text-emphasis" - }, - border: { - shorthand: "border", - pattern: "border-color border-style border-width", - keywords: [ - "none" - ], - "default": [ - "0", - "none" - ], - properties: { - "border-color": { - types: [ - "Color" - ], - "default": [ - "currentcolor" - ], - keywords: [ - ] - }, - "border-style": { - types: [ - ], - "default": [ - "none" - ], - keywords: [ - "none", - "hidden", - "dotted", - "dashed", - "solid", - "double", - "groove", - "ridge", - "inset", - "outset" - ] - }, - "border-width": { - types: [ - "Length", - "Perc" - ], - "default": [ - "medium" - ], - keywords: [ - "thin", - "medium", - "thick" - ] - } - } - }, - "border-color": { - shorthand: "border" - }, - "border-style": { - shorthand: "border" - }, - "border-width": { - shorthand: "border" - }, - "list-style": { - shorthand: "list-style", - pattern: "list-style-type list-style-position list-style-image", - keywords: [ - "none", - "outside" - ], - "default": [ - "none", - "outside" - ], - properties: { - "list-style-position": { - types: [ - ], - "default": [ - "outside" - ], - keywords: [ - "inside", - "outside" - ] - }, - "list-style-image": { - "default": [ - "none" - ], - keywords: [ - "node" - ], - types: [ - "UrlFunc", - "ImageFunc" - ] - }, - "list-style-type": { - types: [ - "String", - "Iden", - "Symbols" - ], - "default": [ - "disc" - ], - keywords: [ - "disc", - "circle", - "square", - "decimal", - "decimal-leading-zero", - "lower-roman", - "upper-roman", - "lower-greek", - "lower-latin", - "upper-latin", - "none" - ] - } - } - }, - "list-style-position": { - shorthand: "list-style" - }, - "list-style-image": { - shorthand: "list-style" - }, - "list-style-type": { - shorthand: "list-style" - }, - overflow: { - shorthand: "overflow", - pattern: "overflow-x overflow-y", - keywords: [ - "auto", - "visible", - "hidden", - "clip", - "scroll" - ], - "default": [ - ], - mapping: { - "visible visible": "visible", - "auto auto": "auto", - "hidden hidden": "hidden", - "scroll scroll": "scroll" - }, - properties: { - "overflow-x": { - "default": [ - ], - types: [ - ], - keywords: [ - "auto", - "visible", - "hidden", - "clip", - "scroll" - ] - }, - "overflow-y": { - "default": [ - ], - types: [ - ], - keywords: [ - "auto", - "visible", - "hidden", - "clip", - "scroll" - ] - } - } - }, - "overflow-x": { - shorthand: "overflow" - }, - "overflow-y": { - shorthand: "overflow" - }, - outline: { - shorthand: "outline", - pattern: "outline-color outline-style outline-width", - keywords: [ - "none" - ], - "default": [ - "0", - "none", - "currentcolor" - ], - properties: { - "outline-color": { - types: [ - "Color" - ], - "default": [ - "currentcolor" - ], - keywords: [ - "currentcolor" - ] - }, - "outline-style": { - types: [ - ], - "default": [ - "none" - ], - keywords: [ - "auto", - "none", - "dotted", - "dashed", - "solid", - "double", - "groove", - "ridge", - "inset", - "outset" - ] - }, - "outline-width": { - types: [ - "Length", - "Perc" - ], - "default": [ - "medium" - ], - keywords: [ - "thin", - "medium", - "thick" - ] - } - } - }, - "outline-color": { - shorthand: "outline" - }, - "outline-style": { - shorthand: "outline" - }, - "outline-width": { - shorthand: "outline" - }, - font: { - shorthand: "font", - pattern: "font-weight font-style font-size line-height font-stretch font-variant font-family", - keywords: [ - "caption", - "icon", - "menu", - "message-box", - "small-caption", - "status-bar", - "-moz-window, ", - "-moz-document, ", - "-moz-desktop, ", - "-moz-info, ", - "-moz-dialog", - "-moz-button", - "-moz-pull-down-menu", - "-moz-list", - "-moz-field" - ], - "default": [ - ], - properties: { - "font-weight": { - types: [ - "Number" - ], - "default": [ - "400", - "normal" - ], - keywords: [ - "normal", - "bold", - "lighter", - "bolder" - ], - constraints: { - value: { - min: "1", - max: "1000" - } - }, - mapping: { - thin: "100", - hairline: "100", - "extra light": "200", - "ultra light": "200", - light: "300", - normal: "400", - regular: "400", - medium: "500", - "semi bold": "600", - "demi bold": "600", - bold: "700", - "extra bold": "800", - "ultra bold": "800", - black: "900", - heavy: "900", - "extra black": "950", - "ultra black": "950" - } - }, - "font-style": { - types: [ - "Angle" - ], - "default": [ - "normal" - ], - keywords: [ - "normal", - "italic", - "oblique" - ] - }, - "font-size": { - types: [ - "Length", - "Perc" - ], - "default": [ - ], - keywords: [ - "xx-small", - "x-small", - "small", - "medium", - "large", - "x-large", - "xx-large", - "xxx-large", - "larger", - "smaller" - ], - required: true - }, - "line-height": { - types: [ - "Length", - "Perc", - "Number" - ], - "default": [ - "normal" - ], - keywords: [ - "normal" - ], - previous: "font-size", - prefix: { - typ: "Literal", - val: "/" - } - }, - "font-stretch": { - types: [ - "Perc" - ], - "default": [ - "normal" - ], - keywords: [ - "ultra-condensed", - "extra-condensed", - "condensed", - "semi-condensed", - "normal", - "semi-expanded", - "expanded", - "extra-expanded", - "ultra-expanded" - ], - mapping: { - "ultra-condensed": "50%", - "extra-condensed": "62.5%", - condensed: "75%", - "semi-condensed": "87.5%", - normal: "100%", - "semi-expanded": "112.5%", - expanded: "125%", - "extra-expanded": "150%", - "ultra-expanded": "200%" - } - }, - "font-variant": { - types: [ - ], - "default": [ - "normal" - ], - keywords: [ - "normal", - "none", - "common-ligatures", - "no-common-ligatures", - "discretionary-ligatures", - "no-discretionary-ligatures", - "historical-ligatures", - "no-historical-ligatures", - "contextual", - "no-contextual", - "historical-forms", - "small-caps", - "all-small-caps", - "petite-caps", - "all-petite-caps", - "unicase", - "titling-caps", - "ordinal", - "slashed-zero", - "lining-nums", - "oldstyle-nums", - "proportional-nums", - "tabular-nums", - "diagonal-fractions", - "stacked-fractions", - "ordinal", - "slashed-zero", - "ruby", - "jis78", - "jis83", - "jis90", - "jis04", - "simplified", - "traditional", - "full-width", - "proportional-width", - "ruby", - "sub", - "super", - "text", - "emoji", - "unicode" - ] - }, - "font-family": { - types: [ - "String", - "Iden" - ], - "default": [ - ], - keywords: [ - "serif", - "sans-serif", - "monospace", - "cursive", - "fantasy", - "system-ui", - "ui-serif", - "ui-sans-serif", - "ui-monospace", - "ui-rounded", - "math", - "emoji", - "fangsong" - ], - required: true, - multiple: true, - separator: { - typ: "Comma" - } - } - } - }, - "font-weight": { - shorthand: "font" - }, - "font-style": { - shorthand: "font" - }, - "font-size": { - shorthand: "font" - }, - "line-height": { - shorthand: "font" - }, - "font-stretch": { - shorthand: "font" - }, - "font-variant": { - shorthand: "font" - }, - "font-family": { - shorthand: "font" - }, - background: { - shorthand: "background", - pattern: "background-attachment background-origin background-clip background-color background-image background-repeat background-position background-size", - keywords: [ - "none" - ], - "default": [ - "0 0", - "none", - "auto", - "repeat", - "transparent", - "#0000", - "scroll", - "padding-box", - "border-box" - ], - multiple: true, - set: { - "background-origin": [ - "background-clip" - ] - }, - separator: { - typ: "Comma" - }, - properties: { - "background-repeat": { - types: [ - ], - "default": [ - "repeat" - ], - multiple: true, - keywords: [ - "repeat-x", - "repeat-y", - "repeat", - "space", - "round", - "no-repeat" - ], - mapping: { - "repeat no-repeat": "repeat-x", - "no-repeat repeat": "repeat-y", - "repeat repeat": "repeat", - "space space": "space", - "round round": "round", - "no-repeat no-repeat": "no-repeat" - } - }, - "background-color": { - types: [ - "Color" - ], - "default": [ - "#0000", - "transparent" - ], - multiple: true, - keywords: [ - ] - }, - "background-image": { - types: [ - "UrlFunc", - "ImageFunc" - ], - "default": [ - "none" - ], - keywords: [ - "none" - ] - }, - "background-attachment": { - types: [ - ], - "default": [ - "scroll" - ], - multiple: true, - keywords: [ - "scroll", - "fixed", - "local" - ] - }, - "background-clip": { - types: [ - ], - "default": [ - "border-box" - ], - multiple: true, - keywords: [ - "border-box", - "padding-box", - "content-box", - "text" - ] - }, - "background-origin": { - types: [ - ], - "default": [ - "padding-box" - ], - multiple: true, - keywords: [ - "border-box", - "padding-box", - "content-box" - ] - }, - "background-position": { - multiple: true, - types: [ - "Perc", - "Length" - ], - "default": [ - "0 0", - "top left", - "left top" - ], - keywords: [ - "top", - "left", - "center", - "bottom", - "right" - ], - mapping: { - left: "0", - top: "0", - center: "50%", - "center center": "50%", - "50% 50%": "50%", - bottom: "100%", - right: "100%" - }, - constraints: { - mapping: { - max: 2 - } - } - }, - "background-size": { - multiple: true, - previous: "background-position", - prefix: { - typ: "Literal", - val: "/" - }, - types: [ - "Perc", - "Length" - ], - "default": [ - "auto", - "auto auto" - ], - keywords: [ - "auto", - "cover", - "contain" - ], - mapping: { - "auto auto": "auto" - } - } - } - }, - "background-repeat": { - shorthand: "background" - }, - "background-color": { - shorthand: "background" - }, - "background-image": { - shorthand: "background" - }, - "background-attachment": { - shorthand: "background" - }, - "background-clip": { - shorthand: "background" - }, - "background-origin": { - shorthand: "background" - }, - "background-position": { - shorthand: "background" - }, - "background-size": { - shorthand: "background" - }, - "grid-area": { - shorthand: "grid-area", - pattern: "grid-row-start grid-column-start grid-row-end grid-column-end", - keywords: [ - "auto" - ], - "default": [ - "auto" - ], - multiple: false, - expandShorthands: true, - separator: { - typ: "Literal", - val: "/" - }, - mapping: { - "auto/auto": "auto", - "auto/auto/auto": "auto", - "auto/auto/auto/auto": "auto" - }, - expandedProperties: { - "grid-row": [ - "grid-row-start", - "grid-row-end" - ], - "grid-column": [ - "grid-column-start", - "grid-column-end" - ] - }, - properties: { - "grid-row-start": { - map: "grid-area", - shorthand: "grid-row", - types: [ - "Iden", - "Number" - ], - "default": [ - "auto" - ], - keywords: [ - "auto" - ] - }, - "grid-column-start": { - map: "grid-area", - shorthand: "grid-column", - types: [ - "Iden", - "Number" - ], - prefix: { - typ: "Literal", - val: "/" - }, - "default": [ - "auto" - ], - keywords: [ - "auto" - ] - }, - "grid-row-end": { - map: "grid-area", - shorthand: "grid-row", - types: [ - "Iden", - "Number" - ], - prefix: { - typ: "Literal", - val: "/" - }, - "default": [ - "auto" - ], - keywords: [ - "auto" - ] - }, - "grid-column-end": { - map: "grid-area", - shorthand: "grid-column", - types: [ - "Iden", - "Number" - ], - prefix: { - typ: "Literal", - val: "/" - }, - "default": [ - "auto" - ], - keywords: [ - "auto" - ] - } - } - }, - "grid-row-start": { - shorthand: "grid-row" - }, - "grid-column-start": { - shorthand: "grid-column" - }, - "grid-row-end": { - shorthand: "grid-row" - }, - "grid-column-end": { - shorthand: "grid-column" - }, - "grid-row": { - shorthand: "grid-row", - map: "grid-area", - pattern: "grid-row-start grid-row-end", - types: [ - "Iden", - "Number" - ], - keywords: [ - "auto" - ], - "default": [ - "auto" - ], - multiple: false, - mapping: { - "auto auto": "auto", - "auto/auto": "auto" - }, - properties: { - "grid-row-start": { - map: "grid-area", - types: [ - "Iden", - "Number" - ], - "default": [ - "auto" - ], - multiple: true, - keywords: [ - "auto" - ] - }, - "grid-row-end": { - map: "grid-area", - types: [ - "Iden", - "Number" - ], - "default": [ - "auto" - ], - prefix: { - typ: "Literal", - val: "/" - }, - multiple: true, - keywords: [ - "auto" - ] - } - } - }, - "grid-column": { - shorthand: "grid-column", - map: "grid-area", - pattern: "grid-column-start grid-column-end", - types: [ - "Iden", - "Number" - ], - keywords: [ - "auto" - ], - "default": [ - "auto" - ], - multiple: false, - mapping: { - "auto/auto": "auto" - }, - properties: { - "grid-column-start": { - map: "grid-area", - types: [ - "Iden", - "Number" - ], - "default": [ - "auto" - ], - multiple: true, - keywords: [ - "auto" - ] - }, - "grid-column-end": { - map: "grid-area", - types: [ - "Iden", - "Number" - ], - "default": [ - "auto" - ], - multiple: true, - prefix: { - typ: "Literal", - val: "/" - }, - keywords: [ - "auto" - ] - } - } - }, - "grid-template": { - shorthand: "grid-template", - pattern: "grid-template-areas grid-template-rows grid-template-columns", - keywords: [ - "none" - ], - "default": [ - "none" - ], - multiple: false, - mapping: { - "none none": "none", - "none/none": "none", - "none none/none": "none" - }, - properties: { - "grid-template-areas": { - types: [ - "String" - ], - "default": [ - "none" - ], - multiple: true, - keywords: [ - "none", - "auto" - ], - mapping: { - } - }, - "grid-template-rows": { - types: [ - "Perc", - "Iden", - "Length", - "Flex" - ], - "default": [ - "none" - ], - multiple: true, - keywords: [ - "none" - ] - }, - "grid-template-columns": { - types: [ - "Perc", - "Iden", - "Length", - "Flex" - ], - "default": [ - "none" - ], - prefix: { - typ: "Literal", - val: "/" - }, - multiple: true, - keywords: [ - "none" - ] - } - } - }, - "grid-template-areas": { - shorthand: "grid-template" - }, - "grid-template-rows": { - shorthand: "grid-template" - }, - "grid-template-columns": { - shorthand: "grid-template" - } - }; - var property = { - "transform-origin": { - pattern: [ - [ - "left|center|right", - "top|center|bottom", - "" - ], - [ - "left|center|right", - "top|center|bottom" - ], - [ - "center|left|right|center|top|bottom|" - ] - ], - mapping: { - center: { - typ: "Perc", - val: 50 - }, - left: { - typ: "Perc", - val: 0 - }, - right: { - typ: "Perc", - val: 100 - }, - top: { - typ: "Perc", - val: 0 - }, - bottom: { - typ: "Perc", - val: 100 - } - } - } - }; - var config$1 = { - properties: properties, - map: map, - property: property - }; + let k = -1; + while (++k < parts.length) { + // if (parts[k] == ".") { + // parts.splice(k--, 1); + // } else + if (k > 0 && parts[k] == "..") { + parts.splice(k - 1, 2); + k -= 2; + } + } + return (path.charAt(0) == "/" ? "/" : "") + parts.join("/"); + }); + /** + * diff path + * @param path1 + * @param path2 + * @private + */ + const diff$1 = memoize(function (path1, path2) { + let { parts } = splitPath(path1); + const { parts: dirs } = splitPath(path2); + for (const p of dirs) { + if (parts[0] == p) { + parts.shift(); + } + else { + parts.unshift(".."); + } + } + return parts.join("/"); + }); + /** + * resolve path + * @param url url or path to resolve + * @param currentDirectory directory used to resolve the path + * @param cwd current working directory + * + * @private + */ + const resolve = memoize(function (url, currentDirectory, cwd) { + if (matchUrl.test(url)) { + return { + absolute: url, + relative: url, + }; + } + cwd ??= ""; + currentDirectory ??= ""; + url = normalize$1(url); + if (cwd !== "") { + cwd = normalize$1(cwd); + } + if (currentDirectory !== "") { + currentDirectory = normalize$1(currentDirectory); + } + let dir = cwd || currentDirectory; + if (windowsPathnameRegexp.test(dir)) { + dir = dir.replace(windowsPathnameRegexp, ""); + } + const absolute = dir == "" || url.startsWith("/") || url.startsWith(dir) || windowsPathnameRegexp.test(url) + ? resolvePath(url) + : resolvePath(dir, url); + return { + absolute, + relative: dir === "" ? absolute : diff$1(absolute, dir), + }; + }); + /** + * + * @param parts + * @returns + * @private + */ + function resolvePath(...parts) { + const path = parts.filter(Boolean).join("/"); + const isAbsolute = /^[\\/]/.test(path); + const segments = path.split(/[\\/]+/); + const resolved = []; + for (const segment of segments) { + if (!segment || segment === ".") { + continue; + } + if (segment === "..") { + if (resolved.length && resolved[resolved.length - 1] !== "..") { + resolved.pop(); + } + else if (!isAbsolute) { + resolved.push(".."); + } + } + else { + resolved.push(segment); + } + } + let result = resolved.join("/"); + if (isAbsolute) { + result = "/" + result; + } + return result || (isAbsolute ? "/" : "."); + } - Object.freeze(config$1); - // @ts-expect-error - const getConfig = () => config$1; + /** + * Source file ID + */ + let sourceId = 0; + /** + * Source file helper class + */ + class SourceFile { + /** + * Source file ID + */ + id; + /** + * Source file path + */ + file; + /** + * Line map + */ + lineStarts; + /** + * Source file content + */ + content; + inputSourceMap = null; + /** + * Constructor + * @param content + * @param lines + * @param file + */ + constructor(content, lines, file = null) { + this.id = sourceId++; + this.content = content; + this.file = file; + this.lineStarts = new LineMap(lines); + } + /** + * Update source content + * @param content + */ + append(content) { + this.content += content; + } + /** + * get file name + * @returns + */ + getFileName() { + return this.file; + } + /** + * get content + * @returns + */ + getContent() { + return this.content; + } + /** + * get text + * @param start + * @param length + * @returns + */ + getText(start, length) { + return this.content.slice(start, start + length); + } + /** + * Compute line and column of the offset + * @param offset + * @returns + */ + getOffsets(offset) { + return this.lineStarts.getOffsets(offset); + } + /** + * get source location + * @param offset + * @returns + */ + getSourceLocation(offset) { + return [this.file, ...this.getOffsets(offset)]; + } + /** + * get line starts + * @returns + */ + getLineStarts() { + return this.lineStarts.getLineStarts(); + } + /** + * add line start + * @param lineStart + */ + addLineStart(lineStart) { + this.lineStarts.addLineStart(lineStart); + } + /** + * set input source map + * @param inputSourceMap + */ + setInputSourceMap(inputSourceMap) { + this.inputSourceMap = inputSourceMap == null ? null : new SourceMap(inputSourceMap); + } + /** + * return input source map + * @returns + */ + getInputSourceMap() { + return this.inputSourceMap; + } + } - function matchType(val, properties) { - if ((val.typ === exports.EnumToken.IdenTokenType && properties.keywords.includes(val.val)) || - properties.types.some( - // @ts-expect-error - (t) => - // @ts-expect-error - (val.typ === exports.EnumToken.IdenTokenType && exports.EnumToken[t] === exports.EnumToken.ColorTokenType && isColor(val)) || - // @ts-expect-error - exports.EnumToken[t] === val.typ)) { - return true; + /** + * render ast + * @param data + * @param options + * @param mapping + * @private + */ + function doRender(data, options = {}, mapping) { + const minify = options.minify ?? true; + const beautify = options.beautify ?? !minify; + options = { + ...(beautify + ? { + indent: " ", + newLine: "\n", + } + : { + indent: "", + newLine: "", + }), + ...(minify + ? { + removeEmpty: true, + removeComments: true, + minify: true, + } + : { + removeEmpty: false, + removeComments: false, + }), + minifyValues: true, + sourcemap: false, + convertColor: true, + expandNestingRules: false, + preserveLicense: false, + ...options, + }; + if (options.withParents) { + let parent; + while (data[PARENT] != null) { + // @ts-ignore + // parent = { ...data[PARENT], chi: [data] }; + parent = cloneNode(data[PARENT]); + // @ts-ignore + parent.chi = [data]; + // @ts-ignore + parent[PARENT] = data[PARENT][PARENT]; + // @ts-ignore + data = parent; + } + } + const startTime = performance.now(); + const errors = []; + const sourcemap = options.sourcemap ? new SourceMap() : null; + const sourcemaps = options.sourcemap ? { sources: [], maps: [] } : null; + const cache = Object.create(null); + const sourceLocation = { + end: 0, + }; + const lineMap = options.sourcemap ? new LineMap() : null; + let code = ""; + if (mapping != null) { + if (mapping.importMapping != null) { + const absolutePath = options.resolve(options.output != null ? dirname(options.output) : dirname(options.src), options.cwd).absolute; + for (let [key, value] of Object.entries(mapping.importMapping)) { + key = options.resolve(options.resolve(key, options.cwd).absolute, absolutePath).relative; + if (!key.startsWith("/") && !key.startsWith(".")) { + key = "./" + key; + } + code += `:import("${key}")${options.indent}{${options.newLine}${Object.entries(value).reduce((acc, [k, v]) => acc + (acc.length > 0 ? options.newLine : "") + `${options.indent}${v}:${options.indent}${k};`, "")}${options.newLine}}${options.newLine}`; + } + } + code += `:export${options.indent}{${options.newLine}${Object.entries(mapping.mapping).reduce((acc, [k, v]) => acc + (acc.length > 0 ? options.newLine : "") + `${options.indent}${k}:${options.indent}${v};`, "")}${options.newLine}}${options.newLine}`; + if (sourcemap != null) { + move(sourceLocation, lineMap, code); + } + } + if (options.output != null) { + // @ts-ignore + options.output = options.resolve(options.output, options.cwd).absolute; + } + const result = { + code: code + + renderAstNode(options.expandNestingRules && + [exports.EnumToken.StyleSheetNodeType, exports.EnumToken.AtRuleNodeType, exports.EnumToken.RuleNodeType].includes(data.typ) && + "chi" in data + ? expand(data) + : data, options, sourcemaps, sourceLocation, lineMap, errors, function reducer(acc, curr) { + if (curr.typ == exports.EnumToken.CommentTokenType && options.removeComments) { + if (!options.preserveLicense || !curr.val.startsWith("/*!")) { + return acc; + } + return acc + curr.val; + } + return acc + renderValue$1(curr, options, cache, reducer); + }, cache), + errors, + stats: { + total: `${(performance.now() - startTime).toFixed(2)}ms`, + }, + }; + if (sourcemap != null) { + let source; + for (const sourceId of sourcemaps.sources) { + source = options.sourcesMap.get(sourceId); + sourcemap.addSourceContent(source.id, source.getFileName(), source.getContent()); + } + sourcemap.add(sourcemaps.maps); + result.map = sourcemap; + if (options.sourcemap === "inline") { + result.code += `\n/*# sourceMappingURL=${result.map.toUrl()} */`; + } + } + return result; + } + /** + * Update source map + * @param node + * @param options + * @param cache + * @param sourcemaps + * @param sourceLocation + * @param lineMap + * @param str + * + * @internal + */ + function updateSourceMap(node, options, cache, sourcemaps, sourceLocation, lineMap, str) { + let offset = 0; + // eat leanding whitespace + while (offset < str.length && isWhiteSpace(str.charCodeAt(offset))) { + offset++; + } + if (offset > 0) { + move(sourceLocation, lineMap, str, 0, offset + 1); + } + if (node[LOCSTA] != null) { + const source = options.sourcesMap.get(node[LOCSRCID]); + const inputSourceMap = source.getInputSourceMap(); + const offsets = source.getOffsets(node[LOCSTA]); + const [newLine, newColumn] = lineMap.getOffsets(sourceLocation.end); + let records = null; + let srcId = node[LOCSRCID]; + let sourceFileName = source.getFileName() || null; + let sourceContent; // = (source.getContent() as string) || null; + if (inputSourceMap != null && (records = inputSourceMap.find(offsets[0], offsets[1])) != null) { + let newId = null; + for (const record of records) { + newId = null; + // @ts-ignore + sourceFileName = record[0] || null; + // @ts-ignore + offsets[0] = record[1]; + // @ts-ignore + offsets[1] = record[2]; + sourceContent = record[3] || null; + if (sourceFileName != null && options.output != null && !sourceFileName.startsWith("data:")) { + if (cache[sourceFileName] == null) { + const absolute = options.resolve(dirname(options.output), options.cwd) + .absolute; + const absoluteSourcePath = options.resolve(dirname(options.src || ""), options.cwd).absolute; + // resolution is relative to the source file + const absoluteSourceFileName = options.resolve(sourceFileName, absoluteSourcePath) + .absolute; + cache[sourceFileName] = options.resolve(absoluteSourceFileName, absolute).relative; + } + sourceFileName = cache[sourceFileName]; + } + for (const [id, file] of options.sourcesMap.entries()) { + if (file.getFileName() === sourceFileName) { + newId = id; + break; + } + if (sourceFileName == null && file.getContent() === sourceContent) { + newId = id; + break; + } + } + if (newId == null) { + const source = new SourceFile(sourceContent, [], sourceFileName); + options.sourcesMap.set(source.id, source); + newId = source.id; + } + srcId = newId; + if (!sourcemaps.sources.includes(srcId)) { + sourcemaps.sources.push(srcId); + } + sourcemaps.maps.push([newLine, newColumn, srcId, offsets[0], offsets[1]]); + } + } + else { + // if (sourceFileName != null && options.output != null && !sourceFileName.startsWith("data:")) { + // if (cache[sourceFileName] == null) { + // const absolute = options.resolve!(dirname(options.output as string), options.cwd as string) + // .absolute as string; + // const absoluteSourceFileName = options.resolve!(sourceFileName, options.cwd as string) + // .absolute as string; + // cache[sourceFileName] = options.resolve!(absoluteSourceFileName, absolute).relative as string; + // } + // sourceFileName = cache[sourceFileName] as string; + // } + if (!sourcemaps.sources.includes(srcId)) { + sourcemaps.sources.push(srcId); + } + sourcemaps.maps.push([newLine, newColumn, srcId, offsets[0], offsets[1]]); + } + } + move(sourceLocation, lineMap, str, offset); + } + /** + * Update position + * @param sourceLocation + * @param lineMap + * @param str + * @param start + * @param end + */ + function move(sourceLocation, lineMap, str, start, end) { + let i = start ?? 0; + let j = end ?? str.length; + let codepoint; + let char; + for (; i < j; i++) { + char = str.charAt(i); + codepoint = char.charCodeAt(0); + sourceLocation.end += char.length; + if (codepoint == 0xa || // \n + codepoint == 0xb || // \v + codepoint == 0xc || // \f + codepoint == 0xd || // \r + codepoint == 0x2028 || // \u2028 + codepoint == 0x2029 // \u2029 + ) { + if (codepoint == 0xd && i + 1 < str.length && str.charCodeAt(i + 1) == 0xa) ; + else if (codepoint == 0xa && i > 0 && str.charCodeAt(i - 1) == 0xd) ; + else { + lineMap.addLineStart(sourceLocation.end); + } + } } - if (val.typ === exports.EnumToken.NumberTokenType && val.val === 0) { - // @ts-ignore - return properties.types.some((type) => { - // @ts-ignore - const typ = exports.EnumToken[type]; - return typ == exports.EnumToken.LengthTokenType || typ == exports.EnumToken.AngleTokenType; - }); + } + /** + * render ast node + * @param data + * @param options + * @param sourcemaps + * @param sourceLocation + * @param lineMap + * @param errors + * @param reducer + * @param cache + * @param level + * @param indents + * + * @internal + */ + function renderAstNode(data, options, sourcemaps, sourceLocation, lineMap, errors, reducer, cache, level = 0, indents = []) { + if (indents.length < level + 1) { + indents.push(options.indent.repeat(level)); } - if (tokensfuncSet.has(val.typ) && val.typ === exports.EnumToken.MathFunctionTokenType) { - return val.chi.every((t) => [ - exports.EnumToken.Add, - exports.EnumToken.Mul, - exports.EnumToken.Div, - exports.EnumToken.Sub, - exports.EnumToken.LiteralTokenType, - exports.EnumToken.CommaTokenType, - exports.EnumToken.WhitespaceTokenType, - exports.EnumToken.DimensionTokenType, - exports.EnumToken.NumberTokenType, - exports.EnumToken.LengthTokenType, - exports.EnumToken.AngleTokenType, - exports.EnumToken.PercentageTokenType, - exports.EnumToken.ResolutionTokenType, - exports.EnumToken.TimeTokenType, - exports.EnumToken.BinaryExpressionTokenType, - ].includes(t.typ) || matchType(t, properties)); + if (indents.length < level + 2) { + indents.push(options.indent.repeat(level + 1)); + } + // @ts-ignore + let children = ""; + let str = ""; + const indent = indents[level]; + const indentSub = indents[level + 1]; + switch (data.typ) { + case exports.EnumToken.DeclarationNodeType: + return `${data.nam}:${options.indent}${(options.minifyValues + ? filterValues$1(data.val) + : data.val).reduce(reducer, "")}`; + case exports.EnumToken.CommentNodeType: + case exports.EnumToken.CDOCOMMNodeType: + if (data.val.startsWith("/*# sourceMappingURL=")) { + // ignore sourcemap comment + return ""; + } + return !options.removeComments || (options.preserveLicense && data.val.startsWith("/*!")) + ? data.val + : ""; + case exports.EnumToken.StyleSheetNodeType: + for (const node of data.chi) { + str = renderAstNode(node, options, sourcemaps, sourceLocation, lineMap, errors, reducer, cache, level, indents); + if (str === "") { + continue; + } + if (children.length > 0) { + str = options.newLine + str; + } + children += str; + if (sourcemaps != null && str !== "" && options.newLine) { + move(sourceLocation, lineMap, options.newLine); + } + } + return children; + case exports.EnumToken.AtRuleNodeType: + case exports.EnumToken.RuleNodeType: + case exports.EnumToken.KeyframesRuleNodeType: + case exports.EnumToken.KeyframesAtRuleNodeType: + if ([exports.EnumToken.AtRuleNodeType, exports.EnumToken.KeyframesAtRuleNodeType].includes(data.typ) && !("chi" in data)) { + return `${indent}@${data.nam}${data.val === "" ? "" : options.indent || " "}${data.val};`; + } + const lineMapLength = lineMap ? lineMap.getLineStarts().length : 0; + const prelude = (indent.length > 0 ? options.newLine : "") + + indent + + ([exports.EnumToken.AtRuleNodeType, exports.EnumToken.KeyframesAtRuleNodeType].includes(data.typ) + ? `@${data.nam}${data.val === "" ? "" : options.indent || " "}${data.val}${options.indent}{` + : data.sel + `${options.indent}{`); + if (sourcemaps != null) { + updateSourceMap(data, options, cache, sourcemaps, sourceLocation, lineMap, prelude); + } + let node; + let recordDeclarationSourceMap = data.typ == exports.EnumToken.AtRuleNodeType; + for (let i = 0; i < data.chi.length; i++) { + node = data.chi[i]; + if (node.typ == exports.EnumToken.CommentNodeType) { + str = + options.removeComments && + (!options.preserveLicense || !node.val.startsWith("/*!")) + ? "" + : node.val; + } + else if (node.typ == exports.EnumToken.DeclarationNodeType) { + str = `${node.nam}:${options.indent}${(options.minifyValues + ? filterValues$1(node.val) + : node.val) + .reduce(reducer, "") + .trimEnd()};`; + } + else { + str = renderAstNode(node, options, sourcemaps, sourceLocation, lineMap, errors, reducer, cache, level + 1, indents); + if (str === "") { + continue; + } + children += str; + str = ""; + continue; + } + if (str === "") { + continue; + } + str = options.newLine + indentSub + str; + children += str; + if (sourcemaps != null && str !== "") { + if (node.typ == exports.EnumToken.DeclarationNodeType && recordDeclarationSourceMap) { + // if declaration is child of at-rule, then record it + // .rule { + // @media screen { + // color: red; + // } + // } + // @ts-ignore + updateSourceMap(node, options, cache, sourcemaps, sourceLocation, lineMap, str); + } + else { + move(sourceLocation, lineMap, str); + } + } + } + if (children.endsWith(";")) { + children = children.slice(0, -1); + sourceLocation.end--; + } + if (options.removeEmpty && children === "") { + if (sourcemaps != null) { + sourceLocation.end -= prelude.length; + lineMap.getLineStarts().length = lineMapLength; + } + return ""; + } + const end = options.newLine + indent + `}`; + if (sourcemaps != null) { + move(sourceLocation, lineMap, end); + } + return prelude + children + end; + default: + return ""; } - return false; } - - const propertiesConfig = getConfig(); - class PropertyMap { - declarations; - config; - requiredCount; - pattern; - constructor(config) { - const values = Object.values(config.properties); - this.requiredCount = - values.reduce((acc, curr) => (curr.required ? ++acc : acc), 0) || - values.length; - this.config = config; - this.declarations = new Map(); - this.pattern = config.pattern.split(/\s/); + /** + * render ast token + * @param token + * @param options + * @param cache + * @param reducer + * @param errors + * @private + */ + function renderValue$1(token, options = {}, cache = Object.create(null), reducer, errors) { + if (token.typ === exports.EnumToken.WhenElseFunctionTokenType && + equalsIgnoreCase(token.val, "supports")) { + options = { ...options, minifyValues: false, minify: false, convertColor: false }; + reducer = null; } - add(declaration) { - if (declaration.nam == this.config.shorthand) { - this.declarations.clear(); - this.declarations.set(declaration.nam, declaration); - this.matchTypes(declaration); - } - else { - const separator = this.config.separator != null - ? { - ...this.config.separator, - // @ts-ignore - typ: exports.EnumToken[this.config.separator.typ], + options.minifyValues ??= options.minify; + if (reducer == null) { + reducer = function (acc, curr) { + if (curr.typ == exports.EnumToken.CommentTokenType && options.removeComments) { + if (!options.preserveLicense || !curr.val.startsWith("/*!")) { + return acc; } - : null; - // expand shorthand - if (this.declarations.has(this.config.shorthand)) { - const tokens = {}; - const values = []; - const val = [[]]; - for (const curr of this.declarations.get(this.config.shorthand).val) { - // @ts-ignore - // @ts-ignore - if (separator != null && separator.typ == curr.typ && separator.val == curr.val) { - val.push([]); - continue; + return acc + curr.val; + } + return acc + renderValue$1(curr, options, cache, reducer); + }; + } + switch (token.typ) { + case exports.EnumToken.UrlFunctionTokenDefType: + case exports.EnumToken.FunctionTokenDefType: + case exports.EnumToken.MathFunctionTokenDefType: + case exports.EnumToken.ImageFunctionTokenDefType: + case exports.EnumToken.ColorFunctionTokenDefType: + case exports.EnumToken.TimingFunctionTokenDefType: + case exports.EnumToken.WhenElseFunctionTokenDefType: + case exports.EnumToken.SupportsFunctionTokenDefType: + case exports.EnumToken.TimelineFunctionTokenDefType: + case exports.EnumToken.GridTemplateFuncTokenDefType: + case exports.EnumToken.TransformFunctionTokenDefType: + case exports.EnumToken.ContainerFunctionTokenDefType: + case exports.EnumToken.PseudoClassFunctionTokenDefType: + case exports.EnumToken.GeneralEnclosedFunctionTokenDefType: + case exports.EnumToken.CustomFunctionTokenDefType: + case exports.EnumToken.WildCardFunctionTokenDefType: + return token.val + "("; + case exports.EnumToken.ListTokenType: + return token.chi.reduce((acc, curr) => acc + renderValue$1(curr, options, cache), ""); + case exports.EnumToken.BinaryExpressionTokenType: + if (exports.EnumToken.Mul === token.op || + exports.EnumToken.Div === token.op) { + let result = ""; + if (token.l.typ == exports.EnumToken.BinaryExpressionTokenType && + [exports.EnumToken.Add, exports.EnumToken.Sub].includes(token.l.op)) { + result = "(" + renderValue$1(token.l, options, cache) + ")"; + } + else { + result = renderValue$1(token.l, options, cache); + } + result += token.op == exports.EnumToken.Mul ? "*" : "/"; + if (token.r.typ == exports.EnumToken.BinaryExpressionTokenType && + [exports.EnumToken.Add, exports.EnumToken.Sub].includes(token.r.op)) { + result += "(" + renderValue$1(token.r, options, cache) + ")"; + } + else { + result += renderValue$1(token.r, options, cache); + } + return result; + } + return (renderValue$1(token.l, options, cache) + + (token.op == exports.EnumToken.Add ? " + " : " - ") + + renderValue$1(token.r, options, cache)); + case exports.EnumToken.FractionTokenType: + const fraction = renderValue$1(token.l) + "/" + renderValue$1(token.r); + if (+token.r.val != 0) { + const value = minifyNumber(+token.l.val / +token.r.val); + if (value.length <= fraction.length) { + return value; + } + } + return fraction; + case exports.EnumToken.Add: + return " + "; + case exports.EnumToken.Sub: + return " - "; + case exports.EnumToken.UniversalSelectorTokenType: + case exports.EnumToken.Star: + case exports.EnumToken.Mul: + return "*"; + case exports.EnumToken.Div: + return "/"; + case exports.EnumToken.WrappedValuesTokenType: + return ("{" + + token.chi.reduce((acc, curr) => acc + renderValue$1(curr, options, cache), "") + + "}"); + case exports.EnumToken.ColorTokenType: + if (token.kin == exports.ColorType.LIGHT_DARK || + ("chi" in token && options.convertColor === false && token.chi.length == 2)) { + return (token.val + + "(" + + token.chi.reduce((acc, curr) => { + if (curr.typ === exports.EnumToken.IdenTokenType) { + parseColor(curr); + } + return acc + renderValue$1(curr, options, cache); + }, "") + + ")"); + } + if (options.convertColor !== false) { + const value = convertColor(token, typeof options.convertColor === "boolean" + ? exports.ColorType.HEX + : (exports.ColorType[exports.ColorType[options.convertColor ?? "HEX"] + ?.toUpperCase?.() + .replaceAll?.("-", "_")] ?? exports.ColorType.HEX)); + if (value != null) { + token = value; + } + if (token.kin == exports.ColorType.HEX || token.kin == exports.ColorType.LIT) { + return reduceHexValue(token.val); + } + } + if (token.kin === exports.ColorType.SYS || + token.kin === exports.ColorType.DPSYS || + token.kin === exports.ColorType.NON_STD) { + return token.val; + } + if (token.kin == exports.ColorType.HEX || token.kin == exports.ColorType.LIT) { + return token.val; + } + if (Array.isArray(token.chi)) { + const fnName = token.val; + const isLegacy = ["rgb", "rgba", "hsl", "hsla"].includes(token.val); + const hasAlpha = [ + "rgb", + "rgba", + "hsl", + "hsla", + "hwb", + "oklab", + "oklch", + "lab", + "lch", + ].includes(fnName); + const useAlpha = (hasAlpha && token.chi.length == (isLegacy ? 7 : 4)) || + ("color" == token.val && token.chi.length == 5); + return ((hasAlpha && token.val.endsWith("a") ? fnName.slice(0, -1) : fnName) + + "(" + + token + .chi.reduce((acc, curr, index, array) => { + if (/[,/]\s*$/.test(acc)) { + if (curr.typ == exports.EnumToken.WhitespaceTokenType) { + return acc.trimEnd(); + } + return acc.trimStart() + renderValue$1(curr, options, cache); + } + if (isLegacy && curr.typ == exports.EnumToken.CommaTokenType) { + return acc.trimEnd() + " "; + } + if (curr.typ == exports.EnumToken.WhitespaceTokenType) { + return acc.trimEnd() + " "; + } + if (curr.typ == exports.EnumToken.CommaTokenType || + (curr.typ == exports.EnumToken.LiteralTokenType && curr.val == "/")) { + return acc.trimEnd() + (curr.typ == exports.EnumToken.CommaTokenType ? "," : "/"); + } + return (acc.trimEnd() + + (useAlpha && index == array.length - 1 ? "/" : " ") + + renderValue$1(curr, options, cache)); + }, "") + .trimStart() + + ")"); + } + case exports.EnumToken.UrlFunctionTokenType: + if (options.minifyValues && token.typ === exports.EnumToken.UrlFunctionTokenType) { + if (token.chi[0]?.typ === exports.EnumToken.BadUrlTokenType) { + return "url()"; + } + for (const child of token.chi) { + if (child.typ === exports.EnumToken.StringTokenType) { + if (child.val.slice(1, 5) !== "data:" && + urlTokenMatcher.test(child.val)) { + Object.assign(child, { + typ: exports.EnumToken.UrlTokenTokenType, + val: child.val.slice(1, -1), + }); + } + break; } - // @ts-ignore - val.at(-1).push(curr); } - for (let current = 0; current < val.length; current++) { - values.push(...this.pattern.reduce((acc, property) => { - // let current: number = 0; - const props = this.config.properties[property]; - for (let i = 0; i < acc.length; i++) { - if (acc[i].typ == exports.EnumToken.CommentTokenType || - acc[i].typ == exports.EnumToken.WhitespaceTokenType) { - acc.splice(i, 1); - i--; - continue; + } + case exports.EnumToken.ParensTokenType: + case exports.EnumToken.FunctionTokenType: + case exports.EnumToken.MathFunctionTokenType: + case exports.EnumToken.ImageFunctionTokenType: + if (options.minifyValues && token.typ === exports.EnumToken.ImageFunctionTokenType) { + const slice = token.chi.slice(); + switch (token.val) { + case "linear-gradient": + case "repeating-linear-gradient": + { + let i; + let k; + let j; + let key; + for (i = 0; i < slice.length; i++) { + if (slice[i].typ == exports.EnumToken.ColorTokenType || + slice[i].typ === exports.EnumToken.CommaTokenType) { + break; + } + if (slice[i].typ == exports.EnumToken.IdenTokenType && + equalsIgnoreCase(slice[i].val, "to")) { + k = i + 1; + while (k < slice.length && + (slice[k].typ === exports.EnumToken.WhitespaceTokenType || + slice[k].typ === exports.EnumToken.CommentTokenType)) { + k++; + } + if (k < slice.length && slice[k].typ === exports.EnumToken.IdenTokenType) { + // convert keyword to angle + key = slice[k].val.toLowerCase(); + switch (key) { + case "right": + slice.splice(i, k - i + 1, { + typ: exports.EnumToken.AngleTokenType, + val: 90, + }); + break; + case "left": + slice.splice(i, k - i + 1, { + typ: exports.EnumToken.AngleTokenType, + val: 270, + }); + break; + case "top": + case "bottom": + j = k + 1; + while (j < slice.length && + (slice[j].typ === exports.EnumToken.WhitespaceTokenType || + slice[j].typ === exports.EnumToken.CommentTokenType)) { + j++; + } + if (j < slice.length && slice[j].typ !== exports.EnumToken.CommaTokenType) { + i = j - 1; + break; + } + // https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/gradient/linear-gradient#:~:text=If%20unspecified%2C%20it%20defaults%20to%20to%20bottom + if (key === "bottom") { + // to bottom -> 180deg is the default angle and will be removed + while (k < slice.length && + slice[k].typ != exports.EnumToken.CommaTokenType) { + k++; + } + slice.splice(i, k - i + 1); + i--; + continue; + } + slice.splice(i, k - i + 1, { + typ: exports.EnumToken.AngleTokenType, + val: key === "top" ? 0 : 180, + }); + i--; + break; + } + } + } + else if (slice[i].typ == exports.EnumToken.AngleTokenType) { + if ((180 - toDegrees(slice[i]).val) % 360 === 0) { + k = i + 1; + // while (k < slice.length && slice[k].typ !== EnumToken.CommaTokenType) { + // k++; + // } + slice.splice(i, k - i + 1); + } + } + } + if (slice[i]?.typ === exports.EnumToken.CommaTokenType) { + i++; + // while ( + // i < slice.length && + // (slice[i].typ === EnumToken.WhitespaceTokenType || + // slice[i].typ === EnumToken.CommentTokenType) + // ) { + // i++; + // } + } + if (slice[i]?.typ === exports.EnumToken.ColorTokenType) { + for (const token of reduceColorStops(slice.splice(i, slice.length - i))) { + slice.push(token); + } + } + } + break; + case "radial-gradient": + case "repeating-radial-gradient": + { + let i = 0; + // while ( + // i < slice.length && + // (slice[i].typ === EnumToken.WhitespaceTokenType || + // slice[i].typ === EnumToken.CommentTokenType) + // ) { + // i++; + // } + const positions = []; + const form = []; + const size = []; + const colorSpaceDef = []; + if (slice[i]?.typ === exports.EnumToken.IdenTokenType) { + if (equalsIgnoreCase(slice[i].val, "farthest-corner") || + equalsIgnoreCase(slice[i].val, "ellipse")) { + i++; + } + else if (equalsIgnoreCase(slice[i].val, "closest-side") || + equalsIgnoreCase(slice[i].val, "closest-corner") || + equalsIgnoreCase(slice[i].val, "farthest-side")) { + size.push(slice[i++]); + } + else if (equalsIgnoreCase(slice[i].val, "circle")) { + form.push(slice[i++]); + } + while (slice[i]?.typ === exports.EnumToken.WhitespaceTokenType || + slice[i]?.typ === exports.EnumToken.CommentTokenType) { + i++; + } } - if ( - // @ts-ignore - acc[i][PROPERTYNAME] == property || - matchType(acc[i], props)) { - if ("prefix" in props && props.previous != null && !(props.previous in tokens)) { - return acc; + if (slice[i]?.typ === exports.EnumToken.IdenTokenType) { + if (equalsIgnoreCase(slice[i].val, "farthest-corner") || + equalsIgnoreCase(slice[i].val, "ellipse")) { + i++; } - if (!(property in tokens)) { - tokens[property] = [[acc[i]]]; + else if (equalsIgnoreCase(slice[i].val, "closest-side") || + equalsIgnoreCase(slice[i].val, "closest-corner") || + equalsIgnoreCase(slice[i].val, "farthest-side")) { + size.push(slice[i++]); } - else { - if (current == tokens[property].length) { - tokens[property].push([acc[i]]); + else if (equalsIgnoreCase(slice[i].val, "circle")) { + form.push(slice[i++]); + } + while (slice[i]?.typ === exports.EnumToken.WhitespaceTokenType || + slice[i]?.typ === exports.EnumToken.CommentTokenType) { + i++; + } + } + if (slice[i]?.typ === exports.EnumToken.IdenTokenType && + equalsIgnoreCase(slice[i].val, "at")) { + i++; + } + while (slice[i]?.typ === exports.EnumToken.WhitespaceTokenType || + slice[i]?.typ === exports.EnumToken.CommentTokenType) { + i++; + } + if (slice[i]?.typ === exports.EnumToken.PercentageTokenType || + (slice[i]?.typ === exports.EnumToken.NumberTokenType && 0 === slice[i].val) || + (slice[i]?.typ === exports.EnumToken.IdenTokenType && + (equalsIgnoreCase(slice[i].val, "center") || + equalsIgnoreCase(slice[i].val, "top") || + equalsIgnoreCase(slice[i].val, "bottom") || + equalsIgnoreCase(slice[i].val, "left") || + equalsIgnoreCase(slice[i].val, "right")))) { + do { + if (slice[i]?.typ === exports.EnumToken.IdenTokenType && + "in" === slice[i].val) { + break; } - else { - tokens[property][current].push({ typ: exports.EnumToken.WhitespaceTokenType }, acc[i]); + positions.push(slice[i]); + i++; + } while (slice[i]?.typ !== exports.EnumToken.CommaTokenType); + } + while (slice[i]?.typ !== exports.EnumToken.CommaTokenType) { + colorSpaceDef.push(slice[i++]); + } + if (slice[i]?.typ === exports.EnumToken.CommaTokenType) { + i++; + } + if (positions.length > 0) { + let k; + let position1 = ""; + let position2 = ""; + for (k = 0; k < positions.length; k++) { + if (positions[k].typ === exports.EnumToken.IdenTokenType) { + if (position1.length === 0) { + position1 = positions[k].val; + } + else { + position2 = positions[k].val; + } + } + else if (positions[k].typ === exports.EnumToken.PercentageTokenType || + positions[k].typ === exports.EnumToken.NumberTokenType) { + if (position1.length === 0) { + position1 = positions[k].val + "%"; + } + else { + position2 = positions[k].val + "%"; + } + } + if (position2.length > 0) { + break; } } - acc.splice(i, 1); - i--; - // @ts-ignore - if ("prefix" in props && acc[i]?.typ == exports.EnumToken[props.prefix.typ]) { - if ( - // @ts-ignore - acc[i].typ == exports.EnumToken[props.prefix.typ] && - // @ts-ignore - acc[i].val == this.config.properties[property].prefix.val) { - acc.splice(i, 1); - i--; + if (position1.length > 0) { + let key = `${position1} ${position2}`.trim(); + if (key === "50% 50%" || + key === "center center" || + key === "50%" || + key === "center") { + positions.length = 0; + } + else if (key === "0% 0%" || + key === "0 0" || + key === "top left" || + key === "left top") { + positions.splice(0, positions.length, { + typ: exports.EnumToken.PercentageTokenType, + val: 0, + }); + } + else if (key === "50% 0%" || + key === "50% 0" || + key === "center top" || + key === "top center") { + positions.splice(0, positions.length, { + typ: exports.EnumToken.IdenTokenType, + val: "top", + }); + } + else if (key === "top right" || key === "right top") { + positions.splice(0, positions.length, { + typ: exports.EnumToken.PercentageTokenType, + val: 100, + }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.PercentageTokenType, val: 0 }); + } + else if (key === "0 50%" || + key === "0% 50%" || + key === "left center" || + key === "center left") { + positions.splice(0, positions.length, { + typ: exports.EnumToken.IdenTokenType, + val: "left", + }); + } + else if (key === "100% 50%" || key === "right center" || key === "center right") { + positions.splice(0, positions.length, { + typ: exports.EnumToken.IdenTokenType, + val: "right", + }); + } + else if (key === "0 100%" || + key === "0% 100%" || + key === "left bottom" || + key === "bottom left") { + positions.splice(0, positions.length, { + typ: exports.EnumToken.IdenTokenType, + val: "bottom", + }); + } + else if (key === "50% 100%" || + key === "center bottom" || + key === "bottom center") { + positions.splice(0, positions.length, { + typ: exports.EnumToken.IdenTokenType, + val: "bottom", + }); + } + else if (key === "100% 100%" || + key === "right bottom" || + key === "bottom right") { + positions.splice(0, positions.length, { + typ: exports.EnumToken.PercentageTokenType, + val: 100, + }); + } + else if (position1 === position2) { + positions.splice(0, positions.length, positions[k]); } } - if (props.multiple) { - continue; + } + const result = []; + if (form.length > 0) { + for (const token of form) { + result.push(token); } - return acc; } - else { - if (property in tokens && tokens[property].length > current) { - return acc; + if (size.length > 0) { + if (result.length > 0) { + result.push({ typ: exports.EnumToken.WhitespaceTokenType }); + } + for (const token of size) { + result.push(token); } } + if (positions.length > 0) { + if (result.length > 0) { + result.push({ typ: exports.EnumToken.WhitespaceTokenType }); + } + result.push({ typ: exports.EnumToken.IdenTokenType, val: "at" }, { typ: exports.EnumToken.WhitespaceTokenType }); + for (const token of positions) { + result.push(token); + } + } + if (colorSpaceDef.length > 0) { + if (result.length > 0) { + result.push({ typ: exports.EnumToken.WhitespaceTokenType }); + } + for (const token of colorSpaceDef) { + result.push(token); + } + } + if (result.length > 0) { + result.push({ typ: exports.EnumToken.CommaTokenType }); + } + for (const token of reduceColorStops(slice.slice(i))) { + result.push(token); + } + slice.length = 0; + for (const token of result) { + slice.push(token); + } } - if (property in tokens && tokens[property].length > current) { - return acc; - } - // default - if (props.default.length > 0) { - const defaults = parseString(props.default[0]); - if (!(property in tokens)) { - tokens[property] = [[...defaults]]; + break; + case "conic-gradient": + case "repeating-conic-gradient": + { + let i = 0; + const angles = []; + const positions = []; + const colorSpaceDef = []; + // while ( + // i < slice.length && + // (slice[i].typ === EnumToken.WhitespaceTokenType || + // slice[i].typ === EnumToken.CommentTokenType) + // ) { + // i++; + // } + if (slice[i]?.typ === exports.EnumToken.IdenTokenType && + equalsIgnoreCase(slice[i].val, "from")) { + angles.push(slice[i++]); + while (i < slice.length && + (slice[i].typ === exports.EnumToken.WhitespaceTokenType || + slice[i].typ === exports.EnumToken.CommentTokenType)) { + angles.push(slice[i++]); + } + if ((slice[i]?.typ === exports.EnumToken.NumberTokenType || + slice[i]?.typ === exports.EnumToken.AngleTokenType) && + 0 === toDegrees(slice[i]).val) { + angles.length = 0; + i++; + } + else if (slice[i]?.typ !== exports.EnumToken.CommaTokenType && + slice[i].typ != exports.EnumToken.IdenTokenType) { + angles.push(slice[i++]); + } + while (i < slice.length && + (slice[i].typ === exports.EnumToken.WhitespaceTokenType || + slice[i].typ === exports.EnumToken.CommentTokenType)) { + i++; + } } - else { - if (current == tokens[property].length) { - tokens[property].push([]); - for (let i = 0; i < defaults.length; i++) { - tokens[property][current].push(defaults[i]); + if (slice[i]?.typ === exports.EnumToken.IdenTokenType && + equalsIgnoreCase(slice[i].val, "at")) { + i++; + while (i < slice.length && + (slice[i].typ === exports.EnumToken.WhitespaceTokenType || + slice[i].typ === exports.EnumToken.CommentTokenType)) { + i++; + } + let position1 = ""; + let position2 = ""; + if (slice[i]?.typ === exports.EnumToken.IdenTokenType && + !equalsIgnoreCase("in", slice[i].val)) { + position1 = slice[i].val; + positions.push(slice[i++]); + } + else if (slice[i]?.typ === exports.EnumToken.PercentageTokenType || + slice[i]?.typ === exports.EnumToken.NumberTokenType) { + position1 = slice[i].val + "%"; + positions.push(slice[i++]); + } + while (i < slice.length && + (slice[i].typ === exports.EnumToken.WhitespaceTokenType || + slice[i].typ === exports.EnumToken.CommentTokenType)) { + positions.push(slice[i++]); + } + if (slice[i]?.typ === exports.EnumToken.IdenTokenType && + !equalsIgnoreCase("in", slice[i].val)) { + position2 = slice[i].val; + positions.push(slice[i++]); + } + else if (slice[i]?.typ === exports.EnumToken.PercentageTokenType || + slice[i]?.typ === exports.EnumToken.NumberTokenType) { + position2 = slice[i].val + "%"; + positions.push(slice[i++]); + } + while (i < slice.length && + (slice[i].typ === exports.EnumToken.WhitespaceTokenType || + slice[i].typ === exports.EnumToken.CommentTokenType)) { + i++; + } + if (position1.length > 0) { + reducegradientBackgroundPosition(positions, `${position1} ${position2}`.trim()); + } + } + while (i < slice.length && + (slice[i].typ === exports.EnumToken.WhitespaceTokenType || + slice[i].typ === exports.EnumToken.CommentTokenType)) { + i++; + } + if (slice[i]?.typ === exports.EnumToken.IdenTokenType && + equalsIgnoreCase("in", slice[i].val)) { + while (i < slice.length && slice[i].typ !== exports.EnumToken.CommaTokenType) { + colorSpaceDef.push(slice[i++]); + } + } + if (slice[i]?.typ === exports.EnumToken.CommaTokenType) { + i++; + } + const result = []; + if (positions.length > 0) { + if (positions.length > 0) { + if (angles.length > 0) { + angles.push({ typ: exports.EnumToken.WhitespaceTokenType }); + } + angles.push({ typ: exports.EnumToken.IdenTokenType, val: "at" }, { typ: exports.EnumToken.WhitespaceTokenType }); + for (const position of positions) { + angles.push(position); } } - else { - tokens[property][current].push({ - typ: exports.EnumToken.WhitespaceTokenType, - }); - for (let i = 0; i < defaults.length; i++) { - tokens[property][current].push(defaults[i]); + } + if (angles.length > 0) { + for (const angle of angles) { + result.push(angle); + } + result.push({ typ: exports.EnumToken.CommaTokenType }); + } + if (colorSpaceDef.length > 0) { + if (colorSpaceDef.length > 0) { + if (result.length > 0) { + result.push({ typ: exports.EnumToken.WhitespaceTokenType }); + } + for (const token of colorSpaceDef) { + result.push(token); } } + result.push({ typ: exports.EnumToken.CommaTokenType }); + } + for (const token of reduceConicColorStops(slice.slice(i))) { + result.push(token); + } + slice.length = 0; + for (let j = 0; j < result.length; j++) { + slice.push(result[j]); } } - return acc; - }, val[current])); + break; } - if (values.length == 0) { - this.declarations = Object.entries(tokens).reduce((acc, curr) => { - acc.set(curr[0], { - typ: exports.EnumToken.DeclarationNodeType, - nam: curr[0], - val: curr[1].reduce((acc, curr) => { - if (acc.length > 0) { - acc.push({ ...separator }); - } - for (let i = 0; i < curr.length; i++) { - acc.push(curr[i]); - } - return acc; - }, []), - }); - return acc; - }, new Map()); + return token.val + "(" + slice.reduce(reducer, "") + ")"; + } + case exports.EnumToken.TimingFunctionTokenType: + case exports.EnumToken.PseudoClassFuncTokenType: + case exports.EnumToken.WhenElseFunctionTokenType: + case exports.EnumToken.TimelineFunctionTokenType: + case exports.EnumToken.GridTemplateFuncTokenType: + case exports.EnumToken.SupportsFunctionTokenType: + case exports.EnumToken.ContainerFunctionTokenType: + case exports.EnumToken.TransformFunctionTokenType: + case exports.EnumToken.GeneralEnclosedFunctionTokenType: + case exports.EnumToken.CustomFunctionTokenType: + case exports.EnumToken.WildCardFunctionTokenType: + if (token.typ == exports.EnumToken.MathFunctionTokenType && + token.chi.length == 1 && + ![exports.EnumToken.BinaryExpressionTokenType, exports.EnumToken.FractionTokenType, exports.EnumToken.IdenTokenType].includes(token.chi[0].typ) && + // @ts-ignore + token.chi[0].val + ?.typ != exports.EnumToken.FractionTokenType) { + return (token.val + + "(" + + token.chi.reduce((acc, curr) => acc + + renderValue$1(curr, token.typ == exports.EnumToken.FunctionTokenType ? { minify: false } : options, cache, reducer), "") + + ")"); + } + return (token.val ?? "") + "(" + token.chi.reduce(reducer, "") + ")"; + case exports.EnumToken.BlockStartTokenType: + return "{"; + case exports.EnumToken.BlockEndTokenType: + return "}"; + case exports.EnumToken.StartParensTokenType: + return "("; + case exports.EnumToken.DelimTokenType: + case exports.EnumToken.EqualMatchTokenType: + return "="; + case exports.EnumToken.IncludeMatchTokenType: + return "~="; + case exports.EnumToken.DashMatchTokenType: + return "|="; + case exports.EnumToken.StartMatchTokenType: + return "^="; + case exports.EnumToken.EndMatchTokenType: + return "$="; + case exports.EnumToken.ContainMatchTokenType: + return "*="; + case exports.EnumToken.LtTokenType: + return "<"; + case exports.EnumToken.LteTokenType: + return "<="; + case exports.EnumToken.Tilda: + case exports.EnumToken.SubsequentSiblingCombinatorTokenType: + return "~"; + case exports.EnumToken.Plus: + case exports.EnumToken.NextSiblingCombinatorTokenType: + return "+"; + case exports.EnumToken.GtTokenType: + case exports.EnumToken.ChildCombinatorTokenType: + return ">"; + case exports.EnumToken.GteTokenType: + return ">="; + case exports.EnumToken.ColumnCombinatorTokenType: + return "||"; + case exports.EnumToken.EndParensTokenType: + return ")"; + case exports.EnumToken.AttrStartTokenType: + return "["; + case exports.EnumToken.AttrEndTokenType: + return "]"; + case exports.EnumToken.DescendantCombinatorTokenType: + case exports.EnumToken.WhitespaceTokenType: + return " "; + case exports.EnumToken.ColonTokenType: + return ":"; + case exports.EnumToken.DoubleColonTokenType: + return "::"; + case exports.EnumToken.SemiColonTokenType: + return ";"; + case exports.EnumToken.CommaTokenType: + return ","; + case exports.EnumToken.ImportantTokenType: + return "!important"; + case exports.EnumToken.Pipe: + return "|"; + case exports.EnumToken.AttrTokenType: + case exports.EnumToken.IdenListTokenType: + return "[" + token.chi.reduce(reducer, "") + "]"; + case exports.EnumToken.TimeTokenType: + case exports.EnumToken.AngleTokenType: + case exports.EnumToken.LengthTokenType: + case exports.EnumToken.DimensionTokenType: + case exports.EnumToken.FrequencyTokenType: + case exports.EnumToken.ResolutionTokenType: + let val = token.val.typ == exports.EnumToken.FractionTokenType + ? renderValue$1(token.val, options, cache) + : minifyNumber(token.val); + let unit = token.unit; + if (token.typ == exports.EnumToken.AngleTokenType && !val.includes("/")) { + const angle = getAngle(token); + let v; + let value = val + unit; + for (const u of ["deg", "turn", "rad", "grad"]) { + if (token.unit == u) { + continue; + } + switch (u) { + case "deg": + v = minifyNumber(toPrecisionAngle(angle * 360, anglePrecision, false).toFixed(anglePrecision)); + if (v.length + 3 < value.length) { + val = v; + unit = u; + value = v + u; + } + break; + case "turn": + v = minifyNumber(toPrecisionAngle(angle, anglePrecision, false).toFixed(anglePrecision)); + if (v.length + 4 < value.length) { + val = v; + unit = u; + value = v + u; + } + break; + case "rad": + v = minifyNumber(toPrecisionAngle(angle * (2 * Math.PI), anglePrecision, false).toFixed(anglePrecision)); + if (v.length + 3 < value.length) { + val = v; + unit = u; + value = v + u; + } + break; + case "grad": + v = minifyNumber(toPrecisionAngle(angle * 400, anglePrecision, false).toFixed(anglePrecision)); + if (v.length + 4 < value.length) { + val = v; + unit = u; + value = v + u; + } + break; + } } } - // @ts-ignore - const config = propertiesConfig.properties[declaration.nam]; - let property = declaration.nam; - if (config != null) { - property = config.shorthand; - let value = this.declarations.get(property); - if (!(value instanceof PropertySet)) { + if (val === "0") { + if (token.typ == exports.EnumToken.TimeTokenType) { + return "0s"; + } + if (token.typ == exports.EnumToken.FrequencyTokenType) { + return "0Hz"; + } + // @ts-ignore + if (token.typ == exports.EnumToken.ResolutionTokenType) { + return "0x"; + } + return "0"; + } + if (token.typ == exports.EnumToken.TimeTokenType) { + if (unit == "ms") { // @ts-ignore - this.declarations.set(property, new PropertySet(propertiesConfig.properties[config.shorthand])); - // Token[] - if (value != null) { - // @ts-ignore - this.declarations.get(property).add(value); + const v = minifyNumber(val / 1000); + if (v.length + 1 <= val.length) { + return v + "s"; } + return val + "ms"; } - this.declarations.get(property).add(declaration); + return val + "s"; } - else { - this.declarations.set(declaration.nam, declaration); + if (token.typ == exports.EnumToken.ResolutionTokenType && unit == "dppx") { + unit = "x"; + } + return val.includes("/") ? val.replace("/", unit + "/") : minifyNumber(toPrecisionValue(val)) + unit; + case exports.EnumToken.FlexTokenType: + case exports.EnumToken.PercentageTokenType: + const uni = token.typ == exports.EnumToken.PercentageTokenType ? "%" : "fr"; + const perc = token.val.typ == exports.EnumToken.FractionTokenType + ? renderValue$1(token.val, options, cache) + : minifyNumber(token.val); + return options.minifyValues && perc == "0" + ? "0" + : perc.includes("/") + ? perc.replace("/", uni + "/") + : perc + uni; + case exports.EnumToken.NumberTokenType: + return token.val.typ == exports.EnumToken.FractionTokenType + ? renderValue$1(token.val, options, cache) + : minifyNumber(token.val); + case exports.EnumToken.InfinityTokenType: + return "1/0"; + case exports.EnumToken.NegativeInfinityTokenType: + return "-1/0"; + case exports.EnumToken.NaNTokenType: + return "NaN"; + case exports.EnumToken.AtRuleTokenType: + return "@" + token.nam; + case exports.EnumToken.CommentTokenType: + case exports.EnumToken.CDOCOMMNodeType: + if (options.removeComments && + (!options.preserveLicense || !token.val.startsWith("/*!"))) { + return ""; + } + case exports.EnumToken.PseudoClassTokenType: + case exports.EnumToken.PseudoElementTokenType: + // https://www.w3.org/TR/selectors-4/#single-colon-pseudos + if (token.typ == exports.EnumToken.PseudoElementTokenType && + pseudoElements.includes(token.val.slice(1))) { + return token.val.slice(1); } + case exports.EnumToken.UrlTokenTokenType: + case exports.EnumToken.HashTokenType: + case exports.EnumToken.IdenTokenType: + case exports.EnumToken.StringTokenType: + case exports.EnumToken.LiteralTokenType: + case exports.EnumToken.DashedIdenTokenType: + case exports.EnumToken.PseudoPageTokenType: + case exports.EnumToken.ClassSelectorTokenType: + return token.val; + case exports.EnumToken.NestingSelectorTokenType: + return "&"; + case exports.EnumToken.InvalidAttrTokenType: + return ("[" + + token.chi.reduce((acc, curr) => acc + renderValue$1(curr, options, cache), "")); + case exports.EnumToken.InvalidClassSelectorTokenType: + return token.val; + case exports.EnumToken.SupportsQueryUnaryConditionTokenType: + case exports.EnumToken.WhenElseUnaryConditionTokenType: + return (renderValue$1(token.l, options, cache, reducer) + + " " + + token.r.reduce((acc, curr) => acc + renderValue$1(curr, options, cache, reducer), "")); + case exports.EnumToken.SupportsQueryConditionTokenType: + case exports.EnumToken.WhenElseQueryConditionTokenType: + return (token.l.reduce((acc, curr) => acc + renderValue$1(curr, options, cache, reducer), "") + + " " + + renderValue$1(token.op, options, cache, reducer) + + " " + + token.r.reduce((acc, curr) => acc + renderValue$1(curr, options, cache, reducer), "")); + case exports.EnumToken.IfConditionTokenType: + return token.l.length == 0 + ? "" + : token.l.reduce((acc, curr) => acc + renderValue$1(curr, options, cache, reducer), "") + + ":" + + (options.indent ?? "") + + token.r.reduce((acc, curr) => acc + renderValue$1(curr, options, cache, reducer), ""); + case exports.EnumToken.IfElseConditionTokenType: + return (renderValue$1(token.l, options, cache, reducer) + + (options.indent ?? "") + + renderValue$1(token.r, options, cache, reducer)); + case exports.EnumToken.DeclarationNodeType: + return (token.nam + + ":" + + (options.minifyValues ? filterValues$1(token.val) : token.val).reduce((acc, curr) => acc + renderValue$1(curr, options, cache, reducer), "")); + case exports.EnumToken.MediaQueryUnaryFeatureTokenType: + return (renderValue$1(token.l, options, cache, reducer) + + " " + + token.r.reduce((acc, curr) => acc + renderValue$1(curr, options, cache, reducer), "")); + case exports.EnumToken.MediaQueryConditionTokenType: { + const indent = token.op.typ == exports.EnumToken.LtTokenType || + token.op.typ == exports.EnumToken.GtTokenType || + token.op.typ == exports.EnumToken.ColonTokenType || + token.op.typ == exports.EnumToken.DelimTokenType || + token.op.typ == exports.EnumToken.LteTokenType || + token.op.typ == exports.EnumToken.GteTokenType + ? "" + : " "; + return (token.l.reduce((acc, curr) => acc + renderValue$1(curr, options, cache, reducer), "") + + indent + + renderValue$1(token.op, options, cache, reducer) + + indent + + token.r.reduce((acc, curr) => acc + renderValue$1(curr, options, cache, reducer), "")); } - return this; + case exports.EnumToken.MediaRangeQueryTokenType: + return (token.l.reduce((acc, curr) => acc + renderValue$1(curr, options, cache, reducer), "") + + renderValue$1(token.op1, options, cache, reducer) + + token.val.reduce((acc, curr) => acc + renderValue$1(curr, options, cache, reducer), "") + + renderValue$1(token.op2, options, cache, reducer) + + token.r.reduce((acc, curr) => acc + renderValue$1(curr, options, cache, reducer), "")); + case exports.EnumToken.MediaFeatureTokenType: + return token.val; + case exports.EnumToken.NotTokenType: + return "not"; + case exports.EnumToken.OnlyTokenType: + return "only"; + case exports.EnumToken.AndTokenType: + return "and"; + case exports.EnumToken.OrTokenType: + return "or"; + case exports.EnumToken.InvalidMediaQueryTokenType: + case exports.EnumToken.InvalidCommentTokenType: + case exports.EnumToken.BadCommentTokenType: + case exports.EnumToken.BadCdoTokenType: + case exports.EnumToken.BadStringTokenType: + case exports.EnumToken.BadUrlTokenType: + case exports.EnumToken.EOFTokenType: + return ""; + default: + console.debug({ token }); + throw new Error(`Unsupported token type for ${exports.EnumToken[token.typ]}`); } - [Symbol.iterator]() { - const propertiesMapping = { ...this.config.properties }; - const patterns = this.config.pattern.split(" "); - let hasMapping = false; - let iterable; - let requiredCount = 0; - let property; - let isShorthand = true; - let declarations = this.declarations; - for (const key of declarations.keys()) { - if (declarations.has(key) && declarations.get(key) instanceof PropertyMap) { - hasMapping = true; - break; - } + // errors?.push({ action: "ignore", message: `render: unexpected token ${JSON.stringify(token, null, 1)}` }); + // return ""; + } + /** + * Remove whitespace tokens that are not needed + * @param values + * + * @internal + */ + function filterValues$1(values) { + let i = 0; + for (; i < values.length; i++) { + if (values[i].typ == exports.EnumToken.ImportantTokenType && values[i - 1]?.typ === exports.EnumToken.WhitespaceTokenType) { + values.splice(i - 1, 1); } - if (hasMapping) { - const mapped = {}; - let key; - for (const value of declarations.values()) { - key = value.nam; - if (value instanceof PropertyMap) { - for (const [k, v] of value.declarations) { - if (k == value.config.shorthand) { - for (const [key, val] of Object.entries(value.config.properties)) { - mapped[key] = cloneNode(v); - for (let i = 0; i < v.val.length; i++) { - // @ts-ignore - if (key == v.val[i][PROPERTYNAME]) { - if (mapped[key].val.length > 0) { - mapped[key].val.push({ - typ: exports.EnumToken.WhitespaceTokenType, - }); - } - mapped[key].val.push(v.val[i]); - } - } - if (mapped[key].val.length == 0) { - mapped[key].val.push(...parseString(value.config.properties[key].default[0])); - } - } - } - else { - mapped[k] = v; - } - } - } - else { - mapped[key] = value; - } - } - // console.error(mapped); - if (patterns.length === Object.keys(mapped).length) { - declarations = new Map(); - for (const key of patterns) { - if (mapped[key] != null) { - declarations.set(key, mapped[key]); - } - } - } + else if (tokensfuncSet.has(values[i].typ) && + "chi" in values[i] && + values[i].typ != exports.EnumToken.WildCardFunctionTokenType && + values[i + 1]?.typ == exports.EnumToken.WhitespaceTokenType) { + values.splice(i + 1, 1); } - for (property of Object.keys(propertiesMapping)) { - if (propertiesMapping[property].required) { - if (!declarations.has(property)) { - isShorthand = false; - break; - } - else { - const val = declarations.get(property); - if (val instanceof PropertySet && !val.isShortHand()) { - isShorthand = false; - break; - } - else { - requiredCount++; - } - } - } + } + return values; + } + + /** + * feature walk mode + * + * @private + */ + exports.FeatureWalkMode = void 0; + (function (FeatureWalkMode) { + /** + * pre process + */ + FeatureWalkMode[FeatureWalkMode["Pre"] = 1] = "Pre"; + /** + * post process + */ + FeatureWalkMode[FeatureWalkMode["Post"] = 2] = "Post"; + })(exports.FeatureWalkMode || (exports.FeatureWalkMode = {})); + + const SymbolsMapTokens = Object.create(null); + const SymbolsMapTokensLower = Object.create(null); + // Regex for escape sequence decoding - compile once, reuse many times + const ESCAPE_SEQUENCE_REGEX = /\\((\n|\r|\f|\v|\u2028|\u2029|([0-9a-fA-F]{1,6})) ?)/gms; + function decodeEscapeSequences(value) { + return value.replace(ESCAPE_SEQUENCE_REGEX, (_, sequence) => { + // \n \r \f \v + switch (sequence.charCodeAt(0)) { + case 0xa: + case 0xb: + case 0xc: + case 0xd: + case 0x2028: + case 0x2029: + return ""; } - if (requiredCount === 0) { - requiredCount = declarations.size; + const codepoint = parseInt(sequence, 16); + if (codepoint == 0 || + // leading surrogate + (0xd800 <= codepoint && codepoint <= 0xdbff) || + // trailing surrogate + (0xdc00 <= codepoint && codepoint <= 0xdfff) || + codepoint > 0x10ffff) { + return "\uFFFD"; } - if (!isShorthand || requiredCount < this.requiredCount) { - if (isShorthand && declarations.has(this.config.shorthand)) { - const cache = new Map(); - const removeDefaults = (declaration) => { - let i; - let t; - let map = new Map(); - let value = []; - let values = []; - // @ts-ignore - let typ = (exports.EnumToken[this.config.separator?.typ] ?? exports.EnumToken.CommaTokenType); - // @ts-ignore - const sep = this.config.separator == null - ? null - : { - ...this.config.separator, - typ: exports.EnumToken[this.config.separator.typ], - }; - // @ts-ignore - const separator = this.config.separator - ? renderValue({ - ...this.config.separator, - typ: exports.EnumToken[this.config.separator.typ], - }) - : ","; - this.matchTypes(declaration); - values.push(value); - for (i = 0; i < declaration.val.length; i++) { - t = declaration.val[i]; - if (!cache.has(t)) { - cache.set(t, renderValue(t, { minify: true })); - } - if (t.typ == typ && separator == cache.get(t)) { - this.removeDefaults(map, value); - value = []; - values.push(value); - map.clear(); - continue; - } - value.push(t); - // @ts-ignore - if (t[PROPERTYNAME] != null) { - // @ts-ignore - if (!map.has(t[PROPERTYNAME])) { - // @ts-ignore - map.set(t[PROPERTYNAME], { t: [t], value: [cache.get(t)] }); - } - else { - // @ts-ignore - const v = map.get(t[PROPERTYNAME]); - v.t.push(t); - v.value.push(cache.get(t)); - } - } - } - this.removeDefaults(map, value); - declaration.val = values.reduce((acc, curr) => { - if (sep != null && acc.length > 0) { - acc.push({ ...sep }); - } - for (const cr of curr) { - if (cr.typ == exports.EnumToken.WhitespaceTokenType && acc.at(-1)?.typ == cr.typ) { - continue; - } - acc.push(cr); - } - return acc; - }, []); - while (declaration.val.at(-1)?.typ == exports.EnumToken.WhitespaceTokenType) { - declaration.val.pop(); - } - while (declaration.val.at(0)?.typ == exports.EnumToken.WhitespaceTokenType) { - declaration.val.shift(); - } - return declaration; - }; - const values = [...declarations.values()].reduce((acc, curr) => { - if (curr instanceof PropertySet || curr instanceof PropertyMap) { - for (const declaration of curr) { - acc.push(declaration); - } - } - else { - acc.push(curr); - } - return acc; - }, []); - let isImportant = false; - let dec; - const filtered = []; - for (const declaration of values) { - dec = removeDefaults(declaration); - for (const t of dec.val) { - if (t.typ == exports.EnumToken.ImportantTokenType) { - isImportant = true; - } - if (filtered.length == 0 && - t.typ != exports.EnumToken.WhitespaceTokenType && - t.typ != exports.EnumToken.ImportantTokenType) { - filtered.push(dec); - } - } - } - if (filtered.length == 0 && this.config.default.length > 0) { - filtered.push({ - typ: exports.EnumToken.DeclarationNodeType, - nam: this.config.shorthand, - val: parseString(this.config.default[0]), - }); - if (isImportant) { - filtered[0].val.push({ - typ: exports.EnumToken.ImportantTokenType, - }); - } - } - return (filtered.length > 0 ? filtered : values)[Symbol.iterator](); + return String.fromCodePoint(codepoint); + }); + } + function assignTokenMap(entries, tokenType, suffix = "", lowercase = false) { + for (const entry of entries) { + const key = (lowercase ? entry.toLowerCase() : entry) + suffix; + SymbolsMapTokens[key] = tokenType; + SymbolsMapTokensLower[key.toLowerCase()] = tokenType; + } + } + SymbolsMapTokens[""] = exports.EnumToken.DelimTokenType; + SymbolsMapTokens["+"] = exports.EnumToken.Plus; + SymbolsMapTokens["="] = exports.EnumToken.DelimTokenType; + SymbolsMapTokens["|"] = exports.EnumToken.Pipe; + SymbolsMapTokens["||"] = exports.EnumToken.ColumnCombinatorTokenType; + SymbolsMapTokens["|="] = exports.EnumToken.DashMatchTokenType; + SymbolsMapTokens["&"] = exports.EnumToken.NestingSelectorTokenType; + SymbolsMapTokens["*"] = exports.EnumToken.Star; + SymbolsMapTokens["*="] = exports.EnumToken.ContainMatchTokenType; + SymbolsMapTokens["~"] = exports.EnumToken.Tilda; + SymbolsMapTokens["~="] = exports.EnumToken.IncludeMatchTokenType; + SymbolsMapTokens["^="] = exports.EnumToken.StartMatchTokenType; + SymbolsMapTokens["$="] = exports.EnumToken.EndMatchTokenType; + SymbolsMapTokens[","] = exports.EnumToken.Comma; + SymbolsMapTokens[":"] = exports.EnumToken.ColonTokenType; + SymbolsMapTokens["::"] = exports.EnumToken.DoubleColonTokenType; + SymbolsMapTokens[";"] = exports.EnumToken.SemiColonTokenType; + SymbolsMapTokens["("] = exports.EnumToken.StartParensTokenType; + SymbolsMapTokens[")"] = exports.EnumToken.EndParensTokenType; + SymbolsMapTokens["["] = exports.EnumToken.AttrStartTokenType; + SymbolsMapTokens["]"] = exports.EnumToken.AttrEndTokenType; + SymbolsMapTokens["{"] = exports.EnumToken.BlockStartTokenType; + SymbolsMapTokens["}"] = exports.EnumToken.BlockEndTokenType; + SymbolsMapTokens["<="] = exports.EnumToken.LteTokenType; + SymbolsMapTokens[">"] = exports.EnumToken.GtTokenType; + SymbolsMapTokens[">="] = exports.EnumToken.GteTokenType; + SymbolsMapTokens[" "] = exports.EnumToken.Whitespace; + SymbolsMapTokens["\t"] = exports.EnumToken.Whitespace; + SymbolsMapTokens["\r"] = exports.EnumToken.Whitespace; + SymbolsMapTokens["\n"] = exports.EnumToken.Whitespace; + SymbolsMapTokens["\f"] = exports.EnumToken.Whitespace; + assignTokenMap(flexUnits, exports.EnumToken.FlexTokenType); + assignTokenMap(dimensionUnits, exports.EnumToken.LengthTokenType); + assignTokenMap(resolutionUnits, exports.EnumToken.ResolutionTokenType); + assignTokenMap(angleUnits, exports.EnumToken.AngleTokenType); + assignTokenMap(timeUnits, exports.EnumToken.TimeTokenType); + assignTokenMap(frequencyUnits, exports.EnumToken.FrequencyTokenType); + assignTokenMap(pseudoElements, exports.EnumToken.PseudoElementTokenType); + assignTokenMap(containerFunc, exports.EnumToken.ContainerFunctionTokenDefType, "("); + assignTokenMap(urlFunc, exports.EnumToken.UrlFunctionTokenDefType, "("); + assignTokenMap(gridTemplateFunc, exports.EnumToken.GridTemplateFuncTokenDefType, "("); + assignTokenMap(imageFunc, exports.EnumToken.ImageFunctionTokenDefType, "("); + assignTokenMap(timelineFunc, exports.EnumToken.TimelineFunctionTokenDefType, "("); + assignTokenMap(supportFunc, exports.EnumToken.SupportsFunctionTokenDefType, "("); + assignTokenMap(timingFunc, exports.EnumToken.TimingFunctionTokenDefType, "("); + assignTokenMap(colorsFunc, exports.EnumToken.ColorFunctionTokenDefType, "("); + assignTokenMap(mathFuncs, exports.EnumToken.MathFunctionTokenDefType, "("); + assignTokenMap(transformFunctions, exports.EnumToken.TransformFunctionTokenDefType, "(", true); + assignTokenMap(whenElseFunc, exports.EnumToken.WhenElseFunctionTokenDefType, "("); + assignTokenMap(wildCardFuncs, exports.EnumToken.WildCardFunctionTokenDefType, "("); + const SymbolsMapTokensKeys = Object.keys(SymbolsMapTokens); + // do not capture the value + const hintsEnum = new Set([ + exports.EnumToken.CommaTokenType, + exports.EnumToken.ImportantTokenType, + exports.EnumToken.SemiColonTokenType, + exports.EnumToken.BlockStartTokenType, + exports.EnumToken.BlockEndTokenType, + exports.EnumToken.StartParensTokenType, + exports.EnumToken.EndParensTokenType, + exports.EnumToken.ColonTokenType, + exports.EnumToken.EOFTokenType, + ]); + var TokenMap; + (function (TokenMap) { + TokenMap[TokenMap["EXCLAMATION"] = 33] = "EXCLAMATION"; + TokenMap[TokenMap["SLASH"] = 47] = "SLASH"; + TokenMap[TokenMap["LOWERTHAN"] = 60] = "LOWERTHAN"; + TokenMap[TokenMap["HASH"] = 35] = "HASH"; + TokenMap[TokenMap["REVERSE_SOLIDUS"] = 92] = "REVERSE_SOLIDUS"; + TokenMap[TokenMap["DOUBLE_QUOTE"] = 34] = "DOUBLE_QUOTE"; + TokenMap[TokenMap["SINGLE_QUOTE"] = 39] = "SINGLE_QUOTE"; + TokenMap[TokenMap["DOT"] = 46] = "DOT"; + TokenMap[TokenMap["AT"] = 64] = "AT"; + TokenMap[TokenMap["PIPE"] = 124] = "PIPE"; + TokenMap[TokenMap["EQUALS"] = 61] = "EQUALS"; + TokenMap[TokenMap["AMPERSAND"] = 38] = "AMPERSAND"; + TokenMap[TokenMap["STAR"] = 42] = "STAR"; + TokenMap[TokenMap["TILDA"] = 126] = "TILDA"; + TokenMap[TokenMap["CARET"] = 94] = "CARET"; + TokenMap[TokenMap["DOLLAR"] = 36] = "DOLLAR"; + TokenMap[TokenMap["COMMA"] = 44] = "COMMA"; + TokenMap[TokenMap["COLON"] = 58] = "COLON"; + TokenMap[TokenMap["SEMICOLON"] = 59] = "SEMICOLON"; + TokenMap[TokenMap["LEFT_PARENTHESIS"] = 40] = "LEFT_PARENTHESIS"; + TokenMap[TokenMap["RIGHT_PARENTHESIS"] = 41] = "RIGHT_PARENTHESIS"; + TokenMap[TokenMap["LEFT_BRACKETS"] = 91] = "LEFT_BRACKETS"; + TokenMap[TokenMap["RIGHT_BRACKETS"] = 93] = "RIGHT_BRACKETS"; + TokenMap[TokenMap["LEFT_BRACE"] = 123] = "LEFT_BRACE"; + TokenMap[TokenMap["RIGHT_BRACE"] = 125] = "RIGHT_BRACE"; + TokenMap[TokenMap["PLUS"] = 43] = "PLUS"; + TokenMap[TokenMap["MINUS"] = 45] = "MINUS"; + TokenMap[TokenMap["GREATERTHAN"] = 62] = "GREATERTHAN"; + TokenMap[TokenMap["PERCENTAGE"] = 37] = "PERCENTAGE"; + })(TokenMap || (TokenMap = {})); + function getSymbolHint(parseInfo, start, end) { + const len = end - start; + if (len <= 0) + return null; + const value = parseInfo.stream.slice(start, end); + // Direct lookup handles the hottest punctuation and keyword cases without + // scanning the entire symbol table on every token. + const match = SymbolsMapTokens[value] ?? SymbolsMapTokensLower[value.toLowerCase()]; + if (match != null) { + return match; + } + const keysLength = SymbolsMapTokensKeys.length; + for (let i = 0; i < keysLength; i++) { + const key = SymbolsMapTokensKeys[i]; + if (key.length !== len) + continue; + let match = true; + for (let j = 0; j < len; j++) { + let ca = key.charCodeAt(j); + let cb = value.charCodeAt(j); + if (ca >= 65 && ca <= 90) + ca += 32; + if (cb >= 65 && cb <= 90) + cb += 32; + if (ca !== cb) { + match = false; + break; } - for (const declaration of declarations.values()) { - if (declaration instanceof PropertySet || declaration instanceof PropertyMap) { - continue; - } - const config = declaration.nam == this.config.shorthand - ? this.config - : (propertiesMapping[declaration.nam] ?? this.config); - if (!("mapping" in config)) { - continue; - } - // @ts-ignore - for (const [key, val] of Object.entries(config.mapping)) { - const keys = parseString(key); - if (keys.length != declaration.val.length) { - continue; - } - if (eq(declaration.val, keys)) { - declaration.val = parseString(val); - break; - } - } + } + if (match) { + return SymbolsMapTokens[key]; + } + } + return null; + } + function searchArray(array, parseInfo, start, end) { + const len = end - start; + if (len <= 0) + return null; + let match; + let item; + for (let i = 0; i < array.length; i++) { + item = array[i]; + if (item.length !== len) + continue; + match = true; + for (let j = 0; j < len; j++) { + let ca = parseInfo.stream.charCodeAt(start + j); + let cb = item.charCodeAt(j); + if (ca >= 65 && ca <= 90) + ca += 32; + if (cb >= 65 && cb <= 90) + cb += 32; + if (ca !== cb) { + match = false; + break; } - // @ts-ignore - iterable = declarations.values(); } - else { - let count = 0; - let match; - const separator = this.config.separator != null + if (match) { + return item; + } + } + return null; + } + /** + * tokenizer class + */ + class Tokenizer { + parseInfo; + input; + /** + * token type + */ + typ = null; + /** + * token kind + */ + kin = null; + /** + * token name + */ + nam = null; + /** + * token value + */ + val = null; + /** + * token unit + */ + unit = null; + /** + * source id + */ + srcId = null; + /** + * token start + */ + sta = null; + /** + * token end + */ + end = null; + /** + * bytes in + */ + bytesIn = null; + /** + * decode string + */ + decodeString = null; + /** + * token slice + */ + slice = null; + /** + * source file + */ + source = null; + /** + * token hint + */ + hint = null; + state = null; + constructor(parseInfo, input = null) { + this.parseInfo = parseInfo; + this.input = input; + this.parseInfo = + typeof parseInfo == "string" ? { - ...this.config.separator, - // @ts-ignore - typ: exports.EnumToken[this.config.separator.typ], - } - : null; - const tokens = {}; - // @ts-ignore - Object.entries(propertiesMapping).reduce((acc, curr) => { - if (!declarations.has(curr[0])) { - if (curr[1].required) { - acc.push(curr[0]); - } - return acc; + stream: parseInfo, + source: new SourceFile(parseInfo, [], ""), + offset: 0, + time: 0, + position: 0, + currentPosition: 0, } - let current = 0; - const props = propertiesMapping[curr[0]]; - const properties = (declarations.get(curr[0])); - for (const declaration of properties instanceof PropertySet || properties instanceof PropertyMap - ? [...properties].flat() - : [properties]) { - // @ts-ignore - for (const val of declaration.val) { - // @ts-ignore - if (separator != null && separator.typ == val.typ && separator.val == val.val) { - current++; - if (tokens[curr[0]].length == current) { - tokens[curr[0]].push([]); - } - continue; - } - if (val.typ == exports.EnumToken.WhitespaceTokenType || val.typ == exports.EnumToken.CommentTokenType) { - continue; - } - // @ts-ignore - if (props.multiple && - props.separator != null && - // @ts-ignore - exports.EnumToken[props.separator.typ] === val.typ && - // @ts-ignore - (val.typ === exports.EnumToken.CommaTokenType || props.separator.val == val.val)) { - continue; - } - // @ts-ignore - match = val.typ == exports.EnumToken.CommentTokenType || matchType(val, curr[1]); - if (isShorthand) { - isShorthand = match; - } - // @ts-ignore - if (val[PROPERTYNAME] == property || match) { - if (!(curr[0] in tokens)) { - tokens[curr[0]] = [[]]; - } - // is default value - tokens[curr[0]][current].push(val); - } - else { - acc.push(curr[0]); - break; - } - } + : parseInfo; + } + /** + * + * @param parseInfo + * @returns + */ + consumeString(parseInfo) { + const quote = this.advance(parseInfo).charCodeAt(0); + let charCode; + let decodeSegments = false; + while ((charCode = parseInfo.stream.charCodeAt(parseInfo.currentPosition - parseInfo.offset)) == charCode) { + if (charCode == 92 /* TokenMap.REVERSE_SOLIDUS */) { + if (charCode == parseInfo.stream.charCodeAt(parseInfo.currentPosition - parseInfo.offset + 1)) { + this.advance(parseInfo, 2); + continue; } - if (count === 0) { - count = current; + charCode = parseInfo.stream.charCodeAt(parseInfo.currentPosition - parseInfo.offset + 1); + // \n \r \f \v + if (charCode == 0xa || + charCode == 0xb || + charCode == 0xc || + charCode == 0xd || + charCode == 0x2028 || + charCode == 0x2029) { + this.advance(parseInfo, 2); + decodeSegments = true; + continue; } - return acc; - }, []); - // grid-template - if (this.config.shorthand == "grid-template" && patterns.length == Object.keys(tokens).length) { - const k = tokens[patterns[0]][0].length; - const l = tokens[patterns[1]][0].length; - const j = k < l ? k : l; - const result = []; + const sequence = this.peek(parseInfo, 7); + let escapeSequence = ""; + let codepoint; let i; - for (i = 0; i < j; i++) { - if (i < k) { - result.push(tokens[patterns[0]][0][i]); - } - if (i < l) { - result.push(tokens[patterns[1]][0][i]); + for (i = 1; i < sequence.length; i++) { + codepoint = sequence.charCodeAt(i); + if (codepoint == 0x20 || + (codepoint >= 0x61 && codepoint <= 0x66) || + (codepoint >= 0x41 && codepoint <= 0x46) || + (codepoint >= 0x30 && codepoint <= 0x39)) { + escapeSequence += sequence[i]; + if (codepoint == 0x20) { + break; + } + continue; } + break; } - if (j < k) { - result.push(...tokens[patterns[0]][0].slice(j)); - } - if (j < l) { - result.push(...tokens[patterns[1]][0].slice(j)); + if (escapeSequence.trimEnd().length > 0) { + const length = escapeSequence.length + + 1 + + (isWhiteSpace(parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset)?.charCodeAt(0)) + ? 1 + : 0); + decodeSegments = true; + this.advance(parseInfo, length); + continue; } - const separator = this.config.properties[patterns[2]]?.prefix; - result.push( - // @ts-ignore - { ...separator, typ: exports.EnumToken[separator.typ] }, ...tokens[patterns[2]][0].reduce((acc, curr) => { - if (acc.length > 0) { - acc.push({ typ: exports.EnumToken.WhitespaceTokenType }); - } - acc.push(curr); - return acc; - }, [])); - const declaration = cloneNode(declarations.get(patterns[0])); - declaration.nam = this.config.shorthand; - declaration.val = result; - return [declaration][Symbol.iterator](); + this.advance(parseInfo, 2); + continue; } - count++; - if (!isShorthand || - Object.entries(propertiesMapping).some((entry) => { - // missing required property - return entry[1].required && !(entry[0] in tokens); - }) || - // @ts-ignore - !Object.values(tokens).every((v) => - // @ts-expect-error - v.filter((t) => t.typ != exports.EnumToken.CommentTokenType).length === count)) { - // @ts-ignore - iterable = declarations.values(); + if (charCode == quote) { + this.advance(parseInfo); + return this.makeToken(parseInfo, + /* hasNewLine ? EnumToken.BadStringTokenType : */ exports.EnumToken.StringTokenType, decodeSegments ? { decodeSegments } : null); } - else if (hasMapping) { - if (patterns.length == Object.keys(tokens).length) { - const declaration = cloneNode(declarations.get(patterns[0])); - declaration.nam = this.config.shorthand; - for (let i = 0; i < patterns.length; i++) { - if (declaration.val.length > 0) { - declaration.val.push( - // @ts-ignore - this.config.separator != null - ? { - ...this.config.separator, - typ: exports.EnumToken[this.config.separator.typ], - } - : { typ: exports.EnumToken.WhitespaceTokenType }); - } - declaration.val.push(...tokens[patterns[i]].reduce((acc, curr) => { - if (acc.length > 0) { - acc.push({ typ: exports.EnumToken.WhitespaceTokenType }); - } - acc.push(...curr.reduce((acc, curr) => { - if (acc.length > 0) { - acc.push({ typ: exports.EnumToken.WhitespaceTokenType }); - } - acc.push(curr); - return acc; - }, [])); - return acc; - }, [])); - } - return [declaration][Symbol.iterator](); - } - iterable = declarations.values(); + if (isNewLine(charCode)) { + this.advance(parseInfo); + return this.makeToken(parseInfo, exports.EnumToken.BadStringTokenType); } - else { - let values = Object.entries(tokens) - .reduce((acc, curr) => { - const props = propertiesMapping[curr[0]]; - for (let i = 0; i < curr[1].length; i++) { - if (acc.length == i) { - acc.push([]); - } - // if (acc[acc.length - 1].length > 0) { - // acc[acc.length - 1].push( - // // @ts-ignore - // this.config.separator != null ? - // { - // ...this.config.separator, - // typ: EnumToken[this.config.separator.typ as keyof typeof EnumToken], - // } : { typ: EnumToken.WhitespaceTokenType }, - // ) - // } - let values = curr[1][i]; - // .reduce( - // (acc, curr) => { - // // if (acc.length > 0) { - // // acc.push({ typ: EnumToken.WhitespaceTokenType }); - // // } - // acc.push(curr); - // return acc; - // }, - // [], - // ); - if (props.default.includes(curr[1][i] - .reduce((acc, curr) => acc + renderValue(curr) + " ", "") - .trimEnd())) { - if (!propertiesMapping[curr[0]].required) { - continue; - } - } - // remove default values - let doFilterDefault = true; - if (curr[0] in propertiesConfig.properties) { - for (let v of values) { - if (![ - exports.EnumToken.WhitespaceTokenType, - exports.EnumToken.CommentTokenType, - exports.EnumToken.IdenTokenType, - ].includes(v.typ) || - (v.typ == exports.EnumToken.IdenTokenType && - !propertiesMapping[curr[0]].default.includes(v.val))) { - doFilterDefault = false; - break; - } - } - } - // remove default values - const filtered = values.filter((val) => { - if (val.typ == exports.EnumToken.WhitespaceTokenType || - val.typ == exports.EnumToken.CommentTokenType) { - return false; - } - return (!doFilterDefault || - !(val.typ == exports.EnumToken.IdenTokenType && - props.default.includes(val.val))); - }); - if (filtered.length > 0 || - !(this.requiredCount == requiredCount && propertiesMapping[curr[0]].required)) { - values = filtered; - } - if (values.length > 0) { - if ("mapping" in props) { - if (!("constraints" in props) || - // @ts-ignore - !("max" in props.constraints) || - values.length <= props.constraints.mapping.max) { - let i = values.length; - while (i--) { - if (values[i].typ == exports.EnumToken.IdenTokenType && - // @ts-expect-error - values[i].val in props.mapping) { - // @ts-ignore - values.splice(i, 1, ...parseString(props.mapping[values[i].val])); - } - } - } - } - if ("prefix" in props) { - // @ts-ignore - acc[i].push({ ...props.prefix, typ: exports.EnumToken[props.prefix.typ] }); - } - else if (acc[i].length > 0) { - // @ts-ignore - acc[i].push({ typ: exports.EnumToken.WhitespaceTokenType }); - } - for (const v of values.reduce((acc, curr) => { - if (acc.length > 0) { - // @ts-ignore - acc.push({ - ...((props.separator && { - ...props.separator, - // @ts-ignore - typ: exports.EnumToken[props.separator.typ], - }) ?? { typ: exports.EnumToken.WhitespaceTokenType }), - }); - } - // @ts-ignore - acc.push(curr); - return acc; - }, [])) { - acc[i].push(v); - } - } - } - return acc; - }, []) - .reduce((acc, curr) => { - if (acc.length > 0) { - acc.push({ ...separator }); - } - if (curr.length == 0 && this.config.default.length > 0) { - curr.push(...parseString(this.config.default[0]).reduce((acc, curr) => { - if (acc.length > 0) { - acc.push({ typ: exports.EnumToken.WhitespaceTokenType }); - } - acc.push(curr); - return acc; - }, [])); - } - for (const c of curr) { - acc.push(c); - } - return acc; - }, []); - if (this.config.mapping != null) { - const val = values.reduce((acc, curr) => acc + - renderValue(curr, { - removeComments: true, - minify: true, - }), ""); - if (val in this.config.mapping) { - values.length = 0; - values.push({ - typ: ['"', "'"].includes(val.charAt(0)) - ? exports.EnumToken.StringTokenType - : exports.EnumToken.IdenTokenType, - // @ts-ignore - val: this.config.mapping[val], - }); - } - } - // @ts-ignore - if (values.length == 1 && - // @ts-ignore - typeof values[0].val == "string" && - this.config.default.includes(values[0].val.toLowerCase()) && - this.config.default[0] != values[0].val.toLowerCase()) { - // @ts-ignore/ - values = parseString(this.config.default[0]); + this.advance(parseInfo); + } + // EOF - 'Unclosed-string' fixed + return this.makeToken(parseInfo, exports.EnumToken.StringTokenType, decodeSegments ? { decodeSegments } : null); + // return result; + } + /** + * + * @param parseInfo + * @returns + */ + consumeStringAsURLToken(parseInfo) { + const quote = this.advance(parseInfo).charCodeAt(0); + let charCode; + let decodeSegments = false; + while ((charCode = parseInfo.stream.charCodeAt(parseInfo.currentPosition - parseInfo.offset)) == charCode) { + if (charCode == 92 /* TokenMap.REVERSE_SOLIDUS */) { + if (charCode == parseInfo.stream.charCodeAt(parseInfo.currentPosition - parseInfo.offset + 1)) { + this.advance(parseInfo, 2); + continue; } - iterable = [ - { - typ: exports.EnumToken.DeclarationNodeType, - nam: this.config.shorthand, - val: values, - }, - ][Symbol.iterator](); - } - } - const iterators = []; - // @ts-ignore - return { - // @ts-ignore - next() { - let v = iterable.next(); - // @ts-ignore - while (v.done || v.value instanceof PropertySet) { - if (v.value instanceof PropertySet) { - // @ts-ignore - iterators.push(iterable); - iterable = v.value[Symbol.iterator](); - v = iterable.next(); - } - if (v.done) { - if (iterators.length > 0) { - // @ts-ignore - iterable = iterators.pop(); - v = iterable.next(); - } - if (v.done && iterators.length == 0) { + const sequence = this.peek(parseInfo, 7); + let escapeSequence = ""; + let codepoint; + let i; + for (i = 1; i < sequence.length; i++) { + codepoint = sequence.charCodeAt(i); + if (codepoint == 0x20 || + (codepoint >= 0x61 && codepoint <= 0x66) || + (codepoint >= 0x41 && codepoint <= 0x46) || + (codepoint >= 0x30 && codepoint <= 0x39)) { + escapeSequence += sequence[i]; + if (codepoint == 0x20) { break; } + continue; } + break; } - return v; - }, - }; - } - matchTypes(declaration) { - const patterns = this.pattern.slice(); - const values = []; - let i; - let j; - const map = new Map(); - for (i = 0; i < declaration.val.length; i++) { - values.push(declaration.val[i]); - } - for (i = 0; i < patterns.length; i++) { - for (j = 0; j < values.length; j++) { - if (!map.has(patterns[i])) { - // @ts-ignore - map.set(patterns[i], this.config.properties?.[patterns[i]]?.constraints?.mapping?.max ?? 1); - } - let count = map.get(patterns[i]); - if (count > 0 && matchType(values[j], this.config.properties[patterns[i]])) { - // @ts-expect-error - values[j][PROPERTYNAME] = patterns[i]; - map.set(patterns[i], --count); - values.splice(j--, 1); + if (escapeSequence.trimEnd().length > 0) { + const length = escapeSequence.length + + 1 + + (isWhiteSpace(parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset)?.charCodeAt(0)) + ? 1 + : 0); + decodeSegments = true; + this.advance(parseInfo, length); + continue; } + this.advance(parseInfo, 2); + continue; } - } - if (this.config.set != null) { - for (const [key, val] of Object.entries(this.config.set)) { - if (map.has(key)) { - for (const v of val) { - // missing - if (map.get(v) == 1) { - let i = declaration.val.length; - while (i--) { - // @ts-expect-error - if (declaration.val[i][PROPERTYNAME] == key) { - const val = { ...declaration.val[i] }; - // @ts-expect-error - val[PROPERTYNAME] = v; - declaration.val.splice(i, 0, val, { typ: exports.EnumToken.WhitespaceTokenType }); - } - } - } + if (charCode == quote) { + this.advance(parseInfo); + let k = 1; + let end = parseInfo.stream.length - parseInfo.offset; + let position = parseInfo.currentPosition - parseInfo.offset; + while (position + k < end) { + charCode = parseInfo.stream.charCodeAt(position); + // NaN != NaN + if (charCode != charCode) { + this.advance(parseInfo, k); + return this.makeToken(parseInfo, exports.EnumToken.BadUrlTokenType); + } + if (isWhiteSpace(charCode)) { + // this.advance(parseInfo, k); + k++; + continue; + } + if (charCode != 41 /* TokenMap.RIGHT_PARENTHESIS */) { + this.advance(parseInfo, k); + return this.makeToken(parseInfo, exports.EnumToken.BadUrlTokenType); } + break; } + // consume until the ')' + return this.makeToken(parseInfo, + /* hasNewLine ? EnumToken.BadStringTokenType : */ exports.EnumToken.StringTokenType, decodeSegments ? { decodeSegments } : null); + // return result; } - } - } - removeDefaults(map, value) { - for (const [key, val] of map) { - const config = this.config.properties[key]; - if (config == null) { - continue; - } - const v = val.value.join(" "); - if (config.default.includes(v) || (value.length == 1 && this.config.default.includes(v))) { - for (const token of value) { - if (val.t.includes(token)) { - let index = value.indexOf(token); - value.splice(index, 1); - if (config.prefix != null) { - while (index-- > 0) { - if (value[index].typ == exports.EnumToken.WhitespaceTokenType) { - continue; - } - if ( - // @ts-expect-error - value[index].typ == exports.EnumToken[config.prefix.typ] && - // @ts-ignore - value[index].val == config.prefix.val) { - value.splice(index, 1); - break; - } - } - } + if (isNewLine(charCode)) { + // bad string + this.advance(parseInfo); + while ((charCode = parseInfo.stream.charCodeAt(parseInfo.currentPosition - parseInfo.offset)) == charCode) { + if (charCode == 92 /* TokenMap.REVERSE_SOLIDUS */) { + this.advance(parseInfo, 2); + continue; + } + if (charCode == 41 /* TokenMap.RIGHT_PARENTHESIS */) { + return this.makeToken(parseInfo, exports.EnumToken.BadUrlTokenType); } + this.advance(parseInfo); } + return this.makeToken(parseInfo, exports.EnumToken.BadStringTokenType); } + this.advance(parseInfo); } + // EOF - bad url token + return this.makeToken(parseInfo, exports.EnumToken.BadUrlTokenType); + // return result; } - } - - // Alphabet: a-z, A-Z, 0-9, _, - - const LOWER = "abcdefghijklmnopqrstuvwxyz"; - const DIGITS = "0123456789"; - const FULL_ALPHABET = (LOWER + DIGITS).split(""); // 64 chars - const FIRST_ALPHABET = LOWER.split(""); // 54 chars (no digits) - /** - * supported hash algorithms - */ - const hashAlgorithms = ["hex", "base64", "base64url", "sha1", "sha256", "sha384", "sha512"]; - // simple deterministic hash → number - function hashCode(str) { - let hash = 0; - let l = str.length; - let i = 0; - while (i < l) { - hash = (hash * 31 + str.charCodeAt(i++)) >>> 0; - } - return hash; - } - /** - * generate a hash id - * @param input - * @param length - */ - function hashId(input, length = 6) { - let n = hashCode(input); - const chars = []; - // First character: must not be a digit - chars.push(FIRST_ALPHABET[n % FIRST_ALPHABET.length]); - // Remaining characters - for (let i = 1; i < length; i++) { - n = (n + chars.length * i) % FULL_ALPHABET.length; - chars.push(FULL_ALPHABET[n]); - } - return chars.join(""); - } - /** - * Object to string - * @param input - * @returns - */ - function toSortedString(input) { - if (input == null) { - return "null"; - } - if (typeof input !== "object") { - return String(input); - } - if (Array.isArray(input)) { - const result = new Array(input.length); - for (const value of input) { - result.push(toSortedString(value)); - } - return JSON.stringify(result); - } - const keys = Object.keys(input).sort(); - for (let i = 0; i < keys.length; i++) { - keys[i] = `${keys[i]}:${toSortedString(input[keys[i]])}`; - } - return `{${keys.join(",")}}`; - } - /** - * Object hash - * @param object - * @returns - */ - function objectHash(object) { - return hashCode(toSortedString(object)).toString(16); - } - /** - * convert input to hex - * @param input - * @param length - */ - function toHex(input, length) { - let result = ""; - if (input instanceof ArrayBuffer || ArrayBuffer.isView(input)) { - for (const byte of Array.from(new Uint8Array(input))) { - result += byte.toString(16).padStart(2, "0"); + /** + * consume number, dimension, or percentage + * @param parseInfo + * @returns + */ + consumeNumericToken(parseInfo) { + let position = parseInfo.currentPosition - parseInfo.offset; + let offset = position; + let hasDigits = false; + let hasLetter = false; + let hasPercent = false; + let codepoint = parseInfo.stream.charCodeAt(position); + this.slice = null; + this.hint = null; + // '+' '-' + if (codepoint == 0x2b || codepoint == 0x2d) { + position++; } - } - else { - for (const char of String(input)) { - result += char.charCodeAt(0).toString(16).padStart(2, "0"); + // consume digits + while (position < parseInfo.stream.length) { + codepoint = parseInfo.stream.charCodeAt(position); + if (isDigit(codepoint)) { + hasDigits = true; + position++; + continue; + } + // '.' 'E' 'e' + if (codepoint == 0x2e || codepoint == 0x45 || codepoint == 0x65) { + position++; + break; + } + if (isWhiteSpace(codepoint) || + codepoint == 41 /* TokenMap.RIGHT_PARENTHESIS */ || + codepoint == 59 /* TokenMap.SEMICOLON */ || + codepoint == 125 /* TokenMap.RIGHT_BRACE */ || + codepoint == 47 /* TokenMap.SLASH */ || + codepoint == 42 /* TokenMap.STAR */ || + codepoint == 44 /* TokenMap.COMMA */) { + return !hasDigits ? 0 : position - offset; + } + if (codepoint == 37 /* TokenMap.PERCENTAGE */) { + hasPercent = true; + break; + } + if (isLetter(codepoint)) { + hasLetter = true; + break; + } + return 0; } - } - return result; - } - /** - * generate a hash - * @param input - * @param length - * @param algo - */ - async function hash(input, length = 6, algo) { - let result; - if (algo != null) { - switch (algo) { - case "hex": - return toHex(input).slice(0, length); - case "base64url": - case "base64": - result = btoa(input); - if (algo == "base64url") { - result = result.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); + if (!hasLetter && !hasPercent) { + // '.' + if (codepoint == 0x2e) { + codepoint = parseInfo.stream.charCodeAt(position); + if (codepoint != codepoint) { + return !hasDigits ? 0 : position - offset; } - return result.slice(0, length); - case "sha256": - case "sha384": - case "sha512": - case "sha1": - return toHex(await crypto.subtle.digest(algo.replace("sha", "SHA-"), new TextEncoder().encode(input))).slice(0, length); - default: - throw new Error(`Unsupported hash algorithm: ${algo}`); - } - } - return hashId(input, length); - } - /** - * generate a hash - * @param input - * @param length - * @param algo - */ - function syncHash(input, length = 6, algo) { - let result; - if (algo != null) { - switch (algo) { - case "hex": - return toHex(input).slice(0, length); - case "base64url": - case "base64": - result = btoa(input); - if (algo == "base64url") { - result = result.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); + if (isWhiteSpace(codepoint) || + codepoint == 41 /* TokenMap.RIGHT_PARENTHESIS */ || + codepoint == 59 /* TokenMap.SEMICOLON */ || + codepoint == 125 /* TokenMap.RIGHT_BRACE */ || + codepoint == 47 /* TokenMap.SLASH */ || + codepoint == 42 /* TokenMap.STAR */ || + codepoint == 44 /* TokenMap.COMMA */) { + return !hasDigits ? 0 : position - offset; } - return result.slice(0, length); - default: - throw new Error(`Unsupported hash algorithm: ${algo}`); - } - } - return hashId(input, length); - } - - const config = getConfig(); - /** - * Represents a list of property declarations, compute shorthand and remove duplicate declarations. - */ - class PropertyList { - options = { removeDuplicateDeclarations: true, computeShorthand: true }; - declarations; - constructor(options = {}) { - this.options = options; - this.declarations = new Map(); - } - add(declarations) { - let name; - let syntaxRules = null; - let result; - for (const declaration of declarations) { - name = declaration.typ != exports.EnumToken.DeclarationNodeType ? null : declaration.nam; - if (declaration[STATE] & - (exports.EnumAstNodeStatus.Invalid | exports.EnumAstNodeStatus.Unknown | exports.EnumAstNodeStatus.ValidationFailed) || - declaration.typ != exports.EnumToken.DeclarationNodeType || - equalsIgnoreCase("composes", name) || - (typeof this.options.removeDuplicateDeclarations === "string" && - this.options.removeDuplicateDeclarations === name) || - (Array.isArray(this.options.removeDuplicateDeclarations) - ? this.options.removeDuplicateDeclarations.includes(declaration.nam) - : !this.options.removeDuplicateDeclarations)) { - const key = objectHash(declaration); - if (this.declarations.has(key)) { - this.declarations.delete(key); + if (!isDigit(codepoint)) { + if (!hasDigits) { + return 0; + } + if (codepoint == 37 /* TokenMap.PERCENTAGE */) { + hasPercent = true; + } + else if (isLetter(codepoint)) { + hasLetter = true; + } + else { + return 0; + } + } + else { + position++; + hasDigits = true; } - this.declarations.set(key, declaration); - continue; - } - if (!this.options.computeShorthand) { - this.declarations.set(declaration.nam, declaration); - continue; } - if (declaration[STATE] & exports.EnumAstNodeStatus.Unvalidated) { - syntaxRules = getParsedSyntax(ValidationSyntaxGroupEnum.Declarations, declaration.nam.toLowerCase()); - if (syntaxRules != null) { - result = matchAllSyntaxes(syntaxRules, createValidationContext(declaration.val), this.options); - declaration[STATE] |= - (result.success ? exports.EnumAstNodeStatus.Validated : exports.EnumAstNodeStatus.ValidationFailed) & - ~exports.EnumAstNodeStatus.Unvalidated; + if (!hasLetter && !hasPercent) { + while (position + 1 <= parseInfo.stream.length) { + codepoint = parseInfo.stream.charCodeAt(position); + if (isDigit(codepoint)) { + position++; + continue; + } + if (!hasDigits) { + return 0; + } + // 'E' 'e' + if (codepoint == 0x45 || codepoint == 0x65) { + position++; + break; + } + if (isWhiteSpace(codepoint) || + codepoint == 41 /* TokenMap.RIGHT_PARENTHESIS */ || + codepoint == 59 /* TokenMap.SEMICOLON */ || + codepoint == 125 /* TokenMap.RIGHT_BRACE */ || + codepoint == 47 /* TokenMap.SLASH */ || + codepoint == 42 /* TokenMap.STAR */ || + codepoint == 44 /* TokenMap.COMMA */) { + return position - offset; + } + if (isLetter(codepoint)) { + hasLetter = true; + break; + } + if (codepoint == 37 /* TokenMap.PERCENTAGE */) { + hasPercent = true; + break; + } + return 0; + } + // 'E' 'e' - 'em' + if ((codepoint == 0x45 || codepoint == 0x65) && hasDigits && !hasLetter && !hasPercent) { + if (isLetter(parseInfo.stream.charCodeAt(position))) { + hasLetter = true; + } + } + if (!hasLetter && !hasPercent) { + // 'E' 'e' + if (codepoint == 0x45 || codepoint == 0x65) { + codepoint = parseInfo.stream.charCodeAt(position + 1); + // '+' '-' + if (codepoint == 0x2b || codepoint == 0x2d) { + position++; + } + codepoint = position = parseInfo.stream.charCodeAt(position + 1); + if (!isDigit(codepoint)) { + if (!hasDigits) { + return 0; + } + if (isLetter(codepoint)) { + hasLetter = true; + } + else if (codepoint == 37 /* TokenMap.PERCENTAGE */) { + hasPercent = true; + } + else { + return 0; + } + } + } + if (!hasLetter && !hasPercent) { + while (++position < parseInfo.stream.length) { + codepoint = parseInfo.stream.charCodeAt(position); + // eof + if (codepoint != codepoint) { + break; + } + if (isDigit(codepoint)) { + position++; + continue; + } + if (!hasDigits) { + return 0; + } + if (isWhiteSpace(codepoint) || + codepoint == 41 /* TokenMap.RIGHT_PARENTHESIS */ || + codepoint == 59 /* TokenMap.SEMICOLON */ || + codepoint == 125 /* TokenMap.RIGHT_BRACE */ || + codepoint == 47 /* TokenMap.SLASH */ || + codepoint == 42 /* TokenMap.STAR */ || + codepoint == 44 /* TokenMap.COMMA */) { + return position - offset; + } + else if (isLetter(codepoint)) { + hasLetter = true; + break; + } + else if (codepoint == 37 /* TokenMap.PERCENTAGE */) { + hasPercent = true; + break; + } + else { + return 0; + } + } + if (!hasLetter && !hasPercent) { + return position - offset; + } + } } } - // do not compute shorthand for invalid declarations - if (!(declaration[STATE] & exports.EnumAstNodeStatus.Validated)) { - this.declarations.set(objectHash(declaration), declaration); - continue; + } + if (!hasDigits) { + return 0; + } + if (hasPercent) { + const slice = position; + codepoint = parseInfo.stream.charCodeAt(++position); + if (codepoint != codepoint || + isWhiteSpace(codepoint) || + codepoint == 41 /* TokenMap.RIGHT_PARENTHESIS */ || + codepoint == 59 /* TokenMap.SEMICOLON */ || + codepoint == 125 /* TokenMap.RIGHT_BRACE */ || + codepoint == 47 /* TokenMap.SLASH */ || + codepoint == 42 /* TokenMap.STAR */ || + codepoint == 44 /* TokenMap.COMMA */) { + this.slice = slice; + this.hint = exports.EnumToken.PercentageTokenType; + return position - offset; } - let propertyName = declaration.nam; - let shortHandType; - let shorthand = null; - if (propertyName in config.properties) { - // @ts-ignore - if ("map" in config.properties[propertyName]) { - shortHandType = "map"; - // @ts-ignore - shorthand = config.properties[propertyName].map; - } - else { - shortHandType = "set"; - // @ts-ignore - shorthand = config.properties[propertyName].shorthand; + return 0; + } + if (hasLetter) { + codepoint = parseInfo.stream.charCodeAt(position - 1); + // 'E' 'e' + const slice = codepoint == 0x45 || codepoint == 0x65 ? position - 1 : position; + while (position + 1 <= parseInfo.stream.length) { + codepoint = parseInfo.stream.charCodeAt(++position); + if (!isLetter(codepoint)) { + break; } } - else if (propertyName in config.map) { - shortHandType = "map"; - // @ts-ignore - shorthand = config.map[propertyName].shorthand; - } - else if (propertyName in config.property) { - shortHandType = "single"; - // @ts-ignore - shorthand = config.property[propertyName]; + if (codepoint != codepoint || + isWhiteSpace(codepoint) || + codepoint == 41 /* TokenMap.RIGHT_PARENTHESIS */ || + codepoint == 59 /* TokenMap.SEMICOLON */ || + codepoint == 125 /* TokenMap.RIGHT_BRACE */ || + codepoint == 43 /* TokenMap.PLUS */ || + codepoint == 47 /* TokenMap.SLASH */ || + codepoint == 42 /* TokenMap.STAR */ || + codepoint == 44 /* TokenMap.COMMA */) { + this.slice = slice; + this.hint = getSymbolHint(parseInfo, slice, position) ?? exports.EnumToken.DimensionTokenType; + return position - offset; } - if (this.declarations.has(shorthand)) { - let existing = this.declarations.get(shorthand); - if (existing instanceof PropertySet || existing instanceof PropertyMap) { - existing = existing.declarations.get(declaration.nam); - } - if (existing?.[STATE] & exports.EnumAstNodeStatus.ImportantDeclaration && - declaration[STATE] & ~exports.EnumAstNodeStatus.ImportantDeclaration) { - continue; - } + return 0; + } + return 0; + } + /** + * + * @param parseInfo + * @returns + */ + consumeIdentToken(parseInfo) { + let position = parseInfo.currentPosition - parseInfo.offset; + let offset = position; + let codepoint = parseInfo.stream.charCodeAt(position); + if (!isIdentStart(codepoint) && codepoint != 45 /* TokenMap.MINUS */) { + return 0; + } + if (codepoint == 45 /* TokenMap.MINUS */) { + position++; + codepoint = parseInfo.stream.charCodeAt(position); + if (!isIdentStart(codepoint) && codepoint != 45 /* TokenMap.MINUS */) { + return 0; } - // @ts-ignore - if (shortHandType == "map") { - let owner = this; - const mapName = - // @ts-ignore - config.map[propertyName]?.map ?? config.map[shorthand]?.properties?.[propertyName]?.map; - if (typeof mapName === "string") { - if (!this.declarations.has(mapName)) { - // @ts-ignore - this.declarations.set(mapName, new PropertyMap(config.map[mapName])); - } - owner = this.declarations.get(mapName); - } - else if (!this.declarations.has(shorthand)) { - // @ts-ignore - this.declarations.set(shorthand, new PropertyMap(config.map[shorthand])); - } - if (owner == this) { - // @ts-ignore - owner.declarations.get(shorthand).add(declaration); + } + while ((codepoint = parseInfo.stream.charCodeAt(position)) == codepoint) { + if (codepoint == 92 /* TokenMap.REVERSE_SOLIDUS */) { + // eof + if ((codepoint = parseInfo.stream.charCodeAt(position + 1)) != codepoint) { + // this.next(parseInfo, position); + return 0; } - else { - if (!owner.declarations.has(shorthand)) { - // @ts-ignore - owner.declarations.set(shorthand, new PropertyMap(config.map[shorthand])); - } - // @ts-ignore - owner.declarations.get(shorthand).add(declaration); + // \n \r \f \v + if (codepoint == 0xa || + codepoint == 0xb || + codepoint == 0xc || + codepoint == 0xd || + codepoint == 0x2028 || + codepoint == 0x2029) { + return 0; } + position += 2; + continue; } - // @ts-ignore - else if (shortHandType == "set") { - // @ts-ignore - if (!this.declarations.has(shorthand)) { - // @ts-ignore - this.declarations.set( - // @ts-ignore - shorthand, - // @ts-ignore - new PropertySet(config.properties[shorthand])); - } - // @ts-ignore - this.declarations.get(shorthand).add(declaration); + if (codepoint == 0x2d || isIdentCodepoint(codepoint)) { + position++; } else { - if (shorthand != null) { - this.mapValues(declaration, shorthand); + switch (codepoint) { + case 58 /* TokenMap.COLON */: + case 123 /* TokenMap.LEFT_BRACE */: + case 125 /* TokenMap.RIGHT_BRACE */: + case 40 /* TokenMap.LEFT_PARENTHESIS */: + case 41 /* TokenMap.RIGHT_PARENTHESIS */: + case 91 /* TokenMap.LEFT_BRACKETS */: + case 93 /* TokenMap.RIGHT_BRACKETS */: + case 59 /* TokenMap.SEMICOLON */: + case 33 /* TokenMap.EXCLAMATION */: + case 47 /* TokenMap.SLASH */: + case 35 /* TokenMap.HASH */: + case 42 /* TokenMap.STAR */: + case 61 /* TokenMap.EQUALS */: + case 126 /* TokenMap.TILDA */: + case 124 /* TokenMap.PIPE */: + case 94 /* TokenMap.CARET */: + case 36 /* TokenMap.DOLLAR */: + case 44 /* TokenMap.COMMA */: + case 62 /* TokenMap.GREATERTHAN */: + case 46 /* TokenMap.DOT */: + case 43 /* TokenMap.PLUS */: + return position - offset; } - this.declarations.set(propertyName, declaration); + if (codepoint != codepoint || isWhiteSpace(codepoint)) { + return position - offset; + } + return 0; } } - return this; + return position - offset; } - mapValues(declaration, mapping) { - let name; - let values = declaration.val; - if (mapping.pattern != null) { - // match pattern - for (const patterns of mapping.pattern) { - const set = new Set(values.filter((v) => v.typ !== exports.EnumToken.CommentTokenType && v.typ !== exports.EnumToken.WhitespaceTokenType)); - const val = []; - for (const pattern of patterns) { - switch (pattern) { - case "": - for (const v of set) { - if (v.typ === exports.EnumToken.LengthTokenType || - (v.typ === exports.EnumToken.NumberTokenType && v.val === 0)) { - val.push(v); - set.delete(v); - } - } - default: { - for (const v of set) { - if (v.typ === exports.EnumToken.IdenTokenType) { - const value = v.val.toLowerCase(); - if (value === pattern || new RegExp(`(^|\|)${pattern}(\||$)`).test(value)) { - val.push(v); - set.delete(v); - } - } - } - } - } - } - if (set.size === 0) { - values = val.reduce((acc, curr) => { - if (acc.length > 0) { - acc.push({ typ: exports.EnumToken.WhitespaceTokenType }); - } - acc.push(curr); - return acc; - }, []); - break; - } - } + /** + * + * @param parseInfo + * @returns + */ + consumeColor(parseInfo) { + let position = parseInfo.currentPosition - parseInfo.offset; + let offset = position; + let codepoint = parseInfo.stream.charCodeAt(position); + if (codepoint != 35 /* TokenMap.HASH */) { + return 0; } - for (const val of declaration.val) { - if (val.typ === exports.EnumToken.IdenTokenType) { - name = val.val.toLowerCase(); - if (mapping.mapping[name] != null) { - // @ts-expect-error - Object.assign(val, mapping.mapping[name], { typ: exports.EnumToken[mapping.mapping[name].typ] }); - } + position++; + let count = 0; + while (true) { + codepoint = parseInfo.stream.charCodeAt(position); + // 'a-f0-9' 'A-F0-9' + if ((codepoint >= 0x30 && codepoint <= 0x39) || + (codepoint >= 0x61 && codepoint <= 0x66) || + (codepoint >= 0x41 && codepoint <= 0x46)) { + position++; + count++; + continue; } + break; } - if (values != declaration.val) { - declaration.val.length = 0; - for (const v of values) { - declaration.val.push(v); - } + if (count != 3 && count != 4 && count != 6 && count != 8) { + return 0; } - } - [Symbol.iterator]() { - let iterator = this.declarations.values(); - const iterators = []; - // @ts-ignore - return { - next() { - let value = iterator.next(); - while ((value.done && iterators.length > 0) || - value.value instanceof PropertySet || - value.value instanceof PropertyMap) { - if (value.value instanceof PropertySet || value.value instanceof PropertyMap) { - iterators.unshift(iterator); - // @ts-ignore - iterator = value.value[Symbol.iterator](); - value = iterator.next(); - } - if (value.done && iterators.length > 0) { - iterator = iterators.shift(); - value = iterator.next(); - } - } - return value; - }, - }; - } - } - - class ComputeShorthandFeature { - accept = new Set([ - exports.EnumToken.RuleNodeType, - exports.EnumToken.AtRuleNodeType, - exports.EnumToken.KeyframesRuleNodeType, - ]); - get ordering() { - return 5; - } - get processMode() { - return exports.FeatureWalkMode.Post; - } - static register(options) { - if (options.computeShorthand) { - // @ts-ignore - options.features.push(new ComputeShorthandFeature(options)); + codepoint = parseInfo.stream.charCodeAt(position); + if (codepoint != codepoint || + isWhiteSpace(codepoint) || + codepoint == 41 /* TokenMap.RIGHT_PARENTHESIS */ || + codepoint == 59 /* TokenMap.SEMICOLON */ || + codepoint == 125 /* TokenMap.RIGHT_BRACE */ || + codepoint == 44 /* TokenMap.COMMA */) { + return position - offset; } + return 0; } - run(ast, options) { - if (!("chi" in ast || ast.chi?.length == 0)) { - return null; + consumeURLToken(parseInfo, endPosition) { + let charCode; + // consume an + while (isWhiteSpace(this.peekCharCode(parseInfo))) { + this.advance(parseInfo); } - // @ts-ignore - const j = ast.chi.length; - let k = 0; - let l; - const rules = []; - const declarations = []; - // @ts-ignore - for (; k < j; k++) { - l = k; - // capture comments with the next token - while (l + 1 < j) { - // @ts-ignore - const node = ast.chi[l]; - if (node.typ == exports.EnumToken.CommentNodeType) { - l++; + charCode = this.peekCharCode(parseInfo); + if (charCode == 34 /* TokenMap.DOUBLE_QUOTE */ || charCode == 39 /* TokenMap.SINGLE_QUOTE */) { + return this.consumeStringAsURLToken(parseInfo); + } + do { + this.advance(parseInfo); + charCode = this.peekCharCode(parseInfo); + } while ( + // !(value === "/" && this.match(parseInfo, "/*") && + charCode !== 41 /* TokenMap.RIGHT_PARENTHESIS */ && + !isWhiteSpace(charCode) && + parseInfo.currentPosition < endPosition); + if (charCode !== 41 /* TokenMap.RIGHT_PARENTHESIS */) { + let k = 1; + while (k < endPosition) { + charCode = parseInfo.stream.charCodeAt(parseInfo.currentPosition - parseInfo.offset + k); + if (isWhiteSpace(charCode)) { + k++; continue; } break; } - // @ts-ignore - const node = ast.chi[l]; - if (node.typ == exports.EnumToken.DeclarationNodeType) { - for (let m = k; m <= l; m++) { - declarations.push(ast.chi[m]); - } + if (charCode != charCode || (charCode != 41 /* TokenMap.RIGHT_PARENTHESIS */ && !isIdentStart(charCode))) { + this.advance(parseInfo, k); + return this.makeToken(parseInfo, exports.EnumToken.BadUrlTokenType); + } + } + return this.makeToken(parseInfo, + // parseInfo.position < parseInfo.currentPosition + (charCode = this.peekCharCode(parseInfo)) != charCode || !this.isURLToken(parseInfo) + ? exports.EnumToken.BadUrlTokenType + : exports.EnumToken.UrlTokenTokenType); + } + /** + * + * @param parseInfo + * @param hint + * @param options + * @returns + */ + makeToken(parseInfo, hint, options) { + let val = null; + this.typ = null; + this.nam = null; + this.val = null; + this.unit = null; + this.kin = null; + this.decodeString = null; + this.slice = null; + this.hint = null; + if (options?.slice) { + this.slice = options.slice; + } + if (options?.decodeSegments) { + this.decodeString = true; + } + if (hint != null) { + let array = null; + let hasUnit = false; + switch (hint) { + case exports.EnumToken.TransformFunctionTokenDefType: + array = transformFunctions; + break; + case exports.EnumToken.ColorFunctionTokenDefType: + array = colorsFunc; + break; + case exports.EnumToken.ContainerFunctionTokenDefType: + array = containerFunc; + break; + case exports.EnumToken.UrlFunctionTokenDefType: + array = urlFunc; + break; + case exports.EnumToken.GridTemplateFuncTokenDefType: + array = gridTemplateFunc; + break; + case exports.EnumToken.ImageFunctionTokenDefType: + array = imageFunc; + break; + case exports.EnumToken.TimelineFunctionTokenDefType: + array = timelineFunc; + break; + // case EnumToken.GeneralEnclosedFunctionTokenDefType: + // searchArray = generalEnclosedFunc; + // break; + case exports.EnumToken.SupportsFunctionTokenDefType: + array = supportFunc; + break; + case exports.EnumToken.TimingFunctionTokenDefType: + array = timingFunc; + break; + case exports.EnumToken.MathFunctionTokenDefType: + array = mathFuncs; + break; + case exports.EnumToken.WhenElseFunctionTokenDefType: + array = whenElseFunc; + break; + case exports.EnumToken.WildCardFunctionTokenDefType: + array = wildCardFuncs; + break; + case exports.EnumToken.FrequencyTokenType: + array = frequencyUnits; + hasUnit = true; + break; + case exports.EnumToken.ResolutionTokenType: + array = resolutionUnits; + hasUnit = true; + break; + case exports.EnumToken.LengthTokenType: + array = dimensionUnits; + hasUnit = true; + break; + case exports.EnumToken.FlexTokenType: + array = flexUnits; + hasUnit = true; + break; + case exports.EnumToken.AngleTokenType: + array = angleUnits; + hasUnit = true; + break; + case exports.EnumToken.TimeTokenType: + array = timeUnits; + hasUnit = true; + break; + case exports.EnumToken.DimensionTokenType: + hasUnit = true; + break; + } + if (array != null) { + val = searchArray(array, parseInfo, hasUnit ? options?.slice : parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset); + } + else if (!hintsEnum.has(hint)) { + val = parseInfo.stream.slice(options?.slice ?? parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset); + } + if (this.decodeString) { + val = decodeEscapeSequences(val); + } + if (hintsEnum.has(hint)) { + this.typ = hint; } else { - for (let m = k; m <= l; m++) { - rules.push(ast.chi[m]); + this.typ = hint; + if (hasUnit || hint == exports.EnumToken.PercentageTokenType || hint == exports.EnumToken.DimensionTokenType) { + this.val = parseFloat(parseInfo.stream.slice(parseInfo.position - parseInfo.offset, options?.slice)); + if (hint != exports.EnumToken.PercentageTokenType) { + this.unit = val; + } + } + else if (hint == exports.EnumToken.NumberTokenType) { + this.val = parseFloat(val); + } + else if (hint == exports.EnumToken.AtRuleTokenType) { + this.nam = val; + } + else { + this.val = val; + if (hint == exports.EnumToken.ColorTokenType) { + this.kin = exports.ColorType.HEX; + } } } - k = l; } - if (declarations.length > 0) { - ast.chi.length = 0; - // @ts-expect-error - ast.chi.push(...new PropertyList(options).add(declarations), ...rules); + else { + if (this.equalsIgnoreCase(parseInfo, "!important")) { + this.typ = exports.EnumToken.ImportantTokenType; + } } - return ast; - } - } - - const trimTokenSpace = new Set([ - exports.EnumToken.CommaTokenType, - exports.EnumToken.DelimTokenType, - exports.EnumToken.GtTokenType, - exports.EnumToken.GteTokenType, - exports.EnumToken.LtTokenType, - exports.EnumToken.LteTokenType, - exports.EnumToken.ChildCombinatorTokenType, - ]); - const trimSpaceAfter = new Set([ - ...tokensfuncSet.values(), - exports.EnumToken.ChildCombinatorTokenType, - exports.EnumToken.SupportsQueryConditionTokenType, - exports.EnumToken.SupportsQueryUnaryConditionTokenType, - ]); - /** - * Replace token in its parent node - * @param parent - * @param node - * @param replacement - * @throws TypeError replacement is null - * @throws ReferenceError node not found in parent - */ - function replaceNodeOrValue(parent, node, replacement) { - if (replacement == null || (Array.isArray(replacement) && replacement.length === 0)) { - throw new TypeError(`replacement is null`); - } - for (const newNode of Array.isArray(replacement) ? replacement : [replacement]) { - if (newNode[PARENT] != node[PARENT]) { - newNode[PARENT] = node[PARENT]; + if (this.typ == null) { + val = parseInfo.stream.slice(parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset); + if (options?.decodeSegments) { + val = decodeEscapeSequences(val); + this.decodeString = true; + } + this.typ = exports.EnumToken.LiteralTokenType; + this.val = val; } + this.srcId = parseInfo.source.id; + this.sta = parseInfo.position; + this.end = parseInfo.currentPosition; + this.bytesIn = parseInfo.currentPosition; + parseInfo.position = parseInfo.currentPosition; + return this; } - if (parent.typ == exports.EnumToken.BinaryExpressionTokenType) { - if (parent.l == node) { - parent.l = replacement; - } - else if (parent.r == node) { - parent.r = replacement; - } - else { - throw new ReferenceError("Node not found"); + /** + * + * @param parseInfo + * @param input + * @returns + */ + equalsIgnoreCase(parseInfo, input) { + let position = parseInfo.currentPosition - parseInfo.offset; + let ca; + let cb; + for (let i = 0; i < input.length; i++) { + ca = parseInfo.stream.charCodeAt(position + i); + cb = input.charCodeAt(i); + // Normalize A-Z to a-z + if (ca >= 65 && ca <= 90) + ca += 32; + if (cb >= 65 && cb <= 90) + cb += 32; + if (ca != cb) { + return false; + } } + return true; } - else { - const target = "val" in parent && Array.isArray(parent.val) - ? parent.val - : (parent.chi ?? parent); - if (Array.isArray(target)) { - // @ts-ignore - const index = target.indexOf(node); - if (index == -1) { - throw new ReferenceError("Node not found"); + /** + * + * @param parseInfo + * @param input + * @returns + */ + match(parseInfo, input) { + let position = parseInfo.currentPosition - parseInfo.offset; + for (let i = 0; i < input.length; i++) { + if (parseInfo.stream[position + i] != input.charAt(i)) { + return false; } - target.splice(index, 1, ...(Array.isArray(replacement) ? replacement : [replacement])); - } - else if ("l" in target && target.l == node) { - target.l = replacement; - } - else if ("r" in target && target.r == node) { - target.r = replacement; - } - else { - throw new ReferenceError("Node not found"); } + return true; } - node[PARENT] = null; - return true; - } - function trimWhiteSpaceTokens(tokens) { - let i = 0; - if (tokens[0]?.typ === exports.EnumToken.WhitespaceTokenType) { - tokens.shift(); - } - if (tokens[tokens.length - 1]?.typ === exports.EnumToken.WhitespaceTokenType) { - tokens.pop(); + /** + * Get the current character code without creating a string + * @param parseInfo + * @returns charCode at current position + */ + peekCharCode(parseInfo) { + return parseInfo.stream.charCodeAt(parseInfo.currentPosition - parseInfo.offset); } - if (tokens.length === 1) { - if (tokens[0].typ === exports.EnumToken.WhitespaceTokenType) { - tokens.length = 0; + /** + * + * @param parseInfo + * @param count + * @returns + */ + peek(parseInfo, count = 1) { + if (count == 1) { + return parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset); } - return tokens; + const position = parseInfo.currentPosition - parseInfo.offset; + return parseInfo.stream.slice(position, position + count); } - for (; i < tokens.length; i++) { - if (trimSpaceAfter.has(tokens[i].typ) && tokens[i + 1]?.typ === exports.EnumToken.WhitespaceTokenType) { - tokens.splice(i + 1, 1); + /** + * + * @param parseInfo + * @param count + * @returns + */ + advance(parseInfo, count = 1) { + let position = parseInfo.currentPosition - parseInfo.offset; + let char = count == 1 ? parseInfo.stream.charAt(position) : parseInfo.stream.slice(position, position + count); + let i = 0; + let codepoint; + const lineStarts = parseInfo.source.lineStarts.lineStarts; + for (; i < char.length; i++) { + codepoint = char.charCodeAt(i); + if (codepoint == 0xa || // \n + codepoint == 0xb || // \v + codepoint == 0xc || // \f + codepoint == 0xd || // \r + codepoint == 0x2028 || // \u2028 + codepoint == 0x2029 // \u2029 + ) { + // \r\n + if (codepoint == 0xa && i > 0 && char.charCodeAt(i - 1) == 0xd) ; + else { + lineStarts.push(position + parseInfo.offset + i); + } + } } - else if (trimTokenSpace.has(tokens[i].typ)) { - if (tokens[i + 1]?.typ === exports.EnumToken.WhitespaceTokenType) { - tokens.splice(i + 1, 1); + parseInfo.currentPosition += char.length; + return char; + } + /** + * + * @param parseInfo + * @returns + */ + isIdentToken(parseInfo /* , start?: number, end?: number */) { + let j = parseInfo.currentPosition - parseInfo.offset; + let i = parseInfo.position - parseInfo.offset; + // if (start != null) { + // if (end == null) { + // if (start < 0) { + // j += start; + // } else { + // i += start; + // } + // } else { + // if (end < 0) { + // j += end; + // } else { + // j = parseInfo.position + end; + // } + // } + // } + j--; + let codepoint = parseInfo.stream.charCodeAt(i); + // - + if (codepoint == 0x2d) { + let nextCodepoint; + // NaN != NaN + if ((nextCodepoint = parseInfo.stream.charCodeAt(i + 1)) != nextCodepoint) { + return false; } - if (i > 0 && tokens[i - 1].typ === exports.EnumToken.Whitespace) { - tokens.splice(--i, 1); + if (!isIdentStart(nextCodepoint) && nextCodepoint != 0x2d) { + return false; } + codepoint = nextCodepoint; + i++; } - } - return tokens; - } - - class ComputeCalcExpressionFeature { - accept = new Set([exports.EnumToken.RuleNodeType, exports.EnumToken.AtRuleNodeType]); - get ordering() { - return 3; - } - get processMode() { - return exports.FeatureWalkMode.Post; - } - static register(options) { - if (options.computeCalcExpression) { - // @ts-ignore - options.features.push(new ComputeCalcExpressionFeature()); + if (codepoint !== 0x2d && !isIdentStart(codepoint)) { + return false; } - } - run(ast) { - // @ts-ignore - if (ast.chi == null) { - return null; + if (codepoint == 92 /* TokenMap.REVERSE_SOLIDUS */) { + codepoint = parseInfo.stream.charCodeAt(i + 1); + i += String.fromCodePoint(codepoint).length; } - for (const node of ast.chi) { - if (node.typ != exports.EnumToken.DeclarationNodeType) { + while (i < j) { + i += codepoint < 0x80 ? 1 : String.fromCodePoint(codepoint).length; + codepoint = parseInfo.stream.charCodeAt(i); + if (codepoint == 92 /* TokenMap.REVERSE_SOLIDUS */) { + i += codepoint < 0x80 ? 1 : String.fromCodePoint(codepoint).length; + codepoint = parseInfo.stream.charCodeAt(i); + i += codepoint < 0x80 ? 1 : String.fromCodePoint(codepoint).length; continue; } - const set = new Set(); - for (const { value, parent } of walkValues(node.val, node)) { - if (parent?.typ == exports.EnumToken.BinaryExpressionTokenType) { - continue; - } - if (value.typ == exports.EnumToken.BinaryExpressionTokenType) { - const result = evaluate([value]); - try { - // @ts-ignore - replaceNodeOrValue(parent, value, result); - } - catch (e) { } - continue; - } - if (value != null && tokensfuncSet.has(value.typ)) { - if (!set.has(value)) { - set.add(value); - if (parent != null) { - const shouldEvaluate = value.typ === exports.EnumToken.MathFunctionTokenType || - (value.typ == exports.EnumToken.FunctionTokenType && - mathFuncs.includes(value.val)); - if (shouldEvaluate) { - // @ts-ignore - const cp = value.val != "calc" - ? [value] - : value.typ == exports.EnumToken.DeclarationNodeType - ? value.val - : value.chi; - const values = evaluate(cp); - // fix a + -b into a - b - for (const { value, parent: p } of walkValues(values)) { - if (value.typ === exports.EnumToken.BinaryExpressionTokenType) { - if (value.op === exports.EnumToken.Add && - Math.sign(value.r.val) == -1) { - value.op = exports.EnumToken.Sub; - // @ts-expect-error - value.r.val *= - -1; - } - } - } - // @ts-ignore - const children = parent.typ == exports.EnumToken.DeclarationNodeType - ? parent.val - : // @ts-ignore - parent.chi; - if (values.length == 1 && values[0].typ != exports.EnumToken.BinaryExpressionTokenType) { - for (let i = 0; i < children.length; i++) { - if (children[i] == value) { - children.splice(i, 1, !(parent.typ === exports.EnumToken.MathFunctionTokenType || - (parent.typ == exports.EnumToken.FunctionTokenType && - parent.val == "calc")) && - typeof values[0].val != "number" && - !(values[0].typ == exports.EnumToken.MathFunctionTokenType || - (values[0].typ == exports.EnumToken.FunctionTokenType && - mathFuncs.includes(values[0].val))) - ? { - typ: exports.EnumToken.MathFunctionTokenType, - val: "calc", - chi: values, - [LOCSRCID]: value[LOCSRCID], - [LOCSTA]: value[LOCSTA], - [LOCEND]: value[LOCEND], - } - : values[0]); - break; - } - } - } - else { - for (let i = 0; i < children.length; i++) { - if (children[i] == value) { - children.splice(i, 1, { - typ: exports.EnumToken.MathFunctionTokenType, - val: "calc", - chi: values, - [LOCSRCID]: value[LOCSRCID], - [LOCSTA]: value[LOCSTA], - [LOCEND]: value[LOCEND], - }); - break; - } - } - } - } - } - } - } + if (codepoint !== 0x2d && !isIdentCodepoint(codepoint)) { + return false; } } - return null; - } - } - - const identityMatrix = new Float64Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); - function identity() { - return identityMatrix.slice(); - } - function normalize$1(point) { - const [x, y, z] = point; - const norm = Math.sqrt(point[0] * point[0] + point[1] * point[1] + point[2] * point[2]); - return norm === 0 ? [0, 0, 0] : [x / norm, y / norm, z / norm]; - } - function dot(point1, point2) { - if (point1.length === 4 && point2.length === 4) { - return point1[0] * point2[0] + point1[1] * point2[1] + point1[2] * point2[2] + point1[3] * point2[3]; + return true; } - return point1[0] * point2[0] + point1[1] * point2[1] + point1[2] * point2[2]; - } - function multiply(matrixA, matrixB) { - const result = new Float64Array(16); - result[0] = matrixA[0] * matrixB[0] + matrixA[4] * matrixB[1] + matrixA[8] * matrixB[2] + matrixA[12] * matrixB[3]; - result[1] = matrixA[1] * matrixB[0] + matrixA[5] * matrixB[1] + matrixA[9] * matrixB[2] + matrixA[13] * matrixB[3]; - result[2] = matrixA[2] * matrixB[0] + matrixA[6] * matrixB[1] + matrixA[10] * matrixB[2] + matrixA[14] * matrixB[3]; - result[3] = matrixA[3] * matrixB[0] + matrixA[7] * matrixB[1] + matrixA[11] * matrixB[2] + matrixA[15] * matrixB[3]; - result[4] = matrixA[0] * matrixB[4] + matrixA[4] * matrixB[5] + matrixA[8] * matrixB[6] + matrixA[12] * matrixB[7]; - result[5] = matrixA[1] * matrixB[4] + matrixA[5] * matrixB[5] + matrixA[9] * matrixB[6] + matrixA[13] * matrixB[7]; - result[6] = matrixA[2] * matrixB[4] + matrixA[6] * matrixB[5] + matrixA[10] * matrixB[6] + matrixA[14] * matrixB[7]; - result[7] = matrixA[3] * matrixB[4] + matrixA[7] * matrixB[5] + matrixA[11] * matrixB[6] + matrixA[15] * matrixB[7]; - result[8] = - matrixA[0] * matrixB[8] + matrixA[4] * matrixB[9] + matrixA[8] * matrixB[10] + matrixA[12] * matrixB[11]; - result[9] = - matrixA[1] * matrixB[8] + matrixA[5] * matrixB[9] + matrixA[9] * matrixB[10] + matrixA[13] * matrixB[11]; - result[10] = - matrixA[2] * matrixB[8] + matrixA[6] * matrixB[9] + matrixA[10] * matrixB[10] + matrixA[14] * matrixB[11]; - result[11] = - matrixA[3] * matrixB[8] + matrixA[7] * matrixB[9] + matrixA[11] * matrixB[10] + matrixA[15] * matrixB[11]; - result[12] = - matrixA[0] * matrixB[12] + matrixA[4] * matrixB[13] + matrixA[8] * matrixB[14] + matrixA[12] * matrixB[15]; - result[13] = - matrixA[1] * matrixB[12] + matrixA[5] * matrixB[13] + matrixA[9] * matrixB[14] + matrixA[13] * matrixB[15]; - result[14] = - matrixA[2] * matrixB[12] + matrixA[6] * matrixB[13] + matrixA[10] * matrixB[14] + matrixA[14] * matrixB[15]; - result[15] = - matrixA[3] * matrixB[12] + matrixA[7] * matrixB[13] + matrixA[11] * matrixB[14] + matrixA[15] * matrixB[15]; - return result; - } - function inverse(matrix) { - // Create augmented matrix [matrix | identity] - let augmented = [ - matrix[0], - matrix[1], - matrix[2], - matrix[3], - 1, - 0, - 0, - 0, - matrix[4], - matrix[5], - matrix[6], - matrix[7], - 0, - 1, - 0, - 0, - matrix[8], - matrix[9], - matrix[10], - matrix[11], - 0, - 0, - 1, - 0, - matrix[12], - matrix[13], - matrix[14], - matrix[15], - 0, - 0, - 0, - 1, - ]; - // Gaussian elimination with partial pivoting - for (let col = 0; col < 4; col++) { - // Find pivot row with maximum absolute value - let maxRow = col; - let maxVal = Math.abs(augmented[col * 4 + col]); - for (let row = col + 1; row < 4; row++) { - let val = Math.abs(augmented[row * 4 + col]); - if (val > maxVal) { - maxVal = val; - maxRow = row; + /** + * + * @param parseInfo + * @param input + * @returns + */ + startsWith(parseInfo, input) { + let i = 0; + let j = input.length; + while (i < j) { + if (parseInfo.stream.charAt(parseInfo.position - parseInfo.offset + i) != input.charAt(i)) { + return false; } + i++; } - // Check for singularity - if (maxVal < epsilon) { - return null; - } - // Swap rows if necessary - if (maxRow !== col) { - [augmented[col], augmented[maxRow]] = [augmented[maxRow], augmented[col]]; - } - // Scale pivot row to make pivot element 1 - let pivot = augmented[col * 4 + col]; - for (let j = 0; j < 8; j++) { - augmented[col * 4 + j] /= pivot; - } - // Eliminate column in other rows - for (let row = 0; row < 4; row++) { - if (row !== col) { - let factor = augmented[row * 4 + col]; - for (let j = 0; j < 8; j++) { - augmented[row * 4 + j] -= factor * augmented[col * 4 + j]; + return true; + } + /** + * + * @param parseInfo + * @returns + */ + isURLToken(parseInfo) { + let i = parseInfo.position - parseInfo.offset; + let c; + while (++i < parseInfo.currentPosition) { + c = parseInfo.stream.charCodeAt(i); + // single quote or double quote or start parenthesis or close parenthesis + if (isNonPrintable(c) || c == 0x27 || c == 0x22 || c == 0x28 || c == 0x29) { + return false; + } + // valid escape + if (c == 92 /* TokenMap.REVERSE_SOLIDUS */) { + i++; + if (i >= parseInfo.currentPosition) { + return false; + } + c = parseInfo.stream.charCodeAt(i); + // c is not '\n' or '\r' or '\f' + if (c == 0x6e || c == 0x72 || c == 0x66) { + return false; } + continue; + } + // is white space + if (c == 0x20 || c == 0x09) { + break; } } + return i == parseInfo.currentPosition; } - // Extract the inverse from the right side of the augmented matrix - return augmented.slice(0, 16); - } - function round(number) { - const rounded = Math.round(number); - return Math.abs(rounded - number) <= epsilon ? rounded : +number.toPrecision(6); - } - // translate3d(25.9808px, 0, 15px ) rotateY(60deg) skewX(49.9999deg) scale(1, 1.2) - // translate → rotate → skew → scale - function decompose(original) { - const matrix = original.slice(); - // Normalize last row - if (matrix[15] === 0) { - return null; - } - for (let i = 0; i < 16; i++) - matrix[i] /= matrix[15]; - // Perspective extraction - const perspective = [0, 0, 0, 1]; - if (matrix[3] !== 0 || matrix[7] !== 0 || matrix[11] !== 0) { - const rightHandSide = [matrix[3], matrix[7], matrix[11], matrix[15]]; - const perspectiveMatrix = matrix.slice(); - perspectiveMatrix[3] = 0; - perspectiveMatrix[7] = 0; - perspectiveMatrix[11] = 0; - perspectiveMatrix[15] = 1; - // @ts-ignore - const inverted = inverse(original.slice()); - if (inverted === null) { - return null; - } - const transposedInverse = transposeMatrix4(inverted); - perspective[0] = dot(rightHandSide, transposedInverse.slice(0, 4)); - perspective[1] = dot(rightHandSide, transposedInverse.slice(4, 8)); - perspective[2] = dot(rightHandSide, transposedInverse.slice(8, 12)); - perspective[3] = dot(rightHandSide, transposedInverse.slice(12, 16)); - // Clear perspective from matrix - matrix[3] = 0; - matrix[7] = 0; - matrix[11] = 0; - matrix[15] = 1; - } - // Translation - const translate = [matrix[12], matrix[13], matrix[14]]; - matrix[12] = matrix[13] = matrix[14] = 0; - // Build the 3x3 matrix - const row0 = [matrix[0], matrix[1], matrix[2]]; - const row1 = [matrix[4], matrix[5], matrix[6]]; - const row2 = [matrix[8], matrix[9], matrix[10]]; - const cross = [ - row1[1] * row2[2] - row1[2] * row2[1], - row1[2] * row2[0] - row1[0] * row2[2], - row1[0] * row2[1] - row1[1] * row2[0], - ]; - // Compute scale - const scaleX = Math.hypot(row0[0], row0[1], row0[2]); - const row0Norm = normalize$1(row0); - const skewXY = dot(row0Norm, row1); - const row1Proj = [row1[0] - skewXY * row0Norm[0], row1[1] - skewXY * row0Norm[1], row1[2] - skewXY * row0Norm[2]]; - const scaleY = Math.hypot(row1Proj[0], row1Proj[1], row1Proj[2]); - const row1Norm = normalize$1(row1Proj); - const skewXZ = dot(row0Norm, row2); - const skewYZ = dot(row1Norm, row2); - const row2Proj = [ - row2[0] - skewXZ * row0Norm[0] - skewYZ * row1Norm[0], - row2[1] - skewXZ * row0Norm[1] - skewYZ * row1Norm[1], - row2[2] - skewXZ * row0Norm[2] - skewYZ * row1Norm[2], - ]; - const row2Norm = normalize$1(row2Proj); - const determinant = row0[0] * cross[0] + row0[1] * cross[1] + row0[2] * cross[2]; - const scaleZ = Math.hypot(row2Proj[0], row2Proj[1], row2Proj[2]) * (determinant < 0 ? -1 : 1); - // Build rotation matrix from orthonormalized vectors - const r00 = row0Norm[0], r01 = row1Norm[0], r02 = row2Norm[0]; - const r10 = row0Norm[1], r11 = row1Norm[1], r12 = row2Norm[1]; - const r20 = row0Norm[2], r21 = row1Norm[2], r22 = row2Norm[2]; - // Convert to quaternion - const trace = r00 + r11 + r22; - let qw, qx, qy, qz; - if (trace > 0) { - const s = 0.5 / Math.sqrt(trace + 1.0); - qw = 0.25 / s; - qx = (r21 - r12) * s; - qy = (r02 - r20) * s; - qz = (r10 - r01) * s; - } - else if (r00 > r11 && r00 > r22) { - const s = 2.0 * Math.sqrt(1.0 + r00 - r11 - r22); - qw = (r21 - r12) / s; - qx = 0.25 * s; - qy = (r01 + r10) / s; - qz = (r02 + r20) / s; - } - else if (r11 > r22) { - const s = 2.0 * Math.sqrt(1.0 + r11 - r00 - r22); - qw = (r02 - r20) / s; - qx = (r01 + r10) / s; - qy = 0.25 * s; - qz = (r12 + r21) / s; - } - else { - const s = 2.0 * Math.sqrt(1.0 + r22 - r00 - r11); - qw = (r10 - r01) / s; - qx = (r02 + r20) / s; - qy = (r12 + r21) / s; - qz = 0.25 * s; + done() { + return this.typ === exports.EnumToken.EOF; } - [qx, qy, qz] = toZero([qx, qy, qz]); - let q = [Math.abs(qx), Math.abs(qy), Math.abs(qz)].reduce((acc, curr) => { - if (acc == 0 || (curr > 0 && curr < acc)) { - acc = curr; + /** + * Tokenize CSS string + */ + next( /* parseInfo: ParseInfo | string, yieldEOFToken: boolean = true */) { + const parseInfo = this.parseInfo; + this.source = parseInfo.source; + let charCode; + let nextCharCode; + // const result: TokenizeResult[] = []; + // allow 10 characters buffer for the streaming parser to avoid incomplete tokens + const endPosition = parseInfo.stream.length - 1; // yieldEOFToken ? parseInfo.stream.length - 1 : parseInfo.stream.length - 10; + let tokensCount; + // NaN is not equal to NaN + while ((charCode = this.peekCharCode(parseInfo)) == charCode) { + if (this.state === exports.EnumToken.UrlFunctionTokenDefType) { + this.state = null; + return this.consumeURLToken(parseInfo, endPosition); + } + if (parseInfo.position == parseInfo.currentPosition) { + if (charCode == 45 /* TokenMap.MINUS */ || + charCode == 43 /* TokenMap.PLUS */ || + charCode == 46 /* TokenMap.DOT */ || + isDigit(charCode)) { + tokensCount = this.consumeNumericToken(parseInfo); + if (tokensCount > 0) { + this.advance(parseInfo, tokensCount); + return this.makeToken(parseInfo, this.hint ?? exports.EnumToken.NumberTokenType, { + slice: this.slice, + sign: charCode == 45 /* TokenMap.MINUS */ ? "-" : charCode == 43 /* TokenMap.PLUS */ ? "+" : null, + }); + } + } + if (isIdentStart(charCode) || charCode == 45 /* TokenMap.MINUS */) { + tokensCount = this.consumeIdentToken(parseInfo); + if (tokensCount > 0) { + this.advance(parseInfo, tokensCount); + charCode = this.peekCharCode(parseInfo); + // do not match function + if (40 /* TokenMap.LEFT_PARENTHESIS */ != charCode) { + return this.makeToken(parseInfo, this.startsWith(parseInfo, "--") + ? exports.EnumToken.DashedIdenTokenType + : exports.EnumToken.IdenTokenType); + } + } + } + if (charCode == 64 /* TokenMap.AT */) { + this.advance(parseInfo); + charCode = this.peekCharCode(parseInfo); + // match at-rule + if (charCode == 45 /* TokenMap.MINUS */ || isIdentStart(this.peekCharCode(parseInfo))) { + // consume '@' + parseInfo.position = parseInfo.currentPosition; + tokensCount = this.consumeIdentToken(parseInfo); + if (tokensCount > 0) { + this.advance(parseInfo, tokensCount); + return this.makeToken(parseInfo, exports.EnumToken.AtRuleTokenType); + } + } + } + if (charCode == 35 /* TokenMap.HASH */) { + tokensCount = this.consumeColor(parseInfo); + if (tokensCount > 0) { + this.advance(parseInfo, tokensCount); + return this.makeToken(parseInfo, exports.EnumToken.ColorTokenType); + } + this.advance(parseInfo); + tokensCount = this.consumeIdentToken(parseInfo); + if (tokensCount > 0) { + this.advance(parseInfo, tokensCount); + return this.makeToken(parseInfo, exports.EnumToken.HashTokenType); + } + } + } + // EOF + switch (charCode) { + case 61 /* TokenMap.EQUALS */: + // if (parseInfo.position < parseInfo.currentPosition) { + // return this.makeToken(parseInfo); + // } + this.advance(parseInfo); + return this.makeToken(parseInfo, exports.EnumToken.DelimTokenType); + // '+' or '-' + case 43 /* TokenMap.PLUS */: + // if (parseInfo.position < parseInfo.currentPosition) { + // return this.makeToken(parseInfo); + // } + this.advance(parseInfo); + charCode = parseInfo.stream.charCodeAt(parseInfo.currentPosition - parseInfo.offset); + // if (isDigit(charCode)) { + // tokensCount = this.consumeNumericToken(parseInfo); + // if (tokensCount > 0) { + // this.advance(parseInfo, tokensCount); + // return this.makeToken(parseInfo, this.hint ?? EnumToken.NumberTokenType, { + // slice: this.slice, + // sign: "+", + // }); + // } + // } + return this.makeToken(parseInfo, exports.EnumToken.Plus); + case 45 /* TokenMap.MINUS */: + if (parseInfo.position == parseInfo.currentPosition) { + nextCharCode = parseInfo.stream.charCodeAt(parseInfo.currentPosition - parseInfo.offset + 1); + // not a number + if (isWhiteSpace(nextCharCode)) { + this.advance(parseInfo); + return this.makeToken(parseInfo, exports.EnumToken.Sub); + } + // if ( + // charCode == TokenMap.MINUS && + // (nextCharCode == TokenMap.MINUS || isIdentStart(nextCharCode)) + // ) { + // this.advance(parseInfo); + // tokensCount = this.consumeIdentToken(parseInfo); + // if (tokensCount > 0) { + // this.advance(parseInfo, tokensCount); + // return this.makeToken(parseInfo, EnumToken.IdenTokenType); + // } + // } + } + this.advance(parseInfo); + break; + // '{' + case 123 /* TokenMap.LEFT_BRACE */: + if (parseInfo.position < parseInfo.currentPosition) { + return this.makeToken(parseInfo); + } + this.advance(parseInfo); + return this.makeToken(parseInfo, exports.EnumToken.BlockStartTokenType); + // '}' + case 125 /* TokenMap.RIGHT_BRACE */: + if (parseInfo.position < parseInfo.currentPosition) { + return this.makeToken(parseInfo); + } + this.advance(parseInfo); + return this.makeToken(parseInfo, exports.EnumToken.BlockEndTokenType); + // '(' + case 40 /* TokenMap.LEFT_PARENTHESIS */: + if (parseInfo.position < parseInfo.currentPosition) { + // if ( + // parseInfo.stream[parseInfo.position - parseInfo.offset] === ":" && + // this.isPseudo(parseInfo) + // ) { + // this.advance(parseInfo); + // return this.makeToken(parseInfo, EnumToken.PseudoClassFunctionTokenDefType); + // } else + if (this.isIdentToken(parseInfo)) { + const hint = this.startsWith(parseInfo, "--") + ? exports.EnumToken.CustomFunctionTokenDefType + : (getSymbolHint(parseInfo, parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset + 1) ?? exports.EnumToken.FunctionTokenDefType); + this.makeToken(parseInfo, hint); + this.advance(parseInfo); + // consume '(' + parseInfo.position = parseInfo.currentPosition; + if (hint === exports.EnumToken.UrlFunctionTokenDefType) { + this.state = hint; + } + return this; + } + } + this.advance(parseInfo); + return this.makeToken(parseInfo, exports.EnumToken.StartParensTokenType); + // ')' + case 41 /* TokenMap.RIGHT_PARENTHESIS */: + if (parseInfo.position < parseInfo.currentPosition) { + return this.makeToken(parseInfo); + } + this.advance(parseInfo); + return this.makeToken(parseInfo, exports.EnumToken.EndParensTokenType); + // '[' + case 91 /* TokenMap.LEFT_BRACKETS */: + // if (parseInfo.position < parseInfo.currentPosition) { + // return this.makeToken(parseInfo); + // } + this.advance(parseInfo); + return this.makeToken(parseInfo, exports.EnumToken.AttrStartTokenType); + // ']' + case 93 /* TokenMap.RIGHT_BRACKETS */: + // if (parseInfo.position < parseInfo.currentPosition) { + // return this.makeToken(parseInfo); + // } + this.advance(parseInfo); + return this.makeToken(parseInfo, exports.EnumToken.AttrEndTokenType); + case 59 /* TokenMap.SEMICOLON */: + // if (parseInfo.position < parseInfo.currentPosition) { + // return this.makeToken(parseInfo); + // } + this.advance(parseInfo); + return this.makeToken(parseInfo, exports.EnumToken.SemiColonTokenType); + case 58 /* TokenMap.COLON */: + // if (parseInfo.position < parseInfo.currentPosition) { + // return this.makeToken(parseInfo); + // } + this.advance(parseInfo); + if (this.peekCharCode(parseInfo) == 58 /* TokenMap.COLON */) { + this.advance(parseInfo); + return this.makeToken(parseInfo, exports.EnumToken.DoubleColonTokenType); + } + return this.makeToken(parseInfo, exports.EnumToken.ColonTokenType); + // \n \r \f \v \t space + case 0x9: + case 0x20: + case 0xa: + case 0xb: + case 0xc: + case 0xd: + case 0x2028: + case 0x2029: + if (parseInfo.position < parseInfo.currentPosition) { + return this.makeToken(parseInfo); + } + this.advance(parseInfo); + nextCharCode = parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset).charCodeAt(0); + while (nextCharCode == 0x20 || + (nextCharCode >= 0x9 && nextCharCode <= 0xd) || + nextCharCode == 0x2028 || + nextCharCode == 0x2029) { + this.advance(parseInfo); + nextCharCode = parseInfo.stream + .charAt(parseInfo.currentPosition - parseInfo.offset) + .charCodeAt(0); + } + return this.makeToken(parseInfo, exports.EnumToken.WhitespaceTokenType); + case 44 /* TokenMap.COMMA */: + // if (parseInfo.position < parseInfo.currentPosition) { + // return this.makeToken(parseInfo); + // } + this.advance(parseInfo); + return this.makeToken(parseInfo, exports.EnumToken.CommaTokenType); + case 36 /* TokenMap.DOLLAR */: + // if (parseInfo.position < parseInfo.currentPosition) { + // return this.makeToken(parseInfo); + // } + if (this.match(parseInfo, "$=")) { + this.advance(parseInfo, 2); + return this.makeToken(parseInfo, exports.EnumToken.EndMatchTokenType); + } + this.advance(parseInfo); + break; + case 126 /* TokenMap.TILDA */: + // if (parseInfo.position < parseInfo.currentPosition) { + // return this.makeToken(parseInfo); + // } + if (this.match(parseInfo, "~=")) { + this.advance(parseInfo, 2); + return this.makeToken(parseInfo, exports.EnumToken.IncludeMatchTokenType); + } + this.advance(parseInfo); + return this.makeToken(parseInfo, exports.EnumToken.Tilda); + // case '^': + case 94 /* TokenMap.CARET */: + // if (parseInfo.position < parseInfo.currentPosition) { + // return this.makeToken(parseInfo); + // } + if (this.match(parseInfo, "^=")) { + this.advance(parseInfo, 2); + return this.makeToken(parseInfo, exports.EnumToken.StartMatchTokenType); + } + this.advance(parseInfo); + break; + case 42 /* TokenMap.STAR */: + // if (parseInfo.position < parseInfo.currentPosition) { + // return this.makeToken(parseInfo); + // } + if (this.match(parseInfo, "*=")) { + this.advance(parseInfo, 2); + return this.makeToken(parseInfo, exports.EnumToken.ContainMatchTokenType); + } + this.advance(parseInfo); + return this.makeToken(parseInfo, exports.EnumToken.Star); + case 38 /* TokenMap.AMPERSAND */: + if (parseInfo.position < parseInfo.currentPosition) { + return this.makeToken(parseInfo); + } + this.advance(parseInfo); + return this.makeToken(parseInfo, exports.EnumToken.NestingSelectorTokenType); + case 124 /* TokenMap.PIPE */: + // if (parseInfo.position < parseInfo.currentPosition) { + // return this.makeToken(parseInfo); + // } + // '||' + if (this.match(parseInfo, "||")) { + this.advance(parseInfo, 2); + return this.makeToken(parseInfo, exports.EnumToken.ColumnCombinatorTokenType); + } + else if (this.match(parseInfo, "|=")) { + this.advance(parseInfo, 2); + return this.makeToken(parseInfo, exports.EnumToken.DashMatchTokenType); + } + this.advance(parseInfo); + return this.makeToken(parseInfo, exports.EnumToken.Pipe); + case 33 /* TokenMap.EXCLAMATION */: + if (parseInfo.position < parseInfo.currentPosition) { + return this.makeToken(parseInfo); + } + if (this.match(parseInfo, "!important")) { + this.advance(parseInfo, 10); + return this.makeToken(parseInfo, exports.EnumToken.ImportantTokenType); + } + this.advance(parseInfo); + break; + case 47 /* TokenMap.SLASH */: + // if (parseInfo.position < parseInfo.currentPosition) { + // return this.makeToken(parseInfo); + // } + if (!this.match(parseInfo, "/*")) { + this.advance(parseInfo); + return this.makeToken(parseInfo, getSymbolHint(parseInfo, parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset)); + } + this.advance(parseInfo, 2); + while ((charCode = this.advance(parseInfo).charCodeAt(0)) == charCode) { + if (charCode == 42 /* TokenMap.STAR */) { + if (this.match(parseInfo, "/")) { + this.advance(parseInfo); + return this.makeToken(parseInfo, exports.EnumToken.CommentTokenType); + } + } + } + if (parseInfo.position < parseInfo.currentPosition) { + return this.makeToken(parseInfo, exports.EnumToken.BadCommentTokenType); + } + break; + case 62 /* TokenMap.GREATERTHAN */: + // if (parseInfo.position < parseInfo.currentPosition) { + // return this.makeToken(parseInfo); + // } + if (this.match(parseInfo, ">=")) { + this.advance(parseInfo, 2); + return this.makeToken(parseInfo, exports.EnumToken.GteTokenType); + } + this.advance(parseInfo); + return this.makeToken(parseInfo, exports.EnumToken.GtTokenType); + case 60 /* TokenMap.LOWERTHAN */: + if (parseInfo.position < parseInfo.currentPosition) { + return this.makeToken(parseInfo); + } + if (this.match(parseInfo, "<=")) { + this.advance(parseInfo, 2); + return this.makeToken(parseInfo, exports.EnumToken.LteTokenType); + } + this.advance(parseInfo); + if (this.match(parseInfo, "!--")) { + this.advance(parseInfo, 3); + while ((charCode = this.advance(parseInfo).charCodeAt(0)) == charCode) { + if (charCode == 45 /* TokenMap.MINUS */ && this.match(parseInfo, "->")) { + break; + } + } + if (parseInfo.currentPosition >= endPosition) { + return this.makeToken(parseInfo, exports.EnumToken.BadCdoTokenType); + } + else { + this.advance(parseInfo, 2); + return this.makeToken(parseInfo, exports.EnumToken.CDOCOMMTokenType); + } + } + break; + case 35 /* TokenMap.HASH */: + if (parseInfo.position < parseInfo.currentPosition) { + return this.makeToken(parseInfo); + } + this.advance(parseInfo); + break; + case 92 /* TokenMap.REVERSE_SOLIDUS */: + this.advance(parseInfo); + // EOF + // end of stream ignore \\ + if (!this.peek(parseInfo) && parseInfo.position < parseInfo.currentPosition) { + return this.makeToken(parseInfo); + } + this.advance(parseInfo); + break; + case 39 /* TokenMap.SINGLE_QUOTE */: + case 34 /* TokenMap.DOUBLE_QUOTE */: + if (parseInfo.position < parseInfo.currentPosition) { + return this.makeToken(parseInfo); + } + return this.consumeString(parseInfo); + case 46 /* TokenMap.DOT */: + const codepoint = parseInfo.stream.charCodeAt(parseInfo.currentPosition - parseInfo.offset + 1); + if (isIdentStart(codepoint) || codepoint == 45 /* TokenMap.MINUS */) { + // if (parseInfo.position < parseInfo.currentPosition) { + // return this.makeToken(parseInfo); + // } + this.advance(parseInfo); + let tokensCount = this.consumeIdentToken(parseInfo); + if (tokensCount > 0) { + this.advance(parseInfo, tokensCount); + return this.makeToken(parseInfo, exports.EnumToken.ClassSelectorTokenType); + } + } + // if (!isDigit(codepoint) && parseInfo.position !== parseInfo.currentPosition) { + // this.makeToken(parseInfo); + // this.advance(parseInfo, 2); + // return this; + // } + this.advance(parseInfo); + break; + default: + this.advance(parseInfo); + break; + } } - return acc; - }, 0); - if (q > 0) { - qx /= q; - qy /= q; - qz /= q; - } - const rotate = [ - qx, - qy, - qz, - Object.is(qw, 0) ? 0 : (2 * Math.acos(qw) * 180) / Math.PI, - ]; - const scale = [scaleX, scaleY, scaleZ]; - const skew = [skewXY, skewXZ, skewYZ]; - return { - translate, - scale, - rotate, - skew, - perspective, - }; - } - function transposeMatrix4(m) { - return [m[0], m[4], m[8], m[12], m[1], m[5], m[9], m[13], m[2], m[6], m[10], m[14], m[3], m[7], m[11], m[15]]; - } - function toZero(v) { - for (let i = 0; i < v.length; i++) { - if (Math.abs(v[i]) <= epsilon) { - v[i] = 0; + if (parseInfo.position < parseInfo.currentPosition) { + return this.makeToken(parseInfo); } - else { - v[i] = +v[i].toPrecision(6); + return this.makeToken(parseInfo, exports.EnumToken.EOFTokenType); + } + /** + * tokenize readable stream + */ + async tokenizeStream() { + const decoder = new TextDecoder("utf-8"); + const reader = this.input.getReader(); + let parseInfo = this.parseInfo; + parseInfo.stream = ""; + while (true) { + const { done, value } = await reader.read(); + const stream = ArrayBuffer.isView(value) ? decoder.decode(value, { stream: true }) : value; + if (!done) { + parseInfo.source.append(stream); + } + else { + break; + } } + parseInfo.stream = parseInfo.source.getContent(); + return this; // .next(); } - return v; - } - // https://drafts.csswg.org/css-transforms-1/#2d-matrix - function is2DMatrix(matrix) { - // m13,m14, m23, m24, m31, m32, m34, m43 are all 0 - return (matrix[2] === 0 && - matrix[3] === 0 && - matrix[6] === 0 && - matrix[7] === 0 && - matrix[8] === 0 && - matrix[9] === 0 && - matrix[11] === 0 && - matrix[14] === 0 && - matrix[10] === 1 && - matrix[15] === 1); - } - - function translateX(x, from) { - const matrix = identity(); - matrix[3 * 4] = x; - return multiply(from, matrix); - } - function translateY(y, from) { - const matrix = identity(); - matrix[3 * 4 + 1] = y; - return multiply(from, matrix); - } - function translateZ(z, from) { - const matrix = identity(); - matrix[3 * 4 + 2] = z; - return multiply(from, matrix); - } - function translate(translate, from) { - const matrix = identity(); - matrix[3 * 4] = translate[0]; - matrix[3 * 4 + 1] = translate[1] ?? 0; - return multiply(from, matrix); - } - function translate3d(translate, from) { - const matrix = identity(); - matrix[3 * 4] = translate[0]; - matrix[3 * 4 + 1] = translate[1]; - matrix[3 * 4 + 2] = translate[2]; - return multiply(from, matrix); } /** - * angle in radian - * @param angle - * @param x - * @param y - * @param z - * @param from + * response type */ - function rotate3D(angle, x, y, z, from) { - const matrix = identity(); - const sc = Math.sin(angle / 2) * Math.cos(angle / 2); - const sq = Math.sin(angle / 2) * Math.sin(angle / 2); - const norm = Math.sqrt(x * x + y * y + z * z); - const unit = norm === 0 ? 0 : 1 / norm; - x *= unit; - y *= unit; - z *= unit; - matrix[0] = 1 - 2 * (y * y + z * z) * sq; - matrix[1] = 2 * (x * y * sq + z * sc); - matrix[2] = 2 * (x * z * sq - y * sc); - matrix[4] = 2 * (x * y * sq - z * sc); - matrix[4 + 1] = 1 - 2 * (x * x + z * z) * sq; - matrix[4 + 2] = 2 * (y * z * sq + x * sc); - matrix[2 * 4] = 2 * (x * z * sq + y * sc); - matrix[2 * 4 + 1] = 2 * (y * z * sq - x * sc); - matrix[2 * 4 + 2] = 1 - 2 * (x * x + y * y) * sq; - return multiply(from, matrix); - } - function rotate(angle, from) { - const matrix = identity(); - matrix[0] = Math.cos(angle); - matrix[1] = Math.sin(angle); - matrix[4] = -Math.sin(angle); - matrix[4 + 1] = Math.cos(angle); - return multiply(from, matrix); - } - - function scaleX(x, from) { - const matrix = identity(); - matrix[0] = x; - return multiply(from, matrix); - } - function scaleY(y, from) { - const matrix = identity(); - matrix[4 + 1] = y; - return multiply(from, matrix); - } - function scaleZ(z, from) { - const matrix = identity(); - matrix[2 * 4 + 2] = z; - return multiply(from, matrix); - } - function scale(x, y, from) { - const matrix = identity(); - matrix[0] = x; - matrix[4 + 1] = y; - return multiply(from, matrix); - } - function scale3d(x, y, z, from) { - const matrix = identity(); - matrix[0] = x; - matrix[4 + 1] = y; - matrix[2 * 4 + 2] = z; - return multiply(from, matrix); - } - - function parseMatrix(mat) { - if (mat.typ == exports.EnumToken.IdenTokenType) { - return mat.val == "none" ? identity() : null; - } - const children = mat.chi.filter((t) => t.typ == exports.EnumToken.NumberTokenType || t.typ == exports.EnumToken.IdenTokenType); - const values = []; - for (const child of children) { - if (child.typ != exports.EnumToken.NumberTokenType) { - return null; - } - // @ts-ignore - values.push(getNumber(child)); - } - // @ts-ignore - return matrix(values); - } - // use column-major order - function matrix(values) { - const matrix = identity(); - if (values.length === 6) { - matrix[0] = values[0]; - matrix[1] = values[1]; - matrix[4] = values[2]; - matrix[4 + 1] = values[3]; - matrix[3 * 4] = values[4]; - matrix[3 * 4 + 1] = values[5]; - } - else if (values.length === 16) { - matrix[0] = values[0]; - matrix[1] = values[1]; - matrix[2] = values[2]; - matrix[3] = values[3]; - matrix[4] = values[4]; - matrix[4 + 1] = values[5]; - matrix[4 + 2] = values[6]; - matrix[4 + 3] = values[7]; - matrix[2 * 4] = values[8]; - matrix[2 * 4 + 1] = values[9]; - matrix[2 * 4 + 2] = values[10]; - matrix[2 * 4 + 3] = values[11]; - matrix[3 * 4] = values[12]; - matrix[3 * 4 + 1] = values[13]; - matrix[3 * 4 + 2] = values[14]; - matrix[3 * 4 + 3] = values[15]; - } - else { - return null; - } - return matrix; - } - function serialize(matrix) { - matrix = matrix.slice(); - // @ts-ignore - if (eq(matrix, identity())) { - return { - typ: exports.EnumToken.IdenTokenType, - val: "none", - }; - } - if (is2DMatrix(matrix)) { - // https://drafts.csswg.org/css-transforms-2/#two-dimensional-subset - return { - typ: exports.EnumToken.TransformFunctionTokenType, - val: "matrix", - chi: [matrix[0], matrix[1], matrix[4], matrix[4 + 1], matrix[3 * 4], matrix[3 * 4 + 1]].reduce((acc, t) => { - if (acc.length > 0) { - acc.push({ typ: exports.EnumToken.CommaTokenType }); - } - acc.push({ - typ: exports.EnumToken.NumberTokenType, - val: t, - }); - return acc; - }, []), - }; - } - return { - typ: exports.EnumToken.TransformFunctionTokenType, - val: "matrix3d", - chi: matrix.reduce((acc, curr) => { - if (acc.length > 0) { - acc.push({ typ: exports.EnumToken.CommaTokenType }); - } - acc.push({ - typ: exports.EnumToken.NumberTokenType, - val: curr, - }); - return acc; - }, []), - }; - } + exports.ResponseType = void 0; + (function (ResponseType) { + /** + * return text + */ + ResponseType[ResponseType["Text"] = 0] = "Text"; + /** + * return a readable stream + */ + ResponseType[ResponseType["ReadableStream"] = 1] = "ReadableStream"; + /** + * return an arraybuffer + */ + ResponseType[ResponseType["ArrayBuffer"] = 2] = "ArrayBuffer"; + /** + * return a json object + */ + ResponseType[ResponseType["JSON"] = 3] = "JSON"; + })(exports.ResponseType || (exports.ResponseType = {})); - // translate → rotate → skew → scale - function minify$1(matrix) { - const decomposed = decompose(matrix); - if (decomposed == null) { - return null; - } - const transforms = new Set(["translate", "scale", "skew", "perspective", "rotate"]); - const scales = new Set(["x", "y", "z"]); - const skew = new Set(["x", "y"]); - let result = []; - // check identity - if (round(decomposed.translate[0]) == 0 && - round(decomposed.translate[1]) == 0 && - round(decomposed.translate[2]) == 0) { - transforms.delete("translate"); - } - if (round(decomposed.scale[0]) == 1 && round(decomposed.scale[1]) == 1 && round(decomposed.scale[2]) == 1) { - transforms.delete("scale"); - } - if (round(decomposed.skew[0]) == 0 && round(decomposed.skew[1]) == 0) { - transforms.delete("skew"); - } - if (round(decomposed.perspective[2]) == 0) { - transforms.delete("perspective"); - } - if (round(decomposed.rotate[3]) == 0) { - transforms.delete("rotate"); - } - if (transforms.has("translate")) { - let coordinates = new Set(["x", "y", "z"]); - for (let i = 0; i < 3; i++) { - if (round(decomposed.translate[i]) == 0) { - coordinates.delete(i == 0 ? "x" : i == 1 ? "y" : "z"); - } - } - if (coordinates.size == 1) { - if (coordinates.has("x")) { - result.push({ - typ: exports.EnumToken.TransformFunctionTokenType, - val: "translate", - chi: [{ typ: exports.EnumToken.LengthTokenType, val: round(decomposed.translate[0]), unit: "px" }], - }); - } - else { - let axis = coordinates.has("y") ? "y" : "z"; - let index = axis == "y" ? 1 : 2; - result.push({ - typ: exports.EnumToken.TransformFunctionTokenType, - val: "translate" + axis.toUpperCase(), - chi: [{ typ: exports.EnumToken.LengthTokenType, val: round(decomposed.translate[index]), unit: "px" }], - }); - } - } - else if (coordinates.has("z")) { - result.push({ - typ: exports.EnumToken.TransformFunctionTokenType, - val: "translate3d", - chi: [ - decomposed.translate[0] == 0 - ? { - typ: exports.EnumToken.NumberTokenType, - val: 0, - } - : { typ: exports.EnumToken.LengthTokenType, val: round(decomposed.translate[0]), unit: "px" }, - { typ: exports.EnumToken.CommaTokenType }, - decomposed.translate[1] == 0 - ? { - typ: exports.EnumToken.NumberTokenType, - val: 0, - } - : { typ: exports.EnumToken.LengthTokenType, val: round(decomposed.translate[1]), unit: "px" }, - { typ: exports.EnumToken.CommaTokenType }, - { typ: exports.EnumToken.LengthTokenType, val: round(decomposed.translate[2]), unit: "px" }, - ], - }); - } - else { - result.push({ - typ: exports.EnumToken.TransformFunctionTokenType, - val: "translate", - chi: [ - { typ: exports.EnumToken.LengthTokenType, val: round(decomposed.translate[0]), unit: "px" }, - { typ: exports.EnumToken.CommaTokenType }, - { typ: exports.EnumToken.LengthTokenType, val: round(decomposed.translate[1]), unit: "px" }, - ], - }); - } - } - if (transforms.has("rotate")) { - const [x, y, z, angle] = decomposed.rotate; - if (y == 0 && z == 0) { - result.push({ - typ: exports.EnumToken.TransformFunctionTokenType, - val: "rotateX", - chi: [ - { - typ: exports.EnumToken.AngleTokenType, - val: round(angle), - unit: "deg", - }, - ], - }); - } - else if (x == 0 && z == 0) { - result.push({ - typ: exports.EnumToken.TransformFunctionTokenType, - val: "rotateY", - chi: [ - { - typ: exports.EnumToken.AngleTokenType, - val: round(angle), - unit: "deg", - }, - ], - }); - } - else if (x == 0 && y == 0) { - result.push({ - typ: exports.EnumToken.TransformFunctionTokenType, - val: "rotate", - chi: [ - { - typ: exports.EnumToken.AngleTokenType, - val: round(angle), - unit: "deg", - }, - ], - }); - } - else { - result.push({ - typ: exports.EnumToken.TransformFunctionTokenType, - val: "rotate3d", - chi: [ - { - typ: exports.EnumToken.NumberTokenType, - val: round(x), - }, - { typ: exports.EnumToken.CommaTokenType }, - { - typ: exports.EnumToken.NumberTokenType, - val: round(y), - }, - { typ: exports.EnumToken.CommaTokenType }, - { - typ: exports.EnumToken.NumberTokenType, - val: round(z), - }, - { typ: exports.EnumToken.CommaTokenType }, - { - typ: exports.EnumToken.AngleTokenType, - val: round(angle), - unit: "deg", - }, - ], - }); - } - } - if (transforms.has("skew")) { - if (round(decomposed.skew[1]) == 0) { - skew.delete("y"); - } - for (let i = 0; i < 2; i++) { - decomposed.skew[i] = round((Math.atan(decomposed.skew[i]) * 180) / Math.PI); - } - if (skew.size == 1) { - result.push({ - typ: exports.EnumToken.TransformFunctionTokenType, - val: "skew" + (skew.has("x") ? "" : "Y"), - chi: [{ typ: exports.EnumToken.AngleTokenType, val: round(decomposed.skew[0]), unit: "deg" }], - }); + /** + * parse result. process input sourcemap + * @param result + * @param options + * @returns + * @private + */ + function parseResult(result, options) { + if (options.sourcemap != null && options.source.getInputSourceMap() == null) { + if (options.inputSourceMap != null) { + options.source.setInputSourceMap(options.inputSourceMap); } else { - result.push({ - typ: exports.EnumToken.TransformFunctionTokenType, - val: "skew", - chi: [ - { typ: exports.EnumToken.AngleTokenType, val: round(decomposed.skew[0]), unit: "deg" }, - { typ: exports.EnumToken.CommaTokenType }, - { typ: exports.EnumToken.AngleTokenType, val: round(decomposed.skew[1]), unit: "deg" }, - ], - }); - } - } - if (transforms.has("scale")) { - const [sx, sy, sz] = toZero(decomposed.scale); - if (sz == 1) { - scales.delete("z"); - } - if (sy == 1) { - scales.delete("y"); - } - if (sx == 1) { - scales.delete("x"); - } - if (scales.size == 1) { - let prefix = scales.has("x") ? "X" : scales.has("y") ? "Y" : "Z"; - result.push({ - typ: exports.EnumToken.TransformFunctionTokenType, - val: "scale" + prefix, - chi: [{ typ: exports.EnumToken.NumberTokenType, val: round(prefix == "Z" ? sz : prefix == "Y" ? sy : sx) }], - }); + // extract inline source map from the input CSS + const token = result.ast.chi.at(-1); + if (token?.typ == exports.EnumToken.CommentTokenType && + token.val.startsWith("/*# sourceMappingURL=")) { + let data = token.val.slice(21, -2).trim(); + if (data.endsWith(".map")) { + if (options.load == null) { + data = ""; + } + else { + options + .load(options.resolve(data, dirname(options.src)).absolute, ".", exports.ResponseType.JSON) + .catch((error) => console.error({ error })) + .then((res) => { + if (res != null) { + // @ts-expect-error + options.source.setInputSourceMap(res); + } + }); + } + } + else { + options.source.setInputSourceMap(data); + } + } } - else if (!scales.has("z")) { - result.push({ - typ: exports.EnumToken.TransformFunctionTokenType, - val: "scale", - chi: [ - { typ: exports.EnumToken.NumberTokenType, val: round(sx) }, - { typ: exports.EnumToken.CommaTokenType }, - { typ: exports.EnumToken.NumberTokenType, val: round(sy) }, - ], - }); + } + if (options.module) { + const { revMapping, ...res } = result; + return res; + } + return result; + } + /** + * + * @param options + * @param prefix + * @private + */ + function validateSyncArguments(options, prefix = "options.") { + const args = Object.entries(options); + let i; + for (i = 0; i < args.length; i++) { + const [key, value] = args[i]; + if (typeof value == "function") { + if (value instanceof Promise || Object.getPrototypeOf(value).constructor.name == "AsyncFunction") { + throw new Error(`[${prefix + key}]: Async functions are not supported in sync mode. Use parse() or transform() instead.`); + } } - else { - result.push({ - typ: exports.EnumToken.TransformFunctionTokenType, - val: "scale3d", - chi: [ - { typ: exports.EnumToken.NumberTokenType, val: round(sx) }, - { typ: exports.EnumToken.CommaTokenType }, - { typ: exports.EnumToken.NumberTokenType, val: round(sy) }, - { typ: exports.EnumToken.CommaTokenType }, - { typ: exports.EnumToken.NumberTokenType, val: round(sz) }, - ], - }); + else if (value != null && typeof value == "object") { + validateSyncArguments(value, prefix + key + "."); } } - // identity - return result.length == 0 || (result.length == 1 && eqMatrix(identity(), result)) - ? [ - { - typ: exports.EnumToken.IdenTokenType, - val: "none", - }, - ] - : result; } - function eqMatrix(a, b) { - let mat = identity(); - let tmp = identity(); - const data = (Array.isArray(a) || ArrayBuffer.isView(a) ? a : parseMatrix(a)); - for (const transform of b) { - tmp = computeMatrix([transform], identity()); - if (tmp == null) { - return false; + + class Arena { + stringIds = new Map(); + strings = []; + root = 1; + current = 1; + dataTypes; + parents; + nextSibling; + previousSibling; + firstToken; + lastToken; + firstChild; + lastChild; + name; + stringValue; + numericValue; + unit; + flags; + colorType; + /** + * token spans. 8 bits for source type. 28 bits for start position, 28 bits for end position. This allows us to store the start and end positions of tokens in a single 64-bit integer, which is more efficient than storing them separately. + */ + spans; + /** + * error cache + */ + errors = new Map(); + /** + * rendered tokens cache + */ + renderedTokens; + constructor(size = 4096) { + this.dataTypes = new Uint8Array(size); + this.parents = new Uint32Array(size); + this.nextSibling = new Uint32Array(size); + this.previousSibling = new Uint32Array(size); + this.lastToken = new Uint32Array(size); + this.firstToken = new Uint32Array(size); + this.firstChild = new Uint32Array(size); + this.lastChild = new Uint32Array(size); + this.name = new Uint32Array(size); + this.stringValue = new Uint32Array(size); + this.numericValue = new Float64Array(size); + this.unit = new Uint32Array(size); + this.flags = new Uint32Array(size); + // this.left = new Uint32Array(size); + // this.right = new Uint32Array(size); + // this.op = new Uint32Array(size); + // this.op2 = new Uint32Array(size); + this.renderedTokens = new Uint32Array(size); + this.colorType = new Uint8Array(size); + this.spans = new BigUint64Array(size); + this.dataTypes[this.root] = exports.EnumToken.StyleSheetNodeType; + } + allocate() { + const id = ++this.current; + if (this.current >= this.dataTypes.length) { + this.resize(this.dataTypes.length * 2); + } + return id; + } + // Interns a string and returns its unique ID. If the string has already been interned, it returns the existing ID. + internString(str) { + let id = this.stringIds.get(str); + if (id != null) { + return id; + } + id = this.strings.length; + this.stringIds.set(str, id); + this.strings.push(str); + return id; + } + /** + * set node data for a given id. The first argument is the id, the second is the type, the third is the string value, the fourth is the numeric value, and the fifth is the unit. + * @param id + * @param args + */ + set(id, ...args) { + this.dataTypes[id] = args[0]; + if (args.length > 1) { + // 0 is null string, so we can use it to indicate that there is no string value for this node. + this.stringValue[id] = args[1]; + } + if (args.length > 2) { + this.numericValue[id] = args[2]; + } + if (args.length > 3) { + this.unit[id] = args[3]; + } + } + getType(id) { + return this.dataTypes[id]; + } + setType(id, type) { + this.dataTypes[id] = type; + } + setName(id, nameId) { + this.name[id] = nameId; + } + getName(id) { + return this.strings[this.name[id]] ?? null; + } + setValue(id, valueId) { + this.stringValue[id] = valueId; + } + getValue(id) { + return this.strings[this.stringValue[id]] ?? null; + } + setNumericValue(id, numericValue) { + this.numericValue[id] = numericValue; + } + setUnit(id, unit) { + this.unit[id] = unit; + } + getUnit(id) { + return this.strings[this.unit[id]] ?? null; + } + getNumericValue(id) { + return this.numericValue[id]; + } + setParent(id, parentId) { + this.parents[id] = parentId; + } + getFirstChild(id) { + return this.firstChild[id]; + } + setFirstChild(id, firstChildId) { + this.detach(firstChildId); + const firstChild = this.firstChild[id]; + if (this.firstChild[id] != 0) { + const nextSibling = this.nextSibling[firstChild]; + if (nextSibling != 0) { + this.nextSibling[firstChildId] = nextSibling; + this.previousSibling[nextSibling] = firstChildId; + } + this.detach(firstChild); + } + this.firstChild[id] = firstChildId; + this.parents[firstChild] = id; + } + getLastChild(id) { + return this.lastChild[id]; + } + setLastChild(id, lastChildId) { + this.detach(lastChildId); + const lastChild = this.lastChild[id]; + if (lastChild != 0) { + const previousSibling = this.previousSibling[lastChild]; + if (previousSibling != 0) { + this.previousSibling[lastChildId] = previousSibling; + this.nextSibling[previousSibling] = lastChildId; + } + this.detach(lastChild); } - mat = multiply(mat, tmp); + this.lastChild[id] = lastChildId; + this.nextSibling[lastChildId] = 0; + this.parents[lastChildId] = id; } - if (mat == null) { - return false; + setNextSibling(id, nextSiblingId) { + this.detach(nextSiblingId); + this.nextSibling[nextSiblingId] = this.nextSibling[id]; + this.nextSibling[id] = nextSiblingId; + this.previousSibling[nextSiblingId] = id; + this.parents[nextSiblingId] = this.parents[id]; } - for (let i = 0; i < 4; i++) { - for (let j = 0; j < 4; j++) { - if (Math.abs(mat[i * 4 + j] - data[i * 4 + j]) > epsilon) { - return false; + setPreviousSibling(id, previousSiblingId) { + this.detach(previousSiblingId); + if (this.previousSibling[id] != 0) { + const previousSibling = this.previousSibling[id]; + if (previousSibling != 0) { + this.nextSibling[previousSibling] = previousSiblingId; + this.previousSibling[previousSiblingId] = previousSibling; } + this.nextSibling[previousSiblingId] = this.nextSibling[id]; } + this.previousSibling[id] = previousSiblingId; + this.parents[previousSiblingId] = this.parents[id]; } - return true; - } - function minifyTransformFunctions(transform) { - const name = transform.val; - if ("skewX" == name) { - transform.val = "skew"; - return transform; - } - if (!["translate", "translate3d", "scale", "scale3d"].includes(name)) { - return transform; + setFirstToken(id, firstTokenId) { + this.detach(firstTokenId); + const firstToken = this.firstToken[id]; + this.previousSibling[firstTokenId] = 0; + this.nextSibling[firstTokenId] = this.nextSibling[firstTokenId]; + this.parents[firstTokenId] = id; + this.firstToken[id] = firstTokenId; + this.detach(firstToken); } - const values = []; - for (const token of transform.chi) { - if (token.typ == exports.EnumToken.CommentTokenType || - token.typ == exports.EnumToken.WhitespaceTokenType || - token.typ == exports.EnumToken.CommaTokenType) { - continue; - } - if (![ - exports.EnumToken.NumberTokenType, - exports.EnumToken.LengthTokenType, - exports.EnumToken.AngleTokenType, - exports.EnumToken.PercentageTokenType, - ].includes(token.typ)) { - return transform; - } - if (token.typ == exports.EnumToken.PercentageTokenType && - typeof token.val == "number" && - name.startsWith("scale")) { - Object.assign(token, { - typ: exports.EnumToken.NumberTokenType, - val: token.val / 100, - }); - } - values.push(token); + setColorType(id, colorType) { + this.colorType[id] = colorType; } - if (values.length == 0) { - return transform; + setFlags(id, flags) { + this.flags[id] = flags; } - if ((name == "translate" || name == "scale") && values.length > 2) { - return transform; + getFlags(id) { + return this.flags[id]; } - const ignoredValue = name.startsWith("scale") ? 1 : 0; - const t = new Set(["x", "y", "z"]); - for (let i = 0; i < 3; i++) { - const axis = i == 0 ? "x" : i == 1 ? "y" : "z"; - if (values.length <= i || values[i].val == ignoredValue) { - t.delete(axis); - } + hasFlags(id, flag) { + return (this.flags[id] & flag) != 0; } - if (name == "translate3d" || name == "translate") { - if (t.size == 0) { - return { - typ: exports.EnumToken.TransformFunctionTokenType, - val: "translate", - chi: [{ typ: exports.EnumToken.NumberTokenType, val: 0 }], - }; - } - if (t.size == 1) { - return { - typ: exports.EnumToken.TransformFunctionTokenType, - val: "translate" + (t.has("x") ? "" : t.has("y") ? "Y" : "Z"), - chi: [values[t.has("x") ? 0 : t.has("y") ? 1 : 2]], - }; + getChildAt(id, index) { + let child = this.firstChild[id]; + while (index > 0 && child != 0) { + child = this.nextSibling[child]; + index--; } - if (t.size == 2) { - if (t.has("z")) { - return transform; + return child == 0 ? null : child; + } + setChildAt(id, index, child) { + if (index == 0) { + if (child != this.firstChild[id]) { + this.detach(child); + if (this.firstChild[id] != 0) { + this.nextSibling[child] = this.nextSibling[this.firstChild[id]]; + this.previousSibling[this.nextSibling[this.firstChild[id]]] = child; + } + this.firstChild[id] = child; + this.parents[child] = id; } - return { - typ: exports.EnumToken.TransformFunctionTokenType, - val: "translate", - chi: [values[0], { typ: exports.EnumToken.CommaTokenType }, values[1]], - }; + return; } - } - if (name == "scale3d" || name == "scale") { - if (t.size == 0) { - return { - typ: exports.EnumToken.TransformFunctionTokenType, - val: "scale", - chi: [{ typ: exports.EnumToken.NumberTokenType, val: 1 }], - }; + index--; + let currentChild = this.firstChild[id]; + while (index > 0 && this.nextSibling[currentChild] != 0) { + currentChild = this.nextSibling[currentChild]; + index--; } - if (t.size == 1) { - return { - typ: exports.EnumToken.TransformFunctionTokenType, - val: "scale" + (t.has("x") ? "X" : t.has("y") ? "Y" : "Z"), - chi: [values[t.has("x") ? 0 : t.has("y") ? 1 : 2]], - }; + this.detach(child); + if (index > 0) { + // append + this.nextSibling[currentChild] = child; + this.previousSibling[child] = currentChild; + this.nextSibling[child] = 0; } - if (t.size == 2) { - if (t.has("z")) { - return transform; - } - return { - typ: exports.EnumToken.TransformFunctionTokenType, - val: "scale", - chi: [values[0], { typ: exports.EnumToken.CommaTokenType }, values[1]], - }; + else { + // prepend + this.previousSibling[child] = this.previousSibling[currentChild]; + this.nextSibling[child] = this.nextSibling[currentChild]; + this.nextSibling[this.previousSibling[currentChild]] = child; + this.previousSibling[currentChild] = child; + this.previousSibling[currentChild] = 0; + this.nextSibling[currentChild] = 0; + this.parents[currentChild] = 0; } + this.parents[child] = id; } - return transform; - } - - function skewX(x, from) { - const matrix = identity(); - matrix[4] = Math.tan(x); - return multiply(from, matrix); - } - function skewY(y, from) { - const matrix = identity(); - matrix[1] = Math.tan(y); - return multiply(from, matrix); - } - // convert angle to radian - function skew(values, from) { - const matrix = identity(); - matrix[4] = Math.tan(values[0]); - if (values.length > 1) { - matrix[1] = Math.tan(values[1]); + /** + * let child should be of type EnumToken.ListToken or EnumToken.ParensToken + * @param id + * @param child + */ + setLeft(id, child) { + this.setChildAt(id, 0, child); } - return multiply(from, matrix); - } - - function perspective(x, from) { - const matrix = identity(); - // @ts-ignore - matrix[2 * 4 + 3] = typeof x == "object" && x.val == "none" ? 0 : x == 0 ? Number.NEGATIVE_INFINITY : -1 / x; - return multiply(from, matrix); - } - - function compute(transformLists) { - transformLists = transformLists.slice(); - stripCommaToken(transformLists); - let matrix = identity(); - let mat; - let transforms; - const cumulative = []; - // all transform function arguments must be 0 or scale(1) - for (const transform of transformLists) { - switch (transform.val) { - case "rotate": - case "rotateX": - case "rotateY": - case "rotateZ": - case "rotate3d": - for (const child of transform.chi) { - if (child.typ == exports.EnumToken.WhitespaceTokenType || child.typ == exports.EnumToken.CommaTokenType) { - continue; - } - if ((child.typ != exports.EnumToken.AngleTokenType && - child.typ != exports.EnumToken.NumberTokenType && - child.typ != exports.EnumToken.PercentageTokenType) || - // angle >= 360deg, do not transform - Math.abs(getAngle(child)) >= 1) { - return null; - } - } - // break; - } + getLeft(id) { + return this.getChildAt(id, 0); } - for (const transformList of splitTransformList(transformLists)) { - mat = computeMatrix(transformList, identity()); - if (mat == null) { - return null; - } - matrix = multiply(matrix, mat); - transforms = minify$1(mat) ?? transformList; - for (let i = 0; i < transforms.length; i++) { - cumulative.push(transforms[i]); - } + setRight(id, rightId) { + this.setChildAt(id, 1, rightId); } - const serialized = serialize(matrix); - if (cumulative.length > 0) { - for (let i = 0; i < cumulative.length; i++) { - if (cumulative[i].typ == exports.EnumToken.IdenTokenType && cumulative[i].val == "none") { - cumulative.splice(i--, 1); - } + getRight(id) { + return this.getChildAt(id, 1); + } + setOp(id, opId) { + this.setChildAt(id, 2, opId); + } + getOp(id) { + return this.getChildAt(id, 2); + } + setOp2(id, op2Id) { + this.setChildAt(id, 3, op2Id); + } + getOp2(id) { + return this.getChildAt(id, 3); + } + setRenderedTokens(id, renderedTokensId) { + this.renderedTokens[id] = renderedTokensId; + } + setSpan(id, sourceId, start, end) { + this.spans[id] = BigInt(((sourceId & 0xff) << 56) | ((start & 0xfffffff) << 28) | (end & 0xfffffff)); + } + getSourceId(id) { + return Number((this.spans[id] >> 56n) & 0xffn); + } + getSpanStart(id) { + return Number((this.spans[id] >> 28n) & 0xfffffffn); + } + setSpandStart(id, start) { + this.spans[id] = + BigInt((this.getSourceId(id) & 0xff) << 56) | + BigInt((start & 0xfffffff) << 28) | + BigInt(this.getSpanEnd(id) & 0xfffffff); + } + getSpanEnd(id) { + return Number(this.spans[id] & 0xfffffffn); + } + setSpanEnd(id, end) { + this.spans[id] = + BigInt((this.getSourceId(id) & 0xff) << 56) | + BigInt((this.getSpanStart(id) & 0xfffffff) << 28) | + BigInt(end & 0xfffffff); + } + getParent(id) { + return this.parents[id]; + } + resolveString(id) { + return this.strings[id]; + } + detach(id) { + let parent = this.parents[id]; + if (this.lastChild[parent] == id) { + this.lastChild[parent] = this.previousSibling[id]; } - if (cumulative.length == 0) { - cumulative.push({ - typ: exports.EnumToken.IdenTokenType, - val: "none", - }); + if (this.nextSibling[id] != 0) { + // first child + if (this.previousSibling[id] == 0) { + this.firstChild[parent] = this.nextSibling[id]; + } + else { + // not first child + this.nextSibling[this.previousSibling[id]] = this.nextSibling[id]; + } } + this.nextSibling[id] = 0; + this.previousSibling[id] = 0; + this.parents[id] = 0; } - const result = { - matrix: serialize(toZero(matrix)), - cumulative, - minified: minify$1(matrix) ?? [serialized], - }; - return result; - } - function computeMatrix(transformList, matrixVar) { - let values = []; - let val; - let i = 0; - for (; i < transformList.length; i++) { - if (transformList[i].typ != exports.EnumToken.TransformFunctionTokenType || - !transformFunctions.includes(transformList[i].val)) { - return null; - } - switch (transformList[i].val) { - case "translate": - case "translateX": - case "translateY": - case "translateZ": - case "translate3d": - { - values.length = 0; - const children = stripCommaToken(transformList[i].chi.slice()); - const valCount = transformList[i].val == "translate3d" - ? 3 - : transformList[i].val == "translate" - ? 2 - : 1; - for (let j = 0; j < children.length; j++) { - if (children[j].typ == exports.EnumToken.WhitespaceTokenType) { - continue; - } - val = length2Px(children[j]); - if (val == null) { - return null; - } - values.push(val); - } - if (values.length == 0 || values.length > valCount) { - return null; - } - if (transformList[i].val == "translateX") { - matrixVar = translateX(values[0], matrixVar); - } - else if (transformList[i].val == "translateY") { - matrixVar = translateY(values[0], matrixVar); - } - else if (transformList[i].val == "translateZ") { - matrixVar = translateZ(values[0], matrixVar); - } - else if (transformList[i].val == "translate") { - matrixVar = translate(values, matrixVar); - } - else { - // @ts-ignore - matrixVar = translate3d(values, matrixVar); - } - } - break; - case "rotate": - case "rotateX": - case "rotateY": - case "rotateZ": - case "rotate3d": - { - let x = 0; - let y = 0; - let z = 0; - let angle; - let values = []; - let valuesCount = transformList[i].val == "rotate3d" ? 4 : 1; - for (const child of stripCommaToken(transformList[i].chi.slice())) { - values.push(child); - if (transformList[i].val == "rotateX") { - x = 1; - } - else if (transformList[i].val == "rotateY") { - y = 1; - } - else if (transformList[i].val == "rotate" || - transformList[i].val == "rotateZ") { - z = 1; - } - } - if (values.length != valuesCount) { - return null; - } - if (transformList[i].val == "rotate3d") { - x = getNumber(values[0]); - y = getNumber(values[1]); - z = getNumber(values[2]); - } - angle = getAngle(values.at(-1)); - if ([x, y, z, angle].some((t) => typeof t != "number" || Number.isNaN(+t))) { - return null; - } - if (transformList[i].val == "rotate" || - transformList[i].val == "rotateZ") { - matrixVar = rotate(angle * 2 * Math.PI, matrixVar); - } - else { - matrixVar = rotate3D(angle * 2 * Math.PI, x, y, z, matrixVar); - } - } - break; - case "scale": - case "scaleX": - case "scaleY": - case "scaleZ": - case "scale3d": - { - values.length = 0; - let child; - const children = stripCommaToken(transformList[i].chi.slice()); - for (let k = 0; k < children.length; k++) { - child = children[k]; - if (child.typ != exports.EnumToken.NumberTokenType && child.typ != exports.EnumToken.PercentageTokenType) { - return null; - } - values.push(getNumber(child)); - } - if (transformList[i].val == "scale3d") { - if (values.length != 3) { - return null; - } - matrixVar = scale3d(values[0], values[1], values[2], matrixVar); - break; + *walk(node, filter, reverse) { + const parents = [node]; + let options = filter; + let isNumeric = false; + let children = true; + let attributes = false; + let i = 0; + let firstChild; + if (options != null && typeof options == "object") { + filter = options.filter; + reverse = options.reverse; + attributes = options.attributes; + children = options.children ?? true; + } + while ((node = parents[i++])) { + let option = null; + if (filter != null) { + // @ts-expect-error + option = filter(node); + isNumeric = typeof option == "number"; + if (isNumeric) { + if (option & exports.WalkerOptionEnum.Ignore) { + continue; } - if (transformList[i].val == "scale") { - if (values.length != 1 && values.length != 2) { - return null; - } - matrixVar = scale(values[0], values[1] ?? values[0], matrixVar); + if (option & exports.WalkerOptionEnum.Stop) { break; } - if (values.length != 1) { - return null; - } - else if (transformList[i].val == "scaleX") { - matrixVar = scaleX(values[0], matrixVar); - } - else if (transformList[i].val == "scaleY") { - matrixVar = scaleY(values[0], matrixVar); - } - else if (transformList[i].val == "scaleZ") { - matrixVar = scaleZ(values[0], matrixVar); - } } - break; - case "skew": - case "skewX": - case "skewY": - { - values.length = 0; - let child; - let value; - for (let k = 0; k < transformList[i].chi.length; k++) { - child = transformList[i].chi[k]; - if (child.typ == exports.EnumToken.CommentTokenType || - child.typ == exports.EnumToken.CommaTokenType || - child.typ == exports.EnumToken.WhitespaceTokenType) { - continue; - } - value = getAngle(child); - values.push(value * 2 * Math.PI); - } - if (values.length == 0 || - values.length > (transformList[i].val == "skew" ? 2 : 1)) { - return null; - } - if (transformList[i].val == "skew") { - matrixVar = skew(values, matrixVar); - } - else { - matrixVar = - transformList[i].val == "skewX" - ? skewX(values[0], matrixVar) - : skewY(values[0], matrixVar); - } + } + if (!isNumeric || (option & exports.WalkerOptionEnum.Children) === 0) { + yield node; + } + if (attributes) { + firstChild = + this.flags[node] & exports.EnumAstNodeStatus.HasValues ? this.firstChild[node] : this.firstToken[node]; + if (firstChild != 0) { + const siblings = this.siblings(firstChild); + parents.splice(i, 0, ...(reverse ? siblings.toReversed() : siblings)); } - break; - case "perspective": - { - const values = []; - let child; - let value; - for (let k = 0; k < transformList[i].chi.length; k++) { - child = transformList[i].chi[k]; - if (child.typ == exports.EnumToken.CommentTokenType || child.typ == exports.EnumToken.WhitespaceTokenType) { - continue; - } - if (child.typ == exports.EnumToken.IdenTokenType && child.val == "none") { - values.push(child); - continue; - } - value = length2Px(child); - if (value == null) { - return null; - } - values.push(value); + // else if (this.flags[node] & EnumAstNodeStatus.HasValues) { + // // @ts-expect-error + // for (const val of node.val) { + // if (val[PARENT] != node) { + // val[PARENT] = node; + // } + // } + // // @ts-expect-error + // parents.splice(i, 0, ...(reverse ? node.val.toReversed() : node.val)); + // } + } + if (children && + this.flags[node] & exports.EnumAstNodeStatus.HasChildren && + (!isNumeric || (option & exports.WalkerOptionEnum.IgnoreChildren) === 0)) { + firstChild = this.firstChild[node]; + if (firstChild != 0) { + const siblings = this.siblings(firstChild); + parents.splice(i, 0, ...(reverse ? siblings.toReversed() : siblings)); + } + // // @ts-expect-error + // parents.splice(i, 0, ...(reverse ? node.chi!.toReversed() : node.chi)); + // for (const child of (node).chi) { + // if (child[PARENT] != node) { + // child[PARENT] = node; + // } + // } + } + } + } + *walkValues(values, filter, reverse) { + const stack = values.slice(); + if (filter != null && typeof filter == "function") { + filter = { + event: exports.WalkerEvent.Enter, + fn: filter, + }; + } + else if (filter == null) { + filter = { + event: exports.WalkerEvent.Enter, + }; + } + let isNumeric = false; + let value; + let option; + let i = -1; + const eventType = filter.event ?? exports.WalkerEvent.Enter; + // @ts-ignore + // const result = { + // value: null, + // parent: null, + // previousValue: null, + // nextValue: null, + // // + // root: root ?? null, + // parents: function* () { + // // @ts-ignore + // let result = node?.[PARENT] ?? root; + // while (result != null) { + // yield result; + // result = result[PARENT] ?? null; + // } + // }, + // } as WalkAttributesResult; + while (++i < stack.length) { + value = stack[i]; + option = null; + if (filter.fn != null && eventType & exports.WalkerEvent.Enter) { + const isValid = filter.type == null || + this.dataTypes[value] == filter.type || + (Array.isArray(filter.type) && filter.type.includes(this.dataTypes[value])) || + (typeof filter.type == "function" && filter.type(value)); + if (isValid) { + option = filter.fn(value, exports.WalkerEvent.Enter); + isNumeric = typeof option == "number"; + if (isNumeric && option & exports.WalkerOptionEnum.Stop) { + return; + } + if (isNumeric && option & exports.WalkerOptionEnum.Ignore) { + continue; } - if (values.length != 1) { - return null; + // + if (option != null && Array.isArray(option)) { + // const op = Array.isArray(option) ? option : [option]; + stack.splice(i, 0, ...(reverse ? option.toReversed() : option)); } - matrixVar = perspective(values[0], matrixVar); } - break; - case "matrix3d": - case "matrix": - { - const values = []; - let value; - for (const token of transformList[i].chi) { - if ([ - exports.EnumToken.WhitespaceTokenType, - exports.EnumToken.CommentTokenType, - exports.EnumToken.CommaTokenType, - ].includes(token.typ)) { - continue; - } - value = getNumber(token); - if (value == null) { - return null; + } + yield value; + if (!isNumeric || (option & exports.WalkerOptionEnum.IgnoreChildren) === 0) { + if (this.flags[value] & exports.EnumAstNodeStatus.HasChildren) { + for (const child of this.children(value)) { + if (reverse) { + stack.unshift(child); } - values.push(value); - } - if (transformList[i].val == "matrix") { - if (values.length != 6) { - return null; + else { + stack.push(child); } } - else if (values.length != 16) { - return null; + } + // else { + // const values: Token[] = []; + // if ("l" in value && value.l != null) { + // // @ts-expect-error + // values.push(value.l); + // // @ts-expect-error + // if (value.l[PARENT] != value) { + // // @ts-expect-error + // value.l[PARENT] = value; + // } + // } + // if ("op" in value && typeof value.op == "object") { + // // @ts-expect-error + // values.push(value.op); + // // @ts-expect-error + // if (value.op[PARENT] != value) { + // // @ts-expect-error + // value.op[PARENT] = value; + // } + // } + // if ("r" in value && value.r != null) { + // if (Array.isArray(value.r)) { + // for (const r of value.r) { + // // + // values.push(r); + // if (r[PARENT] != value) { + // // + // r[PARENT] = value; + // } + // } + // } else { + // // + // values.push(value.r); + // if (value.r[PARENT] != value) { + // // + // value.r[PARENT] = value; + // } + // } + // } + // if (values.length > 0) { + // for (const v of values) { + // if (reverse) { + // stack.unshift(v); + // } else { + // stack.push(v); + // } + // } + // } + // } + } + if (eventType & exports.WalkerEvent.Leave && filter.fn != null) { + const isValid = filter.type == null || + this.dataTypes[value] == filter.type || + (Array.isArray(filter.type) && filter.type.includes(this.dataTypes[value])) || + (typeof filter.type == "function" && filter.type(value)); + if (isValid) { + // @ts-ignore + option = filter.fn(value, value[PARENT], exports.WalkerEvent.Leave); + // @ts-ignore + if (option != null && Array.isArray(option)) { + // const op = Array.isArray(option) ? option : [option]; + stack.splice(i, 0, ...(reverse ? option.toReversed() : option)); + // for (const o of op) { + // if (o[PARENT] != value) { + // o[PARENT] = value; + // } + // } } - matrixVar = multiply(matrixVar, matrix(values)); } - break; - default: - return null; + } } } - return matrixVar; - } - function splitTransformList(transformList) { - let pattern = null; - const tokens = []; - for (let i = 0; i < transformList.length; i++) { - if (transformList[i].typ == exports.EnumToken.CommentTokenType || - transformList[i].typ == exports.EnumToken.WhitespaceTokenType) { - continue; - } - if (pattern == null || - (transformList[i].typ == exports.EnumToken.TransformFunctionTokenType && - !transformList[i].val.startsWith(pattern))) { - if (transformList[i].typ == exports.EnumToken.TransformFunctionTokenType) { - if (transformList[i].val.startsWith("scale")) { - pattern = "scale"; - } - else if (transformList[i].val.startsWith("rotate")) { - pattern = "rotate"; - } - else if (transformList[i].val.startsWith("translate")) { - pattern = "translate"; - } - else { - pattern = null; - } - tokens.push([transformList[i]]); - continue; + findChild(id, predicate) { + let child = this.firstChild[id]; + while (child != 0) { + if (predicate(child)) { + return child; } + child = this.nextSibling[child]; } - if (pattern != null && - transformList[i].typ == exports.EnumToken.TransformFunctionTokenType && - transformList[i].val.startsWith(pattern)) { - tokens[tokens.length - 1].push(transformList[i]); - continue; + return null; + } + siblings(id) { + let siblings = []; + let child = this.firstChild[this.parents[id]]; + while (child != 0) { + siblings.push(child); + child = this.nextSibling[child]; } - tokens.push([transformList[i]]); + return siblings; } - return tokens; - } - - class TransformCssFeature { - accept = new Set([ - exports.EnumToken.RuleNodeType, - exports.EnumToken.AtRuleNodeType, - exports.EnumToken.KeyframesRuleNodeType, - ]); - get ordering() { - return 4; + children(id) { + let children = []; + let child = this.firstChild[id]; + while (child != 0) { + children.push(child); + child = this.nextSibling[child]; + } + return children; } - get processMode() { - return exports.FeatureWalkMode.Post; + setChildren(id, ...children) { + if (this.lastChild[id] != 0) { + this.lastChild[id] = 0; + } + if (this.firstChild[id] != 0) { + this.detach(this.firstChild[id]); + } + this.appendChildren(id, ...children); } - static register(options) { - // @ts-ignore - if (options.computeTransform) { - // @ts-ignore - options.features.push(new TransformCssFeature()); + setTokens(id, ...children) { + if (this.lastToken[id] != 0) { + this.lastToken[id] = 0; + } + if (this.firstToken[id] != 0) { + this.detach(this.firstToken[id]); } + this.appendTokens(id, ...children); } - run(ast) { - if (ast.chi == null) { - return null; + appendChildren(id, ...children) { + if (children.length == 0) { + return; } - let i = 0; - let node; - // @ts-ignore - for (; i < ast.chi.length; i++) { - // @ts-ignore - node = ast.chi[i]; - if (node[STATE] & (exports.EnumAstNodeStatus.Invalid | exports.EnumAstNodeStatus.ValidationFailed)) { - continue; - } - if (node.typ != exports.EnumToken.DeclarationNodeType || - !node.nam.match(/^(-[a-z]+-)?transform$/)) { - continue; - } - const children = []; - for (const child of node.val) { - children.push(child.typ == exports.EnumToken.TransformFunctionTokenType - ? minifyTransformFunctions(child) - : child); - } - let { matrix, cumulative, minified } = compute(children) ?? { - matrix: null, - cumulative: null, - minified: null, - }; - if (matrix == null || cumulative == null || minified == null) { - node.val = children.map((t) => t.typ == exports.EnumToken.TransformFunctionTokenType ? minifyTransformFunctions(t) : t); - continue; - } - let r = [filterValues(children)]; - if (eqMatrix(matrix, cumulative)) { - r.push(cumulative); - } - if (eqMatrix(matrix, minified)) { - r.push(minified); - } - const l = renderValue(matrix).length; - node.val = r.reduce((acc, curr) => { - if (curr.reduce((acc, t) => acc + renderValue(t), "").length < l) { - return curr; - } - return acc; - }, [matrix]); + this.detach(children[0]); + let lastChild = this.lastChild[id]; + if (lastChild == 0) { + this.firstChild[id] = children[0]; } - return null; + else { + this.nextSibling[lastChild] = children[0]; + } + this.parents[children[0]] = id; + for (let i = 1; i < children.length - 1; i++) { + this.detach(children[i]); + this.previousSibling[children[i]] = children[i - 1]; + this.nextSibling[children[i - 1]] = children[i]; + this.parents[children[i]] = id; + } + this.lastChild[id] = children[children.length - 1]; + } + appendTokens(id, ...children) { + if (children.length == 0) { + return; + } + this.detach(children[0]); + let lastChild = this.lastToken[id]; + if (lastChild == 0) { + this.firstToken[id] = children[0]; + } + else { + this.nextSibling[lastChild] = children[0]; + } + this.parents[children[0]] = id; + for (let i = 1; i < children.length - 1; i++) { + this.detach(children[i]); + this.previousSibling[children[i]] = children[i - 1]; + this.nextSibling[children[i - 1]] = children[i]; + this.parents[children[i]] = id; + } + this.lastToken[id] = children[children.length - 1]; + } + equalsIgnoreCase(node, key, string) { + const stringId = this.stringIds.get(string); + if (stringId == null) { + return false; + } + const id = key == "nam" ? this.name[node] : this.stringValue[node]; + if (id == stringId) { + return true; + } + if (string.length != this.strings[id].length) { + return false; + } + return equalsIgnoreCase(this.strings[id], string); + } + startsWith(node, key, string) { + const stringId = this.stringIds.get(string); + if (stringId == null) { + return false; + } + const id = key == "nam" ? this.name[node] : this.stringValue[node]; + if (id == stringId) { + return true; + } + if (string.length != this.strings[id].length) { + return false; + } + return this.strings[id].startsWith(string); + } + resize(newSize) { + if (newSize <= this.dataTypes.length) { + return; + } + const newDataTypes = new Uint8Array(newSize); + newDataTypes.set(this.dataTypes); + this.dataTypes = newDataTypes; + const newParents = new Uint32Array(newSize); + newParents.set(this.parents); + this.parents = newParents; + const newNextSibling = new Uint32Array(newSize); + newNextSibling.set(this.nextSibling); + this.nextSibling = newNextSibling; + const newPreviousSibling = new Uint32Array(newSize); + newPreviousSibling.set(this.previousSibling); + this.previousSibling = newPreviousSibling; + const newFirstToken = new Uint32Array(newSize); + newFirstToken.set(this.firstToken); + this.firstToken = newFirstToken; + const newLastToken = new Uint32Array(newSize); + newLastToken.set(this.lastToken); + this.lastToken = newLastToken; + const newFirstChild = new Uint32Array(newSize); + newFirstChild.set(this.firstChild); + this.firstChild = newFirstChild; + const newLastChild = new Uint32Array(newSize); + newLastChild.set(this.lastChild); + this.lastChild = newLastChild; + const newName = new Uint32Array(newSize); + newName.set(this.name); + this.name = newName; + const newStringValue = new Uint32Array(newSize); + newStringValue.set(this.stringValue); + this.stringValue = newStringValue; + const newNumericValue = new Float64Array(newSize); + newNumericValue.set(this.numericValue); + this.numericValue = newNumericValue; + const newUnit = new Uint32Array(newSize); + newUnit.set(this.unit); + this.unit = newUnit; + const newFlags = new Uint32Array(newSize); + newFlags.set(this.flags); + this.flags = newFlags; + const newRenderedTokens = new Uint32Array(newSize); + newRenderedTokens.set(this.renderedTokens); + this.renderedTokens = newRenderedTokens; + const newColorType = new Uint8Array(newSize); + newColorType.set(this.colorType); + this.colorType = newColorType; + const newSpans = new BigUint64Array(newSize); + newSpans.set(this.spans); + this.spans = newSpans; + } + setErrors(id, errors) { + this.errors.set(id, errors); + } + getErrors(id) { + return this.errors.get(id) ?? null; } } @@ -22699,5827 +21991,8362 @@ // @ts-ignore return { node, value: { node: value, parent, root: rootNode, parents } }; } - } - } - return null; - } - /** - * Search the ast sub-tree and return all matches - * - * ```ts - * // find the first ast declaration node which name is 'aspect-ratio' - * import { findAll, EnumToken, transform } from "@tbela99/css-parser"; - * import type { AstNode } from "@tbela99/css-parser"; - - const css = ` - - button { - aspect-ratio: 1; - width: if(media(any-pointer: fine): 30px; else: 44px); - } - button-small { - aspect-ratio: 1; - width: if(media(any-pointer: fine): 20px; else: 30px); - } - `; - - const nodeMatcher = (node: AstNode) => - return node.typ == EnumToken.DeclarationNodeType && (node as AstDeclaration).nam == 'aspect-ratio'; - - const result = await transform(css); - const nodes = findAll(result.ast, nodeMatcher); - - console.log({nodes}); - - ``` - * - * @param ast - * @param matcher - * @returns - */ - function findAll(ast, matcher) { - const result = []; - for (const { node, parent } of walk(ast)) { - if (matcher(node, parent)) { - result.push(node); - } - } - return result; - } - /** - * Search the ast sub-tree and return the last match. - * - * ```ts - * // find the first ast declaration node which name is 'aspect-ratio' - import { findLast, EnumToken, transform } from "@tbela99/css-parser"; - import type { AstNode } from "@tbela99/css-parser"; - - * const css = ` - - button { - aspect-ratio: 1; - width: if(media(any-pointer: fine): 30px; else: 44px); - } - `; - - const nodeMatcher = (node: AstNode) => - return node.typ == EnumToken.DeclarationNodeType && (node as AstDeclaration).nam == 'aspect-ratio'; - - const result = await transform(css); - const node = findLast(result.ast, nodeMatcher); - - console.log({node}); - - ``` - * - * @param ast - * @param matcher - * @returns - */ - function findLast(ast, matcher) { - for (const { node, parent } of walk(ast, null, true)) { - if (matcher(node, parent)) { - return node; - } - } - return null; - } - - const nodeMatcher = (value) => value.typ === exports.EnumToken.IfConditionTokenType || - (value.typ === exports.EnumToken.WildCardFunctionTokenType && equalsIgnoreCase("if", value.val)); - function substituteIfElseNode(declaration, node, wrapper, parentWrapper, cache) { - const result = []; - let nodeMap = new Map(); - let clonedDeclaration; - let targetParentWrapper = parentWrapper; - let targetWrapper = wrapper; - if (node.typ === exports.EnumToken.IfElseConditionTokenType) { - // - clonedDeclaration = cloneNode(declaration, true, nodeMap); - // replace else: ... with the actual value - if (node.r.typ === exports.EnumToken.IfConditionTokenType) { - const target = node.r.l.find((t) => t.typ != exports.EnumToken.CommentTokenType && t.typ != exports.EnumToken.WhitespaceTokenType); - // - if (target.typ === exports.EnumToken.IdenTokenType && equalsIgnoreCase("else", target.val)) { - replaceNodeOrValue(nodeMap.get(targetParentWrapper), nodeMap.get(targetWrapper), node.r.r.at(-1)?.typ === - exports.EnumToken.SemiColonTokenType - ? trimArray(node.r.r.slice(0, -1)) - : node.r.r); - // @ts-expect-error - if (targetParentWrapper.typ != exports.EnumToken.DeclarationNodeType) { - let index = targetParentWrapper.chi.indexOf(targetWrapper); - if (index != -1) { - let i; - let k; - let siblingWrapper; - let left = node.l.l.find((t) => t.typ != exports.EnumToken.CommentTokenType && t.typ != exports.EnumToken.WhitespaceTokenType); - for (i = index + 1; i < targetParentWrapper.chi.length; i++) { - if (targetParentWrapper.chi[i].typ === targetWrapper.typ) { - siblingWrapper = targetParentWrapper.chi[i]; - for (k = 0; k < siblingWrapper.chi.length; k++) { - if (siblingWrapper.chi[k].typ === exports.EnumToken.IfElseConditionTokenType) { - let leftSide = siblingWrapper.chi[k].l.l.find((t) => t.typ != exports.EnumToken.CommentTokenType && - t.typ != exports.EnumToken.WhitespaceTokenType); - if (eq(left, leftSide)) { - replaceNodeOrValue(nodeMap.get(targetParentWrapper), nodeMap.get(targetParentWrapper.chi[i]), siblingWrapper.chi[k] - .r.r.at(-1)?.typ === exports.EnumToken.SemiColonTokenType - ? trimArray(siblingWrapper.chi[k] - .r.r.slice(0, -1)) - : siblingWrapper.chi[k] - .r.r); - // @ts-ignore - cache.add(siblingWrapper.chi[k].l); + } + } + return null; + } + /** + * Search the ast sub-tree and return all matches + * + * ```ts + * // find the first ast declaration node which name is 'aspect-ratio' + * import { findAll, EnumToken, transform } from "@tbela99/css-parser"; + * import type { AstNode } from "@tbela99/css-parser"; + + const css = ` + + button { + aspect-ratio: 1; + width: if(media(any-pointer: fine): 30px; else: 44px); + } + button-small { + aspect-ratio: 1; + width: if(media(any-pointer: fine): 20px; else: 30px); + } + `; + + const nodeMatcher = (node: AstNode) => + return node.typ == EnumToken.DeclarationNodeType && (node as AstDeclaration).nam == 'aspect-ratio'; + + const result = await transform(css); + const nodes = findAll(result.ast, nodeMatcher); + + console.log({nodes}); + + ``` + * + * @param ast + * @param matcher + * @returns + */ + function findAll(ast, matcher) { + const result = []; + for (const { node, parent } of walk(ast)) { + if (matcher(node, parent)) { + result.push(node); + } + } + return result; + } + /** + * Search the ast sub-tree and return the last match. + * + * ```ts + * // find the first ast declaration node which name is 'aspect-ratio' + import { findLast, EnumToken, transform } from "@tbela99/css-parser"; + import type { AstNode } from "@tbela99/css-parser"; + + * const css = ` + + button { + aspect-ratio: 1; + width: if(media(any-pointer: fine): 30px; else: 44px); + } + `; + + const nodeMatcher = (node: AstNode) => + return node.typ == EnumToken.DeclarationNodeType && (node as AstDeclaration).nam == 'aspect-ratio'; + + const result = await transform(css); + const node = findLast(result.ast, nodeMatcher); + + console.log({node}); + + ``` + * + * @param ast + * @param matcher + * @returns + */ + function findLast(ast, matcher) { + for (const { node, parent } of walk(ast, null, true)) { + if (matcher(node, parent)) { + return node; + } + } + return null; + } + + const trimTokenSpace = new Set([ + exports.EnumToken.CommaTokenType, + exports.EnumToken.DelimTokenType, + exports.EnumToken.GtTokenType, + exports.EnumToken.GteTokenType, + exports.EnumToken.LtTokenType, + exports.EnumToken.LteTokenType, + exports.EnumToken.ChildCombinatorTokenType, + ]); + const trimSpaceAfter = new Set([ + ...tokensfuncSet.values(), + exports.EnumToken.ChildCombinatorTokenType, + exports.EnumToken.SupportsQueryConditionTokenType, + exports.EnumToken.SupportsQueryUnaryConditionTokenType, + ]); + /** + * Replace token in its parent node + * @param parent + * @param node + * @param replacement + * @throws TypeError replacement is null + * @throws ReferenceError node not found in parent + */ + function replaceNodeOrValue(parent, node, replacement) { + if (replacement == null || (Array.isArray(replacement) && replacement.length === 0)) { + throw new TypeError(`replacement is null`); + } + for (const newNode of Array.isArray(replacement) ? replacement : [replacement]) { + if (newNode[PARENT] != node[PARENT]) { + newNode[PARENT] = node[PARENT]; + } + } + if (parent.typ == exports.EnumToken.BinaryExpressionTokenType) { + if (parent.l == node) { + parent.l = replacement; + } + else if (parent.r == node) { + parent.r = replacement; + } + else { + throw new ReferenceError("Node not found"); + } + } + else { + const target = "val" in parent && Array.isArray(parent.val) + ? parent.val + : (parent.chi ?? parent); + if (Array.isArray(target)) { + // @ts-ignore + const index = target.indexOf(node); + if (index == -1) { + throw new ReferenceError("Node not found"); + } + target.splice(index, 1, ...(Array.isArray(replacement) ? replacement : [replacement])); + } + else if ("l" in target && target.l == node) { + target.l = replacement; + } + else if ("r" in target && target.r == node) { + target.r = replacement; + } + else { + throw new ReferenceError("Node not found"); + } + } + node[PARENT] = null; + return true; + } + function trimWhiteSpaceTokens(tokens) { + let i = 0; + if (tokens[0]?.typ === exports.EnumToken.WhitespaceTokenType) { + tokens.shift(); + } + if (tokens[tokens.length - 1]?.typ === exports.EnumToken.WhitespaceTokenType) { + tokens.pop(); + } + if (tokens.length === 1) { + if (tokens[0].typ === exports.EnumToken.WhitespaceTokenType) { + tokens.length = 0; + } + return tokens; + } + for (; i < tokens.length; i++) { + if (trimSpaceAfter.has(tokens[i].typ) && tokens[i + 1]?.typ === exports.EnumToken.WhitespaceTokenType) { + tokens.splice(i + 1, 1); + } + else if (trimTokenSpace.has(tokens[i].typ)) { + if (tokens[i + 1]?.typ === exports.EnumToken.WhitespaceTokenType) { + tokens.splice(i + 1, 1); + } + if (i > 0 && tokens[i - 1].typ === exports.EnumToken.Whitespace) { + tokens.splice(--i, 1); + } + } + } + return tokens; + } + + /** + * + * @param node + * @param key + * @returns + */ + function getNodeProperty(node, key) { + switch (key) { + case "parent": + return node[PARENT]; + case "location": + return node[LOCSRCID] == null && node[LOCSTA] == null && node[LOCEND] == null ? null : { srcId: node[LOCSRCID], sta: node[LOCSTA], end: node[LOCEND] }; + case "state": + return node[STATE]; + case "errors": + return node[ERRORS]; + case "tokens": + return node[TOKENS]; + } + return undefined; + } + /** + * + * @param node + * @param key + * @param value + */ + function setNodeProperty(node, key, value) { + switch (key) { + case "parent": + node[PARENT] = value; + break; + case "location": + node[LOCSRCID] = value.srcId; + node[LOCSTA] = value.sta; + node[LOCEND] = value.end; + break; + case "state": + node[STATE] = value; + break; + case "errors": + node[ERRORS] = value; + break; + case "tokens": + node[TOKENS] = value; + break; + } + } + + var version = "2.0.0-alpha.1"; + + /** + * render ast token + * @param token + * @param options + * @param cache + * @param reducer + * @param errors + * @private + */ + function printValue$1(token, arena, options, cache = Object.create(null), reducer, errors) { + const typ = arena.getType(token); + // console.error(EnumToken[typ]); + if (typ === exports.EnumToken.WhenElseFunctionTokenType && arena.equalsIgnoreCase(token, "val", "supports")) { + options = { ...options, minifyValues: false, minify: false, convertColor: false }; + reducer = null; + } + options.minifyValues ??= options.minify; + if (reducer == null) { + reducer = function (acc, curr) { + if (arena.getType(curr) == exports.EnumToken.CommentTokenType && options.removeComments) { + if (!options.preserveLicense || !arena.startsWith(curr, "val", "/*!")) { + return acc; + } + return acc + arena.getValue(curr); + } + return acc + printValue$1(curr, arena, options, cache, reducer, errors); + }; + } + switch (typ) { + case exports.EnumToken.UrlFunctionTokenDefType: + case exports.EnumToken.FunctionTokenDefType: + case exports.EnumToken.MathFunctionTokenDefType: + case exports.EnumToken.ImageFunctionTokenDefType: + case exports.EnumToken.ColorFunctionTokenDefType: + case exports.EnumToken.TimingFunctionTokenDefType: + case exports.EnumToken.WhenElseFunctionTokenDefType: + case exports.EnumToken.SupportsFunctionTokenDefType: + case exports.EnumToken.TimelineFunctionTokenDefType: + case exports.EnumToken.GridTemplateFuncTokenDefType: + case exports.EnumToken.TransformFunctionTokenDefType: + case exports.EnumToken.ContainerFunctionTokenDefType: + case exports.EnumToken.PseudoClassFunctionTokenDefType: + case exports.EnumToken.GeneralEnclosedFunctionTokenDefType: + case exports.EnumToken.CustomFunctionTokenDefType: + case exports.EnumToken.WildCardFunctionTokenDefType: + return arena.getValue(token) + "("; + case exports.EnumToken.ListTokenType: + return arena + .children(token) + .reduce((acc, curr) => acc + printValue$1(curr, arena, options, cache), ""); + case exports.EnumToken.BinaryExpressionTokenType: + if (exports.EnumToken.Mul === token.op || + exports.EnumToken.Div === token.op) { + let result = ""; + if (token.l.typ == exports.EnumToken.BinaryExpressionTokenType && + [exports.EnumToken.Add, exports.EnumToken.Sub].includes(token.l.op)) { + result = "(" + printValue$1(token.l, arena, options, cache) + ")"; + } + else { + result = printValue$1(token.l, options, cache); + } + result += token.op == exports.EnumToken.Mul ? "*" : "/"; + if (token.r.typ == exports.EnumToken.BinaryExpressionTokenType && + [exports.EnumToken.Add, exports.EnumToken.Sub].includes(token.r.op)) { + result += "(" + printValue$1(token.r, options, cache) + ")"; + } + else { + result += printValue$1(token.r, options, cache); + } + return result; + } + return (printValue$1(token.l, options, cache) + + (token.op == exports.EnumToken.Add ? " + " : " - ") + + printValue$1(token.r, options, cache)); + case exports.EnumToken.FractionTokenType: + const fraction = printValue$1(token.l) + "/" + printValue$1(token.r); + if (+token.r.val != 0) { + const value = minifyNumber(+token.l.val / +token.r.val); + if (value.length <= fraction.length) { + return value; + } + } + return fraction; + case exports.EnumToken.Add: + return " + "; + case exports.EnumToken.Sub: + return " - "; + case exports.EnumToken.UniversalSelectorTokenType: + case exports.EnumToken.Star: + case exports.EnumToken.Mul: + return "*"; + case exports.EnumToken.Div: + return "/"; + case exports.EnumToken.WrappedValuesTokenType: + return ("{" + + token.chi.reduce((acc, curr) => acc + printValue$1(curr, options, cache), "") + + "}"); + case exports.EnumToken.ColorTokenType: + if (token.kin == exports.ColorType.LIGHT_DARK || + ("chi" in token && options.convertColor === false && token.chi.length == 2)) { + return (token.val + + "(" + + token.chi.reduce((acc, curr) => { + if (curr.typ === exports.EnumToken.IdenTokenType) { + parseColor(curr); + } + return acc + printValue$1(curr, options, cache); + }, "") + + ")"); + } + if (options.convertColor !== false) { + const value = convertColor(token, typeof options.convertColor === "boolean" + ? exports.ColorType.HEX + : (exports.ColorType[exports.ColorType[options.convertColor ?? "HEX"] + ?.toUpperCase?.() + .replaceAll?.("-", "_")] ?? exports.ColorType.HEX)); + if (value != null) { + token = value; + } + if (token.kin == exports.ColorType.HEX || token.kin == exports.ColorType.LIT) { + return reduceHexValue(token.val); + } + } + if (token.kin === exports.ColorType.SYS || + token.kin === exports.ColorType.DPSYS || + token.kin === exports.ColorType.NON_STD) { + return token.val; + } + if (token.kin == exports.ColorType.HEX || token.kin == exports.ColorType.LIT) { + return token.val; + } + if (Array.isArray(token.chi)) { + const fnName = token.val; + const isLegacy = ["rgb", "rgba", "hsl", "hsla"].includes(token.val); + const hasAlpha = [ + "rgb", + "rgba", + "hsl", + "hsla", + "hwb", + "oklab", + "oklch", + "lab", + "lch", + ].includes(fnName); + const useAlpha = (hasAlpha && token.chi.length == (isLegacy ? 7 : 4)) || + ("color" == token.val && token.chi.length == 5); + return ((hasAlpha && token.val.endsWith("a") ? fnName.slice(0, -1) : fnName) + + "(" + + token + .chi.reduce((acc, curr, index, array) => { + if (/[,/]\s*$/.test(acc)) { + if (curr.typ == exports.EnumToken.WhitespaceTokenType) { + return acc.trimEnd(); + } + return acc.trimStart() + printValue$1(curr, options, cache); + } + if (isLegacy && curr.typ == exports.EnumToken.CommaTokenType) { + return acc.trimEnd() + " "; + } + if (curr.typ == exports.EnumToken.WhitespaceTokenType) { + return acc.trimEnd() + " "; + } + if (curr.typ == exports.EnumToken.CommaTokenType || + (curr.typ == exports.EnumToken.LiteralTokenType && curr.val == "/")) { + return acc.trimEnd() + (curr.typ == exports.EnumToken.CommaTokenType ? "," : "/"); + } + return (acc.trimEnd() + + (useAlpha && index == array.length - 1 ? "/" : " ") + + printValue$1(curr, options, cache)); + }, "") + .trimStart() + + ")"); + } + case exports.EnumToken.UrlFunctionTokenType: + if (options.minifyValues && token.typ === exports.EnumToken.UrlFunctionTokenType) { + if (token.chi[0]?.typ === exports.EnumToken.BadUrlTokenType) { + return "url()"; + } + for (const child of token.chi) { + if (child.typ === exports.EnumToken.StringTokenType) { + if (child.val.slice(1, 5) !== "data:" && + urlTokenMatcher.test(child.val)) { + Object.assign(child, { + typ: exports.EnumToken.UrlTokenTokenType, + val: child.val.slice(1, -1), + }); + } + break; + } + } + } + case exports.EnumToken.ParensTokenType: + case exports.EnumToken.FunctionTokenType: + case exports.EnumToken.MathFunctionTokenType: + case exports.EnumToken.ImageFunctionTokenType: + if (options.minifyValues && token.typ === exports.EnumToken.ImageFunctionTokenType) { + const slice = token.chi.slice(); + switch (token.val) { + case "linear-gradient": + case "repeating-linear-gradient": + { + let i; + let k; + let j; + let key; + for (i = 0; i < slice.length; i++) { + if (slice[i].typ == exports.EnumToken.ColorTokenType || + slice[i].typ === exports.EnumToken.CommaTokenType) { + break; + } + if (slice[i].typ == exports.EnumToken.IdenTokenType && + equalsIgnoreCase(slice[i].val, "to")) { + k = i + 1; + while (k < slice.length && + (slice[k].typ === exports.EnumToken.WhitespaceTokenType || + slice[k].typ === exports.EnumToken.CommentTokenType)) { + k++; + } + if (k < slice.length && slice[k].typ === exports.EnumToken.IdenTokenType) { + // convert keyword to angle + key = slice[k].val.toLowerCase(); + switch (key) { + case "right": + slice.splice(i, k - i + 1, { + typ: exports.EnumToken.AngleTokenType, + val: 90, + }); + break; + case "left": + slice.splice(i, k - i + 1, { + typ: exports.EnumToken.AngleTokenType, + val: 270, + }); + break; + case "top": + case "bottom": + j = k + 1; + while (j < slice.length && + (slice[j].typ === exports.EnumToken.WhitespaceTokenType || + slice[j].typ === exports.EnumToken.CommentTokenType)) { + j++; + } + if (j < slice.length && slice[j].typ !== exports.EnumToken.CommaTokenType) { + i = j - 1; + break; + } + // https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/gradient/linear-gradient#:~:text=If%20unspecified%2C%20it%20defaults%20to%20to%20bottom + if (key === "bottom") { + // to bottom -> 180deg is the default angle and will be removed + while (k < slice.length && + slice[k].typ != exports.EnumToken.CommaTokenType) { + k++; + } + slice.splice(i, k - i + 1); + i--; + continue; + } + slice.splice(i, k - i + 1, { + typ: exports.EnumToken.AngleTokenType, + val: key === "top" ? 0 : 180, + }); + i--; + break; + } + } + } + else if (slice[i].typ == exports.EnumToken.AngleTokenType) { + if ((180 - toDegrees(slice[i]).val) % 360 === 0) { + k = i + 1; + // while (k < slice.length && slice[k].typ !== EnumToken.CommaTokenType) { + // k++; + // } + slice.splice(i, k - i + 1); + } + } + } + if (slice[i]?.typ === exports.EnumToken.CommaTokenType) { + i++; + // while ( + // i < slice.length && + // (slice[i].typ === EnumToken.WhitespaceTokenType || + // slice[i].typ === EnumToken.CommentTokenType) + // ) { + // i++; + // } + } + if (slice[i]?.typ === exports.EnumToken.ColorTokenType) { + for (const token of reduceColorStops(slice.splice(i, slice.length - i))) { + slice.push(token); + } + } + } + break; + case "radial-gradient": + case "repeating-radial-gradient": + { + let i = 0; + // while ( + // i < slice.length && + // (slice[i].typ === EnumToken.WhitespaceTokenType || + // slice[i].typ === EnumToken.CommentTokenType) + // ) { + // i++; + // } + const positions = []; + const form = []; + const size = []; + const colorSpaceDef = []; + if (slice[i]?.typ === exports.EnumToken.IdenTokenType) { + if (equalsIgnoreCase(slice[i].val, "farthest-corner") || + equalsIgnoreCase(slice[i].val, "ellipse")) { + i++; + } + else if (equalsIgnoreCase(slice[i].val, "closest-side") || + equalsIgnoreCase(slice[i].val, "closest-corner") || + equalsIgnoreCase(slice[i].val, "farthest-side")) { + size.push(slice[i++]); + } + else if (equalsIgnoreCase(slice[i].val, "circle")) { + form.push(slice[i++]); + } + while (slice[i]?.typ === exports.EnumToken.WhitespaceTokenType || + slice[i]?.typ === exports.EnumToken.CommentTokenType) { + i++; + } + } + if (slice[i]?.typ === exports.EnumToken.IdenTokenType) { + if (equalsIgnoreCase(slice[i].val, "farthest-corner") || + equalsIgnoreCase(slice[i].val, "ellipse")) { + i++; + } + else if (equalsIgnoreCase(slice[i].val, "closest-side") || + equalsIgnoreCase(slice[i].val, "closest-corner") || + equalsIgnoreCase(slice[i].val, "farthest-side")) { + size.push(slice[i++]); + } + else if (equalsIgnoreCase(slice[i].val, "circle")) { + form.push(slice[i++]); + } + while (slice[i]?.typ === exports.EnumToken.WhitespaceTokenType || + slice[i]?.typ === exports.EnumToken.CommentTokenType) { + i++; + } + } + if (slice[i]?.typ === exports.EnumToken.IdenTokenType && + equalsIgnoreCase(slice[i].val, "at")) { + i++; + } + while (slice[i]?.typ === exports.EnumToken.WhitespaceTokenType || + slice[i]?.typ === exports.EnumToken.CommentTokenType) { + i++; + } + if (slice[i]?.typ === exports.EnumToken.PercentageTokenType || + (slice[i]?.typ === exports.EnumToken.NumberTokenType && 0 === slice[i].val) || + (slice[i]?.typ === exports.EnumToken.IdenTokenType && + (equalsIgnoreCase(slice[i].val, "center") || + equalsIgnoreCase(slice[i].val, "top") || + equalsIgnoreCase(slice[i].val, "bottom") || + equalsIgnoreCase(slice[i].val, "left") || + equalsIgnoreCase(slice[i].val, "right")))) { + do { + if (slice[i]?.typ === exports.EnumToken.IdenTokenType && + "in" === slice[i].val) { + break; + } + positions.push(slice[i]); + i++; + } while (slice[i]?.typ !== exports.EnumToken.CommaTokenType); + } + while (slice[i]?.typ !== exports.EnumToken.CommaTokenType) { + colorSpaceDef.push(slice[i++]); + } + if (slice[i]?.typ === exports.EnumToken.CommaTokenType) { + i++; + } + if (positions.length > 0) { + let k; + let position1 = ""; + let position2 = ""; + for (k = 0; k < positions.length; k++) { + if (positions[k].typ === exports.EnumToken.IdenTokenType) { + if (position1.length === 0) { + position1 = positions[k].val; + } + else { + position2 = positions[k].val; } } + else if (positions[k].typ === exports.EnumToken.PercentageTokenType || + positions[k].typ === exports.EnumToken.NumberTokenType) { + if (position1.length === 0) { + position1 = positions[k].val + "%"; + } + else { + position2 = positions[k].val + "%"; + } + } + if (position2.length > 0) { + break; + } + } + if (position1.length > 0) { + let key = `${position1} ${position2}`.trim(); + if (key === "50% 50%" || + key === "center center" || + key === "50%" || + key === "center") { + positions.length = 0; + } + else if (key === "0% 0%" || + key === "0 0" || + key === "top left" || + key === "left top") { + positions.splice(0, positions.length, { + typ: exports.EnumToken.PercentageTokenType, + val: 0, + }); + } + else if (key === "50% 0%" || + key === "50% 0" || + key === "center top" || + key === "top center") { + positions.splice(0, positions.length, { + typ: exports.EnumToken.IdenTokenType, + val: "top", + }); + } + else if (key === "top right" || key === "right top") { + positions.splice(0, positions.length, { + typ: exports.EnumToken.PercentageTokenType, + val: 100, + }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.PercentageTokenType, val: 0 }); + } + else if (key === "0 50%" || + key === "0% 50%" || + key === "left center" || + key === "center left") { + positions.splice(0, positions.length, { + typ: exports.EnumToken.IdenTokenType, + val: "left", + }); + } + else if (key === "100% 50%" || key === "right center" || key === "center right") { + positions.splice(0, positions.length, { + typ: exports.EnumToken.IdenTokenType, + val: "right", + }); + } + else if (key === "0 100%" || + key === "0% 100%" || + key === "left bottom" || + key === "bottom left") { + positions.splice(0, positions.length, { + typ: exports.EnumToken.IdenTokenType, + val: "bottom", + }); + } + else if (key === "50% 100%" || + key === "center bottom" || + key === "bottom center") { + positions.splice(0, positions.length, { + typ: exports.EnumToken.IdenTokenType, + val: "bottom", + }); + } + else if (key === "100% 100%" || + key === "right bottom" || + key === "bottom right") { + positions.splice(0, positions.length, { + typ: exports.EnumToken.PercentageTokenType, + val: 100, + }); + } + else if (position1 === position2) { + positions.splice(0, positions.length, positions[k]); + } + } + } + const result = []; + if (form.length > 0) { + for (const token of form) { + result.push(token); + } + } + if (size.length > 0) { + if (result.length > 0) { + result.push({ typ: exports.EnumToken.WhitespaceTokenType }); + } + for (const token of size) { + result.push(token); + } + } + if (positions.length > 0) { + if (result.length > 0) { + result.push({ typ: exports.EnumToken.WhitespaceTokenType }); + } + result.push({ typ: exports.EnumToken.IdenTokenType, val: "at" }, { typ: exports.EnumToken.WhitespaceTokenType }); + for (const token of positions) { + result.push(token); + } + } + if (colorSpaceDef.length > 0) { + if (result.length > 0) { + result.push({ typ: exports.EnumToken.WhitespaceTokenType }); + } + for (const token of colorSpaceDef) { + result.push(token); + } + } + if (result.length > 0) { + result.push({ typ: exports.EnumToken.CommaTokenType }); + } + for (const token of reduceColorStops(slice.slice(i))) { + result.push(token); + } + slice.length = 0; + for (const token of result) { + slice.push(token); + } + } + break; + case "conic-gradient": + case "repeating-conic-gradient": + { + let i = 0; + const angles = []; + const positions = []; + const colorSpaceDef = []; + // while ( + // i < slice.length && + // (slice[i].typ === EnumToken.WhitespaceTokenType || + // slice[i].typ === EnumToken.CommentTokenType) + // ) { + // i++; + // } + if (slice[i]?.typ === exports.EnumToken.IdenTokenType && + equalsIgnoreCase(slice[i].val, "from")) { + angles.push(slice[i++]); + while (i < slice.length && + (slice[i].typ === exports.EnumToken.WhitespaceTokenType || + slice[i].typ === exports.EnumToken.CommentTokenType)) { + angles.push(slice[i++]); + } + if ((slice[i]?.typ === exports.EnumToken.NumberTokenType || + slice[i]?.typ === exports.EnumToken.AngleTokenType) && + 0 === toDegrees(slice[i]).val) { + angles.length = 0; + i++; + } + else if (slice[i]?.typ !== exports.EnumToken.CommaTokenType && + slice[i].typ != exports.EnumToken.IdenTokenType) { + angles.push(slice[i++]); + } + while (i < slice.length && + (slice[i].typ === exports.EnumToken.WhitespaceTokenType || + slice[i].typ === exports.EnumToken.CommentTokenType)) { + i++; + } + } + if (slice[i]?.typ === exports.EnumToken.IdenTokenType && + equalsIgnoreCase(slice[i].val, "at")) { + i++; + while (i < slice.length && + (slice[i].typ === exports.EnumToken.WhitespaceTokenType || + slice[i].typ === exports.EnumToken.CommentTokenType)) { + i++; + } + let position1 = ""; + let position2 = ""; + if (slice[i]?.typ === exports.EnumToken.IdenTokenType && + !equalsIgnoreCase("in", slice[i].val)) { + position1 = slice[i].val; + positions.push(slice[i++]); + } + else if (slice[i]?.typ === exports.EnumToken.PercentageTokenType || + slice[i]?.typ === exports.EnumToken.NumberTokenType) { + position1 = slice[i].val + "%"; + positions.push(slice[i++]); + } + while (i < slice.length && + (slice[i].typ === exports.EnumToken.WhitespaceTokenType || + slice[i].typ === exports.EnumToken.CommentTokenType)) { + positions.push(slice[i++]); + } + if (slice[i]?.typ === exports.EnumToken.IdenTokenType && + !equalsIgnoreCase("in", slice[i].val)) { + position2 = slice[i].val; + positions.push(slice[i++]); + } + else if (slice[i]?.typ === exports.EnumToken.PercentageTokenType || + slice[i]?.typ === exports.EnumToken.NumberTokenType) { + position2 = slice[i].val + "%"; + positions.push(slice[i++]); + } + while (i < slice.length && + (slice[i].typ === exports.EnumToken.WhitespaceTokenType || + slice[i].typ === exports.EnumToken.CommentTokenType)) { + i++; + } + if (position1.length > 0) { + reducegradientBackgroundPosition(positions, `${position1} ${position2}`.trim()); + } + } + while (i < slice.length && + (slice[i].typ === exports.EnumToken.WhitespaceTokenType || + slice[i].typ === exports.EnumToken.CommentTokenType)) { + i++; + } + if (slice[i]?.typ === exports.EnumToken.IdenTokenType && + equalsIgnoreCase("in", slice[i].val)) { + while (i < slice.length && slice[i].typ !== exports.EnumToken.CommaTokenType) { + colorSpaceDef.push(slice[i++]); + } + } + if (slice[i]?.typ === exports.EnumToken.CommaTokenType) { + i++; + } + const result = []; + if (positions.length > 0) { + if (positions.length > 0) { + if (angles.length > 0) { + angles.push({ typ: exports.EnumToken.WhitespaceTokenType }); + } + angles.push({ typ: exports.EnumToken.IdenTokenType, val: "at" }, { typ: exports.EnumToken.WhitespaceTokenType }); + for (const position of positions) { + angles.push(position); + } + } + } + if (angles.length > 0) { + for (const angle of angles) { + result.push(angle); + } + result.push({ typ: exports.EnumToken.CommaTokenType }); + } + if (colorSpaceDef.length > 0) { + if (colorSpaceDef.length > 0) { + if (result.length > 0) { + result.push({ typ: exports.EnumToken.WhitespaceTokenType }); + } + for (const token of colorSpaceDef) { + result.push(token); + } } + result.push({ typ: exports.EnumToken.CommaTokenType }); + } + for (const token of reduceConicColorStops(slice.slice(i))) { + result.push(token); + } + slice.length = 0; + for (let j = 0; j < result.length; j++) { + slice.push(result[j]); } } - } - } - } - } - result.push(clonedDeclaration); - nodeMap.clear(); - clonedDeclaration = cloneNode(declaration, true, nodeMap); - replaceNodeOrValue(nodeMap.get(targetParentWrapper), nodeMap.get(targetWrapper), node.l); - result.push(clonedDeclaration); - } - else if (node.typ === exports.EnumToken.IfConditionTokenType) { - const left = node.l.find((t) => t.typ != exports.EnumToken.CommentTokenType && t.typ != exports.EnumToken.WhitespaceTokenType); - if (left == null) { - return result; - } - if (left.typ === exports.EnumToken.IdenTokenType && equalsIgnoreCase("else", left.val)) { - clonedDeclaration = cloneNode(declaration, true, nodeMap); - replaceNodeOrValue(nodeMap.get(parentWrapper), - // @ts-expect-error - nodeMap.get(targetWrapper.typ === exports.EnumToken.DeclarationNodeType ? node : targetWrapper), node.r.at(-1)?.typ === exports.EnumToken.SemiColonTokenType ? trimArray(node.r.slice(0, -1)) : node.r); - result.push(clonedDeclaration); - } - else if (left?.typ === exports.EnumToken.WhenElseFunctionTokenType) { - const atRule = Object.assign(cloneNode(declaration), { - typ: exports.EnumToken.AtRuleNodeType, - nam: left.val, - chi: [], - }); - if (declaration[PARENT] != null) { - atRule[LOCSRCID] = declaration[PARENT][LOCSRCID]; - atRule[LOCSTA] = declaration[PARENT][LOCSTA]; - atRule[LOCEND] = declaration[PARENT][LOCEND]; - } - atRule[TOKENS] = [{ typ: exports.EnumToken.ParensTokenType, chi: left.chi.slice() }]; - const minify = atRule.nam !== "supports"; - const options = { - minify, - convertColor: minify, - }; - atRule.val = atRule[TOKENS].reduce((acc, curr) => acc + renderValue(curr, options), ""); - clonedDeclaration = cloneNode(declaration, true, nodeMap); - replaceNodeOrValue(nodeMap.get(targetWrapper), nodeMap.get(node), node.r.at(-1)?.typ === exports.EnumToken.SemiColonTokenType ? trimArray(node.r.slice(0, -1)) : node.r); - clonedDeclaration[PARENT] = atRule; - atRule.chi.push(clonedDeclaration); - result.push(atRule); - processNode(clonedDeclaration, cache); - } - else if (left.typ === exports.EnumToken.ContainerFunctionTokenType) { - const atRule = Object.assign(cloneNode(declaration), { - typ: exports.EnumToken.AtRuleNodeType, - nam: "container", - chi: [], - }); - atRule[TOKENS] = [left]; - atRule.val = atRule[TOKENS].reduce((acc, curr) => acc + renderValue(curr), ""); - if (declaration[PARENT] != null) { - atRule[LOCSRCID] = declaration[PARENT][LOCSRCID]; - atRule[LOCSTA] = declaration[PARENT][LOCSTA]; - atRule[LOCEND] = declaration[PARENT][LOCEND]; - } - clonedDeclaration = cloneNode(declaration, true, nodeMap); - replaceNodeOrValue(nodeMap.get(targetWrapper.typ === exports.EnumToken.WildCardFunctionTokenType ? targetParentWrapper : targetWrapper), nodeMap.get(targetWrapper.typ === exports.EnumToken.WildCardFunctionTokenType ? targetWrapper : node), node.r.at(-1)?.typ === exports.EnumToken.SemiColonTokenType - ? trimArray(node.r.slice(0, -1)) - : node.r); - clonedDeclaration[PARENT] = atRule; - atRule.chi.push(clonedDeclaration); - result.push(atRule); - processNode(clonedDeclaration, cache); - } - } - return result; - } - function processNode(declarationNode, cache) { - let i; - let k = -1; - let astNode; - const result = []; - const stack = [declarationNode]; - while (++k < stack.length) { - astNode = stack[k]; - const { node: declaration, value: node } = findByValue(astNode, nodeMatcher) ?? {}; - if (declaration == null || node == null) { - result.push(astNode); - continue; - } - // @ts-expect-error - const parents = [...node.parents?.()]; - const parentWrapper = node.parent ?? parents.find((node) => !nodeMatcher(node)); - if (node.node.typ === exports.EnumToken.WildCardFunctionTokenType) { - for (i = 0; i < node.node.chi.length; i++) { - stack.push( - // @ts-expect-error - ...substituteIfElseNode( - // @ts-expect-error - declaration, node.node.chi[i], node.node, parentWrapper, cache)); - } - } - else { - stack.push( - // @ts-expect-error - ...substituteIfElseNode( - // @ts-expect-error - declaration, node.node, parentWrapper, parents[parents.indexOf(parentWrapper) + 1] ?? declaration, cache)); - } - } - if (result.length > 0) { - // @ts-expect-error - replaceNodeOrValue(declarationNode[PARENT], declarationNode, result); - } - // else remove node? - // @ts-expect-error - return result; - } - class ExpandIfFeature { - accept = new Set([exports.EnumToken.DeclarationNodeType]); - get ordering() { - return 1; - } - get processMode() { - return exports.FeatureWalkMode.Pre; - } - static register(options) { - if (options.expandIfSyntax) { - // @ts-ignore - options.features.push(new ExpandIfFeature()); - } - } - run(declaration) { - return processNode(declaration, new Set()); - } - } - - var allFeatures = /*#__PURE__*/Object.freeze({ - __proto__: null, - ComputeCalcExpressionFeature: ComputeCalcExpressionFeature, - ComputePrefixFeature: ComputePrefixFeature, - ComputeShorthandFeature: ComputeShorthandFeature, - ExpandIfFeature: ExpandIfFeature, - InlineCssVariablesFeature: InlineCssVariablesFeature, - TransformCssFeature: TransformCssFeature - }); - - const notEndingWith = ["(", "["].concat(combinators); - const rules = [ - exports.EnumToken.AtRuleNodeType, - exports.EnumToken.RuleNodeType, - exports.EnumToken.AtRuleTokenType, - exports.EnumToken.KeyframesRuleNodeType, - ]; - // @ts-ignore - const features = Object.values(allFeatures).sort((a, b) => a.ordering - b.ordering); - /** - * Apply minification rules to the ast tree - * @param ast - * @param options - * @param recursive - * @param errors - * @param nestingContent - * - * @param context - * @private - */ - function minify(ast, options = {}, recursive = false, errors, nestingContent, context = {}) { - let preprocess = false; - let postprocess = false; - let parents; - let replacement; - let { sourcemap, module, ...options2 } = options; - if (!(options2.features != null)) { - options2 = { - removeDuplicateDeclarations: true, - computeShorthand: true, - computeCalcExpression: true, - removePrefix: false, - features: [], - ...options2, - }; - for (const feature of features) { - feature.register(options2); - } - options2.features.sort((a, b) => a.ordering - b.ordering); - } - for (const feature of options2.features) { - if (feature.processMode & exports.FeatureWalkMode.Pre) { - preprocess = true; - } - if (feature.processMode & exports.FeatureWalkMode.Post) { - postprocess = true; - } - } - if (preprocess) { - parents = new Set([ast]); - for (const parent of parents) { - if (parent.typ == exports.EnumToken.CommentTokenType || parent.typ == exports.EnumToken.CDOCOMMTokenType) { - continue; - } - replacement = parent; - for (const feature of options2.features) { - if ((feature.processMode & exports.FeatureWalkMode.Pre) === 0 || - (feature.accept != null && !feature.accept.has(parent.typ))) { - continue; - } - if (rules.includes(replacement.typ) && !Array.isArray(replacement[TOKENS])) { - replacement[TOKENS] = parseString(replacement.typ == exports.EnumToken.RuleNodeType || replacement.typ === exports.EnumToken.KeyframesRuleNodeType - ? replacement.sel - : // @ts-ignore - replacement.nam); - } - const result = feature.run(replacement, options2, - // @ts-ignore - parent[PARENT] ?? ast, context, exports.FeatureWalkMode.Pre); - if (result != null) { - replacement = result; - } - } - if (replacement != null && - (!Array.isArray(replacement) || replacement.length > 0) && - replacement != parent && - parent[PARENT] != null) { - // @ts-ignore - replaceNodeOrValue(parent[PARENT], parent, replacement); - } - // @ts-ignore - if (replacement.chi != null) { - // @ts-ignore - for (const node of replacement.chi) { - node[PARENT] = replacement; - parents.add(node); + break; } + return token.val + "(" + slice.reduce(reducer, "") + ")"; } - } - for (const feature of options2.features) { - if (feature.processMode & exports.FeatureWalkMode.Pre && "cleanup" in feature) { - // @ts-ignore - feature.cleanup(ast, options2, context, exports.FeatureWalkMode.Pre); - } - } - } - doMinify(ast, options2, recursive, errors, nestingContent, context); - parents = new Set([ast]); - for (const parent of parents) { - if (parent.typ == exports.EnumToken.CommentTokenType || parent.typ == exports.EnumToken.CDOCOMMTokenType) { - continue; - } - replacement = parent; - if (postprocess) { - for (const feature of options2.features) { - if ((feature.processMode & exports.FeatureWalkMode.Post) === 0 || - (feature.accept != null && !feature.accept.has(parent.typ))) { - continue; - } - const result = feature.run(replacement, options2, + case exports.EnumToken.TimingFunctionTokenType: + case exports.EnumToken.PseudoClassFuncTokenType: + case exports.EnumToken.WhenElseFunctionTokenType: + case exports.EnumToken.TimelineFunctionTokenType: + case exports.EnumToken.GridTemplateFuncTokenType: + case exports.EnumToken.SupportsFunctionTokenType: + case exports.EnumToken.ContainerFunctionTokenType: + case exports.EnumToken.TransformFunctionTokenType: + case exports.EnumToken.GeneralEnclosedFunctionTokenType: + case exports.EnumToken.CustomFunctionTokenType: + case exports.EnumToken.WildCardFunctionTokenType: + if (token.typ == exports.EnumToken.MathFunctionTokenType && + token.chi.length == 1 && + ![exports.EnumToken.BinaryExpressionTokenType, exports.EnumToken.FractionTokenType, exports.EnumToken.IdenTokenType].includes(token.chi[0].typ) && // @ts-ignore - parent[PARENT] ?? ast, context, exports.FeatureWalkMode.Post); - if (result != null) { - replacement = result; - } - } - } - if (replacement != null && - (!Array.isArray(replacement) || replacement.length > 0) && - replacement != parent && - parent[PARENT] != null) { - // @ts-ignore - replaceNodeOrValue(parent[PARENT], parent, replacement); - } - // @ts-ignore - if (replacement.chi != null) { - // @ts-ignore - for (const node of replacement.chi) { - node[PARENT] = replacement; - parents.add(node); + token.chi[0].val + ?.typ != exports.EnumToken.FractionTokenType) { + return (token.val + + "(" + + token.chi.reduce((acc, curr) => acc + + printValue$1(curr, token.typ == exports.EnumToken.FunctionTokenType ? { minify: false } : options, cache, reducer), "") + + ")"); } - } - } - if (postprocess) { - for (const feature of options2.features) { - if (feature.processMode & exports.FeatureWalkMode.Post && "cleanup" in feature) { - // @ts-ignore - feature.cleanup(ast, options2, context, exports.FeatureWalkMode.Post); + return (arena.getValue(token) ?? "") + "(" + arena.children(token).reduce(reducer, "") + ")"; + case exports.EnumToken.BlockStartTokenType: + return "{"; + case exports.EnumToken.BlockEndTokenType: + return "}"; + case exports.EnumToken.StartParensTokenType: + return "("; + case exports.EnumToken.DelimTokenType: + case exports.EnumToken.EqualMatchTokenType: + return "="; + case exports.EnumToken.IncludeMatchTokenType: + return "~="; + case exports.EnumToken.DashMatchTokenType: + return "|="; + case exports.EnumToken.StartMatchTokenType: + return "^="; + case exports.EnumToken.EndMatchTokenType: + return "$="; + case exports.EnumToken.ContainMatchTokenType: + return "*="; + case exports.EnumToken.LtTokenType: + return "<"; + case exports.EnumToken.LteTokenType: + return "<="; + case exports.EnumToken.Tilda: + case exports.EnumToken.SubsequentSiblingCombinatorTokenType: + return "~"; + case exports.EnumToken.Plus: + case exports.EnumToken.NextSiblingCombinatorTokenType: + return "+"; + case exports.EnumToken.GtTokenType: + case exports.EnumToken.ChildCombinatorTokenType: + return ">"; + case exports.EnumToken.GteTokenType: + return ">="; + case exports.EnumToken.ColumnCombinatorTokenType: + return "||"; + case exports.EnumToken.EndParensTokenType: + return ")"; + case exports.EnumToken.AttrStartTokenType: + return "["; + case exports.EnumToken.AttrEndTokenType: + return "]"; + case exports.EnumToken.DescendantCombinatorTokenType: + case exports.EnumToken.WhitespaceTokenType: + return " "; + case exports.EnumToken.ColonTokenType: + return ":"; + case exports.EnumToken.DoubleColonTokenType: + return "::"; + case exports.EnumToken.SemiColonTokenType: + return ";"; + case exports.EnumToken.CommaTokenType: + return ","; + case exports.EnumToken.ImportantTokenType: + return "!important"; + case exports.EnumToken.Pipe: + return "|"; + case exports.EnumToken.AttrTokenType: + case exports.EnumToken.IdenListTokenType: + return "[" + arena.children(token).reduce(reducer, "") + "]"; + case exports.EnumToken.TimeTokenType: + case exports.EnumToken.AngleTokenType: + case exports.EnumToken.LengthTokenType: + case exports.EnumToken.DimensionTokenType: + case exports.EnumToken.FrequencyTokenType: + case exports.EnumToken.ResolutionTokenType: + let val = arena.hasFlags(token, exports.EnumAstNodeStatus.HasFractionValue) ? + printValue$1(arena.getNumericValue(token), arena, options, cache) + : minifyNumber(arena.getNumericValue(token)); + let unit = arena.getUnit(token); + if (typ == exports.EnumToken.AngleTokenType && !val.includes("/")) { + const angle = getAngle(token, arena); + let v; + let value = val + unit; + for (const u of ["deg", "turn", "rad", "grad"]) { + if (unit == u) { + continue; + } + switch (u) { + case "deg": + v = minifyNumber(toPrecisionAngle(angle * 360, anglePrecision, false).toFixed(anglePrecision)); + if (v.length + 3 < value.length) { + val = v; + unit = u; + value = v + u; + } + break; + case "turn": + v = minifyNumber(toPrecisionAngle(angle, anglePrecision, false).toFixed(anglePrecision)); + if (v.length + 4 < value.length) { + val = v; + unit = u; + value = v + u; + } + break; + case "rad": + v = minifyNumber(toPrecisionAngle(angle * (2 * Math.PI), anglePrecision, false).toFixed(anglePrecision)); + if (v.length + 3 < value.length) { + val = v; + unit = u; + value = v + u; + } + break; + case "grad": + v = minifyNumber(toPrecisionAngle(angle * 400, anglePrecision, false).toFixed(anglePrecision)); + if (v.length + 4 < value.length) { + val = v; + unit = u; + value = v + u; + } + break; + } + } } - } - } - return ast; - } - function transformAtRuleMediaPrelude(values) { - let hasUpdates = false; - for (let { value, parent, parents } of walkValues(values)) { - if (value.typ === exports.EnumToken.MediaQueryConditionTokenType) { - if (value.op.typ == exports.EnumToken.AndTokenType && - // @ts-ignore - value.l.typ === exports.EnumToken.IdenTokenType && - // @ts-ignore - value.l.val.toLowerCase() === "all") { - if (parent === null) { - // @ts-ignore - values[values.indexOf(value)] = value.l; + if (val === "0") { + if (typ == exports.EnumToken.TimeTokenType) { + return "0s"; } - else { - // @ts-ignore - replaceNodeOrValue(parent, value, value.l); - // @ts-ignore - value = value.l; + if (typ == exports.EnumToken.FrequencyTokenType) { + return "0Hz"; } - hasUpdates = true; - } - } - // range operator - if (parent != null && - parent.typ === exports.EnumToken.MediaQueryConditionTokenType && - parent.op.typ == exports.EnumToken.AndTokenType && - // @ts-ignore - parent.l.typ == exports.EnumToken.ParensTokenType) { - let token = parent.r.find((t) => t.typ !== exports.EnumToken.WhitespaceTokenType && t.typ !== exports.EnumToken.CommentTokenType); - if (token?.typ === exports.EnumToken.ParensTokenType) { // @ts-ignore - const node1 = parent.l.chi.find((t) => t.typ !== exports.EnumToken.WhitespaceTokenType && t.typ !== exports.EnumToken.CommentTokenType); - const node2 = token.chi.find((t) => t.typ !== exports.EnumToken.WhitespaceTokenType && t.typ !== exports.EnumToken.CommentTokenType); - if (node1?.typ === exports.EnumToken.MediaQueryConditionTokenType && - node2?.typ === exports.EnumToken.MediaQueryConditionTokenType && - node1.op.typ == exports.EnumToken.ColonTokenType && - node2.op.typ == exports.EnumToken.ColonTokenType && - // @ts-ignore - node1.l.typ == exports.EnumToken.IdenTokenType && - // @ts-ignore - node2.l.typ == exports.EnumToken.IdenTokenType && - // @ts-ignore - node1.l.val.startsWith("min-") && - // @ts-ignore - node2.l.val.startsWith("max-") && + if (typ == exports.EnumToken.ResolutionTokenType) { + return "0x"; + } + return "0"; + } + if (typ == exports.EnumToken.TimeTokenType) { + if (unit == "ms") { // @ts-ignore - node1.l.val.slice(4) == - // @ts-ignore - node2.l.val.slice(4)) { - const val1 = node1.r.find((t) => t.typ !== exports.EnumToken.WhitespaceTokenType && t.typ !== exports.EnumToken.CommentTokenType); - const val2 = node2.r.find((t) => t.typ !== exports.EnumToken.WhitespaceTokenType && t.typ !== exports.EnumToken.CommentTokenType); - const replacement = { - typ: exports.EnumToken.ParensTokenType, - chi: [ - // @ts-ignore - { - typ: exports.EnumToken.MediaRangeQueryTokenType, - op: { - typ: exports.EnumToken.IdenTokenType, - // @ts-ignore - val: node1.l.val.slice(4), - }, - l: val1, - r: val2, - [LOCSRCID]: value[LOCSRCID], - [LOCSTA]: value[LOCSTA], - [LOCEND]: value[LOCEND], - }, - ], - }; - // @ts-expect-error - const p = parents?.[parents?.indexOf?.(parent) + 1]; - if (p != null) { - // @ts-ignore - replaceNodeOrValue(p, parent, replacement); - } - else { - // @ts-ignore - values.splice(values.indexOf(parent), 1, replacement); + const v = minifyNumber(val / 1000); + if (v.length + 1 <= val.length) { + return v + "s"; } - hasUpdates = true; - value = replacement; + return val + "ms"; + } + return val + "s"; + } + if (typ == exports.EnumToken.ResolutionTokenType && unit == "dppx") { + unit = "x"; + } + return val.includes("/") ? val.replace("/", unit + "/") : minifyNumber(toPrecisionValue(val)) + unit; + case exports.EnumToken.FlexTokenType: + case exports.EnumToken.PercentageTokenType: + const uni = typ == exports.EnumToken.PercentageTokenType ? "%" : "fr"; + const perc = arena.hasFlags(token, exports.EnumAstNodeStatus.HasFractionValue) + ? printValue$1(arena.getNumericValue(token), arena, options, cache) + : minifyNumber(arena.getNumericValue(token)); + return options.minifyValues && perc == "0" + ? "0" + : perc.includes("/") + ? perc.replace("/", uni + "/") + : perc + uni; + case exports.EnumToken.NumberTokenType: + return arena.hasFlags(token, exports.EnumAstNodeStatus.HasFractionValue) + ? printValue$1(arena.getNumericValue(token), arena, options, cache) + : minifyNumber(arena.getNumericValue(token)); + case exports.EnumToken.InfinityTokenType: + return "1/0"; + case exports.EnumToken.NegativeInfinityTokenType: + return "-1/0"; + case exports.EnumToken.NaNTokenType: + return "NaN"; + case exports.EnumToken.AtRuleTokenType: + return "@" + arena.getName(token); + case exports.EnumToken.CommentTokenType: + case exports.EnumToken.CDOCOMMNodeType: + if (options.removeComments && (!options.preserveLicense || !arena.startsWith(token, "val", "/*!"))) { + return ""; + } + case exports.EnumToken.PseudoClassTokenType: + case exports.EnumToken.PseudoElementTokenType: + // https://www.w3.org/TR/selectors-4/#single-colon-pseudos + if (typ == exports.EnumToken.PseudoElementTokenType) { + let val = arena.getValue(token); + let slice = val.slice(1); + if (pseudoElements.includes(slice)) { + return slice; } } + case exports.EnumToken.UrlTokenTokenType: + case exports.EnumToken.HashTokenType: + case exports.EnumToken.IdenTokenType: + case exports.EnumToken.StringTokenType: + case exports.EnumToken.LiteralTokenType: + case exports.EnumToken.DashedIdenTokenType: + case exports.EnumToken.PseudoPageTokenType: + case exports.EnumToken.ClassSelectorTokenType: + return arena.getValue(token); + case exports.EnumToken.NestingSelectorTokenType: + return "&"; + case exports.EnumToken.InvalidAttrTokenType: + return ("[" + + arena.children(token).reduce((acc, curr) => acc + printValue$1(curr, arena, options, cache), "")); + case exports.EnumToken.InvalidClassSelectorTokenType: + return arena.getValue(token); + case exports.EnumToken.SupportsQueryUnaryConditionTokenType: + case exports.EnumToken.WhenElseUnaryConditionTokenType: + return (printValue$1(arena.getLeft(token), arena, options, cache, reducer, errors) + + " " + + printValue$1(arena.getRight(token), arena, options, cache, reducer, errors)); + case exports.EnumToken.SupportsQueryConditionTokenType: + case exports.EnumToken.WhenElseQueryConditionTokenType: + return (printValue$1(arena.getLeft(token), arena, options, cache, reducer, errors) + + " " + + printValue$1(arena.getOp(token), arena, options, cache, reducer, errors) + + " " + + printValue$1(arena.getRight(token), arena, options, cache, reducer, errors)); + case exports.EnumToken.IfConditionTokenType: + return token.l.length == 0 + ? "" + : token.l.reduce((acc, curr) => acc + printValue$1(curr, options, cache, reducer, errors), "") + + ":" + + (options.indent ?? "") + + token.r.reduce((acc, curr) => acc + printValue$1(curr, options, cache, reducer, errors), ""); + case exports.EnumToken.IfElseConditionTokenType: + return (printValue$1(token.l, options, cache, reducer, errors) + + (options.indent ?? "") + + printValue$1(token.r, options, cache, reducer, errors)); + case exports.EnumToken.DeclarationNodeType: + return (arena.getName(token) + + ":" + + (options.minifyValues ? filterValues(arena.children(token), arena) : arena.children(token)).reduce((acc, curr) => acc + printValue$1(curr, arena, options, cache, reducer, errors), "")); + case exports.EnumToken.MediaQueryUnaryFeatureTokenType: + return (printValue$1(arena.getLeft(token), arena, options, cache, reducer, errors) + + " " + + printValue$1(arena.getRight(token), arena, options, cache, reducer, errors)); + case exports.EnumToken.MediaQueryConditionTokenType: { + const indent = token.op.typ == exports.EnumToken.LtTokenType || + token.op.typ == exports.EnumToken.GtTokenType || + token.op.typ == exports.EnumToken.ColonTokenType || + token.op.typ == exports.EnumToken.DelimTokenType || + token.op.typ == exports.EnumToken.LteTokenType || + token.op.typ == exports.EnumToken.GteTokenType + ? "" + : " "; + return (token.l.reduce((acc, curr) => acc + printValue$1(curr, options, cache, reducer, errors), "") + + indent + + printValue$1(token.op, options, cache, reducer, errors) + + indent + + token.r.reduce((acc, curr) => acc + printValue$1(curr, options, cache, reducer, errors), "")); } + case exports.EnumToken.MediaRangeQueryTokenType: + return (token.l.reduce((acc, curr) => acc + printValue$1(curr, options, cache, reducer, errors), "") + + printValue$1(token.op1, options, cache, reducer, errors) + + token.val.reduce((acc, curr) => acc + printValue$1(curr, options, cache, reducer, errors), "") + + printValue$1(token.op2, options, cache, reducer, errors) + + token.r.reduce((acc, curr) => acc + printValue$1(curr, options, cache, reducer, errors), "")); + case exports.EnumToken.MediaFeatureTokenType: + return arena.getValue(token); + case exports.EnumToken.NotTokenType: + return "not"; + case exports.EnumToken.OnlyTokenType: + return "only"; + case exports.EnumToken.AndTokenType: + return "and"; + case exports.EnumToken.OrTokenType: + return "or"; + case exports.EnumToken.InvalidMediaQueryTokenType: + case exports.EnumToken.InvalidCommentTokenType: + case exports.EnumToken.BadCommentTokenType: + case exports.EnumToken.BadCdoTokenType: + case exports.EnumToken.BadStringTokenType: + case exports.EnumToken.BadUrlTokenType: + case exports.EnumToken.EOFTokenType: + return ""; + default: + console.debug({ token }); + throw new Error(`Unsupported token type for ${exports.EnumToken[typ]}`); } - return { hasUpdates, values: trimArray(values) }; + // errors?.push({ action: "ignore", message: `render: unexpected token ${JSON.stringify(token, null, 1)}` }); + // return ""; } /** - * Minify at-rule media - * - remove redundant tokens - * - generate range queries + * Remove whitespace tokens that are not needed + * @param values * - * @private - * @param tokens + * @internal */ - function minifyAtRuleMedia(tokens) { - let hasUpdates = false; - const sections = tokens - .reduce((acc, t) => { - if (t.typ === exports.EnumToken.CommaTokenType) { - acc.push([]); + function filterValues(values, arena) { + let i = 0; + let typ; + for (; i < values.length; i++) { + typ = arena.getType(values[i]); + if (typ == exports.EnumToken.ImportantTokenType && arena.getType(values[i - 1]) === exports.EnumToken.WhitespaceTokenType) { + values.splice(i - 1, 1); } - else { - acc[acc.length - 1].push(t); + else if (tokensfuncSet.has(typ) && + arena.hasFlags(values[i], exports.EnumAstNodeStatus.HasChildren) && + typ != exports.EnumToken.WildCardFunctionTokenType && + arena.getType(values[i + 1]) == exports.EnumToken.WhitespaceTokenType) { + values.splice(i + 1, 1); } - return acc; - }, [[]]) - .reduce((acc, values) => { - if (acc.has("all")) { - return acc; + } + return values; + } + + const config$2 = getSyntaxConfig(); + function replacePseudo(tokens) { + return tokens.map((raw) => raw.map((r) => { + if (r.includes("(")) { + const index = r.indexOf("("); + const name = r.slice(0, index) + "()"; + if (name in pseudoAliasMap) { + return pseudoAliasMap[name] + r.slice(index); + } + return r; } - const result = transformAtRuleMediaPrelude(values); - if (result.values.length === 0) { - return acc; + return r in pseudoAliasMap && pseudoAliasMap[r] in config$2[ValidationSyntaxGroupEnum.Selectors] + ? pseudoAliasMap[r] + : r; + })); + } + function replaceAstNodes(tokens, root) { + let result = false; + for (const { value, parent } of walkValues(tokens, root)) { + if (value.typ == exports.EnumToken.MediaQueryConditionTokenType) { + const token = value.l.find((t) => t.typ == exports.EnumToken.IdenTokenType); + if (token != null) { + if (token.val in pseudoAliasMap) { + token.val = pseudoAliasMap[token.val]; + if ((equalsIgnoreCase(token.val, "min-resolution") || + equalsIgnoreCase(token.val, "max-resolution")) && + value.r?.[0]?.typ == exports.EnumToken.NumberTokenType) { + Object.assign(value.r?.[0], { + typ: exports.EnumToken.ResolutionTokenType, + unit: "x", + }); + result = true; + } + let isMin = token.val.startsWith("min-"); + let isMax = token.val.startsWith("max-"); + if (isMin || isMax) { + token.val = token.val.slice(4); + value.op.typ = isMax + ? exports.EnumToken.LteTokenType + : exports.EnumToken.GteTokenType; + } + } + } } - if (result.hasUpdates) { - hasUpdates = true; + else if (value.typ == exports.EnumToken.IdenTokenType || + value.typ == exports.EnumToken.PseudoClassFuncTokenType || + value.typ == exports.EnumToken.PseudoClassTokenType || + value.typ == exports.EnumToken.PseudoElementTokenType) { + let key = value.val + + (value.typ == exports.EnumToken.PseudoClassFuncTokenType ? "()" : ""); + if (key in pseudoAliasMap) { + const isPseudClass = pseudoAliasMap[key].startsWith("::"); + value.val = pseudoAliasMap[key]; + // if ( + // value.typ == EnumToken.IdenTokenType && + // ["min-resolution", "max-resolution"].includes((value as IdentToken).val) && + // parent?.typ == EnumToken.MediaQueryConditionTokenType && + // (parent as MediaQueryConditionToken).r?.[0]?.typ == EnumToken.NumberTokenType + // ) { + // Object.assign((parent as MediaQueryConditionToken).r?.[0], { + // typ: EnumToken.ResolutionTokenType, + // unit: "x", + // }); + // } + // else + if (isPseudClass && value.typ == exports.EnumToken.PseudoElementTokenType) { + // @ts-ignore + value.typ = exports.EnumToken.PseudoClassTokenType; + } + result = true; + } } - acc.set(values.reduce((acc, t) => acc + renderValue(t), ""), result.values); - return acc; - }, new Map()); - if (sections.has("all")) { - tokens.length = 0; } - else if (hasUpdates) { + // remove duplicate tokens + if (tokens.find((t) => t.typ == exports.EnumToken.CommaTokenType) != null) { + const set = new Set(); + const split = splitTokenList(tokens, [exports.EnumToken.CommaTokenType]); tokens.length = 0; - tokens.push(...[...sections.values()].reduce((acc, t) => { + for (const token of split.reduce((acc, curr) => { + const str = curr.reduce((acc, curr) => acc + renderValue(curr), ""); + if (set.has(str)) { + return acc; + } + set.add(str); if (acc.length > 0) { acc.push({ typ: exports.EnumToken.CommaTokenType, }); } - for (const token of t) { - acc.push(token); - } - return acc; - }, [])); - } - // return ast; - return tokens; - } - /** - * Reduce selectors - * @param acc - * @param curr - * - * @private - */ - function reduce(acc, curr) { - // trim :is() - if (curr[0] == "&") { - if (curr[1] == " " && !isIdent(curr[2]) && !isFunction(curr[2])) { - const all = new Array(Math.max(curr.length - 2, 0)); - let write = 0; - for (let read = 2; read < curr.length; read++) { - all[write++] = curr[read]; - } - curr.length = 0; - for (let index = 0; index < write; index++) { - curr[index] = all[index]; - } + return acc.concat(curr); + }, [])) { + tokens.push(token); } } - acc.push(curr.join("")); - return acc; + return result; } - /** - * Apply minification rules to the ast tree - * @param ast - * @param options - * @param recursive - * @param errors - * @param nestingContent - * @param context - * - * @private - */ - function doMinify(ast, options = {}, recursive = false, errors, nestingContent, context = {}) { - if (!("nodes" in context)) { - context.nodes = new Set(); + class ComputePrefixFeature { + get ordering() { + return 0; } - if (context.nodes.has(ast)) { - return ast; + get processMode() { + return exports.FeatureWalkMode.Pre; } - context.nodes.add(ast); - // @ts-ignore - if ("chi" in ast && ast.chi.length > 0) { - const reducer = reduce.bind(ast); - if (!nestingContent) { - nestingContent = options.nestingRules && ast.typ == exports.EnumToken.RuleNodeType; + static register(options) { + if (options.removePrefix) { + // @ts-ignore + options.features.push(new ComputePrefixFeature(options)); } - let i = 0; - let previous = null; - let node = null; - let nodeIndex = -1; - for (; i < ast.chi.length; i++) { - if (ast.chi[i].typ === exports.EnumToken.CommentNodeType) { - continue; - } - while (previous?.typ === exports.EnumToken.CommentNodeType) { - // @ts-ignore - previous = ast.chi[--nodeIndex]; - } - node = ast.chi[i]; - if (node.typ === exports.EnumToken.AtRuleNodeType && node.nam === "font-face") { - continue; - } - if (node.typ === exports.EnumToken.KeyframesAtRuleNodeType) { - if (previous?.typ === exports.EnumToken.KeyframesAtRuleNodeType && - node.nam === previous.nam && - node.val === previous.val) { - ast.chi?.splice(nodeIndex--, 1); - previous = ast?.chi?.[nodeIndex] ?? null; - i = nodeIndex; - continue; - } - } - else if (node.typ === exports.EnumToken.KeyframesRuleNodeType) { - if (previous?.typ === exports.EnumToken.KeyframesRuleNodeType && - node.sel === previous.sel) { - // do not merge keyframes - // https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@keyframes#resolving_duplicates - for (const child of node.chi) { - previous.chi.push(child); - } - // @ts-ignore - ast.chi.splice(i, 1); - previous = ast?.chi?.[nodeIndex] ?? null; - i = nodeIndex; - continue; - } - let k; - for (k = 0; k < node.chi.length; k++) { - if (node.chi[k].typ == exports.EnumToken.DeclarationNodeType) { - let l = node.chi[k].val.length; - while (l--) { - if (node.chi[k].val[l].typ == - exports.EnumToken.ImportantTokenType) { - node.chi.splice(k--, 1); - break; - } - if ([exports.EnumToken.WhitespaceTokenType, exports.EnumToken.CommentTokenType].includes(node.chi[k].val[l].typ)) { - continue; - } - break; - } - } - } + } + run(node) { + if (node[STATE] & (exports.EnumAstNodeStatus.Disallowed | exports.EnumAstNodeStatus.Invalid | exports.EnumAstNodeStatus.Malformed)) { + return null; + } + if (node.typ == exports.EnumToken.RuleNodeType) { + node.sel = replacePseudo(splitRule(node.sel)).reduce((acc, curr, index) => acc + (index > 0 ? "," : "") + curr.join(""), ""); + if (node[TOKENS] != null) { + replaceAstNodes(node[TOKENS]); } - else if (node.typ == exports.EnumToken.AtRuleNodeType) { - if (node.nam == "media") { - if (Array.isArray(node[TOKENS])) { - const slice = node[TOKENS].slice(); - minifyAtRuleMedia(slice); - if (slice.length !== node[TOKENS].length) { - node[TOKENS].length = 0; - for (const token of slice) { - node[TOKENS].push(token); - } - node.val = slice.reduce((acc, curr, index, arr) => acc + - (curr.typ === exports.EnumToken.CommentTokenType || - (curr.typ === exports.EnumToken.WhitespaceTokenType && - arr[index + 1]?.typ === exports.EnumToken.CommentTokenType && - (index + 3 < arr.length || - arr[index + 2]?.typ === exports.EnumToken.WhitespaceTokenType)) - ? "" - : renderValue(curr)), ""); - } - } - if (["all", "", null].includes(node.val)) { - ast.chi?.splice(i--, 1, ...node.chi); - continue; - } - } - else if (node.nam === "import" && Array.isArray(node[TOKENS])) { - let l = 0; - let token; - for (; l < node[TOKENS].length; l++) { - token = node[TOKENS][l]; - if (token.typ === exports.EnumToken.ParensTokenType || - token.typ === exports.EnumToken.MediaQueryConditionTokenType || - (token.typ === exports.EnumToken.IdenTokenType && "layer" !== token.val)) { - break; - } - } - if (l < node[TOKENS].length) { - const slice = node[TOKENS]?.slice(l); - node[TOKENS].splice(l, slice.length, ...minifyAtRuleMedia(slice)); - node.val = trimArray(node[TOKENS]).reduce((acc, curr, index, arr) => acc + - (curr.typ === exports.EnumToken.CommentTokenType || - (curr.typ === exports.EnumToken.WhitespaceTokenType && - arr[index + 1]?.typ === exports.EnumToken.CommentTokenType && - (index + 3 < arr.length || arr[index + 2].typ === exports.EnumToken.WhitespaceTokenType)) - ? "" - : renderValue(curr)), ""); - } - } - else if (ast.typ === node.typ && - ast.nam === node.nam && - ast.val === node.val) { - // @ts-ignore - replaceNodeOrValue(ast, node, node.chi); - i--; - continue; - } - if (previous?.typ == exports.EnumToken.AtRuleNodeType && - node.nam != "font-face" && - previous.nam === node.nam && - previous.val === node.val) { - if ("chi" in node) { - for (const child of node.chi) { - previous.chi.push(child); - } - if (!hasDeclaration(previous)) { - context.nodes.delete(previous); - doMinify(previous, options, recursive, errors, nestingContent, context); + } + else if (node.typ == exports.EnumToken.DeclarationNodeType) { + if (node.nam.charAt(0) == "-") { + const match = node.nam.match(/^-([^-]+)-(.+)$/); + if (match != null) { + let nam = match[2]; + if (!(nam in config$2.declarations)) { + if (node.nam in pseudoAliasMap) { + nam = pseudoAliasMap[node.nam]; } } - ast?.chi?.splice(i--, 1); - continue; - } - // if (!hasDeclaration(node as AstAtRule)) { - // doMinify(node, options, recursive, errors, nestingContent, context); - // } - if ("chi" in node) { - doMinify(node, options, recursive, errors, nestingContent, context); + if (nam in config$2.declarations) { + node.nam = nam; + } } - previous = node; - nodeIndex = i; - continue; } - // @ts-ignore - else if (node.typ === exports.EnumToken.RuleNodeType) { - reduceRuleSelector(node); - let wrapper = null; - let match; - if (options.nestingRules) { - if (previous?.typ == exports.EnumToken.RuleNodeType) { - reduceRuleSelector(previous); - // @ts-ignore - match = matchSelectors(previous[RAW], node[RAW]); - if (match != null) { - wrapper = wrapNodes(previous, node, match, ast, reducer, i, nodeIndex); - nodeIndex = i - 1; - previous = ast.chi[nodeIndex]; - } - } - if (wrapper != null) { - while (i < ast.chi.length) { - const nextNode = ast.chi[i]; - if (nextNode.typ != exports.EnumToken.RuleNodeType) { - break; - } - reduceRuleSelector(nextNode); - match = matchSelectors(wrapper[RAW], nextNode[RAW]); - if (match == null) { - break; - } - wrapper = wrapNodes(wrapper, nextNode, match, ast, reducer, i, nodeIndex); + let hasPrefix = false; + for (const { value } of walkValues(node.val)) { + if ((value.typ == exports.EnumToken.IdenTokenType || + (value.typ != exports.EnumToken.ParensTokenType && funcLike.includes(value.typ))) && + value.val.match(/^-([^-]+)-(.+)$/) != null) { + if (value.val.endsWith("-gradient")) { + if (regMatchLinearGradient.test(value.val)) { + this.webkitLinearToLinearGradient(value); } - nodeIndex = --i; - previous = ast.chi[nodeIndex]; - doMinify(wrapper, options, recursive, errors, nestingContent, context); - continue; - } - // @ts-ignore - else if (node[OPTIMIZED] != null && - // @ts-ignore - node[OPTIMIZED].match && - // @ts-ignore - node[OPTIMIZED].selector.length > 1) { - // @ts-ignore - wrapper = { - ...node, - chi: [], - sel: node[OPTIMIZED].optimized[0], - [RAW]: [[node[OPTIMIZED].optimized[0]]], - }; - // @ts-ignore - node.sel = node[OPTIMIZED].selector.reduce(reducer, []).join(","); - // @ts-ignore - node[RAW] = node[OPTIMIZED].selector.slice(); - node[TOKENS] = null; - // @ts-ignore - wrapper.chi.push(node); - // @ts-ignore - ast.chi.splice(i, 1, wrapper); - node = wrapper; - } - else if (node[OPTIMIZED]?.reducible) { - if (node[OPTIMIZED].optimized.length === 1) { - const sel1 = node[OPTIMIZED].optimized[0] + - ":is(" + - node[OPTIMIZED].selector.reduce(reducer, []).join(",") + - ")"; - const sel2 = node[OPTIMIZED].selector.reduce((acc, curr) => - // @ts-ignore - (acc.length > 0 ? acc + "," : "") + node[OPTIMIZED].optimized[0] + curr.join(""), ""); - node.sel = sel1.length < sel2.length ? sel1 : sel2; - node[TOKENS] = null; + else if (regMatchRadialGradient.test(value.val)) { + this.webkitRadialToRadialGradient(value); } - else if (node[OPTIMIZED].optimized.length === 0) { - const testIdent = /^[a-zA-Z]/; - node.sel = node[OPTIMIZED].selector.reduce((acc, curr) => (acc.length > 0 ? acc + "," : "") + - (nestingContent && testIdent.test(curr[0]) ? "& " : "") + - curr.join(""), ""); - node[TOKENS] = null; + else if (equalsIgnoreCase(value.val, "-webkit-gradient")) { + this.webkitGradientToGradient(value); } + // not supported yet + break; } + hasPrefix = true; + break; } - // @ts-ignore - else if (node[OPTIMIZED]?.match) { - let wrap = true; - // @ts-ignore - const selector = node[OPTIMIZED].selector.reduce((acc, curr) => { - if (curr[0] == "&" && curr.length > 1) { - if (curr[1] == " ") { - curr.splice(0, 2); - } - else { - curr.splice(0, 1); - } - } - else if (combinators.includes(curr[0])) { - curr.unshift("&"); - wrap = false; - } - acc.push(curr); - return acc; - }, []); - if (!wrap) { - wrap = selector.some((s) => s[0] != "&"); - } - let rule = null; - const optimized = node[OPTIMIZED].optimized.slice(); - if (optimized.length > 1) { - const check = optimized.at(-2); - if (!combinators.includes(check)) { - const last = optimized[optimized.length - 1]; - const all = new Array(Math.max(optimized.length - 1, 0)); - let write = 0; - for (let read = 0; read < optimized.length - 1; read++) { - all[write++] = optimized[read]; - } - optimized.length = 0; - for (let index = 0; index < write; index++) { - optimized[index] = all[index]; - } - wrap = false; - rule = - optimized.join("") + - `:is(${selector - .map((s) => { - const next = s[0] == "&" ? new Array(s.length) : new Array(s.length + 1); - let write = 0; - if (s[0] == "&") { - next[write++] = last; - for (let read = 1; read < s.length; read++) { - next[write++] = s[read]; - } - } - else { - next[write++] = last; - for (let read = 0; read < s.length; read++) { - next[write++] = s[read]; - } - } - const rebuilt = new Array(write); - for (let index = 0; index < write; index++) { - rebuilt[index] = next[index]; - } - return rebuilt.join(""); - }) - .join(",")})`; + } + if (hasPrefix) { + const nodes = structuredClone(node.val); + for (const { value } of walkValues(nodes)) { + if (value.typ == exports.EnumToken.IdenTokenType || funcLike.includes(value.typ)) { + const match = value.val.match(/^-([^-]+)-(.+)$/); + if (match != null) { + value.val = match[2]; } } - if (rule == null) { - rule = selector - .map((s) => { - if (s[0] == "&") { - const replacement = node[OPTIMIZED].optimized; - const all = new Array(Math.max(s.length - 1 + replacement.length, 0)); - let write = 0; - for (let read = 0; read < replacement.length; read++) { - all[write++] = replacement[read]; - } - for (let read = 1; read < s.length; read++) { - all[write++] = s[read]; - } - const rebuilt = new Array(write); - for (let index = 0; index < write; index++) { - rebuilt[index] = all[index]; - } - return rebuilt.join(""); - } - return s.join(""); - }) - .join(","); - } - let sel = wrap ? node[OPTIMIZED].optimized.join("") + `:is(${rule})` : rule; - if (sel.length < node.sel.length) { - node.sel = sel; - node[TOKENS] = null; - } } - else if (node[OPTIMIZED]?.reducible) { - if (node[OPTIMIZED].optimized.length === 1) { - const sel1 = node[OPTIMIZED].optimized[0] + - ":is(" + - node[OPTIMIZED].selector.reduce(reducer, []).join(",") + - ")"; - const sel2 = node[OPTIMIZED].selector.reduce((acc, curr) => - // @ts-ignore - (acc.length > 0 ? acc + "," : "") + node[OPTIMIZED].optimized[0] + curr.join(""), ""); - node.sel = sel1.length < sel2.length ? sel1 : sel2; - node[TOKENS] = null; - } - else if (node[OPTIMIZED].optimized.length === 0) { - const testIdent = /^[a-zA-Z]/; - node.sel = node[OPTIMIZED].selector.reduce((acc, curr) => (acc.length > 0 ? acc + "," : "") + - (nestingContent && testIdent.test(curr[0]) ? "& " : "") + - curr.join(""), ""); - node[TOKENS] = null; - } - // @ts-ignore + node.val = nodes; + } + } + else if (node.typ == exports.EnumToken.AtRuleNodeType || node.typ == exports.EnumToken.KeyframesAtRuleNodeType) { + if (node.nam.startsWith("-")) { + const match = node.nam.match(/^-([^-]+)-(.+)$/); + if (match != null && "@" + match[2] in config$2.atRules) { + node.nam = match[2]; } - else if (node[OPTIMIZED]?.optimized.length > 0) { - // @ts-ignore - const sel = node[OPTIMIZED].optimized.join(""); - if (sel.length < node.sel.length) { - node.sel = sel; - // @ts-ignore - node[RAW] = [node[OPTIMIZED].optimized.slice()]; - node[TOKENS] = null; - } + } + if (node.typ == exports.EnumToken.AtRuleNodeType && node.val !== "") { + if (replaceAstNodes(node[TOKENS])) { + node.val = node[TOKENS].reduce((acc, curr) => acc + printValue$1(curr), ""); } - doMinify(node, options, recursive, errors, nestingContent, context); } - if (previous != null) { - if ("chi" in previous && "chi" in node) { - if (previous.typ === node.typ) { - let shouldMerge = true; - let k = previous.chi.length; - while (k-- > 0) { - if (previous.chi[k].typ === exports.EnumToken.CommentNodeType || - previous.chi[k].typ === exports.EnumToken.InvalidRuleNodeType || - previous.chi[k].typ === exports.EnumToken.InvalidRuleNodeType) { - continue; - } - shouldMerge = previous.chi[k].typ === exports.EnumToken.DeclarationNodeType; - break; + } + return node; + } + /** + * Convert -webkit-linear-gradient to linear-gradient syntax + * @param tokens + * @returns + */ + webkitLinearToLinearGradient(token) { + let i; + let k; + let to; + let val; + let key; + const tokens = token.chi; + // conversion rules + // translation: + // - left -> to right + // - right -> to left + // - top -> to bottom + // - top right -> to bottom left + // - top left -> to bottom right + // - bottom -> to top + // - bottom right -> to top left + // - bottom left -> to top right + // https://drafts.csswg.org/css-images-3/#linear-gradients + // final angle: + // - to top -> 0deg + // - to right -> 90deg + // - to bottom -> 180deg + // - to left -> 270deg + for (i = 0; i < tokens.length; i++) { + if (tokens[i].typ == exports.EnumToken.AngleTokenType || + (tokens[i].typ == exports.EnumToken.NumberTokenType && tokens[i].val == 0)) { + tokens[i].val = + (90 - + // @ts-expect-error + (tokens[i].typ == exports.EnumToken.NumberTokenType + ? tokens[i] + : toDegrees(tokens[i]).val)) % + 360; + } + else if (tokens[i].typ == exports.EnumToken.IdenTokenType) { + key = tokens[i].val.toLowerCase(); + switch (key) { + case "right": + case "left": + tokens.splice(i, 1, { typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { + typ: exports.EnumToken.IdenTokenType, + val: key == "right" ? "left" : "right", + }); + i += 2; + break; + case "top": + case "bottom": + k = i + 1; + to = key == "top" ? "bottom" : "top"; + while (k < tokens.length && + (tokens[k].typ === exports.EnumToken.WhitespaceTokenType || + tokens[k].typ === exports.EnumToken.CommentTokenType)) { + k++; } - if (shouldMerge) { - if (((node.typ === exports.EnumToken.RuleNodeType || - node.typ === exports.EnumToken.KeyframesRuleNodeType) && - node.sel === previous.sel) || - // @ts-ignore - (node.typ == exports.EnumToken.AtRuleNodeType && - node.nam !== "font-face" && - // @ts-ignore - node.nam === previous.nam)) { - const array = []; - for (let i = 0; i < previous.chi.length; i++) { - array.push(previous.chi[i]); - } - for (let i = 0; i < node.chi.length; i++) { - array.push(node.chi[i]); - } - // @ts-ignore - node.chi = array; - doMinify(node, options, recursive, errors, nestingContent, context); - ast.chi.splice(nodeIndex, 1); - previous = ast.chi[--i]; - nodeIndex = i; - continue; + if (tokens[k]?.typ === exports.EnumToken.IdenTokenType) { + val = ""; + if (equalsIgnoreCase(tokens[k].val, "left")) { + val = "right"; } - else if (node.typ == previous?.typ && - [exports.EnumToken.KeyframesRuleNodeType, exports.EnumToken.RuleNodeType].includes(node.typ)) { - const intersect = diff$1(previous, node, options); - if (intersect != null) { - if (intersect.node1.chi.length == 0) { - ast.chi.splice(i--, 1); - } - else { - ast.chi.splice(i--, 1, intersect.node1); - } - if (intersect.node2.chi.length == 0) { - if (intersect.result != null) { - ast.chi.splice(nodeIndex, 1, intersect.result); - } - else { - ast.chi.splice(nodeIndex, 1); - } - i--; - if (nodeIndex == i) { - nodeIndex = i; - } - } - else { - if (intersect.result != null) { - ast.chi.splice(nodeIndex, 1, intersect.result, intersect.node2); - } - else { - ast.chi.splice(nodeIndex, 1, intersect.node2); - } - i = (nodeIndex ?? 0) + 1; - } - if (node != ast.chi[i]) { - node = ast.chi[i]; - } - previous = intersect.result; - nodeIndex = i; - } + if (equalsIgnoreCase(tokens[k].val, "right")) { + val = "left"; + } + if (val !== "") { + tokens.splice(i, k - i + 1, { typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: to }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val }); + i += 4; + break; } } - } - if (recursive && previous != null && previous != node) { - if (!hasDeclaration(previous)) { - doMinify(previous, options, recursive, errors, nestingContent, context); - } - } - } - } - if (!nestingContent && - previous != null && - previous.typ == exports.EnumToken.RuleNodeType && - previous.sel.includes("&")) { - fixSelector(previous); - } - previous = node; - nodeIndex = i; - } - // @ts-ignore - if (recursive && node != null && node.chi != null) { - if (node.typ == exports.EnumToken.KeyframesAtRuleNodeType || - !node.chi.some((n) => n.typ == exports.EnumToken.DeclarationNodeType)) { - if (!(node.typ == exports.EnumToken.AtRuleNodeType && node.nam != "font-face")) { - doMinify(node, options, recursive, errors, nestingContent, context); + tokens.splice(i, 1, { typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: to }); + i += 2; + break; } } } - if (!nestingContent && - node != null && - node.typ == exports.EnumToken.RuleNodeType && - node.sel.includes("&")) { - fixSelector(node); - } + token.val = equalsIgnoreCase(token.val, "-webkit-repeating-linear-gradient") + ? "repeating-linear-gradient" + : "linear-gradient"; } - return ast; - } - /** - * Check if a rule has a declaration - * @param node - * - * @private - */ - function hasDeclaration(node) { - // @ts-ignore - for (let i = 0; i < node.chi?.length; i++) { - // @ts-ignore - if (node.chi[i].typ == exports.EnumToken.CommentNodeType) { - continue; + /** + * Convert -webkit-gradient(linear) to linear-gradient or linear-gradient syntax + * @param token + * @returns + */ + webkitGradientToGradient(token) { + let i = 0; + let k; + let key = ""; + let commaCount; + let type = ""; + let tokens = token.chi.slice(); + // linear or radial + if (equalsIgnoreCase(tokens[i].val, "linear")) { + type = "linear-gradient"; + i++; } - // @ts-ignore - return node.chi[i].typ == exports.EnumToken.DeclarationNodeType; - } - return true; - } - /** - * Optimize selector - * @param selector - * - * @private - */ - function optimizeSelector(selector) { - const map = new Set(); - selector = selector - .reduce((acc, curr) => { - // @ts-ignore - if (curr.length > 0 && curr.at(-1).startsWith(":is(")) { - // @ts-ignore - const rules = splitRule(curr.at(-1).slice(4, -1)).map((x) => { - if (x[0] == "&" && x.length > 1) { - return x.slice(x[1] == " " ? 2 : 1); + tokens.splice(0, i + 1); + commaCount = 0; + for (i = 0; i < tokens.length; i++) { + if (tokens[i].typ === exports.EnumToken.CommaTokenType) { + commaCount++; + if (commaCount > 1) { + break; } - return x; - }); - const part = curr.slice(0, -1); - for (const rule of rules) { - acc.push(part.concat(rule)); } - return acc; + if (tokens[i].typ === exports.EnumToken.IdenTokenType) { + key += (key.length > 0 ? " " : "") + tokens[i].val; + } + } + // mapping keywords + // left top → left bottom to bottom + // left bottom → left top to top + // left top → right top to right + // right top → left top to left + // left top → right bottom to bottom right + // right top → left bottom to bottom left + // left bottom → right top to top right + // right bottom → left top to top left + const replacements = []; + if (key === "left top left bottom") { + replacements.push({ typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "bottom" }); + } + else if (key === "left bottom left top") { + replacements.push({ typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "top" }); } - acc.push(curr); - return acc; - }, []) - .filter((x) => { - const str = x.join(""); - if (map.has(str)) { - return false; + else if (key === "left top right top") { + replacements.push({ typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "right" }); } - map.add(str); - return true; - }); - const optimized = []; - const k = selector.reduce((acc, curr) => acc == 0 ? curr.length : curr.length == 0 ? acc : Math.min(acc, curr.length), 0); - let i = 0; - let j; - let match; - for (; i < k; i++) { - const item = selector[0][i]; - match = true; - for (j = 1; j < selector.length; j++) { - if (item != selector[j][i]) { - match = false; - break; - } + else if (key === "right top left top") { + replacements.push({ typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "left" }); } - if (!match) { - break; + else if (key === "left top right bottom") { + replacements.push({ typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "bottom" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "right" }); } - optimized.push(item); - } - while (optimized.length > 0) { - const last = optimized.at(-1); - if (last == " " || combinators.includes(last)) { - const all = new Array(Math.max(optimized.length - 1, 0)); - let write = 0; - for (let read = 0; read < optimized.length - 1; read++) { - all[write++] = optimized[read]; - } - optimized.length = 0; - for (let index = 0; index < write; index++) { - optimized[index] = all[index]; - } - continue; + else if (key === "right top left bottom") { + replacements.push({ typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "bottom" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "left" }); } - break; - } - for (let i1 = 0; i1 < selector.length; i1++) { - const all = new Array(Math.max(selector[i1].length - optimized.length, 0)); - let write = 0; - for (let read = optimized.length; read < selector[i1].length; read++) { - all[write++] = selector[i1][read]; + else if (key === "left bottom right top") { + replacements.push({ typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "top" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "right" }); } - selector[i1].length = 0; - for (let index = 0; index < write; index++) { - selector[i1][index] = all[index]; + else if (key === "right bottom left top") { + replacements.push({ typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "top" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "left" }); } - } - let reducible = optimized.length == 1; - if (optimized[0] == "&") { - if (optimized[1] == " ") { - const all = new Array(Math.max(optimized.length - 2, 0)); - let write = 0; - for (let read = 2; read < optimized.length; read++) { - all[write++] = optimized[read]; + tokens.splice(0, i, ...replacements); + let checkStop = true; + let checkStopIndex = replacements.length + 1; + let colorStop = []; + for (i = replacements.length + 1; i < tokens.length; i++) { + if (tokens[i].typ === exports.EnumToken.CommaTokenType && checkStop) { + checkStopIndex = i; + continue; } - optimized.length = 0; - for (let index = 0; index < write; index++) { - optimized[index] = all[index]; + if (tokens[i].typ === exports.EnumToken.FunctionTokenType) { + if (equalsIgnoreCase(tokens[i].val, "to")) { + colorStop.push(tokens[checkStopIndex]); + for (const token of tokens[i].chi) { + colorStop.push(token); + } + tokens.splice(checkStopIndex, i - checkStopIndex + 1); + i = checkStopIndex; + checkStop = false; + } + else if (equalsIgnoreCase(tokens[i].val, "from")) { + k = tokens[i].chi.length + 1; + tokens.splice(i, 1, ...tokens[i].chi, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.PercentageTokenType, val: 0 }); + i += k; + } + else if (equalsIgnoreCase(tokens[i].val, "color-stop")) { + k = tokens[i].chi.length - 1; + tokens.splice(i, 1, + // @ts-expect-error + ...tokens[i].chi.reverse().map((t) => t.typ === exports.EnumToken.CommaTokenType + ? { typ: exports.EnumToken.WhitespaceTokenType } + : t.typ === exports.EnumToken.NumberTokenType + ? { + typ: exports.EnumToken.PercentageTokenType, + // @ts-expect-error + val: t.val * 100, + } + : t)); + i += k; + } } } - } - if (optimized.length == 0 || optimized[0].charAt(0) == "&" || selector.length == 1) { - return { - match: false, - optimized, - selector: selector.map((selector) => selector[0] == "&" && selector[1] == " " ? selector.slice(2) : selector), - reducible: selector.length > 1 && selector.every((selector) => !combinators.includes(selector[0])), - }; - } - return { - match: true, - optimized, - selector: selector.reduce((acc, curr) => { - let hasCompound = true; - if (hasCompound && curr.length > 0) { - hasCompound = !["&"].concat(combinators).includes(curr[0].charAt(0)); - } - // @ts-ignore - if (hasCompound && curr[0] == " ") { - hasCompound = false; - curr.unshift("&"); + if (colorStop.length > 0) { + for (const t of colorStop) { + tokens.push(t); } - if (curr.length == 0) { - curr.push("&"); - hasCompound = false; + } + if (type !== "") { + token.val = type; + token.chi.length = 0; + for (const t of tokens) { + token.chi.push(t); } - if (reducible) { - const chr = curr[0].charAt(0); - // @ts-ignore - reducible = chr == "." || chr == ":" || isIdentStart(chr.charCodeAt(0)); + } + } + /** + * Convert -webkit-radial-gradient to radial-gradient syntax + * @param tokens + * @returns + */ + webkitRadialToRadialGradient(token) { + let i = 0; + const tokens = token.chi; + while (i < tokens.length && + (tokens[i].typ === exports.EnumToken.WhitespaceTokenType || tokens[i].typ === exports.EnumToken.CommentTokenType)) { + i++; + } + const positions = []; + const form = []; + const size = []; + const colorStops = []; + if (tokens[i]?.typ === exports.EnumToken.PercentageTokenType || + (tokens[i]?.typ === exports.EnumToken.NumberTokenType && 0 === tokens[i].val) || + (tokens[i]?.typ === exports.EnumToken.IdenTokenType && + (equalsIgnoreCase(tokens[i].val, "center") || + equalsIgnoreCase(tokens[i].val, "top") || + equalsIgnoreCase(tokens[i].val, "bottom") || + equalsIgnoreCase(tokens[i].val, "left") || + equalsIgnoreCase(tokens[i].val, "right")))) { + do { + positions.push(tokens[i]); + i++; + } while (tokens[i]?.typ !== exports.EnumToken.CommaTokenType); + if (tokens[i]?.typ === exports.EnumToken.CommaTokenType) { + i++; } - acc.push(hasCompound ? ["&"].concat(curr) : curr); - return acc; - }, []), - reducible: selector.every((selector) => ![">", "+", "~", "&"].includes(selector[0])), - }; - } - /** - * Split selector string - * @param buffer - * - * @internal - */ - function splitRule(buffer) { - const result = [[]]; - let str = ""; - for (let i = 0; i < buffer.length; i++) { - let chr = buffer.charAt(i); - if (isWhiteSpace(chr.charCodeAt(0))) { - if (str !== "") { - // @ts-ignore - result.at(-1).push(str); - str = ""; + } + if (tokens[i]?.typ === exports.EnumToken.IdenTokenType) { + if (equalsIgnoreCase(tokens[i].val, "closest-side") || + equalsIgnoreCase(tokens[i].val, "closest-corner") || + equalsIgnoreCase(tokens[i].val, "farthest-side") || + equalsIgnoreCase(tokens[i].val, "farthest-corner")) { + size.push(tokens[i++]); } - // @ts-ignore - if (result.at(-1).length > 0) { - // @ts-ignore - result.at(-1).push(" "); + else if (equalsIgnoreCase(tokens[i].val, "circle") || + equalsIgnoreCase(tokens[i].val, "ellipse")) { + form.push(tokens[i++]); } - // i = k; - continue; - } - if (chr == ",") { - if (str !== "") { - result.at(-1).push(str); - str = ""; + while (tokens[i]?.typ === exports.EnumToken.WhitespaceTokenType || tokens[i]?.typ === exports.EnumToken.CommentTokenType) { + i++; } - result.push([]); - continue; - } - if (chr == ".") { - if (str !== "") { - result.at(-1).push(str); - str = ""; + if (tokens[i]?.typ === exports.EnumToken.CommaTokenType) { + i++; } - str += chr; - continue; } - if (combinators.includes(chr)) { - if (str !== "") { - result.at(-1).push(str); - str = ""; + if (tokens[i]?.typ === exports.EnumToken.IdenTokenType) { + if (equalsIgnoreCase(tokens[i].val, "closest-side") || + equalsIgnoreCase(tokens[i].val, "closest-corner") || + equalsIgnoreCase(tokens[i].val, "farthest-side") || + equalsIgnoreCase(tokens[i].val, "farthest-corner")) { + size.push(tokens[i++]); } - if (chr == "|" && buffer.charAt(i + 1) == "|") { - chr += buffer.charAt(++i); + else if (equalsIgnoreCase(tokens[i].val, "circle") || + equalsIgnoreCase(tokens[i].val, "ellipse")) { + form.push(tokens[i++]); } - result.at(-1).push(chr); - continue; - } - if (chr == ":") { - if (str !== "") { - result.at(-1).push(str); - str = ""; + while (tokens[i]?.typ === exports.EnumToken.WhitespaceTokenType || tokens[i]?.typ === exports.EnumToken.CommentTokenType) { + i++; } - if (buffer.charAt(i + 1) == ":") { - chr += buffer.charAt(++i); + if (tokens[i]?.typ === exports.EnumToken.CommaTokenType) { + i++; } - str += chr; - continue; } - str += chr; - if (chr == "\\") { - str += buffer.charAt(++i); - continue; + for (let m = i; m < tokens.length; m++) { + colorStops.push(tokens[m]); } - if (chr == "(" || chr == "[") { - const open = chr; - const close = chr == "(" ? ")" : "]"; - let inParens = 1; - let k = i; - while (++k < buffer.length) { - chr = buffer.charAt(k); - if (chr == "\\") { - str += buffer.slice(k, k + 2); - k++; - continue; - } - str += chr; - if (chr == open) { - inParens++; - } - else if (chr == close) { - inParens--; + tokens.length = 0; + if (form.length > 0 || size.length > 0) { + if (form.length === 0) { + form.push({ typ: exports.EnumToken.IdenTokenType, val: "ellipse" }); + } + if (size.length > 0) { + form.push({ typ: exports.EnumToken.WhitespaceTokenType }); + for (const token of size) { + form.push(token); } - if (inParens == 0) { - break; + } + if (positions.length > 0) { + form.push({ typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "at" }, { typ: exports.EnumToken.WhitespaceTokenType }); + for (const position of positions) { + form.push(position); } } - i = k; + for (const token of form) { + tokens.push(token); + } + tokens.push({ typ: exports.EnumToken.CommaTokenType }); + } + token.val = equalsIgnoreCase(token.val, "-webkit-repeating-radial-gradient") + ? "repeating-radial-gradient" + : "radial-gradient"; + for (const colorStop of colorStops) { + tokens.push(colorStop); + } + return tokens; + } + } + + function inlineExpression(token) { + const result = []; + if (token.typ == exports.EnumToken.BinaryExpressionTokenType) { + const chi = inlineExpression(token.l); + chi.push({ typ: token.op }); + for (const child of inlineExpression(token.r)) { + chi.push(child); } + result.push({ + typ: exports.EnumToken.ParensTokenType, + chi, + }); } - if (str !== "") { - result.at(-1).push(str); + else { + result.push(token); } return result; } - /** - * Reduce selector - * @param acc - * @param curr - * - * @private - */ - function reduceSelector(acc, curr) { - let hasCompoundSelector = true; - // @ts-ignore - curr = curr.slice(this.match[0].length); - while (curr.length > 0) { - if (curr[0] == " ") { - hasCompoundSelector = false; - curr.unshift("&"); - continue; + function replace(node, variableScope) { + for (const { value, parent: parentValue } of walkValues(node.val)) { + if (value.typ == exports.EnumToken.BinaryExpressionTokenType && parentValue != null && "chi" in parentValue) { + // @ts-ignore + parentValue.chi.splice(parentValue.chi.indexOf(value), 1, ...inlineExpression(value)); } - break; - } - if (hasCompoundSelector && curr.length > 0) { - hasCompoundSelector = !["&"].concat(combinators).includes(curr[0].charAt(0)); } - if (curr[0] == ":is(") { - let canReduce = true; - const isCompound = curr.reduce((acc, token, index) => { - if (index == 0) { - canReduce = curr[1] == "&"; - } - else if (token == ")") ; - else if (token == ",") { - if (!canReduce) { - canReduce = curr[index + 1] == "&"; + for (const { value, parent: parentValue } of walkValues(node.val)) { + if (value.typ == exports.EnumToken.MathFunctionTokenDefType || + (value.typ == exports.EnumToken.WildCardFunctionTokenType && value.val == "var")) { + if (value.chi.length == 1 && + value.chi[0].typ == exports.EnumToken.DashedIdenTokenType) { + const info = (variableScope.get(value.chi[0].val)); + if (info?.replaceable) { + if (parentValue != null) { + let i = 0; + for (; i < parentValue.chi.length; i++) { + if (parentValue.chi[i] == value) { + parentValue.chi.splice(i, 1, ...info.node.val); + break; + } + } + } + else { + node.val = info.node.val.slice(); + } } - acc.push([]); } - else - acc.at(-1)?.push(token); - return acc; - }, [[]]); - if (canReduce) { - curr = isCompound.reduce((acc, curr) => { - if (acc.length > 0) { - acc.push(","); - } - for (const c of curr) { - acc.push(c); - } - return acc; - }, []); } } - acc.push( - // @ts-ignore - this.match.length == 0 - ? ["&"] - : hasCompoundSelector && curr[0] != "&" && (curr.length == 0 || !combinators.includes(curr[0].charAt(0))) - ? ["&"].concat(curr) - : curr); - return acc; } - /** - * Match selectors - * @param selector1 - * @param selector2 - * - * @private - */ - function matchSelectors(selector1, selector2) { - let match = [[]]; - const j = Math.min(selector1.reduce((acc, curr) => Math.min(acc, curr.length), selector1.length > 0 ? selector1[0].length : 0), selector2.reduce((acc, curr) => Math.min(acc, curr.length), selector2.length > 0 ? selector2[0].length : 0)); - let i = 0; - let k; - let l; - let token; - let matching = true; - let matchFunction = 0; - let inAttr = 0; - const regEx = /^:is\(([:.][^\s,]+)\)$/; - for (const _1 of selector1) { - if (_1[0] !== "&") { - continue; + class InlineCssVariablesFeature { + accept = new Set([exports.EnumToken.RuleNodeType, exports.EnumToken.AtRuleNodeType]); + get ordering() { + return 2; + } + get processMode() { + return exports.FeatureWalkMode.Post; + } + static register(options) { + if (options.inlineCssVariables) { + // @ts-ignore + options.features.push(new InlineCssVariablesFeature()); } - for (let i = 1; i < _1.length; i++) { - const token = _1[i]; - if (token.startsWith(":is(")) { - const match = regEx.exec(token); - if (match != null) { - _1[i] = match[1]; + } + run(ast, options = {}, parent, context) { + if (!("chi" in ast)) { + return null; + } + if (!("variableScope" in context)) { + context.variableScope = new Map(); + } + // [':root', 'html'] + const isRoot = parent.typ == exports.EnumToken.StyleSheetNodeType && + ast.typ == exports.EnumToken.RuleNodeType && + (ast[RAW] ?? splitRule(ast.sel)).some((segment) => segment.some((s) => s == ":root" || s == "html")); + const variableScope = context.variableScope; + // @ts-ignore + for (const node of ast.chi) { + if (node.typ !== exports.EnumToken.DeclarationNodeType) { + continue; + } + // css variable + if (node.nam.startsWith("--")) { + if (!variableScope.has(node.val)) { + const info = { + globalScope: isRoot, + // @ts-ignore + parent: new Set(), + declarationCount: 1, + replaceable: isRoot, + node: node, + values: structuredClone(node.val), + }; + info.parent.add(ast); + variableScope.set(node.nam, info); + let recursive = false; + for (const { value } of walkValues(node.val)) { + if (value?.typ == exports.EnumToken.MathFunctionTokenDefType || + (value?.typ == exports.EnumToken.WildCardFunctionTokenType && + (mathFuncs.includes(value.val) || + value.val === "var"))) { + recursive = true; + break; + } + } + if (recursive) { + replace(node, variableScope); + } + } + } + else { + replace(node, variableScope); + } + } + return null; + } + cleanup(ast, options = {}, context) { + const variableScope = context.variableScope; + if (variableScope == null) { + return; + } + for (const info of variableScope.values()) { + if (info.replaceable) { + let i; + // drop declarations from :root{} + for (const parent of info.parent) { + i = parent.chi?.length ?? 0; + while (i--) { + if (parent.chi[i] == info.node) { + // @ts-ignore + parent.chi.splice(i, 1, { + typ: exports.EnumToken.CommentTokenType, + val: `/* ${info.node.nam}: ${info.values.reduce((acc, curr) => acc + printNode(curr, { convertColor: false }), "")} */`, + }); + break; + } + } } } } } - for (const _1 of selector2) { - if (_1[0] !== "&") { - continue; - } - for (let i = 1; i < _1.length; i++) { - const token = _1[i]; - if (token.startsWith(":is(")) { - const match = regEx.exec(token); - if (match != null) { - _1[i] = match[1]; + } + + function dedup(values) { + for (const value of values) { + let i = value.length; + while (i-- > 1) { + const t = value[i]; + const k = value[i == 1 ? 0 : i % 2]; + if (t.val == k.val && t.val == 0) { + if ((t.typ == exports.EnumToken.NumberTokenType && isLength(k)) || + (k.typ == exports.EnumToken.NumberTokenType && isLength(t)) || + isLength(k) || + isLength(t)) { + value.splice(i, 1); + continue; } } + if (eq(t, k)) { + value.splice(i, 1); + continue; + } + break; } } - for (; i < j; i++) { - k = 0; - token = selector1[0][i]; - for (; k < selector1.length; k++) { - if (selector1[k][i] != token) { - matching = false; - break; - } + return values; + } + class PropertySet { + config; + declarations; + constructor(config) { + this.config = config; + this.declarations = new Map(); + } + add(declaration) { + if (declaration.nam == this.config.shorthand) { + this.declarations = new Map(); } - if (matching) { - l = 0; - for (; l < selector2.length; l++) { - if (selector2[l][i] != token) { - matching = false; - break; + else { + // expand shorthand + if (this.declarations.has(this.config.shorthand)) { + let isValid = true; + let current = -1; + const tokens = []; + // @ts-ignore + for (let token of this.declarations.get(this.config.shorthand).val) { + if ( + // @ts-expect-error + this.config.types.some((t) => token.typ == exports.EnumToken[t]) || + (token.typ == exports.EnumToken.NumberTokenType && + token.val == 0 && + (this.config.types.includes("Length") || + this.config.types.includes("Angle") || + this.config.types.includes("Dimension")))) { + if (tokens.length == 0) { + tokens.push([]); + current++; + } + tokens[current].push(token); + continue; + } + if (token.typ != exports.EnumToken.WhitespaceTokenType && token.typ != exports.EnumToken.CommentTokenType) { + if (token.typ == exports.EnumToken.IdenTokenType && + this.config.keywords.includes(token.val)) { + if (tokens.length == 0) { + tokens.push([]); + current++; + } + tokens[current].push(token); + } + // @ts-ignore + if (token.typ == exports.EnumToken.LiteralTokenType && token.val == this.config.separator?.val) { + tokens.push([]); + current++; + continue; + } + isValid = false; + break; + } + } + if (isValid && tokens.length > 0) { + this.declarations.delete(this.config.shorthand); + for (const values of tokens) { + this.config.properties.forEach((property, index) => { + if (!this.declarations.has(property)) { + this.declarations.set(property, { + typ: exports.EnumToken.DeclarationNodeType, + nam: property, + val: [], + }); + } + while (index > 0 && index >= values.length) { + if (index > 1) { + index %= 2; + } + else { + index = 0; + break; + } + } + // @ts-ignore + const val = this.declarations.get(property).val; + if (val.length > 0) { + val.push({ typ: exports.EnumToken.WhitespaceTokenType }); + } + val.push({ ...values[index] }); + }); + } } + this.declarations.set(declaration.nam, declaration); + return this; } } - if (!matching) { - break; - } - if (token.endsWith("(")) { - matchFunction++; - } - match.at(-1).push(token); + this.declarations.set(declaration.nam, declaration); + return this; } - // invalid function - if (matchFunction != 0 || inAttr != 0) { - return null; + isShortHand() { + if (this.declarations.has(this.config.shorthand)) { + return this.declarations.size == 1; + } + return this.config.properties.length == this.declarations.size; } - for (const part of match) { - while (part.length > 0) { - const token = part.at(-1); - if (token == " " || combinators.includes(token) || notEndingWith.includes(token.at(-1))) { - part.pop(); - continue; + [Symbol.iterator]() { + let iterator; + const declarations = this.declarations; + if (declarations.size < this.config.properties.length) { + const values = [...declarations.values()]; + if (this.isShortHand()) { + const val = values[0].val.reduce((acc, curr) => { + if (![exports.EnumToken.WhitespaceTokenType, exports.EnumToken.CommentTokenType].includes(curr.typ)) { + acc.push(curr); + } + return acc; + }, []); + values[0].val = val.reduce((acc, curr) => { + if (acc.length > 0) { + acc.push({ typ: exports.EnumToken.WhitespaceTokenType }); + } + acc.push(curr); + return acc; + }, []); } - break; + return values[Symbol.iterator](); + } + else { + const values = []; + this.config.properties.forEach((property) => { + let index = 0; + // @ts-ignore + for (const token of this.declarations.get(property)?.val ?? []) { + if (token == null) { + return this.declarations.values()[Symbol.iterator](); + } + if (token.typ == exports.EnumToken.WhitespaceTokenType) { + continue; + } + if (values.length == index) { + values.push([]); + } + values[index].push(token); + index++; + } + }); + if (this.config.valueSeparator == null) { + dedup(values); + } + iterator = [ + { + typ: exports.EnumToken.DeclarationNodeType, + nam: this.config.shorthand, + val: values.reduce((acc, curr) => { + if (curr.length > 1) { + const k = curr.length * 2 - 1; + let i = 1; + while (i < k) { + curr.splice(i, 0, { typ: exports.EnumToken.WhitespaceTokenType }); + i += 2; + } + } + if (acc.length > 0) { + // @ts-ignore + acc.push({ ...this.config.separator, typ: exports.EnumToken.LiteralTokenType }); + } + for (const token of curr) { + acc.push(token); + } + return acc; + }, []), + }, + ][Symbol.iterator](); } + return iterator; + } + } + + var properties = { + gap: { + shorthand: "gap", + properties: [ + "row-gap", + "column-gap" + ], + types: [ + "Length", + "Perc" + ], + multiple: false, + separator: null, + keywords: [ + "normal" + ] + }, + "row-gap": { + shorthand: "gap" + }, + "column-gap": { + shorthand: "gap" + }, + inset: { + shorthand: "inset", + properties: [ + "top", + "right", + "bottom", + "left" + ], + types: [ + "Length", + "Perc" + ], + multiple: false, + separator: null, + keywords: [ + "auto" + ] + }, + top: { + shorthand: "inset" + }, + right: { + shorthand: "inset" + }, + bottom: { + shorthand: "inset" + }, + left: { + shorthand: "inset" + }, + margin: { + shorthand: "margin", + properties: [ + "margin-top", + "margin-right", + "margin-bottom", + "margin-left" + ], + types: [ + "Length", + "Perc" + ], + multiple: false, + separator: null, + keywords: [ + "auto" + ] + }, + "margin-top": { + shorthand: "margin" + }, + "margin-right": { + shorthand: "margin" + }, + "margin-bottom": { + shorthand: "margin" + }, + "margin-left": { + shorthand: "margin" + }, + padding: { + shorthand: "padding", + properties: [ + "padding-top", + "padding-right", + "padding-bottom", + "padding-left" + ], + types: [ + "Length", + "Perc" + ], + keywords: [ + ] + }, + "padding-top": { + shorthand: "padding" + }, + "padding-right": { + shorthand: "padding" + }, + "padding-bottom": { + shorthand: "padding" + }, + "padding-left": { + shorthand: "padding" + }, + "scroll-margin": { + shorthand: "scroll-margin", + properties: [ + "scroll-margin-top", + "scroll-margin-right", + "scroll-margin-bottom", + "scroll-margin-left" + ], + types: [ + "Length", + "Perc" + ], + "default": [ + "0" + ], + keywords: [ + "auto" + ] + }, + "scroll-margin-top": { + shorthand: "scroll-margin" + }, + "scroll-margin-right": { + shorthand: "scroll-margin" + }, + "scroll-margin-bottom": { + shorthand: "scroll-margin" + }, + "scroll-margin-left": { + shorthand: "scroll-margin" + }, + "scroll-padding": { + shorthand: "scroll-padding", + properties: [ + "scroll-padding-top", + "scroll-padding-right", + "scroll-padding-bottom", + "scroll-padding-left" + ], + types: [ + "Length", + "Perc" + ], + keywords: [ + "auto" + ] + }, + "scroll-padding-top": { + shorthand: "scroll-padding" + }, + "scroll-padding-right": { + shorthand: "scroll-padding" + }, + "scroll-padding-bottom": { + shorthand: "scroll-padding" + }, + "scroll-padding-left": { + shorthand: "scroll-padding" + }, + "border-radius": { + shorthand: "border-radius", + properties: [ + "border-top-left-radius", + "border-top-right-radius", + "border-bottom-right-radius", + "border-bottom-left-radius" + ], + types: [ + "Length", + "Perc" + ], + multiple: true, + separator: { + typ: "Literal", + val: "/" + }, + keywords: [ + ] + }, + "border-top-left-radius": { + shorthand: "border-radius" + }, + "border-top-right-radius": { + shorthand: "border-radius" + }, + "border-bottom-right-radius": { + shorthand: "border-radius" + }, + "border-bottom-left-radius": { + shorthand: "border-radius" + }, + "border-width": { + shorthand: "border-width", + map: "border", + properties: [ + "border-top-width", + "border-right-width", + "border-bottom-width", + "border-left-width" + ], + types: [ + "Length", + "Perc" + ], + "default": [ + "medium" + ], + keywords: [ + "thin", + "medium", + "thick" + ] + }, + "border-top-width": { + map: "border", + shorthand: "border-width" + }, + "border-right-width": { + map: "border", + shorthand: "border-width" + }, + "border-bottom-width": { + map: "border", + shorthand: "border-width" + }, + "border-left-width": { + map: "border", + shorthand: "border-width" + }, + "border-style": { + shorthand: "border-style", + map: "border", + properties: [ + "border-top-style", + "border-right-style", + "border-bottom-style", + "border-left-style" + ], + types: [ + ], + "default": [ + "none" + ], + keywords: [ + "none", + "hidden", + "dotted", + "dashed", + "solid", + "double", + "groove", + "ridge", + "inset", + "outset" + ] + }, + "border-top-style": { + map: "border", + shorthand: "border-style" + }, + "border-right-style": { + map: "border", + shorthand: "border-style" + }, + "border-bottom-style": { + map: "border", + shorthand: "border-style" + }, + "border-left-style": { + map: "border", + shorthand: "border-style" + }, + "border-color": { + shorthand: "border-color", + map: "border", + properties: [ + "border-top-color", + "border-right-color", + "border-bottom-color", + "border-left-color" + ], + types: [ + "Color" + ], + "default": [ + "currentcolor" + ], + keywords: [ + ] + }, + "border-top-color": { + map: "border", + shorthand: "border-color" + }, + "border-right-color": { + map: "border", + shorthand: "border-color" + }, + "border-bottom-color": { + map: "border", + shorthand: "border-color" + }, + "border-left-color": { + map: "border", + shorthand: "border-color" + } + }; + var map = { + "flex-flow": { + shorthand: "flex-flow", + pattern: "flex-direction flex-wrap", + keywords: [ + ], + "default": [ + "row", + "nowrap" + ], + properties: { + "flex-direction": { + keywords: [ + "row", + "row-reverse", + "column", + "column-reverse" + ], + "default": [ + "row" + ], + types: [ + ] + }, + "flex-wrap": { + keywords: [ + "wrap", + "nowrap", + "wrap-reverse" + ], + "default": [ + "nowrap" + ], + types: [ + ] + } + } + }, + "flex-direction": { + shorthand: "flex-flow" + }, + "flex-wrap": { + shorthand: "flex-flow" + }, + "scroll-timeline": { + shorthand: "scroll-timeline", + pattern: "scroll-timeline-name scroll-timeline-axis", + keywords: [ + "none", + "block", + "inline", + "x", + "y" + ], + "default": [ + "none", + "block" + ], + properties: { + "scroll-timeline-name": { + keywords: [ + "none" + ], + "default": [ + "none" + ], + types: [ + "DashedIden" + ] + }, + "scroll-timeline-axis": { + keywords: [ + "block", + "inline", + "x", + "y" + ], + "default": [ + "block" + ], + types: [ + ] + } + } + }, + "scroll-timeline-name": { + shorthand: "scroll-timeline" + }, + "scroll-timeline-axis": { + shorthand: "scroll-timeline" + }, + container: { + shorthand: "container", + pattern: "container-name container-type", + keywords: [ + ], + "default": [ + ], + properties: { + "container-name": { + required: true, + multiple: true, + keywords: [ + "none" + ], + "default": [ + "none" + ], + types: [ + "Iden", + "DashedIden" + ] + }, + "container-type": { + previous: "container-name", + prefix: { + typ: "Literal", + val: "/" + }, + keywords: [ + "size", + "inline-size", + "normal" + ], + "default": [ + "normal" + ], + types: [ + ] + } + } + }, + "container-name": { + shorthand: "container" + }, + "container-type": { + shorthand: "container" + }, + flex: { + shorthand: "flex", + pattern: "flex-grow flex-shrink flex-basis", + keywords: [ + "auto", + "none", + "initial" + ], + "default": [ + ], + mapping: { + "0 1 auto": "initial", + "0 0 auto": "none", + "1 1 auto": "auto" + }, + properties: { + "flex-grow": { + required: true, + keywords: [ + ], + "default": [ + ], + types: [ + "Number" + ] + }, + "flex-shrink": { + keywords: [ + ], + "default": [ + ], + types: [ + "Number" + ] + }, + "flex-basis": { + keywords: [ + "max-content", + "min-content", + "fit-content", + "fit-content", + "content", + "auto" + ], + "default": [ + ], + types: [ + "Length", + "Perc" + ] + } + } + }, + "flex-grow": { + shorthand: "flex" + }, + "flex-shrink": { + shorthand: "flex" + }, + "flex-basis": { + shorthand: "flex" + }, + columns: { + shorthand: "columns", + pattern: "column-count column-width", + keywords: [ + "auto" + ], + "default": [ + "auto", + "auto auto" + ], + properties: { + "column-count": { + keywords: [ + "auto" + ], + "default": [ + "auto" + ], + types: [ + "Number" + ] + }, + "column-width": { + keywords: [ + "auto" + ], + "default": [ + "auto" + ], + types: [ + "Length" + ] + } + } + }, + "column-count": { + shorthand: "columns" + }, + "column-width": { + shorthand: "columns" + }, + transition: { + shorthand: "transition", + multiple: true, + separator: { + typ: "Comma" + }, + pattern: "transition-property transition-duration transition-timing-function transition-delay transition-behavior", + keywords: [ + "none", + "all" + ], + "default": [ + "0s", + "0ms", + "all", + "ease", + "none", + "normal" + ], + mapping: { + "cubic-bezier(.25,.1,.25,1)": "ease", + "cubic-bezier(0,0,1,1)": "linear", + "cubic-bezier(.42,0,1,1)": "ease-in", + "cubic-bezier(0,0,.58,1)": "ease-out", + "cubic-bezier(.42,0,.58,.42)": "ease-in-out" + }, + properties: { + "transition-property": { + keywords: [ + "none", + "all" + ], + "default": [ + "all" + ], + types: [ + "Iden" + ] + }, + "transition-duration": { + keywords: [ + ], + "default": [ + "0s", + "0ms", + "normal" + ], + types: [ + "Time" + ] + }, + "transition-timing-function": { + keywords: [ + "ease", + "ease-in", + "ease-out", + "ease-in-out", + "linear", + "step-start", + "step-end" + ], + "default": [ + "ease" + ], + types: [ + "TimingFunction" + ], + mapping: { + "cubic-bezier(.25,.1,.25,1)": "ease", + "cubic-bezier(0,0,1,1)": "linear", + "cubic-bezier(.42,0,1,1)": "ease-in", + "cubic-bezier(0,0,.58,1)": "ease-out", + "cubic-bezier(.42,0,.58,.42)": "ease-in-out" + } + }, + "transition-delay": { + keywords: [ + ], + "default": [ + "0s" + ], + types: [ + "Time" + ] + }, + "transition-behavior": { + keywords: [ + "normal", + "allow-discrete" + ], + "default": [ + "normal" + ], + types: [ + ] + } + } + }, + "transition-property": { + shorthand: "transition" + }, + "transition-duration": { + shorthand: "transition" + }, + "transition-timing-function": { + shorthand: "transition" + }, + "transition-delay": { + shorthand: "transition" + }, + "transition-behavior": { + shorthand: "transition" + }, + animation: { + shorthand: "animation", + separator: { + typ: "Comma" + }, + pattern: "animation-name animation-duration animation-timing-function animation-delay animation-iteration-count animation-direction animation-fill-mode animation-play-state animation-timeline", + "default": [ + "1", + "0s", + "0ms", + "none", + "ease", + "normal", + "running", + "auto" + ], + properties: { + "animation-name": { + keywords: [ + "none" + ], + "default": [ + "none" + ], + types: [ + "Iden" + ] + }, + "animation-duration": { + keywords: [ + "auto" + ], + "default": [ + "0s", + "0ms", + "auto" + ], + types: [ + "Time" + ], + mapping: { + auto: "0s" + } + }, + "animation-timing-function": { + keywords: [ + "ease", + "ease-in", + "ease-out", + "ease-in-out", + "linear", + "step-start", + "step-end" + ], + "default": [ + "ease" + ], + types: [ + "TimingFunction" + ], + mapping: { + "cubic-bezier(.25,.1,.25,1)": "ease", + "cubic-bezier(0,0,1,1)": "linear", + "cubic-bezier(.42,0,1,1)": "ease-in", + "cubic-bezier(0,0,.58,1)": "ease-out", + "cubic-bezier(.42,0,.58,.42)": "ease-in-out" + } + }, + "animation-delay": { + keywords: [ + ], + "default": [ + "0s", + "0ms" + ], + types: [ + "Time" + ] + }, + "animation-iteration-count": { + keywords: [ + "infinite" + ], + "default": [ + "1" + ], + types: [ + "Number" + ] + }, + "animation-direction": { + keywords: [ + "normal", + "reverse", + "alternate", + "alternate-reverse" + ], + "default": [ + "normal" + ], + types: [ + ] + }, + "animation-fill-mode": { + keywords: [ + "none", + "forwards", + "backwards", + "both" + ], + "default": [ + "none" + ], + types: [ + ] + }, + "animation-play-state": { + keywords: [ + "running", + "paused" + ], + "default": [ + "running" + ], + types: [ + ] + }, + "animation-timeline": { + keywords: [ + "none", + "auto" + ], + "default": [ + "auto" + ], + types: [ + "DashedIden", + "TimelineFunction" + ] + } + } + }, + "animation-name": { + shorthand: "animation" + }, + "animation-duration": { + shorthand: "animation" + }, + "animation-timing-function": { + shorthand: "animation" + }, + "animation-delay": { + shorthand: "animation" + }, + "animation-iteration-count": { + shorthand: "animation" + }, + "animation-direction": { + shorthand: "animation" + }, + "animation-fill-mode": { + shorthand: "animation" + }, + "animation-play-state": { + shorthand: "animation" + }, + "animation-timeline": { + shorthand: "animation" + }, + "text-emphasis": { + shorthand: "text-emphasis", + pattern: "text-emphasis-color text-emphasis-style", + "default": [ + "none", + "currentcolor" + ], + properties: { + "text-emphasis-style": { + keywords: [ + "none", + "filled", + "open", + "dot", + "circle", + "double-circle", + "triangle", + "sesame" + ], + "default": [ + "none" + ], + types: [ + "String" + ] + }, + "text-emphasis-color": { + "default": [ + "currentcolor" + ], + types: [ + "Color" + ] + } + } + }, + "text-emphasis-style": { + shorthand: "text-emphasis" + }, + "text-emphasis-color": { + shorthand: "text-emphasis" + }, + border: { + shorthand: "border", + pattern: "border-color border-style border-width", + keywords: [ + "none" + ], + "default": [ + "0", + "none" + ], + properties: { + "border-color": { + types: [ + "Color" + ], + "default": [ + "currentcolor" + ], + keywords: [ + ] + }, + "border-style": { + types: [ + ], + "default": [ + "none" + ], + keywords: [ + "none", + "hidden", + "dotted", + "dashed", + "solid", + "double", + "groove", + "ridge", + "inset", + "outset" + ] + }, + "border-width": { + types: [ + "Length", + "Perc" + ], + "default": [ + "medium" + ], + keywords: [ + "thin", + "medium", + "thick" + ] + } + } + }, + "border-color": { + shorthand: "border" + }, + "border-style": { + shorthand: "border" + }, + "border-width": { + shorthand: "border" + }, + "list-style": { + shorthand: "list-style", + pattern: "list-style-type list-style-position list-style-image", + keywords: [ + "none", + "outside" + ], + "default": [ + "none", + "outside" + ], + properties: { + "list-style-position": { + types: [ + ], + "default": [ + "outside" + ], + keywords: [ + "inside", + "outside" + ] + }, + "list-style-image": { + "default": [ + "none" + ], + keywords: [ + "node" + ], + types: [ + "UrlFunc", + "ImageFunc" + ] + }, + "list-style-type": { + types: [ + "String", + "Iden", + "Symbols" + ], + "default": [ + "disc" + ], + keywords: [ + "disc", + "circle", + "square", + "decimal", + "decimal-leading-zero", + "lower-roman", + "upper-roman", + "lower-greek", + "lower-latin", + "upper-latin", + "none" + ] + } + } + }, + "list-style-position": { + shorthand: "list-style" + }, + "list-style-image": { + shorthand: "list-style" + }, + "list-style-type": { + shorthand: "list-style" + }, + overflow: { + shorthand: "overflow", + pattern: "overflow-x overflow-y", + keywords: [ + "auto", + "visible", + "hidden", + "clip", + "scroll" + ], + "default": [ + ], + mapping: { + "visible visible": "visible", + "auto auto": "auto", + "hidden hidden": "hidden", + "scroll scroll": "scroll" + }, + properties: { + "overflow-x": { + "default": [ + ], + types: [ + ], + keywords: [ + "auto", + "visible", + "hidden", + "clip", + "scroll" + ] + }, + "overflow-y": { + "default": [ + ], + types: [ + ], + keywords: [ + "auto", + "visible", + "hidden", + "clip", + "scroll" + ] + } + } + }, + "overflow-x": { + shorthand: "overflow" + }, + "overflow-y": { + shorthand: "overflow" + }, + outline: { + shorthand: "outline", + pattern: "outline-color outline-style outline-width", + keywords: [ + "none" + ], + "default": [ + "0", + "none", + "currentcolor" + ], + properties: { + "outline-color": { + types: [ + "Color" + ], + "default": [ + "currentcolor" + ], + keywords: [ + "currentcolor" + ] + }, + "outline-style": { + types: [ + ], + "default": [ + "none" + ], + keywords: [ + "auto", + "none", + "dotted", + "dashed", + "solid", + "double", + "groove", + "ridge", + "inset", + "outset" + ] + }, + "outline-width": { + types: [ + "Length", + "Perc" + ], + "default": [ + "medium" + ], + keywords: [ + "thin", + "medium", + "thick" + ] + } + } + }, + "outline-color": { + shorthand: "outline" + }, + "outline-style": { + shorthand: "outline" + }, + "outline-width": { + shorthand: "outline" + }, + font: { + shorthand: "font", + pattern: "font-weight font-style font-size line-height font-stretch font-variant font-family", + keywords: [ + "caption", + "icon", + "menu", + "message-box", + "small-caption", + "status-bar", + "-moz-window, ", + "-moz-document, ", + "-moz-desktop, ", + "-moz-info, ", + "-moz-dialog", + "-moz-button", + "-moz-pull-down-menu", + "-moz-list", + "-moz-field" + ], + "default": [ + ], + properties: { + "font-weight": { + types: [ + "Number" + ], + "default": [ + "400", + "normal" + ], + keywords: [ + "normal", + "bold", + "lighter", + "bolder" + ], + constraints: { + value: { + min: "1", + max: "1000" + } + }, + mapping: { + thin: "100", + hairline: "100", + "extra light": "200", + "ultra light": "200", + light: "300", + normal: "400", + regular: "400", + medium: "500", + "semi bold": "600", + "demi bold": "600", + bold: "700", + "extra bold": "800", + "ultra bold": "800", + black: "900", + heavy: "900", + "extra black": "950", + "ultra black": "950" + } + }, + "font-style": { + types: [ + "Angle" + ], + "default": [ + "normal" + ], + keywords: [ + "normal", + "italic", + "oblique" + ] + }, + "font-size": { + types: [ + "Length", + "Perc" + ], + "default": [ + ], + keywords: [ + "xx-small", + "x-small", + "small", + "medium", + "large", + "x-large", + "xx-large", + "xxx-large", + "larger", + "smaller" + ], + required: true + }, + "line-height": { + types: [ + "Length", + "Perc", + "Number" + ], + "default": [ + "normal" + ], + keywords: [ + "normal" + ], + previous: "font-size", + prefix: { + typ: "Literal", + val: "/" + } + }, + "font-stretch": { + types: [ + "Perc" + ], + "default": [ + "normal" + ], + keywords: [ + "ultra-condensed", + "extra-condensed", + "condensed", + "semi-condensed", + "normal", + "semi-expanded", + "expanded", + "extra-expanded", + "ultra-expanded" + ], + mapping: { + "ultra-condensed": "50%", + "extra-condensed": "62.5%", + condensed: "75%", + "semi-condensed": "87.5%", + normal: "100%", + "semi-expanded": "112.5%", + expanded: "125%", + "extra-expanded": "150%", + "ultra-expanded": "200%" + } + }, + "font-variant": { + types: [ + ], + "default": [ + "normal" + ], + keywords: [ + "normal", + "none", + "common-ligatures", + "no-common-ligatures", + "discretionary-ligatures", + "no-discretionary-ligatures", + "historical-ligatures", + "no-historical-ligatures", + "contextual", + "no-contextual", + "historical-forms", + "small-caps", + "all-small-caps", + "petite-caps", + "all-petite-caps", + "unicase", + "titling-caps", + "ordinal", + "slashed-zero", + "lining-nums", + "oldstyle-nums", + "proportional-nums", + "tabular-nums", + "diagonal-fractions", + "stacked-fractions", + "ordinal", + "slashed-zero", + "ruby", + "jis78", + "jis83", + "jis90", + "jis04", + "simplified", + "traditional", + "full-width", + "proportional-width", + "ruby", + "sub", + "super", + "text", + "emoji", + "unicode" + ] + }, + "font-family": { + types: [ + "String", + "Iden" + ], + "default": [ + ], + keywords: [ + "serif", + "sans-serif", + "monospace", + "cursive", + "fantasy", + "system-ui", + "ui-serif", + "ui-sans-serif", + "ui-monospace", + "ui-rounded", + "math", + "emoji", + "fangsong" + ], + required: true, + multiple: true, + separator: { + typ: "Comma" + } + } + } + }, + "font-weight": { + shorthand: "font" + }, + "font-style": { + shorthand: "font" + }, + "font-size": { + shorthand: "font" + }, + "line-height": { + shorthand: "font" + }, + "font-stretch": { + shorthand: "font" + }, + "font-variant": { + shorthand: "font" + }, + "font-family": { + shorthand: "font" + }, + background: { + shorthand: "background", + pattern: "background-attachment background-origin background-clip background-color background-image background-repeat background-position background-size", + keywords: [ + "none" + ], + "default": [ + "0 0", + "none", + "auto", + "repeat", + "transparent", + "#0000", + "scroll", + "padding-box", + "border-box" + ], + multiple: true, + set: { + "background-origin": [ + "background-clip" + ] + }, + separator: { + typ: "Comma" + }, + properties: { + "background-repeat": { + types: [ + ], + "default": [ + "repeat" + ], + multiple: true, + keywords: [ + "repeat-x", + "repeat-y", + "repeat", + "space", + "round", + "no-repeat" + ], + mapping: { + "repeat no-repeat": "repeat-x", + "no-repeat repeat": "repeat-y", + "repeat repeat": "repeat", + "space space": "space", + "round round": "round", + "no-repeat no-repeat": "no-repeat" + } + }, + "background-color": { + types: [ + "Color" + ], + "default": [ + "#0000", + "transparent" + ], + multiple: true, + keywords: [ + ] + }, + "background-image": { + types: [ + "UrlFunc", + "ImageFunc" + ], + "default": [ + "none" + ], + keywords: [ + "none" + ] + }, + "background-attachment": { + types: [ + ], + "default": [ + "scroll" + ], + multiple: true, + keywords: [ + "scroll", + "fixed", + "local" + ] + }, + "background-clip": { + types: [ + ], + "default": [ + "border-box" + ], + multiple: true, + keywords: [ + "border-box", + "padding-box", + "content-box", + "text" + ] + }, + "background-origin": { + types: [ + ], + "default": [ + "padding-box" + ], + multiple: true, + keywords: [ + "border-box", + "padding-box", + "content-box" + ] + }, + "background-position": { + multiple: true, + types: [ + "Perc", + "Length" + ], + "default": [ + "0 0", + "top left", + "left top" + ], + keywords: [ + "top", + "left", + "center", + "bottom", + "right" + ], + mapping: { + left: "0", + top: "0", + center: "50%", + "center center": "50%", + "50% 50%": "50%", + bottom: "100%", + right: "100%" + }, + constraints: { + mapping: { + max: 2 + } + } + }, + "background-size": { + multiple: true, + previous: "background-position", + prefix: { + typ: "Literal", + val: "/" + }, + types: [ + "Perc", + "Length" + ], + "default": [ + "auto", + "auto auto" + ], + keywords: [ + "auto", + "cover", + "contain" + ], + mapping: { + "auto auto": "auto" + } + } + } + }, + "background-repeat": { + shorthand: "background" + }, + "background-color": { + shorthand: "background" + }, + "background-image": { + shorthand: "background" + }, + "background-attachment": { + shorthand: "background" + }, + "background-clip": { + shorthand: "background" + }, + "background-origin": { + shorthand: "background" + }, + "background-position": { + shorthand: "background" + }, + "background-size": { + shorthand: "background" + }, + "grid-area": { + shorthand: "grid-area", + pattern: "grid-row-start grid-column-start grid-row-end grid-column-end", + keywords: [ + "auto" + ], + "default": [ + "auto" + ], + multiple: false, + expandShorthands: true, + separator: { + typ: "Literal", + val: "/" + }, + mapping: { + "auto/auto": "auto", + "auto/auto/auto": "auto", + "auto/auto/auto/auto": "auto" + }, + expandedProperties: { + "grid-row": [ + "grid-row-start", + "grid-row-end" + ], + "grid-column": [ + "grid-column-start", + "grid-column-end" + ] + }, + properties: { + "grid-row-start": { + map: "grid-area", + shorthand: "grid-row", + types: [ + "Iden", + "Number" + ], + "default": [ + "auto" + ], + keywords: [ + "auto" + ] + }, + "grid-column-start": { + map: "grid-area", + shorthand: "grid-column", + types: [ + "Iden", + "Number" + ], + prefix: { + typ: "Literal", + val: "/" + }, + "default": [ + "auto" + ], + keywords: [ + "auto" + ] + }, + "grid-row-end": { + map: "grid-area", + shorthand: "grid-row", + types: [ + "Iden", + "Number" + ], + prefix: { + typ: "Literal", + val: "/" + }, + "default": [ + "auto" + ], + keywords: [ + "auto" + ] + }, + "grid-column-end": { + map: "grid-area", + shorthand: "grid-column", + types: [ + "Iden", + "Number" + ], + prefix: { + typ: "Literal", + val: "/" + }, + "default": [ + "auto" + ], + keywords: [ + "auto" + ] + } + } + }, + "grid-row-start": { + shorthand: "grid-row" + }, + "grid-column-start": { + shorthand: "grid-column" + }, + "grid-row-end": { + shorthand: "grid-row" + }, + "grid-column-end": { + shorthand: "grid-column" + }, + "grid-row": { + shorthand: "grid-row", + map: "grid-area", + pattern: "grid-row-start grid-row-end", + types: [ + "Iden", + "Number" + ], + keywords: [ + "auto" + ], + "default": [ + "auto" + ], + multiple: false, + mapping: { + "auto auto": "auto", + "auto/auto": "auto" + }, + properties: { + "grid-row-start": { + map: "grid-area", + types: [ + "Iden", + "Number" + ], + "default": [ + "auto" + ], + multiple: true, + keywords: [ + "auto" + ] + }, + "grid-row-end": { + map: "grid-area", + types: [ + "Iden", + "Number" + ], + "default": [ + "auto" + ], + prefix: { + typ: "Literal", + val: "/" + }, + multiple: true, + keywords: [ + "auto" + ] + } + } + }, + "grid-column": { + shorthand: "grid-column", + map: "grid-area", + pattern: "grid-column-start grid-column-end", + types: [ + "Iden", + "Number" + ], + keywords: [ + "auto" + ], + "default": [ + "auto" + ], + multiple: false, + mapping: { + "auto/auto": "auto" + }, + properties: { + "grid-column-start": { + map: "grid-area", + types: [ + "Iden", + "Number" + ], + "default": [ + "auto" + ], + multiple: true, + keywords: [ + "auto" + ] + }, + "grid-column-end": { + map: "grid-area", + types: [ + "Iden", + "Number" + ], + "default": [ + "auto" + ], + multiple: true, + prefix: { + typ: "Literal", + val: "/" + }, + keywords: [ + "auto" + ] + } + } + }, + "grid-template": { + shorthand: "grid-template", + pattern: "grid-template-areas grid-template-rows grid-template-columns", + keywords: [ + "none" + ], + "default": [ + "none" + ], + multiple: false, + mapping: { + "none none": "none", + "none/none": "none", + "none none/none": "none" + }, + properties: { + "grid-template-areas": { + types: [ + "String" + ], + "default": [ + "none" + ], + multiple: true, + keywords: [ + "none", + "auto" + ], + mapping: { + } + }, + "grid-template-rows": { + types: [ + "Perc", + "Iden", + "Length", + "Flex" + ], + "default": [ + "none" + ], + multiple: true, + keywords: [ + "none" + ] + }, + "grid-template-columns": { + types: [ + "Perc", + "Iden", + "Length", + "Flex" + ], + "default": [ + "none" + ], + prefix: { + typ: "Literal", + val: "/" + }, + multiple: true, + keywords: [ + "none" + ] + } + } + }, + "grid-template-areas": { + shorthand: "grid-template" + }, + "grid-template-rows": { + shorthand: "grid-template" + }, + "grid-template-columns": { + shorthand: "grid-template" + } + }; + var property = { + "transform-origin": { + pattern: [ + [ + "left|center|right", + "top|center|bottom", + "" + ], + [ + "left|center|right", + "top|center|bottom" + ], + [ + "center|left|right|center|top|bottom|" + ] + ], + mapping: { + center: { + typ: "Perc", + val: 50 + }, + left: { + typ: "Perc", + val: 0 + }, + right: { + typ: "Perc", + val: 100 + }, + top: { + typ: "Perc", + val: 0 + }, + bottom: { + typ: "Perc", + val: 100 + } + } + } + }; + var config$1 = { + properties: properties, + map: map, + property: property + }; + + Object.freeze(config$1); + // @ts-expect-error + const getConfig = () => config$1; + + function matchType(val, properties) { + if ((val.typ === exports.EnumToken.IdenTokenType && properties.keywords.includes(val.val)) || + properties.types.some( + // @ts-expect-error + (t) => + // @ts-expect-error + (val.typ === exports.EnumToken.IdenTokenType && exports.EnumToken[t] === exports.EnumToken.ColorTokenType && isColor(val)) || + // @ts-expect-error + exports.EnumToken[t] === val.typ)) { + return true; } - if (match.every((t) => t.length == 0)) { - return null; + if (val.typ === exports.EnumToken.NumberTokenType && val.val === 0) { + // @ts-ignore + return properties.types.some((type) => { + // @ts-ignore + const typ = exports.EnumToken[type]; + return typ == exports.EnumToken.LengthTokenType || typ == exports.EnumToken.AngleTokenType; + }); } - if (eq([["&"]], match)) { - return null; + if (tokensfuncSet.has(val.typ) && val.typ === exports.EnumToken.MathFunctionTokenType) { + return val.chi.every((t) => [ + exports.EnumToken.Add, + exports.EnumToken.Mul, + exports.EnumToken.Div, + exports.EnumToken.Sub, + exports.EnumToken.LiteralTokenType, + exports.EnumToken.CommaTokenType, + exports.EnumToken.WhitespaceTokenType, + exports.EnumToken.DimensionTokenType, + exports.EnumToken.NumberTokenType, + exports.EnumToken.LengthTokenType, + exports.EnumToken.AngleTokenType, + exports.EnumToken.PercentageTokenType, + exports.EnumToken.ResolutionTokenType, + exports.EnumToken.TimeTokenType, + exports.EnumToken.BinaryExpressionTokenType, + ].includes(t.typ) || matchType(t, properties)); } - const reducer = reduceSelector.bind({ match }); - // @ts-ignore - selector1 = selector1.reduce(reducer, []); - // @ts-ignore - selector2 = selector2.reduce(reducer, []); - return selector1 == null || selector2 == null - ? null - : { - eq: eq(selector1, selector2), - match, - selector1, - selector2, - }; + return false; } - /** - * Fix selector - * @param node - * - * @private - */ - function fixSelector(node) { - if (node.sel.includes("&")) { - const attributes = parseString(node.sel); - for (const attr of walkValues(attributes)) { - if (attr.value.typ == exports.EnumToken.PseudoClassFuncTokenType && - attr.value.val == ":is") { - let i = attr.value.chi.length; - while (i--) { - if (attr.value.chi[i].typ == exports.EnumToken.NestingSelectorTokenType) { - attr.value.chi.splice(i, 1); - } - } - } - } - node.sel = attributes.reduce((acc, curr) => acc + renderValue(curr), ""); - node[TOKENS] = null; + + const propertiesConfig = getConfig(); + class PropertyMap { + declarations; + config; + requiredCount; + pattern; + constructor(config) { + const values = Object.values(config.properties); + this.requiredCount = + values.reduce((acc, curr) => (curr.required ? ++acc : acc), 0) || + values.length; + this.config = config; + this.declarations = new Map(); + this.pattern = config.pattern.split(/\s/); } - } - /** - * Wrap nodes - * @param previous - * @param node - * @param match - * @param ast - * @param reducer - * @param i - * @param nodeIndex - * - * @private - */ - function wrapNodes(previous, node, match, ast, reducer, i, nodeIndex) { - // @ts-ignore - let pSel = match.selector1.reduce(reducer, []).join(","); - // @ts-ignore - let nSel = match.selector2.reduce(reducer, []).join(","); - const wrapper = { - ...previous, - chi: [], - // @ts-ignore - sel: match.match.reduce(reducer, []).join(","), - [RAW]: match.match.map((t) => t.slice()), - }; - if (pSel == "&" || pSel === "") { - for (const child of previous.chi) { - wrapper.chi.push(child); - } - if (nSel == "&" || nSel === "") { - for (const child of node.chi) { - wrapper.chi.push(child); - } + add(declaration) { + if (declaration.nam == this.config.shorthand) { + this.declarations.clear(); + this.declarations.set(declaration.nam, declaration); + this.matchTypes(declaration); } else { - wrapper.chi.push(node); - } - } - else { - wrapper.chi.push(previous, node); - } - ast.chi.splice(i, 1, wrapper); - ast.chi.splice(nodeIndex, 1); - previous.sel = pSel; - previous[RAW] = match.selector1; - previous[TOKENS] = null; - node.sel = nSel; - node[RAW] = match.selector2; - node[TOKENS] = null; - reduceRuleSelector(wrapper); - wrapper[TOKENS] = null; - return wrapper; - } - /** - * Diff nodes - * @param n1 - * @param n2 - * @param options - * - * @private - */ - function diff$1(n1, n2, options = {}) { - if (!("cache" in options)) { - options.cache = new WeakMap(); - } - let node1 = n1; - let node2 = n2; - let exchanged = false; - if (node1.chi.length > node2.chi.length) { - const t = node1; - node1 = node2; - node2 = t; - exchanged = true; - } - let i = node1.chi.length; - let j = node2.chi.length; - const raw1 = node1[RAW]; - const raw2 = node2[RAW]; - if (raw1 != null && raw2 != null) { - const prefixes1 = new Set(); - const prefixes2 = new Set(); - for (const token1 of raw1) { - for (const t of token1) { - if (t.includes(":")) { - const matches = t.match(/::?-([a-z]+)-/); - if (matches == null) { - continue; - } - prefixes1.add(matches[1]); - if (prefixes1.size > 1) { - break; - } + const separator = this.config.separator != null + ? { + ...this.config.separator, + // @ts-ignore + typ: exports.EnumToken[this.config.separator.typ], } - } - if (prefixes1.size > 1) { - break; - } - } - for (const token2 of raw2) { - for (const t of token2) { - if (t.includes(":")) { - const matches = t.match(/::?-([a-z]+)-/); - if (matches == null) { + : null; + // expand shorthand + if (this.declarations.has(this.config.shorthand)) { + const tokens = {}; + const values = []; + const val = [[]]; + for (const curr of this.declarations.get(this.config.shorthand).val) { + // @ts-ignore + // @ts-ignore + if (separator != null && separator.typ == curr.typ && separator.val == curr.val) { + val.push([]); continue; } - prefixes2.add(matches[1]); - if (prefixes2.size > 1) { - break; - } - } - } - if (prefixes2.size > 1) { - break; - } - } - if (prefixes1.size != prefixes2.size) { - return null; - } - for (const prefix of prefixes1) { - if (!prefixes2.has(prefix)) { - return null; - } - } - } - const css1 = options.cache.get(node1); - const css2 = options.cache.get(node2); - node1 = { ...node1, chi: node1.chi.slice() }; - node2 = { ...node2, chi: node2.chi.slice() }; - if (css1 != null) { - options.cache.set(node1, css1); - } - if (css2 != null) { - options.cache.set(node2, css2); - } - if (raw1 != null) { - node1[RAW] = raw1; - } - if (raw2 != null) { - node2[RAW] = raw2; - } - const intersect = []; - while (i--) { - if (node1.chi[i].typ == exports.EnumToken.CommentNodeType) { - continue; - } - j = node2.chi.length; - while (j--) { - if (node2.chi[j].typ == exports.EnumToken.CommentNodeType) { - continue; - } - if (node1.chi[i].nam == node2.chi[j].nam) { - if (node1.chi[i].typ == node2.chi[j].typ && eq(node1.chi[i], node2.chi[j])) { - intersect.push(node1.chi[i]); - node1.chi.splice(i, 1); - node2.chi.splice(j, 1); - options.cache.delete(node1); - options.cache.delete(node2); - break; - } - } - } - } - const result = intersect.length === 0 && (node1.chi.length > 0 || node2.chi.length > 0) - ? null - : { - ...node1, - // @ts-ignore - sel: [ - ...new Set(splitRule(node1.sel) - .concat(splitRule(node2.sel)) - .map((s) => s.join(""))), - ].join(","), - // @ts-ignore - chi: intersect.reverse(), - }; - let op = { level: 0, ...options }; - if (result == null || - [n1, n2].reduce((acc, curr) => { - let css = options.cache.get(curr); - if (css == null) { - let level = 0; - let parent = curr[PARENT]; - while (parent != null && parent.typ != exports.EnumToken.StyleSheetNodeType) { - level++; - parent = parent[PARENT]; - } - op.level = level; - css = doRender(curr, op).code; - options.cache.set(curr, css); - } - return curr.chi.length == 0 ? acc : acc + css.length; - }, 0) <= - [node1, node2, result].reduce((acc, curr) => { - let css = options.cache.get(curr); - if (css != null) { - return curr.chi.length == 0 ? acc : acc + css.length; - } - let level = 0; - let parent = curr[PARENT]; - while (parent != null && parent.typ != exports.EnumToken.StyleSheetNodeType) { - level++; - parent = parent[PARENT]; + // @ts-ignore + val.at(-1).push(curr); } - op.level = level; - css = doRender(curr, op).code; - return curr.chi.length == 0 ? acc : acc + css.length; - }, 0)) { - if (node1.chi.length != 0 && node2.chi.length != 0) { - return null; - } - } - if (result != null) { - result[TOKENS] = null; - result[RAW] = null; - const optimized = optimizeSelector(splitRule(result.sel)); - if (optimized?.match) { - const rule = optimized.selector.reduce((acc, curr) => { - if (acc.length > 0) { - acc += ","; + for (let current = 0; current < val.length; current++) { + values.push(...this.pattern.reduce((acc, property) => { + // let current: number = 0; + const props = this.config.properties[property]; + for (let i = 0; i < acc.length; i++) { + if (acc[i].typ == exports.EnumToken.CommentTokenType || + acc[i].typ == exports.EnumToken.WhitespaceTokenType) { + acc.splice(i, 1); + i--; + continue; + } + if ( + // @ts-ignore + acc[i][PROPERTYNAME] == property || + matchType(acc[i], props)) { + if ("prefix" in props && props.previous != null && !(props.previous in tokens)) { + return acc; + } + if (!(property in tokens)) { + tokens[property] = [[acc[i]]]; + } + else { + if (current == tokens[property].length) { + tokens[property].push([acc[i]]); + } + else { + tokens[property][current].push({ typ: exports.EnumToken.WhitespaceTokenType }, acc[i]); + } + } + acc.splice(i, 1); + i--; + // @ts-ignore + if ("prefix" in props && acc[i]?.typ == exports.EnumToken[props.prefix.typ]) { + if ( + // @ts-ignore + acc[i].typ == exports.EnumToken[props.prefix.typ] && + // @ts-ignore + acc[i].val == this.config.properties[property].prefix.val) { + acc.splice(i, 1); + i--; + } + } + if (props.multiple) { + continue; + } + return acc; + } + else { + if (property in tokens && tokens[property].length > current) { + return acc; + } + } + } + if (property in tokens && tokens[property].length > current) { + return acc; + } + // default + if (props.default.length > 0) { + const defaults = parseString(props.default[0]); + if (!(property in tokens)) { + tokens[property] = [[...defaults]]; + } + else { + if (current == tokens[property].length) { + tokens[property].push([]); + for (let i = 0; i < defaults.length; i++) { + tokens[property][current].push(defaults[i]); + } + } + else { + tokens[property][current].push({ + typ: exports.EnumToken.WhitespaceTokenType, + }); + for (let i = 0; i < defaults.length; i++) { + tokens[property][current].push(defaults[i]); + } + } + } + } + return acc; + }, val[current])); } - if (curr.length > 2 && curr[0] === "&" && curr[1] === " ") { - return acc + curr.slice(2).join(""); + if (values.length == 0) { + this.declarations = Object.entries(tokens).reduce((acc, curr) => { + acc.set(curr[0], { + typ: exports.EnumToken.DeclarationNodeType, + nam: curr[0], + val: curr[1].reduce((acc, curr) => { + if (acc.length > 0) { + acc.push({ ...separator }); + } + for (let i = 0; i < curr.length; i++) { + acc.push(curr[i]); + } + return acc; + }, []), + }); + return acc; + }, new Map()); } - else if (curr.length > 1 && curr[0] === "&") { - return acc + curr.slice(1).join(""); + } + // @ts-ignore + const config = propertiesConfig.properties[declaration.nam]; + let property = declaration.nam; + if (config != null) { + property = config.shorthand; + let value = this.declarations.get(property); + if (!(value instanceof PropertySet)) { + // @ts-ignore + this.declarations.set(property, new PropertySet(propertiesConfig.properties[config.shorthand])); + // Token[] + if (value != null) { + // @ts-ignore + this.declarations.get(property).add(value); + } } - return acc + curr.join(""); - }, ""); - const match = optimized.optimized.join(""); - const sel = match + ":is(" + replaceCompound(rule, match) + ")"; - if (sel.length < result.sel.length) { - result.sel = sel; - result[TOKENS] = null; + this.declarations.get(property).add(declaration); + } + else { + this.declarations.set(declaration.nam, declaration); } } + return this; } - return { result, node1: exchanged ? node2 : node1, node2: exchanged ? node1 : node2 }; - } - /** - * Reduce rule selector - * @param node - * - * @private - */ - function reduceRuleSelector(node) { - if (node[RAW] == null) { - node[RAW] = splitRule(node.sel); - } - let optimized = optimizeSelector(node[RAW].reduce((acc, curr) => { - acc.push(curr.slice()); - return acc; - }, [])); - if (optimized != null) { - node[OPTIMIZED] = optimized; - } - if (optimized != null && optimized.match && optimized.reducible && optimized.selector.length > 1) { - for (const selector of optimized.selector) { - if (selector.length > 1 && - selector[0] == "&" && - (combinators.includes(selector[1]) || !/^[a-zA-Z:]/.test(selector[1]))) { - const all = new Array(Math.max(selector.length - 1, 0)); - let write = 0; - for (let read = 1; read < selector.length; read++) { - all[write++] = selector[read]; - } - selector.length = 0; - for (let index = 0; index < write; index++) { - selector[index] = all[index]; - } + [Symbol.iterator]() { + const propertiesMapping = { ...this.config.properties }; + const patterns = this.config.pattern.split(" "); + let hasMapping = false; + let iterable; + let requiredCount = 0; + let property; + let isShorthand = true; + let declarations = this.declarations; + for (const key of declarations.keys()) { + if (declarations.has(key) && declarations.get(key) instanceof PropertyMap) { + hasMapping = true; + break; } } - const unique = new Set(); - const reduced = optimized.selector.reduce((acc, curr) => { - const sig = curr.join(""); - if (!unique.has(sig)) { - if (acc.length > 0) { - acc.push(","); + if (hasMapping) { + const mapped = {}; + let key; + for (const value of declarations.values()) { + key = value.nam; + if (value instanceof PropertyMap) { + for (const [k, v] of value.declarations) { + if (k == value.config.shorthand) { + for (const [key, val] of Object.entries(value.config.properties)) { + mapped[key] = cloneNode(v); + for (let i = 0; i < v.val.length; i++) { + // @ts-ignore + if (key == v.val[i][PROPERTYNAME]) { + if (mapped[key].val.length > 0) { + mapped[key].val.push({ + typ: exports.EnumToken.WhitespaceTokenType, + }); + } + mapped[key].val.push(v.val[i]); + } + } + if (mapped[key].val.length == 0) { + mapped[key].val.push(...parseString(value.config.properties[key].default[0])); + } + } + } + else { + mapped[k] = v; + } + } } - unique.add(sig); - for (const c of curr) { - acc.push(c); + else { + mapped[key] = value; } } - return acc; - }, []); - const raw = [ - [optimized.optimized[0], reduced.length === 1 ? reduced.join("") : ":is("].concat(reduced).concat(")"), - ]; - const sel = raw[0].join(""); - if (sel.length < node.sel.length) { - node.sel = sel; - node[RAW] = raw; - node[TOKENS] = null; - } - } - } - - /** - * expand css nesting ast nodes - * @param ast - * - * @private - */ - function expand(ast) { - if (ast[STATE] & - (exports.EnumAstNodeStatus.Invalid | - exports.EnumAstNodeStatus.Disallowed | - exports.EnumAstNodeStatus.Unknown | - exports.EnumAstNodeStatus.Unparsed | - exports.EnumAstNodeStatus.Malformed)) { - return ast; - } - const result = Object.assign(cloneNode(ast), { chi: [] }); - let children; - for (let i = 0; i < ast.chi.length; i++) { - let node = ast.chi[i]; - if (node.typ === exports.EnumToken.RuleNodeType) { - children = expandRule(node); - for (const child of children) { - child[PARENT] = result; - result.chi.push(child); + // console.error(mapped); + if (patterns.length === Object.keys(mapped).length) { + declarations = new Map(); + for (const key of patterns) { + if (mapped[key] != null) { + declarations.set(key, mapped[key]); + } + } } } - else if (node.typ == exports.EnumToken.AtRuleNodeType && "chi" in node) { - let hasRule = false; - let j = node.chi.length; - while (j--) { - // @ts-ignore - if (node.chi[j].typ == exports.EnumToken.RuleNodeType || node.chi[j].typ == exports.EnumToken.AtRuleNodeType) { - hasRule = true; + for (property of Object.keys(propertiesMapping)) { + if (propertiesMapping[property].required) { + if (!declarations.has(property)) { + isShorthand = false; break; } + else { + const val = declarations.get(property); + if (val instanceof PropertySet && !val.isShortHand()) { + isShorthand = false; + break; + } + else { + requiredCount++; + } + } } - if (hasRule) { - node = expand(node); - for (const child of node.chi) { - child[PARENT] = result; + } + if (requiredCount === 0) { + requiredCount = declarations.size; + } + if (!isShorthand || requiredCount < this.requiredCount) { + if (isShorthand && declarations.has(this.config.shorthand)) { + const cache = new Map(); + const removeDefaults = (declaration) => { + let i; + let t; + let map = new Map(); + let value = []; + let values = []; + // @ts-ignore + let typ = (exports.EnumToken[this.config.separator?.typ] ?? exports.EnumToken.CommaTokenType); + // @ts-ignore + const sep = this.config.separator == null + ? null + : { + ...this.config.separator, + typ: exports.EnumToken[this.config.separator.typ], + }; + // @ts-ignore + const separator = this.config.separator + ? printValue$1({ + ...this.config.separator, + typ: exports.EnumToken[this.config.separator.typ], + }) + : ","; + this.matchTypes(declaration); + values.push(value); + for (i = 0; i < declaration.val.length; i++) { + t = declaration.val[i]; + if (!cache.has(t)) { + cache.set(t, printValue$1(t, { minify: true })); + } + if (t.typ == typ && separator == cache.get(t)) { + this.removeDefaults(map, value); + value = []; + values.push(value); + map.clear(); + continue; + } + value.push(t); + // @ts-ignore + if (t[PROPERTYNAME] != null) { + // @ts-ignore + if (!map.has(t[PROPERTYNAME])) { + // @ts-ignore + map.set(t[PROPERTYNAME], { t: [t], value: [cache.get(t)] }); + } + else { + // @ts-ignore + const v = map.get(t[PROPERTYNAME]); + v.t.push(t); + v.value.push(cache.get(t)); + } + } + } + this.removeDefaults(map, value); + declaration.val = values.reduce((acc, curr) => { + if (sep != null && acc.length > 0) { + acc.push({ ...sep }); + } + for (const cr of curr) { + if (cr.typ == exports.EnumToken.WhitespaceTokenType && acc.at(-1)?.typ == cr.typ) { + continue; + } + acc.push(cr); + } + return acc; + }, []); + while (declaration.val.at(-1)?.typ == exports.EnumToken.WhitespaceTokenType) { + declaration.val.pop(); + } + while (declaration.val.at(0)?.typ == exports.EnumToken.WhitespaceTokenType) { + declaration.val.shift(); + } + return declaration; + }; + const values = [...declarations.values()].reduce((acc, curr) => { + if (curr instanceof PropertySet || curr instanceof PropertyMap) { + for (const declaration of curr) { + acc.push(declaration); + } + } + else { + acc.push(curr); + } + return acc; + }, []); + let isImportant = false; + let dec; + const filtered = []; + for (const declaration of values) { + dec = removeDefaults(declaration); + for (const t of dec.val) { + if (t.typ == exports.EnumToken.ImportantTokenType) { + isImportant = true; + } + if (filtered.length == 0 && + t.typ != exports.EnumToken.WhitespaceTokenType && + t.typ != exports.EnumToken.ImportantTokenType) { + filtered.push(dec); + } + } } - node[PARENT] = result; - // @ts-ignore - result.chi.push(node); + if (filtered.length == 0 && this.config.default.length > 0) { + filtered.push({ + typ: exports.EnumToken.DeclarationNodeType, + nam: this.config.shorthand, + val: parseString(this.config.default[0]), + }); + if (isImportant) { + filtered[0].val.push({ + typ: exports.EnumToken.ImportantTokenType, + }); + } + } + return (filtered.length > 0 ? filtered : values)[Symbol.iterator](); } - else { - node[PARENT] = result; + for (const declaration of declarations.values()) { + if (declaration instanceof PropertySet || declaration instanceof PropertyMap) { + continue; + } + const config = declaration.nam == this.config.shorthand + ? this.config + : (propertiesMapping[declaration.nam] ?? this.config); + if (!("mapping" in config)) { + continue; + } // @ts-ignore - result.chi.push(node); + for (const [key, val] of Object.entries(config.mapping)) { + const keys = parseString(key); + if (keys.length != declaration.val.length) { + continue; + } + if (eq(declaration.val, keys)) { + declaration.val = parseString(val); + break; + } + } } + // @ts-ignore + iterable = declarations.values(); } else { - node[PARENT] = result; + let count = 0; + let match; + const separator = this.config.separator != null + ? { + ...this.config.separator, + // @ts-ignore + typ: exports.EnumToken[this.config.separator.typ], + } + : null; + const tokens = {}; // @ts-ignore - result.chi.push(node); - } - } - return result; - } - function expandRule(node) { - if (node[STATE] & - (exports.EnumAstNodeStatus.Invalid | - exports.EnumAstNodeStatus.Disallowed | - exports.EnumAstNodeStatus.Unknown | - exports.EnumAstNodeStatus.Unparsed | - exports.EnumAstNodeStatus.Malformed)) { - return [node]; - } - const ast = Object.assign(cloneNode(node), { chi: node.chi.slice() }); - const result = []; - if (ast.typ == exports.EnumToken.RuleNodeType) { - let i = 0; - for (; i < ast.chi.length; i++) { - if (ast.chi[i].typ == exports.EnumToken.RuleNodeType) { - const rule = ast.chi[i]; - if (!rule.sel.includes("&")) { - const selRule = splitRule(rule.sel); - const arSelf = splitRule(ast.sel) - .filter((r) => r.every((t) => t != ":before" && t != ":after" && !t.startsWith("::"))) - .reduce((acc, curr) => acc.concat([curr.join("")]), []) - .join(","); - if (arSelf.length == 0) { - ast.chi.splice(i--, 1); - continue; + Object.entries(propertiesMapping).reduce((acc, curr) => { + if (!declarations.has(curr[0])) { + if (curr[1].required) { + acc.push(curr[0]); } - for (let i1 = 0; i1 < selRule.length; i1++) { - const arr = selRule[i1]; - combinators.includes(arr[0].charAt(0)) ? arr.unshift(arSelf) : arr.unshift(arSelf, " "); + return acc; + } + let current = 0; + const props = propertiesMapping[curr[0]]; + const properties = (declarations.get(curr[0])); + for (const declaration of properties instanceof PropertySet || properties instanceof PropertyMap + ? [...properties].flat() + : [properties]) { + // @ts-ignore + for (const val of declaration.val) { + // @ts-ignore + if (separator != null && separator.typ == val.typ && separator.val == val.val) { + current++; + if (tokens[curr[0]].length == current) { + tokens[curr[0]].push([]); + } + continue; + } + if (val.typ == exports.EnumToken.WhitespaceTokenType || val.typ == exports.EnumToken.CommentTokenType) { + continue; + } + // @ts-ignore + if (props.multiple && + props.separator != null && + // @ts-ignore + exports.EnumToken[props.separator.typ] === val.typ && + // @ts-ignore + (val.typ === exports.EnumToken.CommaTokenType || props.separator.val == val.val)) { + continue; + } + // @ts-ignore + match = val.typ == exports.EnumToken.CommentTokenType || matchType(val, curr[1]); + if (isShorthand) { + isShorthand = match; + } + // @ts-ignore + if (val[PROPERTYNAME] == property || match) { + if (!(curr[0] in tokens)) { + tokens[curr[0]] = [[]]; + } + // is default value + tokens[curr[0]][current].push(val); + } + else { + acc.push(curr[0]); + break; + } + } + } + if (count === 0) { + count = current; + } + return acc; + }, []); + // grid-template + if (this.config.shorthand == "grid-template" && patterns.length == Object.keys(tokens).length) { + const k = tokens[patterns[0]][0].length; + const l = tokens[patterns[1]][0].length; + const j = k < l ? k : l; + const result = []; + let i; + for (i = 0; i < j; i++) { + if (i < k) { + result.push(tokens[patterns[0]][0][i]); + } + if (i < l) { + result.push(tokens[patterns[1]][0][i]); + } + } + if (j < k) { + result.push(...tokens[patterns[0]][0].slice(j)); + } + if (j < l) { + result.push(...tokens[patterns[1]][0].slice(j)); + } + const separator = this.config.properties[patterns[2]]?.prefix; + result.push( + // @ts-ignore + { ...separator, typ: exports.EnumToken[separator.typ] }, ...tokens[patterns[2]][0].reduce((acc, curr) => { + if (acc.length > 0) { + acc.push({ typ: exports.EnumToken.WhitespaceTokenType }); + } + acc.push(curr); + return acc; + }, [])); + const declaration = cloneNode(declarations.get(patterns[0])); + declaration.nam = this.config.shorthand; + declaration.val = result; + return [declaration][Symbol.iterator](); + } + count++; + if (!isShorthand || + Object.entries(propertiesMapping).some((entry) => { + // missing required property + return entry[1].required && !(entry[0] in tokens); + }) || + // @ts-ignore + !Object.values(tokens).every((v) => + // @ts-expect-error + v.filter((t) => t.typ != exports.EnumToken.CommentTokenType).length === count)) { + // @ts-ignore + iterable = declarations.values(); + } + else if (hasMapping) { + if (patterns.length == Object.keys(tokens).length) { + const declaration = cloneNode(declarations.get(patterns[0])); + declaration.nam = this.config.shorthand; + for (let i = 0; i < patterns.length; i++) { + if (declaration.val.length > 0) { + declaration.val.push( + // @ts-ignore + this.config.separator != null + ? { + ...this.config.separator, + typ: exports.EnumToken[this.config.separator.typ], + } + : { typ: exports.EnumToken.WhitespaceTokenType }); + } + declaration.val.push(...tokens[patterns[i]].reduce((acc, curr) => { + if (acc.length > 0) { + acc.push({ typ: exports.EnumToken.WhitespaceTokenType }); + } + acc.push(...curr.reduce((acc, curr) => { + if (acc.length > 0) { + acc.push({ typ: exports.EnumToken.WhitespaceTokenType }); + } + acc.push(curr); + return acc; + }, [])); + return acc; + }, [])); } - rule.sel = selRule - .reduce((acc, curr) => { - acc.push(curr.join("")); - return acc; - }, []) - .join(","); + return [declaration][Symbol.iterator](); } - else { - let childSelectorCompound = []; - let withCompound = []; - let withoutCompound = []; - // pseudo elements cannot be used with '&' - // https://www.w3.org/TR/css-nesting-1/#example-7145ff1e - const rules = splitRule(ast.sel).filter((r) => r.every((t) => t != ":before" && t != ":after" && !t.startsWith("::"))); - const parentSelector = !node.sel.includes("&"); - if (rules.length == 0) { - ast.chi.splice(i--, 1); - continue; - } - for (const sel of rule[RAW] ?? splitRule(rule.sel)) { - const s = sel.join(""); - if (s.includes("&") || parentSelector) { - if (s.indexOf("&", 1) == -1) { - if (s.at(0) == "&") { - if (s.at(1) == " ") { - childSelectorCompound.push(s.slice(2)); - } - else { - if (s == "&" || parentSelector) { - withCompound.push(s); + iterable = declarations.values(); + } + else { + let values = Object.entries(tokens) + .reduce((acc, curr) => { + const props = propertiesMapping[curr[0]]; + for (let i = 0; i < curr[1].length; i++) { + if (acc.length == i) { + acc.push([]); + } + // if (acc[acc.length - 1].length > 0) { + // acc[acc.length - 1].push( + // // @ts-ignore + // this.config.separator != null ? + // { + // ...this.config.separator, + // typ: EnumToken[this.config.separator.typ as keyof typeof EnumToken], + // } : { typ: EnumToken.WhitespaceTokenType }, + // ) + // } + let values = curr[1][i]; + // .reduce( + // (acc, curr) => { + // // if (acc.length > 0) { + // // acc.push({ typ: EnumToken.WhitespaceTokenType }); + // // } + // acc.push(curr); + // return acc; + // }, + // [], + // ); + if (props.default.includes(curr[1][i] + .reduce((acc, curr) => acc + printNode(curr) + " ", "") + .trimEnd())) { + if (!propertiesMapping[curr[0]].required) { + continue; + } + } + // remove default values + let doFilterDefault = true; + if (curr[0] in propertiesConfig.properties) { + for (let v of values) { + if (![ + exports.EnumToken.WhitespaceTokenType, + exports.EnumToken.CommentTokenType, + exports.EnumToken.IdenTokenType, + ].includes(v.typ) || + (v.typ == exports.EnumToken.IdenTokenType && + !propertiesMapping[curr[0]].default.includes(v.val))) { + doFilterDefault = false; + break; + } + } + } + // remove default values + const filtered = values.filter((val) => { + if (val.typ == exports.EnumToken.WhitespaceTokenType || + val.typ == exports.EnumToken.CommentTokenType) { + return false; + } + return (!doFilterDefault || + !(val.typ == exports.EnumToken.IdenTokenType && + props.default.includes(val.val))); + }); + if (filtered.length > 0 || + !(this.requiredCount == requiredCount && propertiesMapping[curr[0]].required)) { + values = filtered; + } + if (values.length > 0) { + if ("mapping" in props) { + if (!("constraints" in props) || + // @ts-ignore + !("max" in props.constraints) || + values.length <= props.constraints.mapping.max) { + let i = values.length; + while (i--) { + if (values[i].typ == exports.EnumToken.IdenTokenType && + // @ts-expect-error + values[i].val in props.mapping) { + // @ts-ignore + values.splice(i, 1, ...parseString(props.mapping[values[i].val])); } } } - else { - withoutCompound.push(s); - } } - else { - withCompound.push(s); + if ("prefix" in props) { + // @ts-ignore + acc[i].push({ ...props.prefix, typ: exports.EnumToken[props.prefix.typ] }); + } + else if (acc[i].length > 0) { + // @ts-ignore + acc[i].push({ typ: exports.EnumToken.WhitespaceTokenType }); + } + for (const v of values.reduce((acc, curr) => { + if (acc.length > 0) { + // @ts-ignore + acc.push({ + ...((props.separator && { + ...props.separator, + // @ts-ignore + typ: exports.EnumToken[props.separator.typ], + }) ?? { typ: exports.EnumToken.WhitespaceTokenType }), + }); + } + // @ts-ignore + acc.push(curr); + return acc; + }, [])) { + acc[i].push(v); } } } - const selectors = []; - const selector = rules.length > 1 ? ":is(" + rules.map((a) => a.join("")).join(",") + ")" : rules[0].join(""); - if (childSelectorCompound.length > 0) { - if (childSelectorCompound.length == 1) { - selectors.push(replaceCompound("& " + childSelectorCompound[0].trim(), selector)); - } - else { - selectors.push(replaceCompound("& :is(" + - childSelectorCompound.reduce((acc, curr) => acc + (acc.length > 0 ? "," : "") + curr.trim(), "") + - ")", selector)); - } + return acc; + }, []) + .reduce((acc, curr) => { + if (acc.length > 0) { + acc.push({ ...separator }); } - if (withCompound.length > 0) { - if (withCompound.every((t) => t[0] == "&" && t.indexOf("&", 1) == -1)) { - // for (const w of withCompound) { - // for (let m = 0; m < w.length; m++) { - // // for (let n = 0; n < w[m].length; n++) { - // withoutCompound.push(w[m].slice(1)); - // // } - // } - // } - withoutCompound.push(...withCompound.map((t) => t.slice(1))); - withCompound.length = 0; - } + if (curr.length == 0 && this.config.default.length > 0) { + curr.push(...parseString(this.config.default[0]).reduce((acc, curr) => { + if (acc.length > 0) { + acc.push({ typ: exports.EnumToken.WhitespaceTokenType }); + } + acc.push(curr); + return acc; + }, [])); } - if (withoutCompound.length > 0) { - if (withoutCompound.length == 1) { - const useIs = rules.length == 1 && - selector.match(/^[a-zA-Z.:]/) != null && - selector.includes(" ") && - withoutCompound.length == 1 && - withoutCompound[0].match(/^[a-zA-Z]+$/) != null; - const compound = useIs ? ":is(&)" : "&"; - selectors.push(replaceCompound(rules.length == 1 - ? useIs - ? withoutCompound[0] + ":is(&)" - : selector.match(/^[.:]/) && withoutCompound[0].match(/^[a-zA-Z]+$/) - ? withoutCompound[0] + compound - : compound + withoutCompound[0] - : withoutCompound[0].match(/^[a-zA-Z:]+$/) - ? withoutCompound[0].trim() + compound - : "&" + - (withoutCompound[0].match(/^\S+$/) - ? withoutCompound[0].trim() - : ":is(" + withoutCompound[0].trim() + ")"), selector)); - } - else { - selectors.push(replaceCompound("&:is(" + - withoutCompound.reduce((acc, curr) => acc + (acc.length > 0 ? "," : "") + curr.trim(), "") + - ")", selector)); - } + for (const c of curr) { + acc.push(c); } - if (withCompound.length > 0) { - if (withCompound.length == 1) { - selectors.push(replaceCompound(withCompound[0], selector)); - } + return acc; + }, []); + if (this.config.mapping != null) { + const val = values.reduce((acc, curr) => acc + + printNode(curr, { + removeComments: true, + minify: true, + }), ""); + if (val in this.config.mapping) { + values.length = 0; + values.push({ + typ: ['"', "'"].includes(val.charAt(0)) + ? exports.EnumToken.StringTokenType + : exports.EnumToken.IdenTokenType, + // @ts-ignore + val: this.config.mapping[val], + }); } - rule.sel = selectors.reduce((acc, curr) => (curr.length == 0 ? acc : acc + (acc.length > 0 ? "," : "") + curr), ""); } - ast.chi.splice(i--, 1); - for (const s of expandRule(rule)) { - result.push(s); + // @ts-ignore + if (values.length == 1 && + // @ts-ignore + typeof values[0].val == "string" && + this.config.default.includes(values[0].val.toLowerCase()) && + this.config.default[0] != values[0].val.toLowerCase()) { + // @ts-ignore/ + values = parseString(this.config.default[0]); } + iterable = [ + { + typ: exports.EnumToken.DeclarationNodeType, + nam: this.config.shorthand, + val: values, + }, + ][Symbol.iterator](); } - else if (ast.chi[i].typ == exports.EnumToken.AtRuleNodeType) { - let astAtRule = ast.chi[i]; - const values = []; - if (astAtRule.nam === "scope") { - if (astAtRule.val.includes("&")) { - astAtRule.val = replaceCompound(astAtRule.val, ast.sel); + } + const iterators = []; + // @ts-ignore + return { + // @ts-ignore + next() { + let v = iterable.next(); + // @ts-ignore + while (v.done || v.value instanceof PropertySet) { + if (v.value instanceof PropertySet) { + // @ts-ignore + iterators.push(iterable); + iterable = v.value[Symbol.iterator](); + v = iterable.next(); } - const slice = astAtRule.chi - .slice() - .filter((t) => t.typ == exports.EnumToken.RuleNodeType && t.sel.includes("&")); - if (slice.length > 0) { - expandRule({ ...node, chi: astAtRule.chi.slice() }); + if (v.done) { + if (iterators.length > 0) { + // @ts-ignore + iterable = iterators.pop(); + v = iterable.next(); + } + if (v.done && iterators.length == 0) { + break; + } } } - else { - // @ts-ignore - const clone = { ...ast, chi: astAtRule.chi.slice() }; + return v; + }, + }; + } + matchTypes(declaration) { + const patterns = this.pattern.slice(); + const values = []; + let i; + let j; + const map = new Map(); + for (i = 0; i < declaration.val.length; i++) { + values.push(declaration.val[i]); + } + for (i = 0; i < patterns.length; i++) { + for (j = 0; j < values.length; j++) { + if (!map.has(patterns[i])) { // @ts-ignore - astAtRule.chi.length = 0; - for (const r of expandRule(clone)) { - if (r.typ == exports.EnumToken.AtRuleNodeType && "chi" in r) { - if (astAtRule.val !== "" && r.val !== "") { - if (astAtRule.nam === "media" && r.nam === "media") { - r.val = astAtRule.val + " and " + r.val; - } - else if (astAtRule.nam == "layer" && r.nam == "layer") { - r.val = astAtRule.val + "." + r.val; + map.set(patterns[i], this.config.properties?.[patterns[i]]?.constraints?.mapping?.max ?? 1); + } + let count = map.get(patterns[i]); + if (count > 0 && matchType(values[j], this.config.properties[patterns[i]])) { + // @ts-expect-error + values[j][PROPERTYNAME] = patterns[i]; + map.set(patterns[i], --count); + values.splice(j--, 1); + } + } + } + if (this.config.set != null) { + for (const [key, val] of Object.entries(this.config.set)) { + if (map.has(key)) { + for (const v of val) { + // missing + if (map.get(v) == 1) { + let i = declaration.val.length; + while (i--) { + // @ts-expect-error + if (declaration.val[i][PROPERTYNAME] == key) { + const val = { ...declaration.val[i] }; + // @ts-expect-error + val[PROPERTYNAME] = v; + declaration.val.splice(i, 0, val, { typ: exports.EnumToken.WhitespaceTokenType }); } } - // @ts-ignore - values.push(r); } - else if (r.typ == exports.EnumToken.RuleNodeType) { - for (const rule of expandRule(r)) { - // @ts-ignore - astAtRule.chi.push(rule); + } + } + } + } + } + removeDefaults(map, value) { + for (const [key, val] of map) { + const config = this.config.properties[key]; + if (config == null) { + continue; + } + const v = val.value.join(" "); + if (config.default.includes(v) || (value.length == 1 && this.config.default.includes(v))) { + for (const token of value) { + if (val.t.includes(token)) { + let index = value.indexOf(token); + value.splice(index, 1); + if (config.prefix != null) { + while (index-- > 0) { + if (value[index].typ == exports.EnumToken.WhitespaceTokenType) { + continue; + } + if ( + // @ts-expect-error + value[index].typ == exports.EnumToken[config.prefix.typ] && + // @ts-ignore + value[index].val == config.prefix.val) { + value.splice(index, 1); + break; + } } } } } - if (astAtRule.chi.length > 0) { - result.push(astAtRule); - } - for (const r of values) { - result.push(r); - } - ast.chi.splice(i--, 1); } } } - // @ts-ignore - return ast.chi.length > 0 ? [ast].concat(result) : result; } + + // Alphabet: a-z, A-Z, 0-9, _, - + const LOWER = "abcdefghijklmnopqrstuvwxyz"; + const DIGITS = "0123456789"; + const FULL_ALPHABET = (LOWER + DIGITS).split(""); // 64 chars + const FIRST_ALPHABET = LOWER.split(""); // 54 chars (no digits) /** - * replace compound selector - * @param input - * @param replace + * supported hash algorithms */ - function replaceCompound(input, replace) { - const tokens = parseString(input); - let replacement = null; - for (const t of walkValues(tokens)) { - if (t.value.typ == exports.EnumToken.NestingSelectorTokenType) { - if (tokens.length == 2) { - if (replacement == null) { - replacement = parseString(replace); - } - Object.assign(t.value, { - typ: exports.EnumToken.LiteralTokenType, - val: replaceCompoundLiteral(t.value.val, replace), - }); - continue; - } - const rule = splitRule(replace); - Object.assign(t.value, { - typ: exports.EnumToken.LiteralTokenType, - val: rule.length > 1 ? ":is(" + replace + ")" : replace, - }); - } + const hashAlgorithms = ["hex", "base64", "base64url", "sha1", "sha256", "sha384", "sha512"]; + // simple deterministic hash → number + function hashCode(str) { + let hash = 0; + let l = str.length; + let i = 0; + while (i < l) { + hash = (hash * 31 + str.charCodeAt(i++)) >>> 0; } - return tokens.reduce((acc, curr) => acc + renderValue(curr), ""); + return hash; } - function replaceCompoundLiteral(selector, replace) { - const tokens = [""]; - let i = 0; - for (; i < selector.length; i++) { - if (selector.charAt(i) == "&") { - tokens.push("&", ""); - } + /** + * generate a hash id + * @param input + * @param length + */ + function hashId(input, length = 6) { + let n = hashCode(input); + const chars = []; + // First character: must not be a digit + chars.push(FIRST_ALPHABET[n % FIRST_ALPHABET.length]); + // Remaining characters + for (let i = 1; i < length; i++) { + n = (n + chars.length * i) % FULL_ALPHABET.length; + chars.push(FULL_ALPHABET[n]); } - return tokens - .sort((a, b) => { - if (a == "&") { - return 1; - } - return b == "&" ? -1 : 0; - }) - .reduce((acc, curr) => acc + (curr == "&" ? replace : curr), ""); + return chars.join(""); } - /** - * - * @param value + * Object to string + * @param input * @returns */ - function toBase64(value) { - let result = ''; - for (const c of new TextEncoder().encode(value)) { - result += String.fromCharCode(c); + function toSortedString(input) { + if (input == null) { + return "null"; } - return btoa(result); + if (typeof input !== "object") { + return String(input); + } + if (Array.isArray(input)) { + const result = new Array(input.length); + for (const value of input) { + result.push(toSortedString(value)); + } + return JSON.stringify(result); + } + const keys = Object.keys(input).sort(); + for (let i = 0; i < keys.length; i++) { + keys[i] = `${keys[i]}:${toSortedString(input[keys[i]])}`; + } + return `{${keys.join(",")}}`; } - - // from https://github.com/Rich-Harris/vlq/tree/master - // credit: Rich Harris - const integer_to_char = {}; - const char_to_integer = {}; - let i = 0; - for (const char of 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=') { - char_to_integer[char] = i; - integer_to_char[i++] = char; + /** + * Object hash + * @param object + * @returns + */ + function objectHash(object) { + return hashCode(toSortedString(object)).toString(16); } /** - * @param {string} str + * convert input to hex + * @param input + * @param length */ - function decode(str) { - /** @type {number[]} */ - let result = []; - let shift = 0; - let value = 0; - for (let i = 0; i < str.length; i += 1) { - let integer = char_to_integer[str[i]]; - // if (integer === undefined) { - // throw new Error('Invalid character (' + str[i] + ')'); - // } - const has_continuation_bit = integer & 32; - integer &= 31; - value += integer << shift; - if (has_continuation_bit) { - shift += 5; + function toHex(input, length) { + let result = ""; + if (input instanceof ArrayBuffer || ArrayBuffer.isView(input)) { + for (const byte of Array.from(new Uint8Array(input))) { + result += byte.toString(16).padStart(2, "0"); } - else { - const should_negate = value & 1; - value >>>= 1; - if (should_negate) { - result.push(value === 0 ? -2147483648 : -value); - } - else { - result.push(value); - } - // reset - value = shift = 0; + } + else { + for (const char of String(input)) { + result += char.charCodeAt(0).toString(16).padStart(2, "0"); } } return result; } /** - * - * @param value - * @returns + * generate a hash + * @param input + * @param length + * @param algo */ - function encode(value) { - // if (typeof value === 'number') { - // return encode_integer(value); - // } - let result = ''; - for (let i = 0; i < value.length; i += 1) { - result += encode_integer(value[i]); + async function hash(input, length = 6, algo) { + let result; + if (algo != null) { + switch (algo) { + case "hex": + return toHex(input).slice(0, length); + case "base64url": + case "base64": + result = btoa(input); + if (algo == "base64url") { + result = result.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); + } + return result.slice(0, length); + case "sha256": + case "sha384": + case "sha512": + case "sha1": + return toHex(await crypto.subtle.digest(algo.replace("sha", "SHA-"), new TextEncoder().encode(input))).slice(0, length); + default: + throw new Error(`Unsupported hash algorithm: ${algo}`); + } } - return result; + return hashId(input, length); } /** - * - * @param num - * @returns + * generate a hash + * @param input + * @param length + * @param algo */ - function encode_integer(num) { - let result = ''; - if (num < 0) { - num = (-num << 1) | 1; - } - else { - num <<= 1; - } - do { - let clamped = num & 31; - num >>>= 5; - if (num > 0) { - clamped |= 32; + function syncHash(input, length = 6, algo) { + let result; + if (algo != null) { + switch (algo) { + case "hex": + return toHex(input).slice(0, length); + case "base64url": + case "base64": + result = btoa(input); + if (algo == "base64url") { + result = result.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); + } + return result.slice(0, length); + default: + throw new Error(`Unsupported hash algorithm: ${algo}`); } - result += integer_to_char[clamped]; - } while (num > 0); - return result; + } + return hashId(input, length); } + const config = getConfig(); /** - * Generate and parse source map - * - * ```ts - * const sourcemap = new SourceMap(); - * - * // you can also pass an input sourcemap - * // const sourcemap = new SourceMap(sourcemapObjectOrJSONString); - * - * sourcemap.addSourceContent(0, '/css/styles.css', null); - * sourcemap.addSourceContent(1, '/css/typography.css', null); - * - * const maps = []; - * - * maps.add([ - 1, 1, 0, 1, 1]); - * maps.add([ - 2, 1, 1, 1, 1 - ]); - - sourcemap.add(maps); - - // convert to inline sourcemap - sourcemap.toUrl(); - - // convert to JSON - sourcemap.toJSON(); - - // find the original file, line and column - sourcemap.find(2, 1); - ``` + * Represents a list of property declarations, compute shorthand and remove duplicate declarations. */ - class SourceMap { - /** - * - * @private - */ - keys = new Set(); - /** - * Last location - */ - lastLocation = null; - /** - * Version - * @private - */ - version = 3; - /** - * Sources map - * @private - */ - sourcesMap = []; - /** - * Sources content - * @private - */ - sourcesContent = []; - /** - * Sources - * @private - */ - sources = []; - /** - * Map - * @private - * - */ - map = new Map(); - /** - * Map - * @private - * - */ - reverseMap = new Map(); - /** - * Line - * @private - */ - line = -1; - /** - * - * @param sourcemaps input sourcemap - */ - constructor(sourcemaps) { - if (typeof sourcemaps === "string") { - if (sourcemaps.startsWith("data:")) { - let encoding = ""; - let offset = sourcemaps.indexOf(",") + 1; - if (offset == 0) { - offset = sourcemaps.lastIndexOf(";") + 1; + class PropertyList { + options = { removeDuplicateDeclarations: true, computeShorthand: true }; + declarations; + constructor(options = {}) { + this.options = options; + this.declarations = new Map(); + } + add(declarations) { + let name; + let syntaxRules = null; + let result; + for (const declaration of declarations) { + name = declaration.typ != exports.EnumToken.DeclarationNodeType ? null : declaration.nam; + if (declaration[STATE] & + (exports.EnumAstNodeStatus.Invalid | exports.EnumAstNodeStatus.Unknown | exports.EnumAstNodeStatus.ValidationFailed) || + declaration.typ != exports.EnumToken.DeclarationNodeType || + equalsIgnoreCase("composes", name) || + (typeof this.options.removeDuplicateDeclarations === "string" && + this.options.removeDuplicateDeclarations === name) || + (Array.isArray(this.options.removeDuplicateDeclarations) + ? this.options.removeDuplicateDeclarations.includes(declaration.nam) + : !this.options.removeDuplicateDeclarations)) { + const key = objectHash(declaration); + if (this.declarations.has(key)) { + this.declarations.delete(key); } - else { - encoding = sourcemaps.slice(sourcemaps.lastIndexOf(";") + 1, offset - 1); + this.declarations.set(key, declaration); + continue; + } + if (!this.options.computeShorthand) { + this.declarations.set(declaration.nam, declaration); + continue; + } + if (declaration[STATE] & exports.EnumAstNodeStatus.Unvalidated) { + syntaxRules = getParsedSyntax(ValidationSyntaxGroupEnum.Declarations, declaration.nam.toLowerCase()); + if (syntaxRules != null) { + result = matchAllSyntaxes(syntaxRules, createValidationContext(declaration.val), this.options); + declaration[STATE] |= + (result.success ? exports.EnumAstNodeStatus.Validated : exports.EnumAstNodeStatus.ValidationFailed) & + ~exports.EnumAstNodeStatus.Unvalidated; } - if (encoding == "base64") { - sourcemaps = atob(sourcemaps.slice(offset)); + } + // do not compute shorthand for invalid declarations + if (!(declaration[STATE] & exports.EnumAstNodeStatus.Validated)) { + this.declarations.set(objectHash(declaration), declaration); + continue; + } + let propertyName = declaration.nam; + let shortHandType; + let shorthand = null; + if (propertyName in config.properties) { + // @ts-ignore + if ("map" in config.properties[propertyName]) { + shortHandType = "map"; + // @ts-ignore + shorthand = config.properties[propertyName].map; } else { - sourcemaps = decodeURIComponent(sourcemaps.slice(offset)); + shortHandType = "set"; + // @ts-ignore + shorthand = config.properties[propertyName].shorthand; } } - sourcemaps = JSON.parse(sourcemaps); - } - if (sourcemaps != null) { - this.sources = sourcemaps.sources?.slice() ?? []; - this.sourcesContent = sourcemaps.sourcesContent?.slice() ?? []; - const decodedMappings = sourcemaps.mappings - .split(";") - .map((mapping) => mapping.split(",").map((mapping) => decode(mapping))); - this.line = decodedMappings.length - 1; - for (let index = 0; index < decodedMappings.length; index++) { - if (decodedMappings[index].length == 0 || - (decodedMappings[index].length == 1 && decodedMappings[index][0].length == 0)) { + else if (propertyName in config.map) { + shortHandType = "map"; + // @ts-ignore + shorthand = config.map[propertyName].shorthand; + } + else if (propertyName in config.property) { + shortHandType = "single"; + // @ts-ignore + shorthand = config.property[propertyName]; + } + if (this.declarations.has(shorthand)) { + let existing = this.declarations.get(shorthand); + if (existing instanceof PropertySet || existing instanceof PropertyMap) { + existing = existing.declarations.get(declaration.nam); + } + if (existing?.[STATE] & exports.EnumAstNodeStatus.ImportantDeclaration && + declaration[STATE] & ~exports.EnumAstNodeStatus.ImportantDeclaration) { continue; } - this.map.set(index, decodedMappings[index]); } - this.computePositions(); + // @ts-ignore + if (shortHandType == "map") { + let owner = this; + const mapName = + // @ts-ignore + config.map[propertyName]?.map ?? config.map[shorthand]?.properties?.[propertyName]?.map; + if (typeof mapName === "string") { + if (!this.declarations.has(mapName)) { + // @ts-ignore + this.declarations.set(mapName, new PropertyMap(config.map[mapName])); + } + owner = this.declarations.get(mapName); + } + else if (!this.declarations.has(shorthand)) { + // @ts-ignore + this.declarations.set(shorthand, new PropertyMap(config.map[shorthand])); + } + if (owner == this) { + // @ts-ignore + owner.declarations.get(shorthand).add(declaration); + } + else { + if (!owner.declarations.has(shorthand)) { + // @ts-ignore + owner.declarations.set(shorthand, new PropertyMap(config.map[shorthand])); + } + // @ts-ignore + owner.declarations.get(shorthand).add(declaration); + } + } + // @ts-ignore + else if (shortHandType == "set") { + // @ts-ignore + if (!this.declarations.has(shorthand)) { + // @ts-ignore + this.declarations.set( + // @ts-ignore + shorthand, + // @ts-ignore + new PropertySet(config.properties[shorthand])); + } + // @ts-ignore + this.declarations.get(shorthand).add(declaration); + } + else { + if (shorthand != null) { + this.mapValues(declaration, shorthand); + } + this.declarations.set(propertyName, declaration); + } } + return this; } - /** - * add source - * @param id source id - * @param fileName source file - * @param content source content - * @returns - */ - addSourceContent(id, fileName, content) { - if (this.sourcesMap.includes(id)) { - return; + mapValues(declaration, mapping) { + let name; + let values = declaration.val; + if (mapping.pattern != null) { + // match pattern + for (const patterns of mapping.pattern) { + const set = new Set(values.filter((v) => v.typ !== exports.EnumToken.CommentTokenType && v.typ !== exports.EnumToken.WhitespaceTokenType)); + const val = []; + for (const pattern of patterns) { + switch (pattern) { + case "": + for (const v of set) { + if (v.typ === exports.EnumToken.LengthTokenType || + (v.typ === exports.EnumToken.NumberTokenType && v.val === 0)) { + val.push(v); + set.delete(v); + } + } + default: { + for (const v of set) { + if (v.typ === exports.EnumToken.IdenTokenType) { + const value = v.val.toLowerCase(); + if (value === pattern || new RegExp(`(^|\|)${pattern}(\||$)`).test(value)) { + val.push(v); + set.delete(v); + } + } + } + } + } + } + if (set.size === 0) { + values = val.reduce((acc, curr) => { + if (acc.length > 0) { + acc.push({ typ: exports.EnumToken.WhitespaceTokenType }); + } + acc.push(curr); + return acc; + }, []); + break; + } + } } - this.sourcesMap[this.sourcesMap.length] = id; - this.sources[this.sources.length] = fileName || null; - this.sourcesContent[this.sourcesContent.length] = content || null; - } - /** - * Add multiple sourcemaps - * @param maps - * @throws - */ - add(maps) { - let srcIndex; - for (let [newLine, newColumn, srcId, ln, col] of maps) { - const key = `${srcId}:${ln}:${col}:${newLine}:${newColumn}`; - if (this.keys.has(key)) { - continue; + for (const val of declaration.val) { + if (val.typ === exports.EnumToken.IdenTokenType) { + name = val.val.toLowerCase(); + if (mapping.mapping[name] != null) { + // @ts-expect-error + Object.assign(val, mapping.mapping[name], { typ: exports.EnumToken[mapping.mapping[name].typ] }); + } } - this.keys.add(key); - const line = newLine - 1; - let record; - if (line > this.line) { - this.line = line; + } + if (values != declaration.val) { + declaration.val.length = 0; + for (const v of values) { + declaration.val.push(v); } - srcIndex = this.sourcesMap.indexOf(srcId); - if (srcIndex == -1) { - throw new Error(`Source file ${srcId} not added to sourcemap`); + } + } + [Symbol.iterator]() { + let iterator = this.declarations.values(); + const iterators = []; + // @ts-ignore + return { + next() { + let value = iterator.next(); + while ((value.done && iterators.length > 0) || + value.value instanceof PropertySet || + value.value instanceof PropertyMap) { + if (value.value instanceof PropertySet || value.value instanceof PropertyMap) { + iterators.unshift(iterator); + // @ts-ignore + iterator = value.value[Symbol.iterator](); + value = iterator.next(); + } + if (value.done && iterators.length > 0) { + iterator = iterators.shift(); + value = iterator.next(); + } + } + return value; + }, + }; + } + } + + class ComputeShorthandFeature { + accept = new Set([ + exports.EnumToken.RuleNodeType, + exports.EnumToken.AtRuleNodeType, + exports.EnumToken.KeyframesRuleNodeType, + ]); + get ordering() { + return 5; + } + get processMode() { + return exports.FeatureWalkMode.Post; + } + static register(options) { + if (options.computeShorthand) { + // @ts-ignore + options.features.push(new ComputeShorthandFeature(options)); + } + } + run(ast, options) { + if (!("chi" in ast || ast.chi?.length == 0)) { + return null; + } + // @ts-ignore + const j = ast.chi.length; + let k = 0; + let l; + const rules = []; + const declarations = []; + // @ts-ignore + for (; k < j; k++) { + l = k; + // capture comments with the next token + while (l + 1 < j) { + // @ts-ignore + const node = ast.chi[l]; + if (node.typ == exports.EnumToken.CommentNodeType) { + l++; + continue; + } + break; } - if (!this.map.has(line)) { - record = [Math.max(0, newColumn - 1), srcIndex, ln - 1, col - 1]; - this.map.set(line, [record]); + // @ts-ignore + const node = ast.chi[l]; + if (node.typ == exports.EnumToken.DeclarationNodeType) { + for (let m = k; m <= l; m++) { + declarations.push(ast.chi[m]); + } } else { - const arr = this.map.get(line); - record = [Math.max(0, newColumn - 1) - arr[0][0], srcIndex - arr[0][1], ln - 1, col - 1]; - arr.push(record); - } - if (this.lastLocation != null) { - record[2] -= this.lastLocation.ln - 1; - record[3] -= this.lastLocation.col - 1; + for (let m = k; m <= l; m++) { + rules.push(ast.chi[m]); + } } - this.lastLocation ??= { ln, col }; - this.lastLocation.ln = ln; - this.lastLocation.col = col; + k = l; + } + if (declarations.length > 0) { + ast.chi.length = 0; + // @ts-expect-error + ast.chi.push(...new PropertyList(options).add(declarations), ...rules); } + return ast; } - /** - * compute original positions - */ - computePositions() { - this.reverseMap.clear(); - let sourceFileIndex = 0; // second field - let sourceCodeLine = 0; // third field - let sourceCodeColumn = 0; // fourth field - // let nameIndex: number = 0; // fifth field - let generatedCodeColumn; - let result; - // mappings to original source - for (let [i, line] of this.map.entries()) { - if (line.length === 0 || (line.length === 1 && line[0].length === 0)) { + } + + class ComputeCalcExpressionFeature { + accept = new Set([exports.EnumToken.RuleNodeType, exports.EnumToken.AtRuleNodeType]); + get ordering() { + return 3; + } + get processMode() { + return exports.FeatureWalkMode.Post; + } + static register(options) { + if (options.computeCalcExpression) { + // @ts-ignore + options.features.push(new ComputeCalcExpressionFeature()); + } + } + run(ast) { + // @ts-ignore + if (ast.chi == null) { + return null; + } + for (const node of ast.chi) { + if (node.typ != exports.EnumToken.DeclarationNodeType) { continue; } - generatedCodeColumn = line[0][0]; // first field - reset each time - line = line - .map((segment, index, array) => { - if (segment.length === 0) { - return []; + const set = new Set(); + for (const { value, parent } of walkValues(node.val, node)) { + if (parent?.typ == exports.EnumToken.BinaryExpressionTokenType) { + continue; } - generatedCodeColumn = index == 0 ? segment[0] : segment[0] + array[0][0]; - result = [generatedCodeColumn]; - if (segment.length <= 1) { - return result; + if (value.typ == exports.EnumToken.BinaryExpressionTokenType) { + const result = evaluate([value]); + try { + // @ts-ignore + replaceNodeOrValue(parent, value, result); + } + catch (e) { } + continue; } - sourceFileIndex = index == 0 ? segment[1] : segment[1] + array[0][1]; - sourceCodeLine += segment[2]; - sourceCodeColumn += segment[3]; - result.push(sourceFileIndex, sourceCodeLine, sourceCodeColumn); - // nameIndex not needed - // if (segment.length === 5) { - // nameIndex += segment[4]; - // result.push(nameIndex); - // } - return result; - }) - .sort((a, b) => { - if (a[1] !== b[1]) { - return a[1] - b[1]; + if (value != null && tokensfuncSet.has(value.typ)) { + if (!set.has(value)) { + set.add(value); + if (parent != null) { + const shouldEvaluate = value.typ === exports.EnumToken.MathFunctionTokenType || + (value.typ == exports.EnumToken.FunctionTokenType && + mathFuncs.includes(value.val)); + if (shouldEvaluate) { + // @ts-ignore + const cp = value.val != "calc" + ? [value] + : value.typ == exports.EnumToken.DeclarationNodeType + ? value.val + : value.chi; + const values = evaluate(cp); + // fix a + -b into a - b + for (const { value, parent: p } of walkValues(values)) { + if (value.typ === exports.EnumToken.BinaryExpressionTokenType) { + if (value.op === exports.EnumToken.Add && + Math.sign(value.r.val) == -1) { + value.op = exports.EnumToken.Sub; + // @ts-expect-error + value.r.val *= + -1; + } + } + } + // @ts-ignore + const children = parent.typ == exports.EnumToken.DeclarationNodeType + ? parent.val + : // @ts-ignore + parent.chi; + if (values.length == 1 && values[0].typ != exports.EnumToken.BinaryExpressionTokenType) { + for (let i = 0; i < children.length; i++) { + if (children[i] == value) { + children.splice(i, 1, !(parent.typ === exports.EnumToken.MathFunctionTokenType || + (parent.typ == exports.EnumToken.FunctionTokenType && + parent.val == "calc")) && + typeof values[0].val != "number" && + !(values[0].typ == exports.EnumToken.MathFunctionTokenType || + (values[0].typ == exports.EnumToken.FunctionTokenType && + mathFuncs.includes(values[0].val))) + ? { + typ: exports.EnumToken.MathFunctionTokenType, + val: "calc", + chi: values, + [LOCSRCID]: value[LOCSRCID], + [LOCSTA]: value[LOCSTA], + [LOCEND]: value[LOCEND], + } + : values[0]); + break; + } + } + } + else { + for (let i = 0; i < children.length; i++) { + if (children[i] == value) { + children.splice(i, 1, { + typ: exports.EnumToken.MathFunctionTokenType, + val: "calc", + chi: values, + [LOCSRCID]: value[LOCSRCID], + [LOCSTA]: value[LOCSTA], + [LOCEND]: value[LOCEND], + }); + break; + } + } + } + } + } + } } - return a[0] - b[0]; - }); - if (line.length == 0 || (line.length == 1 && line[0].length == 0)) { - continue; } - this.reverseMap.set(i, line); } + return null; } - /** - * retrieve original sources, lines and columns - * @param line generated line - * @param column generated column - */ - find(line, column) { - if (this.reverseMap.size == 0) { - this.computePositions(); + } + + const identityMatrix = new Float64Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); + function identity() { + return identityMatrix.slice(); + } + function normalize(point) { + const [x, y, z] = point; + const norm = Math.sqrt(point[0] * point[0] + point[1] * point[1] + point[2] * point[2]); + return norm === 0 ? [0, 0, 0] : [x / norm, y / norm, z / norm]; + } + function dot(point1, point2) { + if (point1.length === 4 && point2.length === 4) { + return point1[0] * point2[0] + point1[1] * point2[1] + point1[2] * point2[2] + point1[3] * point2[3]; + } + return point1[0] * point2[0] + point1[1] * point2[1] + point1[2] * point2[2]; + } + function multiply(matrixA, matrixB) { + const result = new Float64Array(16); + result[0] = matrixA[0] * matrixB[0] + matrixA[4] * matrixB[1] + matrixA[8] * matrixB[2] + matrixA[12] * matrixB[3]; + result[1] = matrixA[1] * matrixB[0] + matrixA[5] * matrixB[1] + matrixA[9] * matrixB[2] + matrixA[13] * matrixB[3]; + result[2] = matrixA[2] * matrixB[0] + matrixA[6] * matrixB[1] + matrixA[10] * matrixB[2] + matrixA[14] * matrixB[3]; + result[3] = matrixA[3] * matrixB[0] + matrixA[7] * matrixB[1] + matrixA[11] * matrixB[2] + matrixA[15] * matrixB[3]; + result[4] = matrixA[0] * matrixB[4] + matrixA[4] * matrixB[5] + matrixA[8] * matrixB[6] + matrixA[12] * matrixB[7]; + result[5] = matrixA[1] * matrixB[4] + matrixA[5] * matrixB[5] + matrixA[9] * matrixB[6] + matrixA[13] * matrixB[7]; + result[6] = matrixA[2] * matrixB[4] + matrixA[6] * matrixB[5] + matrixA[10] * matrixB[6] + matrixA[14] * matrixB[7]; + result[7] = matrixA[3] * matrixB[4] + matrixA[7] * matrixB[5] + matrixA[11] * matrixB[6] + matrixA[15] * matrixB[7]; + result[8] = + matrixA[0] * matrixB[8] + matrixA[4] * matrixB[9] + matrixA[8] * matrixB[10] + matrixA[12] * matrixB[11]; + result[9] = + matrixA[1] * matrixB[8] + matrixA[5] * matrixB[9] + matrixA[9] * matrixB[10] + matrixA[13] * matrixB[11]; + result[10] = + matrixA[2] * matrixB[8] + matrixA[6] * matrixB[9] + matrixA[10] * matrixB[10] + matrixA[14] * matrixB[11]; + result[11] = + matrixA[3] * matrixB[8] + matrixA[7] * matrixB[9] + matrixA[11] * matrixB[10] + matrixA[15] * matrixB[11]; + result[12] = + matrixA[0] * matrixB[12] + matrixA[4] * matrixB[13] + matrixA[8] * matrixB[14] + matrixA[12] * matrixB[15]; + result[13] = + matrixA[1] * matrixB[12] + matrixA[5] * matrixB[13] + matrixA[9] * matrixB[14] + matrixA[13] * matrixB[15]; + result[14] = + matrixA[2] * matrixB[12] + matrixA[6] * matrixB[13] + matrixA[10] * matrixB[14] + matrixA[14] * matrixB[15]; + result[15] = + matrixA[3] * matrixB[12] + matrixA[7] * matrixB[13] + matrixA[11] * matrixB[14] + matrixA[15] * matrixB[15]; + return result; + } + function inverse(matrix) { + // Create augmented matrix [matrix | identity] + let augmented = [ + matrix[0], + matrix[1], + matrix[2], + matrix[3], + 1, + 0, + 0, + 0, + matrix[4], + matrix[5], + matrix[6], + matrix[7], + 0, + 1, + 0, + 0, + matrix[8], + matrix[9], + matrix[10], + matrix[11], + 0, + 0, + 1, + 0, + matrix[12], + matrix[13], + matrix[14], + matrix[15], + 0, + 0, + 0, + 1, + ]; + // Gaussian elimination with partial pivoting + for (let col = 0; col < 4; col++) { + // Find pivot row with maximum absolute value + let maxRow = col; + let maxVal = Math.abs(augmented[col * 4 + col]); + for (let row = col + 1; row < 4; row++) { + let val = Math.abs(augmented[row * 4 + col]); + if (val > maxVal) { + maxVal = val; + maxRow = row; + } } - if (!this.reverseMap.has(--line)) { + // Check for singularity + if (maxVal < epsilon) { return null; } - column--; - const result = []; - for (const record of this.reverseMap.get(line)) { - if (record.length == 0 || record[0] < column) { - continue; - } - if (record[0] > column) { - break; + // Swap rows if necessary + if (maxRow !== col) { + [augmented[col], augmented[maxRow]] = [augmented[maxRow], augmented[col]]; + } + // Scale pivot row to make pivot element 1 + let pivot = augmented[col * 4 + col]; + for (let j = 0; j < 8; j++) { + augmented[col * 4 + j] /= pivot; + } + // Eliminate column in other rows + for (let row = 0; row < 4; row++) { + if (row !== col) { + let factor = augmented[row * 4 + col]; + for (let j = 0; j < 8; j++) { + augmented[row * 4 + j] -= factor * augmented[col * 4 + j]; + } } - result.push([ - this.sources?.[record[1]] ?? null, - record[2] + 1, - record[3] + 1, - this.sourcesContent?.[record[1]] ?? null, - ]); } - return result.length == 0 ? null : result; } - /** - * Convert to URL encoded string - */ - toUrl() { - // /*# sourceMappingURL = ${url} */ - return `data:application/json;charset=utf-8;base64,${toBase64(JSON.stringify(this.toJSON()))}`; + // Extract the inverse from the right side of the augmented matrix + return augmented.slice(0, 16); + } + function round(number) { + const rounded = Math.round(number); + return Math.abs(rounded - number) <= epsilon ? rounded : +number.toPrecision(6); + } + // translate3d(25.9808px, 0, 15px ) rotateY(60deg) skewX(49.9999deg) scale(1, 1.2) + // translate → rotate → skew → scale + function decompose(original) { + const matrix = original.slice(); + // Normalize last row + if (matrix[15] === 0) { + return null; } - /** - * Convert to JSON object - */ - toJSON() { - const mappings = []; - let i = 0; - for (; i <= this.line; i++) { - if (!this.map.has(i)) { - mappings.push(""); - } - else { - mappings.push(this.map.get(i).reduce((acc, curr) => acc + (acc === "" ? "" : ",") + encode(curr), "")); - } + for (let i = 0; i < 16; i++) + matrix[i] /= matrix[15]; + // Perspective extraction + const perspective = [0, 0, 0, 1]; + if (matrix[3] !== 0 || matrix[7] !== 0 || matrix[11] !== 0) { + const rightHandSide = [matrix[3], matrix[7], matrix[11], matrix[15]]; + const perspectiveMatrix = matrix.slice(); + perspectiveMatrix[3] = 0; + perspectiveMatrix[7] = 0; + perspectiveMatrix[11] = 0; + perspectiveMatrix[15] = 1; + // @ts-ignore + const inverted = inverse(original.slice()); + if (inverted === null) { + return null; + } + const transposedInverse = transposeMatrix4(inverted); + perspective[0] = dot(rightHandSide, transposedInverse.slice(0, 4)); + perspective[1] = dot(rightHandSide, transposedInverse.slice(4, 8)); + perspective[2] = dot(rightHandSide, transposedInverse.slice(8, 12)); + perspective[3] = dot(rightHandSide, transposedInverse.slice(12, 16)); + // Clear perspective from matrix + matrix[3] = 0; + matrix[7] = 0; + matrix[11] = 0; + matrix[15] = 1; + } + // Translation + const translate = [matrix[12], matrix[13], matrix[14]]; + matrix[12] = matrix[13] = matrix[14] = 0; + // Build the 3x3 matrix + const row0 = [matrix[0], matrix[1], matrix[2]]; + const row1 = [matrix[4], matrix[5], matrix[6]]; + const row2 = [matrix[8], matrix[9], matrix[10]]; + const cross = [ + row1[1] * row2[2] - row1[2] * row2[1], + row1[2] * row2[0] - row1[0] * row2[2], + row1[0] * row2[1] - row1[1] * row2[0], + ]; + // Compute scale + const scaleX = Math.hypot(row0[0], row0[1], row0[2]); + const row0Norm = normalize(row0); + const skewXY = dot(row0Norm, row1); + const row1Proj = [row1[0] - skewXY * row0Norm[0], row1[1] - skewXY * row0Norm[1], row1[2] - skewXY * row0Norm[2]]; + const scaleY = Math.hypot(row1Proj[0], row1Proj[1], row1Proj[2]); + const row1Norm = normalize(row1Proj); + const skewXZ = dot(row0Norm, row2); + const skewYZ = dot(row1Norm, row2); + const row2Proj = [ + row2[0] - skewXZ * row0Norm[0] - skewYZ * row1Norm[0], + row2[1] - skewXZ * row0Norm[1] - skewYZ * row1Norm[1], + row2[2] - skewXZ * row0Norm[2] - skewYZ * row1Norm[2], + ]; + const row2Norm = normalize(row2Proj); + const determinant = row0[0] * cross[0] + row0[1] * cross[1] + row0[2] * cross[2]; + const scaleZ = Math.hypot(row2Proj[0], row2Proj[1], row2Proj[2]) * (determinant < 0 ? -1 : 1); + // Build rotation matrix from orthonormalized vectors + const r00 = row0Norm[0], r01 = row1Norm[0], r02 = row2Norm[0]; + const r10 = row0Norm[1], r11 = row1Norm[1], r12 = row2Norm[1]; + const r20 = row0Norm[2], r21 = row1Norm[2], r22 = row2Norm[2]; + // Convert to quaternion + const trace = r00 + r11 + r22; + let qw, qx, qy, qz; + if (trace > 0) { + const s = 0.5 / Math.sqrt(trace + 1.0); + qw = 0.25 / s; + qx = (r21 - r12) * s; + qy = (r02 - r20) * s; + qz = (r10 - r01) * s; + } + else if (r00 > r11 && r00 > r22) { + const s = 2.0 * Math.sqrt(1.0 + r00 - r11 - r22); + qw = (r21 - r12) / s; + qx = 0.25 * s; + qy = (r01 + r10) / s; + qz = (r02 + r20) / s; + } + else if (r11 > r22) { + const s = 2.0 * Math.sqrt(1.0 + r11 - r00 - r22); + qw = (r02 - r20) / s; + qx = (r01 + r10) / s; + qy = 0.25 * s; + qz = (r12 + r21) / s; + } + else { + const s = 2.0 * Math.sqrt(1.0 + r22 - r00 - r11); + qw = (r10 - r01) / s; + qx = (r02 + r20) / s; + qy = (r12 + r21) / s; + qz = 0.25 * s; + } + [qx, qy, qz] = toZero([qx, qy, qz]); + let q = [Math.abs(qx), Math.abs(qy), Math.abs(qz)].reduce((acc, curr) => { + if (acc == 0 || (curr > 0 && curr < acc)) { + acc = curr; + } + return acc; + }, 0); + if (q > 0) { + qx /= q; + qy /= q; + qz /= q; + } + const rotate = [ + qx, + qy, + qz, + Object.is(qw, 0) ? 0 : (2 * Math.acos(qw) * 180) / Math.PI, + ]; + const scale = [scaleX, scaleY, scaleZ]; + const skew = [skewXY, skewXZ, skewYZ]; + return { + translate, + scale, + rotate, + skew, + perspective, + }; + } + function transposeMatrix4(m) { + return [m[0], m[4], m[8], m[12], m[1], m[5], m[9], m[13], m[2], m[6], m[10], m[14], m[3], m[7], m[11], m[15]]; + } + function toZero(v) { + for (let i = 0; i < v.length; i++) { + if (Math.abs(v[i]) <= epsilon) { + v[i] = 0; + } + else { + v[i] = +v[i].toPrecision(6); } - return { - version: this.version, - sources: this.sources.slice(), - sourcesContent: this.sourcesContent?.slice(), - mappings: mappings.join(";"), - }; } + return v; + } + // https://drafts.csswg.org/css-transforms-1/#2d-matrix + function is2DMatrix(matrix) { + // m13,m14, m23, m24, m31, m32, m34, m43 are all 0 + return (matrix[2] === 0 && + matrix[3] === 0 && + matrix[6] === 0 && + matrix[7] === 0 && + matrix[8] === 0 && + matrix[9] === 0 && + matrix[11] === 0 && + matrix[14] === 0 && + matrix[10] === 1 && + matrix[15] === 1); + } + + function translateX(x, from) { + const matrix = identity(); + matrix[3 * 4] = x; + return multiply(from, matrix); + } + function translateY(y, from) { + const matrix = identity(); + matrix[3 * 4 + 1] = y; + return multiply(from, matrix); + } + function translateZ(z, from) { + const matrix = identity(); + matrix[3 * 4 + 2] = z; + return multiply(from, matrix); + } + function translate(translate, from) { + const matrix = identity(); + matrix[3 * 4] = translate[0]; + matrix[3 * 4 + 1] = translate[1] ?? 0; + return multiply(from, matrix); + } + function translate3d(translate, from) { + const matrix = identity(); + matrix[3 * 4] = translate[0]; + matrix[3 * 4 + 1] = translate[1]; + matrix[3 * 4 + 2] = translate[2]; + return multiply(from, matrix); } /** - * Compute line and column of the offset + * angle in radian + * @param angle + * @param x + * @param y + * @param z + * @param from */ - class LineMap { - /** - * line starts - */ - lineStarts; - /** - * Constructor - * @param lines - */ - constructor(lines = []) { - if (lines.length === 0) { - lines.push(0); + function rotate3D(angle, x, y, z, from) { + const matrix = identity(); + const sc = Math.sin(angle / 2) * Math.cos(angle / 2); + const sq = Math.sin(angle / 2) * Math.sin(angle / 2); + const norm = Math.sqrt(x * x + y * y + z * z); + const unit = norm === 0 ? 0 : 1 / norm; + x *= unit; + y *= unit; + z *= unit; + matrix[0] = 1 - 2 * (y * y + z * z) * sq; + matrix[1] = 2 * (x * y * sq + z * sc); + matrix[2] = 2 * (x * z * sq - y * sc); + matrix[4] = 2 * (x * y * sq - z * sc); + matrix[4 + 1] = 1 - 2 * (x * x + z * z) * sq; + matrix[4 + 2] = 2 * (y * z * sq + x * sc); + matrix[2 * 4] = 2 * (x * z * sq + y * sc); + matrix[2 * 4 + 1] = 2 * (y * z * sq - x * sc); + matrix[2 * 4 + 2] = 1 - 2 * (x * x + y * y) * sq; + return multiply(from, matrix); + } + function rotate(angle, from) { + const matrix = identity(); + matrix[0] = Math.cos(angle); + matrix[1] = Math.sin(angle); + matrix[4] = -Math.sin(angle); + matrix[4 + 1] = Math.cos(angle); + return multiply(from, matrix); + } + + function scaleX(x, from) { + const matrix = identity(); + matrix[0] = x; + return multiply(from, matrix); + } + function scaleY(y, from) { + const matrix = identity(); + matrix[4 + 1] = y; + return multiply(from, matrix); + } + function scaleZ(z, from) { + const matrix = identity(); + matrix[2 * 4 + 2] = z; + return multiply(from, matrix); + } + function scale(x, y, from) { + const matrix = identity(); + matrix[0] = x; + matrix[4 + 1] = y; + return multiply(from, matrix); + } + function scale3d(x, y, z, from) { + const matrix = identity(); + matrix[0] = x; + matrix[4 + 1] = y; + matrix[2 * 4 + 2] = z; + return multiply(from, matrix); + } + + function parseMatrix(mat) { + if (mat.typ == exports.EnumToken.IdenTokenType) { + return mat.val == "none" ? identity() : null; + } + const children = mat.chi.filter((t) => t.typ == exports.EnumToken.NumberTokenType || t.typ == exports.EnumToken.IdenTokenType); + const values = []; + for (const child of children) { + if (child.typ != exports.EnumToken.NumberTokenType) { + return null; } - this.lineStarts = lines; + // @ts-ignore + values.push(getNumber(child)); } - /** - * Compute line and column of the offset - * @param offset - * @returns - */ - getOffsets(offset) { - const line = this.search(offset); - const column = offset - this.lineStarts[line]; - // [line, column] - return [line + 1, line == 0 ? column + 1 : column]; + // @ts-ignore + return matrix(values); + } + // use column-major order + function matrix(values) { + const matrix = identity(); + if (values.length === 6) { + matrix[0] = values[0]; + matrix[1] = values[1]; + matrix[4] = values[2]; + matrix[4 + 1] = values[3]; + matrix[3 * 4] = values[4]; + matrix[3 * 4 + 1] = values[5]; } - /** - * search the greatest index of the value less than or equal to offset - * @param offset - * @returns - */ - search(offset) { - // search lineStarts using binary search - let start = 0; - let end = this.lineStarts.length - 1; - let mid = 0; - let result = -1; - while (start <= end) { - mid = start + ((end - start) >>> 1); - if (this.lineStarts[mid] <= offset) { - result = mid; - start = mid + 1; - } - else if (this.lineStarts[mid] > offset) { - end = mid - 1; - } - } - return result; + else if (values.length === 16) { + matrix[0] = values[0]; + matrix[1] = values[1]; + matrix[2] = values[2]; + matrix[3] = values[3]; + matrix[4] = values[4]; + matrix[4 + 1] = values[5]; + matrix[4 + 2] = values[6]; + matrix[4 + 3] = values[7]; + matrix[2 * 4] = values[8]; + matrix[2 * 4 + 1] = values[9]; + matrix[2 * 4 + 2] = values[10]; + matrix[2 * 4 + 3] = values[11]; + matrix[3 * 4] = values[12]; + matrix[3 * 4 + 1] = values[13]; + matrix[3 * 4 + 2] = values[14]; + matrix[3 * 4 + 3] = values[15]; } - /** - * get line starts - * @returns - */ - getLineStarts() { - return this.lineStarts; + else { + return null; } - /** - * add line start - */ - addLineStart(lineStart) { - this.lineStarts.push(lineStart); + return matrix; + } + function serialize(matrix) { + matrix = matrix.slice(); + // @ts-ignore + if (eq(matrix, identity())) { + return { + typ: exports.EnumToken.IdenTokenType, + val: "none", + }; + } + if (is2DMatrix(matrix)) { + // https://drafts.csswg.org/css-transforms-2/#two-dimensional-subset + return { + typ: exports.EnumToken.TransformFunctionTokenType, + val: "matrix", + chi: [matrix[0], matrix[1], matrix[4], matrix[4 + 1], matrix[3 * 4], matrix[3 * 4 + 1]].reduce((acc, t) => { + if (acc.length > 0) { + acc.push({ typ: exports.EnumToken.CommaTokenType }); + } + acc.push({ + typ: exports.EnumToken.NumberTokenType, + val: t, + }); + return acc; + }, []), + }; } + return { + typ: exports.EnumToken.TransformFunctionTokenType, + val: "matrix3d", + chi: matrix.reduce((acc, curr) => { + if (acc.length > 0) { + acc.push({ typ: exports.EnumToken.CommaTokenType }); + } + acc.push({ + typ: exports.EnumToken.NumberTokenType, + val: curr, + }); + return acc; + }, []), + }; } - /** - * match url - */ - const matchUrl = /^(https?:)?\/\//; - const windowsPathnameRegexp = /^\/?[a-zA-Z]:/; - /** - * return the directory name of a path - * @param path - * - * @private - */ - function dirname(path) { - if (path === "") { - return ""; - } - if (path.startsWith("data:")) { - return path; + // translate → rotate → skew → scale + function minify$1(matrix) { + const decomposed = decompose(matrix); + if (decomposed == null) { + return null; } - let i = 0; - let parts = [""]; - for (; i < path.length; i++) { - const chr = path.charAt(i); - if (chr == "/") { - parts.push(""); - } - else { - parts[parts.length - 1] += chr; - } + const transforms = new Set(["translate", "scale", "skew", "perspective", "rotate"]); + const scales = new Set(["x", "y", "z"]); + const skew = new Set(["x", "y"]); + let result = []; + // check identity + if (round(decomposed.translate[0]) == 0 && + round(decomposed.translate[1]) == 0 && + round(decomposed.translate[2]) == 0) { + transforms.delete("translate"); } - parts.pop(); - return parts.join("/"); - } - /** - * split path - * @param result - * @private - */ - function splitPath(result) { - if (result.length == 0) { - return { parts: [], i: 0 }; + if (round(decomposed.scale[0]) == 1 && round(decomposed.scale[1]) == 1 && round(decomposed.scale[2]) == 1) { + transforms.delete("scale"); } - const parts = result == "/" ? [] : [""]; - let i = 0; - for (; i < result.length; i++) { - const chr = result.charAt(i); - if (chr == "/") { - parts.push(""); - } - // else if (chr == "?" || chr == "#") { - // break; - // } - else { - parts[parts.length - 1] += chr; - } + if (round(decomposed.skew[0]) == 0 && round(decomposed.skew[1]) == 0) { + transforms.delete("skew"); } - // let k: number = -1; - // while (++k < parts.length) { - // if (parts[k] == ".") { - // parts.splice(k--, 1); - // } else if (parts[k] == "..") { - // parts.splice(k - 1, 2); - // k -= 2; - // } - // } - return { parts, i }; - } - /** - * Nomalize path - * @param path - * @private - */ - const normalize = memoize(function (path) { - let parts = []; - let i = 0; - if (path.includes("\\")) { - path = path.replace(/(\\)/g, "/"); + if (round(decomposed.perspective[2]) == 0) { + transforms.delete("perspective"); } - if (windowsPathnameRegexp.test(path)) { - path = path.replace(windowsPathnameRegexp, ""); + if (round(decomposed.rotate[3]) == 0) { + transforms.delete("rotate"); } - for (; i < path.length; i++) { - const chr = path.charAt(i); - if (chr == "/") { - if (parts.length == 0 || parts[parts.length - 1] !== "") { - parts.push(""); + if (transforms.has("translate")) { + let coordinates = new Set(["x", "y", "z"]); + for (let i = 0; i < 3; i++) { + if (round(decomposed.translate[i]) == 0) { + coordinates.delete(i == 0 ? "x" : i == 1 ? "y" : "z"); } } - else if (chr == "?" || chr == "#") { - break; + if (coordinates.size == 1) { + if (coordinates.has("x")) { + result.push({ + typ: exports.EnumToken.TransformFunctionTokenType, + val: "translate", + chi: [{ typ: exports.EnumToken.LengthTokenType, val: round(decomposed.translate[0]), unit: "px" }], + }); + } + else { + let axis = coordinates.has("y") ? "y" : "z"; + let index = axis == "y" ? 1 : 2; + result.push({ + typ: exports.EnumToken.TransformFunctionTokenType, + val: "translate" + axis.toUpperCase(), + chi: [{ typ: exports.EnumToken.LengthTokenType, val: round(decomposed.translate[index]), unit: "px" }], + }); + } + } + else if (coordinates.has("z")) { + result.push({ + typ: exports.EnumToken.TransformFunctionTokenType, + val: "translate3d", + chi: [ + decomposed.translate[0] == 0 + ? { + typ: exports.EnumToken.NumberTokenType, + val: 0, + } + : { typ: exports.EnumToken.LengthTokenType, val: round(decomposed.translate[0]), unit: "px" }, + { typ: exports.EnumToken.CommaTokenType }, + decomposed.translate[1] == 0 + ? { + typ: exports.EnumToken.NumberTokenType, + val: 0, + } + : { typ: exports.EnumToken.LengthTokenType, val: round(decomposed.translate[1]), unit: "px" }, + { typ: exports.EnumToken.CommaTokenType }, + { typ: exports.EnumToken.LengthTokenType, val: round(decomposed.translate[2]), unit: "px" }, + ], + }); } else { - if (parts.length == 0) { - parts.push(""); - } - parts[parts.length - 1] += chr; + result.push({ + typ: exports.EnumToken.TransformFunctionTokenType, + val: "translate", + chi: [ + { typ: exports.EnumToken.LengthTokenType, val: round(decomposed.translate[0]), unit: "px" }, + { typ: exports.EnumToken.CommaTokenType }, + { typ: exports.EnumToken.LengthTokenType, val: round(decomposed.translate[1]), unit: "px" }, + ], + }); } } - let k = -1; - while (++k < parts.length) { - // if (parts[k] == ".") { - // parts.splice(k--, 1); - // } else - if (k > 0 && parts[k] == "..") { - parts.splice(k - 1, 2); - k -= 2; + if (transforms.has("rotate")) { + const [x, y, z, angle] = decomposed.rotate; + if (y == 0 && z == 0) { + result.push({ + typ: exports.EnumToken.TransformFunctionTokenType, + val: "rotateX", + chi: [ + { + typ: exports.EnumToken.AngleTokenType, + val: round(angle), + unit: "deg", + }, + ], + }); } - } - return (path.charAt(0) == "/" ? "/" : "") + parts.join("/"); - }); - /** - * diff path - * @param path1 - * @param path2 - * @private - */ - const diff = memoize(function (path1, path2) { - let { parts } = splitPath(path1); - const { parts: dirs } = splitPath(path2); - for (const p of dirs) { - if (parts[0] == p) { - parts.shift(); + else if (x == 0 && z == 0) { + result.push({ + typ: exports.EnumToken.TransformFunctionTokenType, + val: "rotateY", + chi: [ + { + typ: exports.EnumToken.AngleTokenType, + val: round(angle), + unit: "deg", + }, + ], + }); + } + else if (x == 0 && y == 0) { + result.push({ + typ: exports.EnumToken.TransformFunctionTokenType, + val: "rotate", + chi: [ + { + typ: exports.EnumToken.AngleTokenType, + val: round(angle), + unit: "deg", + }, + ], + }); } else { - parts.unshift(".."); + result.push({ + typ: exports.EnumToken.TransformFunctionTokenType, + val: "rotate3d", + chi: [ + { + typ: exports.EnumToken.NumberTokenType, + val: round(x), + }, + { typ: exports.EnumToken.CommaTokenType }, + { + typ: exports.EnumToken.NumberTokenType, + val: round(y), + }, + { typ: exports.EnumToken.CommaTokenType }, + { + typ: exports.EnumToken.NumberTokenType, + val: round(z), + }, + { typ: exports.EnumToken.CommaTokenType }, + { + typ: exports.EnumToken.AngleTokenType, + val: round(angle), + unit: "deg", + }, + ], + }); } - } - return parts.join("/"); - }); - /** - * resolve path - * @param url url or path to resolve - * @param currentDirectory directory used to resolve the path - * @param cwd current working directory - * - * @private - */ - const resolve = memoize(function (url, currentDirectory, cwd) { - if (matchUrl.test(url)) { - return { - absolute: url, - relative: url, - }; - } - cwd ??= ""; - currentDirectory ??= ""; - url = normalize(url); - if (cwd !== "") { - cwd = normalize(cwd); - } - if (currentDirectory !== "") { - currentDirectory = normalize(currentDirectory); - } - let dir = cwd || currentDirectory; - if (windowsPathnameRegexp.test(dir)) { - dir = dir.replace(windowsPathnameRegexp, ""); - } - const absolute = dir == "" || url.startsWith("/") || url.startsWith(dir) || windowsPathnameRegexp.test(url) - ? resolvePath(url) - : resolvePath(dir, url); - return { - absolute, - relative: dir === "" ? absolute : diff(absolute, dir), - }; - }); - /** - * - * @param parts - * @returns - * @private - */ - function resolvePath(...parts) { - const path = parts.filter(Boolean).join("/"); - const isAbsolute = /^[\\/]/.test(path); - const segments = path.split(/[\\/]+/); - const resolved = []; - for (const segment of segments) { - if (!segment || segment === ".") { - continue; + } + if (transforms.has("skew")) { + if (round(decomposed.skew[1]) == 0) { + skew.delete("y"); } - if (segment === "..") { - if (resolved.length && resolved[resolved.length - 1] !== "..") { - resolved.pop(); - } - else if (!isAbsolute) { - resolved.push(".."); - } + for (let i = 0; i < 2; i++) { + decomposed.skew[i] = round((Math.atan(decomposed.skew[i]) * 180) / Math.PI); + } + if (skew.size == 1) { + result.push({ + typ: exports.EnumToken.TransformFunctionTokenType, + val: "skew" + (skew.has("x") ? "" : "Y"), + chi: [{ typ: exports.EnumToken.AngleTokenType, val: round(decomposed.skew[0]), unit: "deg" }], + }); } else { - resolved.push(segment); + result.push({ + typ: exports.EnumToken.TransformFunctionTokenType, + val: "skew", + chi: [ + { typ: exports.EnumToken.AngleTokenType, val: round(decomposed.skew[0]), unit: "deg" }, + { typ: exports.EnumToken.CommaTokenType }, + { typ: exports.EnumToken.AngleTokenType, val: round(decomposed.skew[1]), unit: "deg" }, + ], + }); } } - let result = resolved.join("/"); - if (isAbsolute) { - result = "/" + result; + if (transforms.has("scale")) { + const [sx, sy, sz] = toZero(decomposed.scale); + if (sz == 1) { + scales.delete("z"); + } + if (sy == 1) { + scales.delete("y"); + } + if (sx == 1) { + scales.delete("x"); + } + if (scales.size == 1) { + let prefix = scales.has("x") ? "X" : scales.has("y") ? "Y" : "Z"; + result.push({ + typ: exports.EnumToken.TransformFunctionTokenType, + val: "scale" + prefix, + chi: [{ typ: exports.EnumToken.NumberTokenType, val: round(prefix == "Z" ? sz : prefix == "Y" ? sy : sx) }], + }); + } + else if (!scales.has("z")) { + result.push({ + typ: exports.EnumToken.TransformFunctionTokenType, + val: "scale", + chi: [ + { typ: exports.EnumToken.NumberTokenType, val: round(sx) }, + { typ: exports.EnumToken.CommaTokenType }, + { typ: exports.EnumToken.NumberTokenType, val: round(sy) }, + ], + }); + } + else { + result.push({ + typ: exports.EnumToken.TransformFunctionTokenType, + val: "scale3d", + chi: [ + { typ: exports.EnumToken.NumberTokenType, val: round(sx) }, + { typ: exports.EnumToken.CommaTokenType }, + { typ: exports.EnumToken.NumberTokenType, val: round(sy) }, + { typ: exports.EnumToken.CommaTokenType }, + { typ: exports.EnumToken.NumberTokenType, val: round(sz) }, + ], + }); + } } - return result || (isAbsolute ? "/" : "."); + // identity + return result.length == 0 || (result.length == 1 && eqMatrix(identity(), result)) + ? [ + { + typ: exports.EnumToken.IdenTokenType, + val: "none", + }, + ] + : result; } - - /** - * Source file ID - */ - let sourceId = 0; - /** - * Source file helper class - */ - class SourceFile { - /** - * Source file ID - */ - id; - /** - * Source file path - */ - file; - /** - * Line map - */ - lineStarts; - /** - * Source file content - */ - content; - inputSourceMap = null; - /** - * Constructor - * @param content - * @param lines - * @param file - */ - constructor(content, lines, file = null) { - this.id = sourceId++; - this.content = content; - this.file = file; - this.lineStarts = new LineMap(lines); - } - /** - * Update source content - * @param content - */ - append(content) { - this.content += content; - } - /** - * get file name - * @returns - */ - getFileName() { - return this.file; - } - /** - * get content - * @returns - */ - getContent() { - return this.content; - } - /** - * get text - * @param start - * @param length - * @returns - */ - getText(start, length) { - return this.content.slice(start, start + length); - } - /** - * Compute line and column of the offset - * @param offset - * @returns - */ - getOffsets(offset) { - return this.lineStarts.getOffsets(offset); - } - /** - * get source location - * @param offset - * @returns - */ - getSourceLocation(offset) { - return [this.file, ...this.getOffsets(offset)]; - } - /** - * get line starts - * @returns - */ - getLineStarts() { - return this.lineStarts.getLineStarts(); - } - /** - * add line start - * @param lineStart - */ - addLineStart(lineStart) { - this.lineStarts.addLineStart(lineStart); - } - /** - * set input source map - * @param inputSourceMap - */ - setInputSourceMap(inputSourceMap) { - this.inputSourceMap = inputSourceMap == null ? null : new SourceMap(inputSourceMap); + function eqMatrix(a, b) { + let mat = identity(); + let tmp = identity(); + const data = (Array.isArray(a) || ArrayBuffer.isView(a) ? a : parseMatrix(a)); + for (const transform of b) { + tmp = computeMatrix([transform], identity()); + if (tmp == null) { + return false; + } + mat = multiply(mat, tmp); } - /** - * return input source map - * @returns - */ - getInputSourceMap() { - return this.inputSourceMap; + if (mat == null) { + return false; } - } - - /** - * render ast - * @param data - * @param options - * @param mapping - * @private - */ - function doRender(data, options = {}, mapping) { - const minify = options.minify ?? true; - const beautify = options.beautify ?? !minify; - options = { - ...(beautify - ? { - indent: " ", - newLine: "\n", - } - : { - indent: "", - newLine: "", - }), - ...(minify - ? { - removeEmpty: true, - removeComments: true, - minify: true, + for (let i = 0; i < 4; i++) { + for (let j = 0; j < 4; j++) { + if (Math.abs(mat[i * 4 + j] - data[i * 4 + j]) > epsilon) { + return false; } - : { - removeEmpty: false, - removeComments: false, - }), - minifyValues: true, - sourcemap: false, - convertColor: true, - expandNestingRules: false, - preserveLicense: false, - ...options, - }; - if (options.withParents) { - let parent; - while (data[PARENT] != null) { - // @ts-ignore - // parent = { ...data[PARENT], chi: [data] }; - parent = cloneNode(data[PARENT]); - // @ts-ignore - parent.chi = [data]; - // @ts-ignore - parent[PARENT] = data[PARENT][PARENT]; - // @ts-ignore - data = parent; } } - const startTime = performance.now(); - const errors = []; - const sourcemap = options.sourcemap ? new SourceMap() : null; - const sourcemaps = options.sourcemap ? { sources: [], maps: [] } : null; - const cache = Object.create(null); - const sourceLocation = { - end: 0, - }; - const linesMap = options.sourcemap ? new LineMap() : null; - let code = ""; - if (mapping != null) { - if (mapping.importMapping != null) { - const absolutePath = options.resolve(options.output != null ? dirname(options.output) : dirname(options.src), options.cwd).absolute; - for (let [key, value] of Object.entries(mapping.importMapping)) { - key = options.resolve(options.resolve(key, options.cwd).absolute, absolutePath).relative; - if (!key.startsWith("/") && !key.startsWith(".")) { - key = "./" + key; - } - code += `:import("${key}")${options.indent}{${options.newLine}${Object.entries(value).reduce((acc, [k, v]) => acc + (acc.length > 0 ? options.newLine : "") + `${options.indent}${v}:${options.indent}${k};`, "")}${options.newLine}}${options.newLine}`; - } - } - code += `:export${options.indent}{${options.newLine}${Object.entries(mapping.mapping).reduce((acc, [k, v]) => acc + (acc.length > 0 ? options.newLine : "") + `${options.indent}${k}:${options.indent}${v};`, "")}${options.newLine}}${options.newLine}`; - if (sourcemap != null) { - move(sourceLocation, linesMap, code); - } + return true; + } + function minifyTransformFunctions(transform) { + const name = transform.val; + if ("skewX" == name) { + transform.val = "skew"; + return transform; } - if (options.output != null) { - // @ts-ignore - options.output = options.resolve(options.output, options.cwd).absolute; + if (!["translate", "translate3d", "scale", "scale3d"].includes(name)) { + return transform; } - const result = { - code: code + - renderAstNode(options.expandNestingRules && - [exports.EnumToken.StyleSheetNodeType, exports.EnumToken.AtRuleNodeType, exports.EnumToken.RuleNodeType].includes(data.typ) && - "chi" in data - ? expand(data) - : data, options, sourcemaps, sourceLocation, linesMap, errors, function reducer(acc, curr) { - if (curr.typ == exports.EnumToken.CommentTokenType && options.removeComments) { - if (!options.preserveLicense || !curr.val.startsWith("/*!")) { - return acc; - } - return acc + curr.val; - } - return acc + renderValue(curr, options, cache, reducer); - }, cache), - errors, - stats: { - total: `${(performance.now() - startTime).toFixed(2)}ms`, - }, - }; - if (sourcemap != null) { - let source; - for (const sourceId of sourcemaps.sources) { - source = options.sourcesMap.get(sourceId); - sourcemap.addSourceContent(source.id, source.getFileName(), source.getContent()); + const values = []; + for (const token of transform.chi) { + if (token.typ == exports.EnumToken.CommentTokenType || + token.typ == exports.EnumToken.WhitespaceTokenType || + token.typ == exports.EnumToken.CommaTokenType) { + continue; } - sourcemap.add(sourcemaps.maps); - result.map = sourcemap; - if (options.sourcemap === "inline") { - result.code += `\n/*# sourceMappingURL=${result.map.toUrl()} */`; + if (![ + exports.EnumToken.NumberTokenType, + exports.EnumToken.LengthTokenType, + exports.EnumToken.AngleTokenType, + exports.EnumToken.PercentageTokenType, + ].includes(token.typ)) { + return transform; + } + if (token.typ == exports.EnumToken.PercentageTokenType && + typeof token.val == "number" && + name.startsWith("scale")) { + Object.assign(token, { + typ: exports.EnumToken.NumberTokenType, + val: token.val / 100, + }); } + values.push(token); } - return result; - } - /** - * Update source map - * @param node - * @param options - * @param cache - * @param sourcemaps - * @param sourceLocation - * @param linesMap - * @param str - * - * @internal - */ - function updateSourceMap(node, options, cache, sourcemaps, sourceLocation, linesMap, str) { - let offset = 0; - // eat leanding whitespace - while (offset < str.length && isWhiteSpace(str.charCodeAt(offset))) { - offset++; + if (values.length == 0) { + return transform; } - if (offset > 0) { - move(sourceLocation, linesMap, str, 0, offset + 1); + if ((name == "translate" || name == "scale") && values.length > 2) { + return transform; } - if (node[LOCSTA] != null) { - const source = options.sourcesMap.get(node[LOCSRCID]); - const inputSourceMap = source.getInputSourceMap(); - const offsets = source.getOffsets(node[LOCSTA]); - const [newLine, newColumn] = linesMap.getOffsets(sourceLocation.end); - let records = null; - let srcId = node[LOCSRCID]; - let sourceFileName = source.getFileName() || null; - let sourceContent; // = (source.getContent() as string) || null; - if (inputSourceMap != null && (records = inputSourceMap.find(offsets[0], offsets[1])) != null) { - let newId = null; - for (const record of records) { - newId = null; - // @ts-ignore - sourceFileName = record[0] || null; - // @ts-ignore - offsets[0] = record[1]; - // @ts-ignore - offsets[1] = record[2]; - sourceContent = record[3] || null; - if (sourceFileName != null && options.output != null && !sourceFileName.startsWith("data:")) { - if (cache[sourceFileName] == null) { - const absolute = options.resolve(dirname(options.output), options.cwd) - .absolute; - const absoluteSourcePath = options.resolve(dirname(options.src || ""), options.cwd).absolute; - // resolution is relative to the source file - const absoluteSourceFileName = options.resolve(sourceFileName, absoluteSourcePath) - .absolute; - cache[sourceFileName] = options.resolve(absoluteSourceFileName, absolute).relative; - } - sourceFileName = cache[sourceFileName]; - } - for (const [id, file] of options.sourcesMap.entries()) { - if (file.getFileName() === sourceFileName) { - newId = id; - break; - } - if (sourceFileName == null && file.getContent() === sourceContent) { - newId = id; - break; - } - } - if (newId == null) { - const source = new SourceFile(sourceContent, [], sourceFileName); - options.sourcesMap.set(source.id, source); - newId = source.id; - } - srcId = newId; - if (!sourcemaps.sources.includes(srcId)) { - sourcemaps.sources.push(srcId); - } - sourcemaps.maps.push([newLine, newColumn, srcId, offsets[0], offsets[1]]); - } + const ignoredValue = name.startsWith("scale") ? 1 : 0; + const t = new Set(["x", "y", "z"]); + for (let i = 0; i < 3; i++) { + const axis = i == 0 ? "x" : i == 1 ? "y" : "z"; + if (values.length <= i || values[i].val == ignoredValue) { + t.delete(axis); } - else { - // if (sourceFileName != null && options.output != null && !sourceFileName.startsWith("data:")) { - // if (cache[sourceFileName] == null) { - // const absolute = options.resolve!(dirname(options.output as string), options.cwd as string) - // .absolute as string; - // const absoluteSourceFileName = options.resolve!(sourceFileName, options.cwd as string) - // .absolute as string; - // cache[sourceFileName] = options.resolve!(absoluteSourceFileName, absolute).relative as string; - // } - // sourceFileName = cache[sourceFileName] as string; - // } - if (!sourcemaps.sources.includes(srcId)) { - sourcemaps.sources.push(srcId); + } + if (name == "translate3d" || name == "translate") { + if (t.size == 0) { + return { + typ: exports.EnumToken.TransformFunctionTokenType, + val: "translate", + chi: [{ typ: exports.EnumToken.NumberTokenType, val: 0 }], + }; + } + if (t.size == 1) { + return { + typ: exports.EnumToken.TransformFunctionTokenType, + val: "translate" + (t.has("x") ? "" : t.has("y") ? "Y" : "Z"), + chi: [values[t.has("x") ? 0 : t.has("y") ? 1 : 2]], + }; + } + if (t.size == 2) { + if (t.has("z")) { + return transform; } - sourcemaps.maps.push([newLine, newColumn, srcId, offsets[0], offsets[1]]); + return { + typ: exports.EnumToken.TransformFunctionTokenType, + val: "translate", + chi: [values[0], { typ: exports.EnumToken.CommaTokenType }, values[1]], + }; } } - move(sourceLocation, linesMap, str, offset); - } - /** - * Update position - * @param sourceLocation - * @param linesMap - * @param str - * @param start - * @param end - */ - function move(sourceLocation, linesMap, str, start, end) { - let i = start ?? 0; - let j = end ?? str.length; - let codepoint; - let char; - for (; i < j; i++) { - char = str.charAt(i); - codepoint = char.charCodeAt(0); - sourceLocation.end += char.length; - if (codepoint == 0xa || // \n - codepoint == 0xb || // \v - codepoint == 0xc || // \f - codepoint == 0xd || // \r - codepoint == 0x2028 || // \u2028 - codepoint == 0x2029 // \u2029 - ) { - if (codepoint == 0xd && i + 1 < str.length && str.charCodeAt(i + 1) == 0xa) ; - else if (codepoint == 0xa && i > 0 && str.charCodeAt(i - 1) == 0xd) ; - else { - linesMap.addLineStart(sourceLocation.end); + if (name == "scale3d" || name == "scale") { + if (t.size == 0) { + return { + typ: exports.EnumToken.TransformFunctionTokenType, + val: "scale", + chi: [{ typ: exports.EnumToken.NumberTokenType, val: 1 }], + }; + } + if (t.size == 1) { + return { + typ: exports.EnumToken.TransformFunctionTokenType, + val: "scale" + (t.has("x") ? "X" : t.has("y") ? "Y" : "Z"), + chi: [values[t.has("x") ? 0 : t.has("y") ? 1 : 2]], + }; + } + if (t.size == 2) { + if (t.has("z")) { + return transform; } + return { + typ: exports.EnumToken.TransformFunctionTokenType, + val: "scale", + chi: [values[0], { typ: exports.EnumToken.CommaTokenType }, values[1]], + }; } } + return transform; } - /** - * render ast node - * @param data - * @param options - * @param sourcemaps - * @param sourceLocation - * @param linesMap - * @param errors - * @param reducer - * @param cache - * @param level - * @param indents - * - * @internal - */ - function renderAstNode(data, options, sourcemaps, sourceLocation, linesMap, errors, reducer, cache, level = 0, indents = []) { - if (indents.length < level + 1) { - indents.push(options.indent.repeat(level)); - } - if (indents.length < level + 2) { - indents.push(options.indent.repeat(level + 1)); + + function skewX(x, from) { + const matrix = identity(); + matrix[4] = Math.tan(x); + return multiply(from, matrix); + } + function skewY(y, from) { + const matrix = identity(); + matrix[1] = Math.tan(y); + return multiply(from, matrix); + } + // convert angle to radian + function skew(values, from) { + const matrix = identity(); + matrix[4] = Math.tan(values[0]); + if (values.length > 1) { + matrix[1] = Math.tan(values[1]); } + return multiply(from, matrix); + } + + function perspective(x, from) { + const matrix = identity(); // @ts-ignore - let children = ""; - let str = ""; - const indent = indents[level]; - const indentSub = indents[level + 1]; - switch (data.typ) { - case exports.EnumToken.DeclarationNodeType: - return `${data.nam}:${options.indent}${(options.minifyValues - ? filterValues(data.val) - : data.val).reduce(reducer, "")}`; - case exports.EnumToken.CommentNodeType: - case exports.EnumToken.CDOCOMMNodeType: - if (data.val.startsWith("/*# sourceMappingURL=")) { - // ignore sourcemap comment - return ""; - } - return !options.removeComments || (options.preserveLicense && data.val.startsWith("/*!")) - ? data.val - : ""; - case exports.EnumToken.StyleSheetNodeType: - for (const node of data.chi) { - str = renderAstNode(node, options, sourcemaps, sourceLocation, linesMap, errors, reducer, cache, level, indents); - if (str === "") { - continue; - } - if (children.length > 0) { - str = options.newLine + str; - } - children += str; - if (sourcemaps != null && str !== "" && options.newLine) { - move(sourceLocation, linesMap, options.newLine); - } - } - return children; - case exports.EnumToken.AtRuleNodeType: - case exports.EnumToken.RuleNodeType: - case exports.EnumToken.KeyframesRuleNodeType: - case exports.EnumToken.KeyframesAtRuleNodeType: - if ([exports.EnumToken.AtRuleNodeType, exports.EnumToken.KeyframesAtRuleNodeType].includes(data.typ) && !("chi" in data)) { - return `${indent}@${data.nam}${data.val === "" ? "" : options.indent || " "}${data.val};`; - } - const lineMapLength = linesMap ? linesMap.getLineStarts().length : 0; - const prelude = (indent.length > 0 ? options.newLine : "") + - indent + - ([exports.EnumToken.AtRuleNodeType, exports.EnumToken.KeyframesAtRuleNodeType].includes(data.typ) - ? `@${data.nam}${data.val === "" ? "" : options.indent || " "}${data.val}${options.indent}{` - : data.sel + `${options.indent}{`); - if (sourcemaps != null) { - updateSourceMap(data, options, cache, sourcemaps, sourceLocation, linesMap, prelude); - } - let node; - let recordDeclarationSourceMap = data.typ == exports.EnumToken.AtRuleNodeType; - for (let i = 0; i < data.chi.length; i++) { - node = data.chi[i]; - if (node.typ == exports.EnumToken.CommentNodeType) { - str = - options.removeComments && - (!options.preserveLicense || !node.val.startsWith("/*!")) - ? "" - : node.val; - } - else if (node.typ == exports.EnumToken.DeclarationNodeType) { - str = `${node.nam}:${options.indent}${(options.minifyValues - ? filterValues(node.val) - : node.val) - .reduce(reducer, "") - .trimEnd()};`; - } - else { - str = renderAstNode(node, options, sourcemaps, sourceLocation, linesMap, errors, reducer, cache, level + 1, indents); - if (str === "") { + matrix[2 * 4 + 3] = typeof x == "object" && x.val == "none" ? 0 : x == 0 ? Number.NEGATIVE_INFINITY : -1 / x; + return multiply(from, matrix); + } + + function compute(transformLists) { + transformLists = transformLists.slice(); + stripCommaToken(transformLists); + let matrix = identity(); + let mat; + let transforms; + const cumulative = []; + // all transform function arguments must be 0 or scale(1) + for (const transform of transformLists) { + switch (transform.val) { + case "rotate": + case "rotateX": + case "rotateY": + case "rotateZ": + case "rotate3d": + for (const child of transform.chi) { + if (child.typ == exports.EnumToken.WhitespaceTokenType || child.typ == exports.EnumToken.CommaTokenType) { continue; } - children += str; - str = ""; - continue; - } - if (str === "") { - continue; - } - str = options.newLine + indentSub + str; - children += str; - if (sourcemaps != null && str !== "") { - if (node.typ == exports.EnumToken.DeclarationNodeType && recordDeclarationSourceMap) { - // if declaration is child of at-rule, then record it - // .rule { - // @media screen { - // color: red; - // } - // } - // @ts-ignore - updateSourceMap(node, options, cache, sourcemaps, sourceLocation, linesMap, str); - } - else { - move(sourceLocation, linesMap, str); + if ((child.typ != exports.EnumToken.AngleTokenType && + child.typ != exports.EnumToken.NumberTokenType && + child.typ != exports.EnumToken.PercentageTokenType) || + // angle >= 360deg, do not transform + Math.abs(getAngle(child)) >= 1) { + return null; } } - } - if (children.endsWith(";")) { - children = children.slice(0, -1); - sourceLocation.end--; - } - if (options.removeEmpty && children === "") { - if (sourcemaps != null) { - sourceLocation.end -= prelude.length; - linesMap.getLineStarts().length = lineMapLength; - } - return ""; - } - const end = options.newLine + indent + `}`; - if (sourcemaps != null) { - move(sourceLocation, linesMap, end); - } - return prelude + children + end; - default: - return ""; + // break; + } } - } - /** - * render ast token - * @param token - * @param options - * @param cache - * @param reducer - * @param errors - * @private - */ - function renderValue(token, options = {}, cache = Object.create(null), reducer, errors) { - if (token.typ === exports.EnumToken.WhenElseFunctionTokenType && - equalsIgnoreCase(token.val, "supports")) { - options = { ...options, minifyValues: false, minify: false, convertColor: false }; - reducer = null; + for (const transformList of splitTransformList(transformLists)) { + mat = computeMatrix(transformList, identity()); + if (mat == null) { + return null; + } + matrix = multiply(matrix, mat); + transforms = minify$1(mat) ?? transformList; + for (let i = 0; i < transforms.length; i++) { + cumulative.push(transforms[i]); + } } - options.minifyValues ??= options.minify; - if (reducer == null) { - reducer = function (acc, curr) { - if (curr.typ == exports.EnumToken.CommentTokenType && options.removeComments) { - if (!options.preserveLicense || !curr.val.startsWith("/*!")) { - return acc; - } - return acc + curr.val; + const serialized = serialize(matrix); + if (cumulative.length > 0) { + for (let i = 0; i < cumulative.length; i++) { + if (cumulative[i].typ == exports.EnumToken.IdenTokenType && cumulative[i].val == "none") { + cumulative.splice(i--, 1); } - return acc + renderValue(curr, options, cache, reducer); - }; + } + if (cumulative.length == 0) { + cumulative.push({ + typ: exports.EnumToken.IdenTokenType, + val: "none", + }); + } } - switch (token.typ) { - case exports.EnumToken.UrlFunctionTokenDefType: - case exports.EnumToken.FunctionTokenDefType: - case exports.EnumToken.MathFunctionTokenDefType: - case exports.EnumToken.ImageFunctionTokenDefType: - case exports.EnumToken.ColorFunctionTokenDefType: - case exports.EnumToken.TimingFunctionTokenDefType: - case exports.EnumToken.WhenElseFunctionTokenDefType: - case exports.EnumToken.SupportsFunctionTokenDefType: - case exports.EnumToken.TimelineFunctionTokenDefType: - case exports.EnumToken.GridTemplateFuncTokenDefType: - case exports.EnumToken.TransformFunctionTokenDefType: - case exports.EnumToken.ContainerFunctionTokenDefType: - case exports.EnumToken.PseudoClassFunctionTokenDefType: - case exports.EnumToken.GeneralEnclosedFunctionTokenDefType: - case exports.EnumToken.CustomFunctionTokenDefType: - case exports.EnumToken.WildCardFunctionTokenDefType: - return token.val + "("; - case exports.EnumToken.ListToken: - return token.chi.reduce((acc, curr) => acc + renderValue(curr, options, cache), ""); - case exports.EnumToken.BinaryExpressionTokenType: - if (exports.EnumToken.Mul === token.op || - exports.EnumToken.Div === token.op) { - let result = ""; - if (token.l.typ == exports.EnumToken.BinaryExpressionTokenType && - [exports.EnumToken.Add, exports.EnumToken.Sub].includes(token.l.op)) { - result = "(" + renderValue(token.l, options, cache) + ")"; - } - else { - result = renderValue(token.l, options, cache); - } - result += token.op == exports.EnumToken.Mul ? "*" : "/"; - if (token.r.typ == exports.EnumToken.BinaryExpressionTokenType && - [exports.EnumToken.Add, exports.EnumToken.Sub].includes(token.r.op)) { - result += "(" + renderValue(token.r, options, cache) + ")"; - } - else { - result += renderValue(token.r, options, cache); - } - return result; - } - return (renderValue(token.l, options, cache) + - (token.op == exports.EnumToken.Add ? " + " : " - ") + - renderValue(token.r, options, cache)); - case exports.EnumToken.FractionTokenType: - const fraction = renderValue(token.l) + "/" + renderValue(token.r); - if (+token.r.val != 0) { - const value = minifyNumber(+token.l.val / +token.r.val); - if (value.length <= fraction.length) { - return value; - } - } - return fraction; - case exports.EnumToken.Add: - return " + "; - case exports.EnumToken.Sub: - return " - "; - case exports.EnumToken.UniversalSelectorTokenType: - case exports.EnumToken.Star: - case exports.EnumToken.Mul: - return "*"; - case exports.EnumToken.Div: - return "/"; - case exports.EnumToken.WrappedValuesTokenType: - return ("{" + - token.chi.reduce((acc, curr) => acc + renderValue(curr, options, cache), "") + - "}"); - case exports.EnumToken.ColorTokenType: - if (token.kin == exports.ColorType.LIGHT_DARK || - ("chi" in token && options.convertColor === false && token.chi.length == 2)) { - return (token.val + - "(" + - token.chi.reduce((acc, curr) => { - if (curr.typ === exports.EnumToken.IdenTokenType) { - parseColor(curr); + const result = { + matrix: serialize(toZero(matrix)), + cumulative, + minified: minify$1(matrix) ?? [serialized], + }; + return result; + } + function computeMatrix(transformList, matrixVar) { + let values = []; + let val; + let i = 0; + for (; i < transformList.length; i++) { + if (transformList[i].typ != exports.EnumToken.TransformFunctionTokenType || + !transformFunctions.includes(transformList[i].val)) { + return null; + } + switch (transformList[i].val) { + case "translate": + case "translateX": + case "translateY": + case "translateZ": + case "translate3d": + { + values.length = 0; + const children = stripCommaToken(transformList[i].chi.slice()); + const valCount = transformList[i].val == "translate3d" + ? 3 + : transformList[i].val == "translate" + ? 2 + : 1; + for (let j = 0; j < children.length; j++) { + if (children[j].typ == exports.EnumToken.WhitespaceTokenType) { + continue; } - return acc + renderValue(curr, options, cache); - }, "") + - ")"); - } - if (options.convertColor !== false) { - const value = convertColor(token, typeof options.convertColor === "boolean" - ? exports.ColorType.HEX - : (exports.ColorType[exports.ColorType[options.convertColor ?? "HEX"] - ?.toUpperCase?.() - .replaceAll?.("-", "_")] ?? exports.ColorType.HEX)); - if (value != null) { - token = value; + val = length2Px(children[j]); + if (val == null) { + return null; + } + values.push(val); + } + if (values.length == 0 || values.length > valCount) { + return null; + } + if (transformList[i].val == "translateX") { + matrixVar = translateX(values[0], matrixVar); + } + else if (transformList[i].val == "translateY") { + matrixVar = translateY(values[0], matrixVar); + } + else if (transformList[i].val == "translateZ") { + matrixVar = translateZ(values[0], matrixVar); + } + else if (transformList[i].val == "translate") { + matrixVar = translate(values, matrixVar); + } + else { + // @ts-ignore + matrixVar = translate3d(values, matrixVar); + } } - if (token.kin == exports.ColorType.HEX || token.kin == exports.ColorType.LIT) { - return reduceHexValue(token.val); + break; + case "rotate": + case "rotateX": + case "rotateY": + case "rotateZ": + case "rotate3d": + { + let x = 0; + let y = 0; + let z = 0; + let angle; + let values = []; + let valuesCount = transformList[i].val == "rotate3d" ? 4 : 1; + for (const child of stripCommaToken(transformList[i].chi.slice())) { + values.push(child); + if (transformList[i].val == "rotateX") { + x = 1; + } + else if (transformList[i].val == "rotateY") { + y = 1; + } + else if (transformList[i].val == "rotate" || + transformList[i].val == "rotateZ") { + z = 1; + } + } + if (values.length != valuesCount) { + return null; + } + if (transformList[i].val == "rotate3d") { + x = getNumber(values[0]); + y = getNumber(values[1]); + z = getNumber(values[2]); + } + angle = getAngle(values.at(-1)); + if ([x, y, z, angle].some((t) => typeof t != "number" || Number.isNaN(+t))) { + return null; + } + if (transformList[i].val == "rotate" || + transformList[i].val == "rotateZ") { + matrixVar = rotate(angle * 2 * Math.PI, matrixVar); + } + else { + matrixVar = rotate3D(angle * 2 * Math.PI, x, y, z, matrixVar); + } } - } - if (token.kin === exports.ColorType.SYS || - token.kin === exports.ColorType.DPSYS || - token.kin === exports.ColorType.NON_STD) { - return token.val; - } - if (token.kin == exports.ColorType.HEX || token.kin == exports.ColorType.LIT) { - return token.val; - } - if (Array.isArray(token.chi)) { - const fnName = token.val; - const isLegacy = ["rgb", "rgba", "hsl", "hsla"].includes(token.val); - const hasAlpha = [ - "rgb", - "rgba", - "hsl", - "hsla", - "hwb", - "oklab", - "oklch", - "lab", - "lch", - ].includes(fnName); - const useAlpha = (hasAlpha && token.chi.length == (isLegacy ? 7 : 4)) || - ("color" == token.val && token.chi.length == 5); - return ((hasAlpha && token.val.endsWith("a") ? fnName.slice(0, -1) : fnName) + - "(" + - token - .chi.reduce((acc, curr, index, array) => { - if (/[,/]\s*$/.test(acc)) { - if (curr.typ == exports.EnumToken.WhitespaceTokenType) { - return acc.trimEnd(); - } - return acc.trimStart() + renderValue(curr, options, cache); + break; + case "scale": + case "scaleX": + case "scaleY": + case "scaleZ": + case "scale3d": + { + values.length = 0; + let child; + const children = stripCommaToken(transformList[i].chi.slice()); + for (let k = 0; k < children.length; k++) { + child = children[k]; + if (child.typ != exports.EnumToken.NumberTokenType && child.typ != exports.EnumToken.PercentageTokenType) { + return null; } - if (isLegacy && curr.typ == exports.EnumToken.CommaTokenType) { - return acc.trimEnd() + " "; + values.push(getNumber(child)); + } + if (transformList[i].val == "scale3d") { + if (values.length != 3) { + return null; } - if (curr.typ == exports.EnumToken.WhitespaceTokenType) { - return acc.trimEnd() + " "; + matrixVar = scale3d(values[0], values[1], values[2], matrixVar); + break; + } + if (transformList[i].val == "scale") { + if (values.length != 1 && values.length != 2) { + return null; + } + matrixVar = scale(values[0], values[1] ?? values[0], matrixVar); + break; + } + if (values.length != 1) { + return null; + } + else if (transformList[i].val == "scaleX") { + matrixVar = scaleX(values[0], matrixVar); + } + else if (transformList[i].val == "scaleY") { + matrixVar = scaleY(values[0], matrixVar); + } + else if (transformList[i].val == "scaleZ") { + matrixVar = scaleZ(values[0], matrixVar); + } + } + break; + case "skew": + case "skewX": + case "skewY": + { + values.length = 0; + let child; + let value; + for (let k = 0; k < transformList[i].chi.length; k++) { + child = transformList[i].chi[k]; + if (child.typ == exports.EnumToken.CommentTokenType || + child.typ == exports.EnumToken.CommaTokenType || + child.typ == exports.EnumToken.WhitespaceTokenType) { + continue; + } + value = getAngle(child); + values.push(value * 2 * Math.PI); + } + if (values.length == 0 || + values.length > (transformList[i].val == "skew" ? 2 : 1)) { + return null; + } + if (transformList[i].val == "skew") { + matrixVar = skew(values, matrixVar); + } + else { + matrixVar = + transformList[i].val == "skewX" + ? skewX(values[0], matrixVar) + : skewY(values[0], matrixVar); + } + } + break; + case "perspective": + { + const values = []; + let child; + let value; + for (let k = 0; k < transformList[i].chi.length; k++) { + child = transformList[i].chi[k]; + if (child.typ == exports.EnumToken.CommentTokenType || child.typ == exports.EnumToken.WhitespaceTokenType) { + continue; } - if (curr.typ == exports.EnumToken.CommaTokenType || - (curr.typ == exports.EnumToken.LiteralTokenType && curr.val == "/")) { - return acc.trimEnd() + (curr.typ == exports.EnumToken.CommaTokenType ? "," : "/"); + if (child.typ == exports.EnumToken.IdenTokenType && child.val == "none") { + values.push(child); + continue; } - return (acc.trimEnd() + - (useAlpha && index == array.length - 1 ? "/" : " ") + - renderValue(curr, options, cache)); - }, "") - .trimStart() + - ")"); - } - case exports.EnumToken.UrlFunctionTokenType: - if (options.minifyValues && token.typ === exports.EnumToken.UrlFunctionTokenType) { - if (token.chi[0]?.typ === exports.EnumToken.BadUrlTokenType) { - return "url()"; - } - for (const child of token.chi) { - if (child.typ === exports.EnumToken.StringTokenType) { - if (child.val.slice(1, 5) !== "data:" && - urlTokenMatcher.test(child.val)) { - Object.assign(child, { - typ: exports.EnumToken.UrlTokenTokenType, - val: child.val.slice(1, -1), - }); + value = length2Px(child); + if (value == null) { + return null; } - break; + values.push(value); + } + if (values.length != 1) { + return null; } + matrixVar = perspective(values[0], matrixVar); } - } - case exports.EnumToken.ParensTokenType: - case exports.EnumToken.FunctionTokenType: - case exports.EnumToken.MathFunctionTokenType: - case exports.EnumToken.ImageFunctionTokenType: - if (options.minifyValues && token.typ === exports.EnumToken.ImageFunctionTokenType) { - const slice = token.chi.slice(); - switch (token.val) { - case "linear-gradient": - case "repeating-linear-gradient": - { - let i; - let k; - let j; - let key; - for (i = 0; i < slice.length; i++) { - if (slice[i].typ == exports.EnumToken.ColorTokenType || - slice[i].typ === exports.EnumToken.CommaTokenType) { - break; - } - if (slice[i].typ == exports.EnumToken.IdenTokenType && - equalsIgnoreCase(slice[i].val, "to")) { - k = i + 1; - while (k < slice.length && - (slice[k].typ === exports.EnumToken.WhitespaceTokenType || - slice[k].typ === exports.EnumToken.CommentTokenType)) { - k++; - } - if (k < slice.length && slice[k].typ === exports.EnumToken.IdenTokenType) { - // convert keyword to angle - key = slice[k].val.toLowerCase(); - switch (key) { - case "right": - slice.splice(i, k - i + 1, { - typ: exports.EnumToken.AngleTokenType, - val: 90, - }); - break; - case "left": - slice.splice(i, k - i + 1, { - typ: exports.EnumToken.AngleTokenType, - val: 270, - }); - break; - case "top": - case "bottom": - j = k + 1; - while (j < slice.length && - (slice[j].typ === exports.EnumToken.WhitespaceTokenType || - slice[j].typ === exports.EnumToken.CommentTokenType)) { - j++; - } - if (j < slice.length && slice[j].typ !== exports.EnumToken.CommaTokenType) { - i = j - 1; - break; - } - // https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/gradient/linear-gradient#:~:text=If%20unspecified%2C%20it%20defaults%20to%20to%20bottom - if (key === "bottom") { - // to bottom -> 180deg is the default angle and will be removed - while (k < slice.length && - slice[k].typ != exports.EnumToken.CommaTokenType) { - k++; - } - slice.splice(i, k - i + 1); - i--; - continue; - } - slice.splice(i, k - i + 1, { - typ: exports.EnumToken.AngleTokenType, - val: key === "top" ? 0 : 180, - }); - i--; - break; - } - } - } - else if (slice[i].typ == exports.EnumToken.AngleTokenType) { - if ((180 - toDegrees(slice[i]).val) % 360 === 0) { - k = i + 1; - // while (k < slice.length && slice[k].typ !== EnumToken.CommaTokenType) { - // k++; - // } - slice.splice(i, k - i + 1); - } - } - } - if (slice[i]?.typ === exports.EnumToken.CommaTokenType) { - i++; - // while ( - // i < slice.length && - // (slice[i].typ === EnumToken.WhitespaceTokenType || - // slice[i].typ === EnumToken.CommentTokenType) - // ) { - // i++; - // } - } - if (slice[i]?.typ === exports.EnumToken.ColorTokenType) { - for (const token of reduceColorStops(slice.splice(i, slice.length - i))) { - slice.push(token); - } - } + break; + case "matrix3d": + case "matrix": + { + const values = []; + let value; + for (const token of transformList[i].chi) { + if ([ + exports.EnumToken.WhitespaceTokenType, + exports.EnumToken.CommentTokenType, + exports.EnumToken.CommaTokenType, + ].includes(token.typ)) { + continue; } - break; - case "radial-gradient": - case "repeating-radial-gradient": - { - let i = 0; - // while ( - // i < slice.length && - // (slice[i].typ === EnumToken.WhitespaceTokenType || - // slice[i].typ === EnumToken.CommentTokenType) - // ) { - // i++; - // } - const positions = []; - const form = []; - const size = []; - const colorSpaceDef = []; - if (slice[i]?.typ === exports.EnumToken.IdenTokenType) { - if (equalsIgnoreCase(slice[i].val, "farthest-corner") || - equalsIgnoreCase(slice[i].val, "ellipse")) { - i++; - } - else if (equalsIgnoreCase(slice[i].val, "closest-side") || - equalsIgnoreCase(slice[i].val, "closest-corner") || - equalsIgnoreCase(slice[i].val, "farthest-side")) { - size.push(slice[i++]); - } - else if (equalsIgnoreCase(slice[i].val, "circle")) { - form.push(slice[i++]); - } - while (slice[i]?.typ === exports.EnumToken.WhitespaceTokenType || - slice[i]?.typ === exports.EnumToken.CommentTokenType) { - i++; - } - } - if (slice[i]?.typ === exports.EnumToken.IdenTokenType) { - if (equalsIgnoreCase(slice[i].val, "farthest-corner") || - equalsIgnoreCase(slice[i].val, "ellipse")) { - i++; - } - else if (equalsIgnoreCase(slice[i].val, "closest-side") || - equalsIgnoreCase(slice[i].val, "closest-corner") || - equalsIgnoreCase(slice[i].val, "farthest-side")) { - size.push(slice[i++]); - } - else if (equalsIgnoreCase(slice[i].val, "circle")) { - form.push(slice[i++]); - } - while (slice[i]?.typ === exports.EnumToken.WhitespaceTokenType || - slice[i]?.typ === exports.EnumToken.CommentTokenType) { - i++; - } - } - if (slice[i]?.typ === exports.EnumToken.IdenTokenType && - equalsIgnoreCase(slice[i].val, "at")) { - i++; - } - while (slice[i]?.typ === exports.EnumToken.WhitespaceTokenType || - slice[i]?.typ === exports.EnumToken.CommentTokenType) { - i++; - } - if (slice[i]?.typ === exports.EnumToken.PercentageTokenType || - (slice[i]?.typ === exports.EnumToken.NumberTokenType && 0 === slice[i].val) || - (slice[i]?.typ === exports.EnumToken.IdenTokenType && - (equalsIgnoreCase(slice[i].val, "center") || - equalsIgnoreCase(slice[i].val, "top") || - equalsIgnoreCase(slice[i].val, "bottom") || - equalsIgnoreCase(slice[i].val, "left") || - equalsIgnoreCase(slice[i].val, "right")))) { - do { - if (slice[i]?.typ === exports.EnumToken.IdenTokenType && - "in" === slice[i].val) { - break; - } - positions.push(slice[i]); - i++; - } while (slice[i]?.typ !== exports.EnumToken.CommaTokenType); - } - while (slice[i]?.typ !== exports.EnumToken.CommaTokenType) { - colorSpaceDef.push(slice[i++]); - } - if (slice[i]?.typ === exports.EnumToken.CommaTokenType) { - i++; - } - if (positions.length > 0) { - let k; - let position1 = ""; - let position2 = ""; - for (k = 0; k < positions.length; k++) { - if (positions[k].typ === exports.EnumToken.IdenTokenType) { - if (position1.length === 0) { - position1 = positions[k].val; - } - else { - position2 = positions[k].val; - } - } - else if (positions[k].typ === exports.EnumToken.PercentageTokenType || - positions[k].typ === exports.EnumToken.NumberTokenType) { - if (position1.length === 0) { - position1 = positions[k].val + "%"; - } - else { - position2 = positions[k].val + "%"; - } - } - if (position2.length > 0) { - break; - } - } - if (position1.length > 0) { - let key = `${position1} ${position2}`.trim(); - if (key === "50% 50%" || - key === "center center" || - key === "50%" || - key === "center") { - positions.length = 0; - } - else if (key === "0% 0%" || - key === "0 0" || - key === "top left" || - key === "left top") { - positions.splice(0, positions.length, { - typ: exports.EnumToken.PercentageTokenType, - val: 0, - }); - } - else if (key === "50% 0%" || - key === "50% 0" || - key === "center top" || - key === "top center") { - positions.splice(0, positions.length, { - typ: exports.EnumToken.IdenTokenType, - val: "top", - }); - } - else if (key === "top right" || key === "right top") { - positions.splice(0, positions.length, { - typ: exports.EnumToken.PercentageTokenType, - val: 100, - }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.PercentageTokenType, val: 0 }); - } - else if (key === "0 50%" || - key === "0% 50%" || - key === "left center" || - key === "center left") { - positions.splice(0, positions.length, { - typ: exports.EnumToken.IdenTokenType, - val: "left", - }); - } - else if (key === "100% 50%" || key === "right center" || key === "center right") { - positions.splice(0, positions.length, { - typ: exports.EnumToken.IdenTokenType, - val: "right", - }); - } - else if (key === "0 100%" || - key === "0% 100%" || - key === "left bottom" || - key === "bottom left") { - positions.splice(0, positions.length, { - typ: exports.EnumToken.IdenTokenType, - val: "bottom", - }); - } - else if (key === "50% 100%" || - key === "center bottom" || - key === "bottom center") { - positions.splice(0, positions.length, { - typ: exports.EnumToken.IdenTokenType, - val: "bottom", - }); - } - else if (key === "100% 100%" || - key === "right bottom" || - key === "bottom right") { - positions.splice(0, positions.length, { - typ: exports.EnumToken.PercentageTokenType, - val: 100, - }); - } - else if (position1 === position2) { - positions.splice(0, positions.length, positions[k]); - } - } - } - const result = []; - if (form.length > 0) { - for (const token of form) { - result.push(token); - } - } - if (size.length > 0) { - if (result.length > 0) { - result.push({ typ: exports.EnumToken.WhitespaceTokenType }); - } - for (const token of size) { - result.push(token); - } - } - if (positions.length > 0) { - if (result.length > 0) { - result.push({ typ: exports.EnumToken.WhitespaceTokenType }); - } - result.push({ typ: exports.EnumToken.IdenTokenType, val: "at" }, { typ: exports.EnumToken.WhitespaceTokenType }); - for (const token of positions) { - result.push(token); - } - } - if (colorSpaceDef.length > 0) { - if (result.length > 0) { - result.push({ typ: exports.EnumToken.WhitespaceTokenType }); - } - for (const token of colorSpaceDef) { - result.push(token); - } - } - if (result.length > 0) { - result.push({ typ: exports.EnumToken.CommaTokenType }); - } - for (const token of reduceColorStops(slice.slice(i))) { - result.push(token); - } - slice.length = 0; - for (const token of result) { - slice.push(token); - } + value = getNumber(token); + if (value == null) { + return null; } - break; - case "conic-gradient": - case "repeating-conic-gradient": - { - let i = 0; - const angles = []; - const positions = []; - const colorSpaceDef = []; - // while ( - // i < slice.length && - // (slice[i].typ === EnumToken.WhitespaceTokenType || - // slice[i].typ === EnumToken.CommentTokenType) - // ) { - // i++; - // } - if (slice[i]?.typ === exports.EnumToken.IdenTokenType && - equalsIgnoreCase(slice[i].val, "from")) { - angles.push(slice[i++]); - while (i < slice.length && - (slice[i].typ === exports.EnumToken.WhitespaceTokenType || - slice[i].typ === exports.EnumToken.CommentTokenType)) { - angles.push(slice[i++]); - } - if ((slice[i]?.typ === exports.EnumToken.NumberTokenType || - slice[i]?.typ === exports.EnumToken.AngleTokenType) && - 0 === toDegrees(slice[i]).val) { - angles.length = 0; - i++; - } - else if (slice[i]?.typ !== exports.EnumToken.CommaTokenType && - slice[i].typ != exports.EnumToken.IdenTokenType) { - angles.push(slice[i++]); - } - while (i < slice.length && - (slice[i].typ === exports.EnumToken.WhitespaceTokenType || - slice[i].typ === exports.EnumToken.CommentTokenType)) { - i++; - } - } - if (slice[i]?.typ === exports.EnumToken.IdenTokenType && - equalsIgnoreCase(slice[i].val, "at")) { - i++; - while (i < slice.length && - (slice[i].typ === exports.EnumToken.WhitespaceTokenType || - slice[i].typ === exports.EnumToken.CommentTokenType)) { - i++; - } - let position1 = ""; - let position2 = ""; - if (slice[i]?.typ === exports.EnumToken.IdenTokenType && - !equalsIgnoreCase("in", slice[i].val)) { - position1 = slice[i].val; - positions.push(slice[i++]); - } - else if (slice[i]?.typ === exports.EnumToken.PercentageTokenType || - slice[i]?.typ === exports.EnumToken.NumberTokenType) { - position1 = slice[i].val + "%"; - positions.push(slice[i++]); - } - while (i < slice.length && - (slice[i].typ === exports.EnumToken.WhitespaceTokenType || - slice[i].typ === exports.EnumToken.CommentTokenType)) { - positions.push(slice[i++]); - } - if (slice[i]?.typ === exports.EnumToken.IdenTokenType && - !equalsIgnoreCase("in", slice[i].val)) { - position2 = slice[i].val; - positions.push(slice[i++]); - } - else if (slice[i]?.typ === exports.EnumToken.PercentageTokenType || - slice[i]?.typ === exports.EnumToken.NumberTokenType) { - position2 = slice[i].val + "%"; - positions.push(slice[i++]); - } - while (i < slice.length && - (slice[i].typ === exports.EnumToken.WhitespaceTokenType || - slice[i].typ === exports.EnumToken.CommentTokenType)) { - i++; - } - if (position1.length > 0) { - reducegradientBackgroundPosition(positions, `${position1} ${position2}`.trim()); - } - } - while (i < slice.length && - (slice[i].typ === exports.EnumToken.WhitespaceTokenType || - slice[i].typ === exports.EnumToken.CommentTokenType)) { - i++; - } - if (slice[i]?.typ === exports.EnumToken.IdenTokenType && - equalsIgnoreCase("in", slice[i].val)) { - while (i < slice.length && slice[i].typ !== exports.EnumToken.CommaTokenType) { - colorSpaceDef.push(slice[i++]); - } - } - if (slice[i]?.typ === exports.EnumToken.CommaTokenType) { - i++; - } - const result = []; - if (positions.length > 0) { - if (positions.length > 0) { - if (angles.length > 0) { - angles.push({ typ: exports.EnumToken.WhitespaceTokenType }); - } - angles.push({ typ: exports.EnumToken.IdenTokenType, val: "at" }, { typ: exports.EnumToken.WhitespaceTokenType }); - for (const position of positions) { - angles.push(position); - } - } - } - if (angles.length > 0) { - for (const angle of angles) { - result.push(angle); - } - result.push({ typ: exports.EnumToken.CommaTokenType }); - } - if (colorSpaceDef.length > 0) { - if (colorSpaceDef.length > 0) { - if (result.length > 0) { - result.push({ typ: exports.EnumToken.WhitespaceTokenType }); - } - for (const token of colorSpaceDef) { - result.push(token); + values.push(value); + } + if (transformList[i].val == "matrix") { + if (values.length != 6) { + return null; + } + } + else if (values.length != 16) { + return null; + } + matrixVar = multiply(matrixVar, matrix(values)); + } + break; + default: + return null; + } + } + return matrixVar; + } + function splitTransformList(transformList) { + let pattern = null; + const tokens = []; + for (let i = 0; i < transformList.length; i++) { + if (transformList[i].typ == exports.EnumToken.CommentTokenType || + transformList[i].typ == exports.EnumToken.WhitespaceTokenType) { + continue; + } + if (pattern == null || + (transformList[i].typ == exports.EnumToken.TransformFunctionTokenType && + !transformList[i].val.startsWith(pattern))) { + if (transformList[i].typ == exports.EnumToken.TransformFunctionTokenType) { + if (transformList[i].val.startsWith("scale")) { + pattern = "scale"; + } + else if (transformList[i].val.startsWith("rotate")) { + pattern = "rotate"; + } + else if (transformList[i].val.startsWith("translate")) { + pattern = "translate"; + } + else { + pattern = null; + } + tokens.push([transformList[i]]); + continue; + } + } + if (pattern != null && + transformList[i].typ == exports.EnumToken.TransformFunctionTokenType && + transformList[i].val.startsWith(pattern)) { + tokens[tokens.length - 1].push(transformList[i]); + continue; + } + tokens.push([transformList[i]]); + } + return tokens; + } + + class TransformCssFeature { + accept = new Set([ + exports.EnumToken.RuleNodeType, + exports.EnumToken.AtRuleNodeType, + exports.EnumToken.KeyframesRuleNodeType, + ]); + get ordering() { + return 4; + } + get processMode() { + return exports.FeatureWalkMode.Post; + } + static register(options) { + // @ts-ignore + if (options.computeTransform) { + // @ts-ignore + options.features.push(new TransformCssFeature()); + } + } + run(ast) { + if (ast.chi == null) { + return null; + } + let i = 0; + let node; + // @ts-ignore + for (; i < ast.chi.length; i++) { + // @ts-ignore + node = ast.chi[i]; + if (node[STATE] & (exports.EnumAstNodeStatus.Invalid | exports.EnumAstNodeStatus.ValidationFailed)) { + continue; + } + if (node.typ != exports.EnumToken.DeclarationNodeType || + !node.nam.match(/^(-[a-z]+-)?transform$/)) { + continue; + } + const children = []; + for (const child of node.val) { + children.push(child.typ == exports.EnumToken.TransformFunctionTokenType + ? minifyTransformFunctions(child) + : child); + } + let { matrix, cumulative, minified } = compute(children) ?? { + matrix: null, + cumulative: null, + minified: null, + }; + if (matrix == null || cumulative == null || minified == null) { + node.val = children.map((t) => t.typ == exports.EnumToken.TransformFunctionTokenType ? minifyTransformFunctions(t) : t); + continue; + } + let r = [filterValues(children)]; + if (eqMatrix(matrix, cumulative)) { + r.push(cumulative); + } + if (eqMatrix(matrix, minified)) { + r.push(minified); + } + const l = printValue$1(matrix).length; + node.val = r.reduce((acc, curr) => { + if (curr.reduce((acc, t) => acc + printValue$1(t), "").length < l) { + return curr; + } + return acc; + }, [matrix]); + } + return null; + } + } + + const nodeMatcher = (value) => value.typ === exports.EnumToken.IfConditionTokenType || + (value.typ === exports.EnumToken.WildCardFunctionTokenType && equalsIgnoreCase("if", value.val)); + function substituteIfElseNode(declaration, node, wrapper, parentWrapper, cache) { + const result = []; + let nodeMap = new Map(); + let clonedDeclaration; + let targetParentWrapper = parentWrapper; + let targetWrapper = wrapper; + if (node.typ === exports.EnumToken.IfElseConditionTokenType) { + // + clonedDeclaration = cloneNode(declaration, true, nodeMap); + // replace else: ... with the actual value + if (node.r.typ === exports.EnumToken.IfConditionTokenType) { + const target = node.r.l.find((t) => t.typ != exports.EnumToken.CommentTokenType && t.typ != exports.EnumToken.WhitespaceTokenType); + // + if (target.typ === exports.EnumToken.IdenTokenType && equalsIgnoreCase("else", target.val)) { + replaceNodeOrValue(nodeMap.get(targetParentWrapper), nodeMap.get(targetWrapper), node.r.r.at(-1)?.typ === + exports.EnumToken.SemiColonTokenType + ? trimArray(node.r.r.slice(0, -1)) + : node.r.r); + // @ts-expect-error + if (targetParentWrapper.typ != exports.EnumToken.DeclarationNodeType) { + let index = targetParentWrapper.chi.indexOf(targetWrapper); + if (index != -1) { + let i; + let k; + let siblingWrapper; + let left = node.l.l.find((t) => t.typ != exports.EnumToken.CommentTokenType && t.typ != exports.EnumToken.WhitespaceTokenType); + for (i = index + 1; i < targetParentWrapper.chi.length; i++) { + if (targetParentWrapper.chi[i].typ === targetWrapper.typ) { + siblingWrapper = targetParentWrapper.chi[i]; + for (k = 0; k < siblingWrapper.chi.length; k++) { + if (siblingWrapper.chi[k].typ === exports.EnumToken.IfElseConditionTokenType) { + let leftSide = siblingWrapper.chi[k].l.l.find((t) => t.typ != exports.EnumToken.CommentTokenType && + t.typ != exports.EnumToken.WhitespaceTokenType); + if (eq(left, leftSide)) { + replaceNodeOrValue(nodeMap.get(targetParentWrapper), nodeMap.get(targetParentWrapper.chi[i]), siblingWrapper.chi[k] + .r.r.at(-1)?.typ === exports.EnumToken.SemiColonTokenType + ? trimArray(siblingWrapper.chi[k] + .r.r.slice(0, -1)) + : siblingWrapper.chi[k] + .r.r); + // @ts-ignore + cache.add(siblingWrapper.chi[k].l); + } } } - result.push({ typ: exports.EnumToken.CommaTokenType }); - } - for (const token of reduceConicColorStops(slice.slice(i))) { - result.push(token); - } - slice.length = 0; - for (let j = 0; j < result.length; j++) { - slice.push(result[j]); } } - break; + } } - return token.val + "(" + slice.reduce(reducer, "") + ")"; } - case exports.EnumToken.TimingFunctionTokenType: - case exports.EnumToken.PseudoClassFuncTokenType: - case exports.EnumToken.WhenElseFunctionTokenType: - case exports.EnumToken.TimelineFunctionTokenType: - case exports.EnumToken.GridTemplateFuncTokenType: - case exports.EnumToken.SupportsFunctionTokenType: - case exports.EnumToken.ContainerFunctionTokenType: - case exports.EnumToken.TransformFunctionTokenType: - case exports.EnumToken.GeneralEnclosedFunctionTokenType: - case exports.EnumToken.CustomFunctionTokenType: - case exports.EnumToken.WildCardFunctionTokenType: - if (token.typ == exports.EnumToken.MathFunctionTokenType && - token.chi.length == 1 && - ![exports.EnumToken.BinaryExpressionTokenType, exports.EnumToken.FractionTokenType, exports.EnumToken.IdenTokenType].includes(token.chi[0].typ) && - // @ts-ignore - token.chi[0].val - ?.typ != exports.EnumToken.FractionTokenType) { - return (token.val + - "(" + - token.chi.reduce((acc, curr) => acc + - renderValue(curr, token.typ == exports.EnumToken.FunctionTokenType ? { minify: false } : options, cache, reducer), "") + - ")"); + } + result.push(clonedDeclaration); + nodeMap.clear(); + clonedDeclaration = cloneNode(declaration, true, nodeMap); + replaceNodeOrValue(nodeMap.get(targetParentWrapper), nodeMap.get(targetWrapper), node.l); + result.push(clonedDeclaration); + } + else if (node.typ === exports.EnumToken.IfConditionTokenType) { + const left = node.l.find((t) => t.typ != exports.EnumToken.CommentTokenType && t.typ != exports.EnumToken.WhitespaceTokenType); + if (left == null) { + return result; + } + if (left.typ === exports.EnumToken.IdenTokenType && equalsIgnoreCase("else", left.val)) { + clonedDeclaration = cloneNode(declaration, true, nodeMap); + replaceNodeOrValue(nodeMap.get(parentWrapper), + // @ts-expect-error + nodeMap.get(targetWrapper.typ === exports.EnumToken.DeclarationNodeType ? node : targetWrapper), node.r.at(-1)?.typ === exports.EnumToken.SemiColonTokenType ? trimArray(node.r.slice(0, -1)) : node.r); + result.push(clonedDeclaration); + } + else if (left?.typ === exports.EnumToken.WhenElseFunctionTokenType) { + const atRule = Object.assign(cloneNode(declaration), { + typ: exports.EnumToken.AtRuleNodeType, + nam: left.val, + chi: [], + }); + if (declaration[PARENT] != null) { + atRule[LOCSRCID] = declaration[PARENT][LOCSRCID]; + atRule[LOCSTA] = declaration[PARENT][LOCSTA]; + atRule[LOCEND] = declaration[PARENT][LOCEND]; } - return (token.val ?? "") + "(" + token.chi.reduce(reducer, "") + ")"; - case exports.EnumToken.BlockStartTokenType: - return "{"; - case exports.EnumToken.BlockEndTokenType: - return "}"; - case exports.EnumToken.StartParensTokenType: - return "("; - case exports.EnumToken.DelimTokenType: - case exports.EnumToken.EqualMatchTokenType: - return "="; - case exports.EnumToken.IncludeMatchTokenType: - return "~="; - case exports.EnumToken.DashMatchTokenType: - return "|="; - case exports.EnumToken.StartMatchTokenType: - return "^="; - case exports.EnumToken.EndMatchTokenType: - return "$="; - case exports.EnumToken.ContainMatchTokenType: - return "*="; - case exports.EnumToken.LtTokenType: - return "<"; - case exports.EnumToken.LteTokenType: - return "<="; - case exports.EnumToken.Tilda: - case exports.EnumToken.SubsequentSiblingCombinatorTokenType: - return "~"; - case exports.EnumToken.Plus: - case exports.EnumToken.NextSiblingCombinatorTokenType: - return "+"; - case exports.EnumToken.GtTokenType: - case exports.EnumToken.ChildCombinatorTokenType: - return ">"; - case exports.EnumToken.GteTokenType: - return ">="; - case exports.EnumToken.ColumnCombinatorTokenType: - return "||"; - case exports.EnumToken.EndParensTokenType: - return ")"; - case exports.EnumToken.AttrStartTokenType: - return "["; - case exports.EnumToken.AttrEndTokenType: - return "]"; - case exports.EnumToken.DescendantCombinatorTokenType: - case exports.EnumToken.WhitespaceTokenType: - return " "; - case exports.EnumToken.ColonTokenType: - return ":"; - case exports.EnumToken.DoubleColonTokenType: - return "::"; - case exports.EnumToken.SemiColonTokenType: - return ";"; - case exports.EnumToken.CommaTokenType: - return ","; - case exports.EnumToken.ImportantTokenType: - return "!important"; - case exports.EnumToken.Pipe: - return "|"; - case exports.EnumToken.AttrTokenType: - case exports.EnumToken.IdenListTokenType: - return "[" + token.chi.reduce(reducer, "") + "]"; - case exports.EnumToken.TimeTokenType: - case exports.EnumToken.AngleTokenType: - case exports.EnumToken.LengthTokenType: - case exports.EnumToken.DimensionTokenType: - case exports.EnumToken.FrequencyTokenType: - case exports.EnumToken.ResolutionTokenType: - let val = token.val.typ == exports.EnumToken.FractionTokenType - ? renderValue(token.val, options, cache) - : minifyNumber(token.val); - let unit = token.unit; - if (token.typ == exports.EnumToken.AngleTokenType && !val.includes("/")) { - const angle = getAngle(token); - let v; - let value = val + unit; - for (const u of ["deg", "turn", "rad", "grad"]) { - if (token.unit == u) { - continue; - } - switch (u) { - case "deg": - v = minifyNumber(toPrecisionAngle(angle * 360, anglePrecision, false).toFixed(anglePrecision)); - if (v.length + 3 < value.length) { - val = v; - unit = u; - value = v + u; - } - break; - case "turn": - v = minifyNumber(toPrecisionAngle(angle, anglePrecision, false).toFixed(anglePrecision)); - if (v.length + 4 < value.length) { - val = v; - unit = u; - value = v + u; - } - break; - case "rad": - v = minifyNumber(toPrecisionAngle(angle * (2 * Math.PI), anglePrecision, false).toFixed(anglePrecision)); - if (v.length + 3 < value.length) { - val = v; - unit = u; - value = v + u; - } - break; - case "grad": - v = minifyNumber(toPrecisionAngle(angle * 400, anglePrecision, false).toFixed(anglePrecision)); - if (v.length + 4 < value.length) { - val = v; - unit = u; - value = v + u; - } - break; - } + atRule[TOKENS] = [{ typ: exports.EnumToken.ParensTokenType, chi: left.chi.slice() }]; + const minify = atRule.nam !== "supports"; + const options = { + minify, + convertColor: minify, + }; + atRule.val = atRule[TOKENS].reduce((acc, curr) => acc + printValue(curr, options), ""); + clonedDeclaration = cloneNode(declaration, true, nodeMap); + replaceNodeOrValue(nodeMap.get(targetWrapper), nodeMap.get(node), node.r.at(-1)?.typ === exports.EnumToken.SemiColonTokenType ? trimArray(node.r.slice(0, -1)) : node.r); + clonedDeclaration[PARENT] = atRule; + atRule.chi.push(clonedDeclaration); + result.push(atRule); + processNode(clonedDeclaration, cache); + } + else if (left.typ === exports.EnumToken.ContainerFunctionTokenType) { + const atRule = Object.assign(cloneNode(declaration), { + typ: exports.EnumToken.AtRuleNodeType, + nam: "container", + chi: [], + }); + atRule[TOKENS] = [left]; + atRule.val = atRule[TOKENS].reduce((acc, curr) => acc + printValue(curr), ""); + if (declaration[PARENT] != null) { + atRule[LOCSRCID] = declaration[PARENT][LOCSRCID]; + atRule[LOCSTA] = declaration[PARENT][LOCSTA]; + atRule[LOCEND] = declaration[PARENT][LOCEND]; + } + clonedDeclaration = cloneNode(declaration, true, nodeMap); + replaceNodeOrValue(nodeMap.get(targetWrapper.typ === exports.EnumToken.WildCardFunctionTokenType ? targetParentWrapper : targetWrapper), nodeMap.get(targetWrapper.typ === exports.EnumToken.WildCardFunctionTokenType ? targetWrapper : node), node.r.at(-1)?.typ === exports.EnumToken.SemiColonTokenType + ? trimArray(node.r.slice(0, -1)) + : node.r); + clonedDeclaration[PARENT] = atRule; + atRule.chi.push(clonedDeclaration); + result.push(atRule); + processNode(clonedDeclaration, cache); + } + } + return result; + } + function processNode(declarationNode, cache) { + let i; + let k = -1; + let astNode; + const result = []; + const stack = [declarationNode]; + while (++k < stack.length) { + astNode = stack[k]; + const { node: declaration, value: node } = findByValue(astNode, nodeMatcher) ?? {}; + if (declaration == null || node == null) { + result.push(astNode); + continue; + } + // @ts-expect-error + const parents = [...node.parents?.()]; + const parentWrapper = node.parent ?? parents.find((node) => !nodeMatcher(node)); + if (node.node.typ === exports.EnumToken.WildCardFunctionTokenType) { + for (i = 0; i < node.node.chi.length; i++) { + stack.push( + // @ts-expect-error + ...substituteIfElseNode( + // @ts-expect-error + declaration, node.node.chi[i], node.node, parentWrapper, cache)); + } + } + else { + stack.push( + // @ts-expect-error + ...substituteIfElseNode( + // @ts-expect-error + declaration, node.node, parentWrapper, parents[parents.indexOf(parentWrapper) + 1] ?? declaration, cache)); + } + } + if (result.length > 0) { + // @ts-expect-error + replaceNodeOrValue(declarationNode[PARENT], declarationNode, result); + } + // else remove node? + // @ts-expect-error + return result; + } + class ExpandIfFeature { + accept = new Set([exports.EnumToken.DeclarationNodeType]); + get ordering() { + return 1; + } + get processMode() { + return exports.FeatureWalkMode.Pre; + } + static register(options) { + if (options.expandIfSyntax) { + // @ts-ignore + options.features.push(new ExpandIfFeature()); + } + } + run(declaration) { + return processNode(declaration, new Set()); + } + } + + var allFeatures = /*#__PURE__*/Object.freeze({ + __proto__: null, + ComputeCalcExpressionFeature: ComputeCalcExpressionFeature, + ComputePrefixFeature: ComputePrefixFeature, + ComputeShorthandFeature: ComputeShorthandFeature, + ExpandIfFeature: ExpandIfFeature, + InlineCssVariablesFeature: InlineCssVariablesFeature, + TransformCssFeature: TransformCssFeature + }); + + const notEndingWith = ["(", "["].concat(combinators); + const rules = [ + exports.EnumToken.AtRuleNodeType, + exports.EnumToken.RuleNodeType, + exports.EnumToken.AtRuleTokenType, + exports.EnumToken.KeyframesRuleNodeType, + ]; + // @ts-ignore + const features = Object.values(allFeatures).sort((a, b) => a.ordering - b.ordering); + /** + * Apply minification rules to the ast tree + * @param ast + * @param options + * @param recursive + * @param errors + * @param nestingContent + * + * @param context + * @private + */ + function minify(ast, options = {}, recursive = false, errors, nestingContent, context = {}) { + let preprocess = false; + let postprocess = false; + let parents; + let replacement; + let { sourcemap, module, ...options2 } = options; + if (!(options2.features != null)) { + options2 = { + removeDuplicateDeclarations: true, + computeShorthand: true, + computeCalcExpression: true, + removePrefix: false, + features: [], + ...options2, + }; + for (const feature of features) { + feature.register(options2); + } + options2.features.sort((a, b) => a.ordering - b.ordering); + } + for (const feature of options2.features) { + if (feature.processMode & exports.FeatureWalkMode.Pre) { + preprocess = true; + } + if (feature.processMode & exports.FeatureWalkMode.Post) { + postprocess = true; + } + } + if (preprocess) { + parents = new Set([ast]); + for (const parent of parents) { + if (parent.typ == exports.EnumToken.CommentTokenType || parent.typ == exports.EnumToken.CDOCOMMTokenType) { + continue; + } + replacement = parent; + for (const feature of options2.features) { + if ((feature.processMode & exports.FeatureWalkMode.Pre) === 0 || + (feature.accept != null && !feature.accept.has(parent.typ))) { + continue; + } + if (rules.includes(replacement.typ) && !Array.isArray(replacement[TOKENS])) { + replacement[TOKENS] = parseString(replacement.typ == exports.EnumToken.RuleNodeType || replacement.typ === exports.EnumToken.KeyframesRuleNodeType + ? replacement.sel + : // @ts-ignore + replacement.nam); + } + const result = feature.run(replacement, options2, + // @ts-ignore + parent[PARENT] ?? ast, context, exports.FeatureWalkMode.Pre); + if (result != null) { + replacement = result; } } - if (val === "0") { - if (token.typ == exports.EnumToken.TimeTokenType) { - return "0s"; + if (replacement != null && + (!Array.isArray(replacement) || replacement.length > 0) && + replacement != parent && + parent[PARENT] != null) { + // @ts-ignore + replaceNodeOrValue(parent[PARENT], parent, replacement); + } + // @ts-ignore + if (replacement.chi != null) { + // @ts-ignore + for (const node of replacement.chi) { + node[PARENT] = replacement; + parents.add(node); } - if (token.typ == exports.EnumToken.FrequencyTokenType) { - return "0Hz"; + } + } + for (const feature of options2.features) { + if (feature.processMode & exports.FeatureWalkMode.Pre && "cleanup" in feature) { + // @ts-ignore + feature.cleanup(ast, options2, context, exports.FeatureWalkMode.Pre); + } + } + } + doMinify(ast, options2, recursive, errors, nestingContent, context); + parents = new Set([ast]); + for (const parent of parents) { + if (parent.typ == exports.EnumToken.CommentTokenType || parent.typ == exports.EnumToken.CDOCOMMTokenType) { + continue; + } + replacement = parent; + if (postprocess) { + for (const feature of options2.features) { + if ((feature.processMode & exports.FeatureWalkMode.Post) === 0 || + (feature.accept != null && !feature.accept.has(parent.typ))) { + continue; } + const result = feature.run(replacement, options2, // @ts-ignore - if (token.typ == exports.EnumToken.ResolutionTokenType) { - return "0x"; + parent[PARENT] ?? ast, context, exports.FeatureWalkMode.Post); + if (result != null) { + replacement = result; } - return "0"; } - if (token.typ == exports.EnumToken.TimeTokenType) { - if (unit == "ms") { - // @ts-ignore - const v = minifyNumber(val / 1000); - if (v.length + 1 <= val.length) { - return v + "s"; - } - return val + "ms"; - } - return val + "s"; + } + if (replacement != null && + (!Array.isArray(replacement) || replacement.length > 0) && + replacement != parent && + parent[PARENT] != null) { + // @ts-ignore + replaceNodeOrValue(parent[PARENT], parent, replacement); + } + // @ts-ignore + if (replacement.chi != null) { + // @ts-ignore + for (const node of replacement.chi) { + node[PARENT] = replacement; + parents.add(node); } - if (token.typ == exports.EnumToken.ResolutionTokenType && unit == "dppx") { - unit = "x"; + } + } + if (postprocess) { + for (const feature of options2.features) { + if (feature.processMode & exports.FeatureWalkMode.Post && "cleanup" in feature) { + // @ts-ignore + feature.cleanup(ast, options2, context, exports.FeatureWalkMode.Post); } - return val.includes("/") ? val.replace("/", unit + "/") : minifyNumber(toPrecisionValue(val)) + unit; - case exports.EnumToken.FlexTokenType: - case exports.EnumToken.PercentageTokenType: - const uni = token.typ == exports.EnumToken.PercentageTokenType ? "%" : "fr"; - const perc = token.val.typ == exports.EnumToken.FractionTokenType - ? renderValue(token.val, options, cache) - : minifyNumber(token.val); - return options.minifyValues && perc == "0" - ? "0" - : perc.includes("/") - ? perc.replace("/", uni + "/") - : perc + uni; - case exports.EnumToken.NumberTokenType: - return token.val.typ == exports.EnumToken.FractionTokenType - ? renderValue(token.val, options, cache) - : minifyNumber(token.val); - case exports.EnumToken.InfinityTokenType: - return "1/0"; - case exports.EnumToken.NegativeInfinityTokenType: - return "-1/0"; - case exports.EnumToken.NaNTokenType: - return "NaN"; - case exports.EnumToken.AtRuleTokenType: - return "@" + token.nam; - case exports.EnumToken.CommentTokenType: - case exports.EnumToken.CDOCOMMNodeType: - if (options.removeComments && - (!options.preserveLicense || !token.val.startsWith("/*!"))) { - return ""; + } + } + return ast; + } + function transformAtRuleMediaPrelude(values) { + let hasUpdates = false; + for (let { value, parent, parents } of walkValues(values)) { + if (value.typ === exports.EnumToken.MediaQueryConditionTokenType) { + if (value.op.typ == exports.EnumToken.AndTokenType && + // @ts-ignore + value.l.typ === exports.EnumToken.IdenTokenType && + // @ts-ignore + value.l.val.toLowerCase() === "all") { + if (parent === null) { + // @ts-ignore + values[values.indexOf(value)] = value.l; + } + else { + // @ts-ignore + replaceNodeOrValue(parent, value, value.l); + // @ts-ignore + value = value.l; + } + hasUpdates = true; } - case exports.EnumToken.PseudoClassTokenType: - case exports.EnumToken.PseudoElementTokenType: - // https://www.w3.org/TR/selectors-4/#single-colon-pseudos - if (token.typ == exports.EnumToken.PseudoElementTokenType && - pseudoElements.includes(token.val.slice(1))) { - return token.val.slice(1); + } + // range operator + if (parent != null && + parent.typ === exports.EnumToken.MediaQueryConditionTokenType && + parent.op.typ == exports.EnumToken.AndTokenType && + // @ts-ignore + parent.l.typ == exports.EnumToken.ParensTokenType) { + let token = parent.r.find((t) => t.typ !== exports.EnumToken.WhitespaceTokenType && t.typ !== exports.EnumToken.CommentTokenType); + if (token?.typ === exports.EnumToken.ParensTokenType) { + // @ts-ignore + const node1 = parent.l.chi.find((t) => t.typ !== exports.EnumToken.WhitespaceTokenType && t.typ !== exports.EnumToken.CommentTokenType); + const node2 = token.chi.find((t) => t.typ !== exports.EnumToken.WhitespaceTokenType && t.typ !== exports.EnumToken.CommentTokenType); + if (node1?.typ === exports.EnumToken.MediaQueryConditionTokenType && + node2?.typ === exports.EnumToken.MediaQueryConditionTokenType && + node1.op.typ == exports.EnumToken.ColonTokenType && + node2.op.typ == exports.EnumToken.ColonTokenType && + // @ts-ignore + node1.l.typ == exports.EnumToken.IdenTokenType && + // @ts-ignore + node2.l.typ == exports.EnumToken.IdenTokenType && + // @ts-ignore + node1.l.val.startsWith("min-") && + // @ts-ignore + node2.l.val.startsWith("max-") && + // @ts-ignore + node1.l.val.slice(4) == + // @ts-ignore + node2.l.val.slice(4)) { + const val1 = node1.r.find((t) => t.typ !== exports.EnumToken.WhitespaceTokenType && t.typ !== exports.EnumToken.CommentTokenType); + const val2 = node2.r.find((t) => t.typ !== exports.EnumToken.WhitespaceTokenType && t.typ !== exports.EnumToken.CommentTokenType); + const replacement = { + typ: exports.EnumToken.ParensTokenType, + chi: [ + // @ts-ignore + { + typ: exports.EnumToken.MediaRangeQueryTokenType, + op: { + typ: exports.EnumToken.IdenTokenType, + // @ts-ignore + val: node1.l.val.slice(4), + }, + l: val1, + r: val2, + [LOCSRCID]: value[LOCSRCID], + [LOCSTA]: value[LOCSTA], + [LOCEND]: value[LOCEND], + }, + ], + }; + // @ts-expect-error + const p = parents?.[parents?.indexOf?.(parent) + 1]; + if (p != null) { + // @ts-ignore + replaceNodeOrValue(p, parent, replacement); + } + else { + // @ts-ignore + values.splice(values.indexOf(parent), 1, replacement); + } + hasUpdates = true; + value = replacement; + } } - case exports.EnumToken.UrlTokenTokenType: - case exports.EnumToken.HashTokenType: - case exports.EnumToken.IdenTokenType: - case exports.EnumToken.StringTokenType: - case exports.EnumToken.LiteralTokenType: - case exports.EnumToken.DashedIdenTokenType: - case exports.EnumToken.PseudoPageTokenType: - case exports.EnumToken.ClassSelectorTokenType: - return token.val; - case exports.EnumToken.NestingSelectorTokenType: - return "&"; - case exports.EnumToken.InvalidAttrTokenType: - return ("[" + - token.chi.reduce((acc, curr) => acc + renderValue(curr, options, cache), "")); - case exports.EnumToken.InvalidClassSelectorTokenType: - return token.val; - case exports.EnumToken.SupportsQueryUnaryConditionTokenType: - case exports.EnumToken.WhenElseUnaryConditionTokenType: - return (renderValue(token.l, options, cache, reducer) + - " " + - token.r.reduce((acc, curr) => acc + renderValue(curr, options, cache, reducer), "")); - case exports.EnumToken.SupportsQueryConditionTokenType: - case exports.EnumToken.WhenElseQueryConditionTokenType: - return (token.l.reduce((acc, curr) => acc + renderValue(curr, options, cache, reducer), "") + - " " + - renderValue(token.op, options, cache, reducer) + - " " + - token.r.reduce((acc, curr) => acc + renderValue(curr, options, cache, reducer), "")); - case exports.EnumToken.IfConditionTokenType: - return token.l.length == 0 - ? "" - : token.l.reduce((acc, curr) => acc + renderValue(curr, options, cache, reducer), "") + - ":" + - (options.indent ?? "") + - token.r.reduce((acc, curr) => acc + renderValue(curr, options, cache, reducer), ""); - case exports.EnumToken.IfElseConditionTokenType: - return (renderValue(token.l, options, cache, reducer) + - (options.indent ?? "") + - renderValue(token.r, options, cache, reducer)); - case exports.EnumToken.DeclarationNodeType: - return (token.nam + - ":" + - (options.minifyValues ? filterValues(token.val) : token.val).reduce((acc, curr) => acc + renderValue(curr, options, cache, reducer), "")); - case exports.EnumToken.MediaQueryUnaryFeatureTokenType: - return (renderValue(token.l, options, cache, reducer) + - " " + - token.r.reduce((acc, curr) => acc + renderValue(curr, options, cache, reducer), "")); - case exports.EnumToken.MediaQueryConditionTokenType: { - const indent = token.op.typ == exports.EnumToken.LtTokenType || - token.op.typ == exports.EnumToken.GtTokenType || - token.op.typ == exports.EnumToken.ColonTokenType || - token.op.typ == exports.EnumToken.DelimTokenType || - token.op.typ == exports.EnumToken.LteTokenType || - token.op.typ == exports.EnumToken.GteTokenType - ? "" - : " "; - return (token.l.reduce((acc, curr) => acc + renderValue(curr, options, cache, reducer), "") + - indent + - renderValue(token.op, options, cache, reducer) + - indent + - token.r.reduce((acc, curr) => acc + renderValue(curr, options, cache, reducer), "")); } - case exports.EnumToken.MediaRangeQueryTokenType: - return (token.l.reduce((acc, curr) => acc + renderValue(curr, options, cache, reducer), "") + - renderValue(token.op1, options, cache, reducer) + - token.val.reduce((acc, curr) => acc + renderValue(curr, options, cache, reducer), "") + - renderValue(token.op2, options, cache, reducer) + - token.r.reduce((acc, curr) => acc + renderValue(curr, options, cache, reducer), "")); - case exports.EnumToken.MediaFeatureTokenType: - return token.val; - case exports.EnumToken.NotTokenType: - return "not"; - case exports.EnumToken.OnlyTokenType: - return "only"; - case exports.EnumToken.AndTokenType: - return "and"; - case exports.EnumToken.OrTokenType: - return "or"; - case exports.EnumToken.InvalidMediaQueryTokenType: - case exports.EnumToken.InvalidCommentTokenType: - case exports.EnumToken.BadCommentTokenType: - case exports.EnumToken.BadCdoTokenType: - case exports.EnumToken.BadStringTokenType: - case exports.EnumToken.BadUrlTokenType: - case exports.EnumToken.EOFTokenType: - return ""; - default: - console.debug({ token }); - throw new Error(`Unsupported token type for ${exports.EnumToken[token.typ]}`); } - // errors?.push({ action: "ignore", message: `render: unexpected token ${JSON.stringify(token, null, 1)}` }); - // return ""; + return { hasUpdates, values: trimArray(values) }; } /** - * Remove whitespace tokens that are not needed - * @param values + * Minify at-rule media + * - remove redundant tokens + * - generate range queries * - * @internal + * @private + * @param tokens */ - function filterValues(values) { - let i = 0; - for (; i < values.length; i++) { - if (values[i].typ == exports.EnumToken.ImportantTokenType && values[i - 1]?.typ === exports.EnumToken.WhitespaceTokenType) { - values.splice(i - 1, 1); + function minifyAtRuleMedia(tokens) { + let hasUpdates = false; + const sections = tokens + .reduce((acc, t) => { + if (t.typ === exports.EnumToken.CommaTokenType) { + acc.push([]); } - else if (tokensfuncSet.has(values[i].typ) && - "chi" in values[i] && - values[i].typ != exports.EnumToken.WildCardFunctionTokenType && - values[i + 1]?.typ == exports.EnumToken.WhitespaceTokenType) { - values.splice(i + 1, 1); + else { + acc[acc.length - 1].push(t); } - } - return values; - } - - const SymbolsMapTokens = Object.create(null); - const SymbolsMapTokensLower = Object.create(null); - // Regex for escape sequence decoding - compile once, reuse many times - const ESCAPE_SEQUENCE_REGEX = /\\((\n|\r|\f|\v|\u2028|\u2029|([0-9a-fA-F]{1,6})) ?)/gms; - function decodeEscapeSequences(value) { - return value.replace(ESCAPE_SEQUENCE_REGEX, (_, sequence) => { - // \n \r \f \v - switch (sequence.charCodeAt(0)) { - case 0xa: - case 0xb: - case 0xc: - case 0xd: - case 0x2028: - case 0x2029: - return ""; + return acc; + }, [[]]) + .reduce((acc, values) => { + if (acc.has("all")) { + return acc; } - const codepoint = parseInt(sequence, 16); - if (codepoint == 0 || - // leading surrogate - (0xd800 <= codepoint && codepoint <= 0xdbff) || - // trailing surrogate - (0xdc00 <= codepoint && codepoint <= 0xdfff) || - codepoint > 0x10ffff) { - return "\uFFFD"; + const result = transformAtRuleMediaPrelude(values); + if (result.values.length === 0) { + return acc; } - return String.fromCodePoint(codepoint); - }); - } - function assignTokenMap(entries, tokenType, suffix = "", lowercase = false) { - for (const entry of entries) { - const key = (lowercase ? entry.toLowerCase() : entry) + suffix; - SymbolsMapTokens[key] = tokenType; - SymbolsMapTokensLower[key.toLowerCase()] = tokenType; - } - } - SymbolsMapTokens[""] = exports.EnumToken.DelimTokenType; - SymbolsMapTokens["+"] = exports.EnumToken.Plus; - SymbolsMapTokens["="] = exports.EnumToken.DelimTokenType; - SymbolsMapTokens["|"] = exports.EnumToken.Pipe; - SymbolsMapTokens["||"] = exports.EnumToken.ColumnCombinatorTokenType; - SymbolsMapTokens["|="] = exports.EnumToken.DashMatchTokenType; - SymbolsMapTokens["&"] = exports.EnumToken.NestingSelectorTokenType; - SymbolsMapTokens["*"] = exports.EnumToken.Star; - SymbolsMapTokens["*="] = exports.EnumToken.ContainMatchTokenType; - SymbolsMapTokens["~"] = exports.EnumToken.Tilda; - SymbolsMapTokens["~="] = exports.EnumToken.IncludeMatchTokenType; - SymbolsMapTokens["^="] = exports.EnumToken.StartMatchTokenType; - SymbolsMapTokens["$="] = exports.EnumToken.EndMatchTokenType; - SymbolsMapTokens[","] = exports.EnumToken.Comma; - SymbolsMapTokens[":"] = exports.EnumToken.ColonTokenType; - SymbolsMapTokens["::"] = exports.EnumToken.DoubleColonTokenType; - SymbolsMapTokens[";"] = exports.EnumToken.SemiColonTokenType; - SymbolsMapTokens["("] = exports.EnumToken.StartParensTokenType; - SymbolsMapTokens[")"] = exports.EnumToken.EndParensTokenType; - SymbolsMapTokens["["] = exports.EnumToken.AttrStartTokenType; - SymbolsMapTokens["]"] = exports.EnumToken.AttrEndTokenType; - SymbolsMapTokens["{"] = exports.EnumToken.BlockStartTokenType; - SymbolsMapTokens["}"] = exports.EnumToken.BlockEndTokenType; - SymbolsMapTokens["<="] = exports.EnumToken.LteTokenType; - SymbolsMapTokens[">"] = exports.EnumToken.GtTokenType; - SymbolsMapTokens[">="] = exports.EnumToken.GteTokenType; - SymbolsMapTokens[" "] = exports.EnumToken.Whitespace; - SymbolsMapTokens["\t"] = exports.EnumToken.Whitespace; - SymbolsMapTokens["\r"] = exports.EnumToken.Whitespace; - SymbolsMapTokens["\n"] = exports.EnumToken.Whitespace; - SymbolsMapTokens["\f"] = exports.EnumToken.Whitespace; - assignTokenMap(flexUnits, exports.EnumToken.FlexTokenType); - assignTokenMap(dimensionUnits, exports.EnumToken.LengthTokenType); - assignTokenMap(resolutionUnits, exports.EnumToken.ResolutionTokenType); - assignTokenMap(angleUnits, exports.EnumToken.AngleTokenType); - assignTokenMap(timeUnits, exports.EnumToken.TimeTokenType); - assignTokenMap(frequencyUnits, exports.EnumToken.FrequencyTokenType); - assignTokenMap(pseudoElements, exports.EnumToken.PseudoElementTokenType); - assignTokenMap(containerFunc, exports.EnumToken.ContainerFunctionTokenDefType, "("); - assignTokenMap(urlFunc, exports.EnumToken.UrlFunctionTokenDefType, "("); - assignTokenMap(gridTemplateFunc, exports.EnumToken.GridTemplateFuncTokenDefType, "("); - assignTokenMap(imageFunc, exports.EnumToken.ImageFunctionTokenDefType, "("); - assignTokenMap(timelineFunc, exports.EnumToken.TimelineFunctionTokenDefType, "("); - assignTokenMap(supportFunc, exports.EnumToken.SupportsFunctionTokenDefType, "("); - assignTokenMap(timingFunc, exports.EnumToken.TimingFunctionTokenDefType, "("); - assignTokenMap(colorsFunc, exports.EnumToken.ColorFunctionTokenDefType, "("); - assignTokenMap(mathFuncs, exports.EnumToken.MathFunctionTokenDefType, "("); - assignTokenMap(transformFunctions, exports.EnumToken.TransformFunctionTokenDefType, "(", true); - assignTokenMap(whenElseFunc, exports.EnumToken.WhenElseFunctionTokenDefType, "("); - assignTokenMap(wildCardFuncs, exports.EnumToken.WildCardFunctionTokenDefType, "("); - const SymbolsMapTokensKeys = Object.keys(SymbolsMapTokens); - // do not capture the value - const hintsEnum = new Set([ - exports.EnumToken.CommaTokenType, - exports.EnumToken.ImportantTokenType, - exports.EnumToken.SemiColonTokenType, - exports.EnumToken.BlockStartTokenType, - exports.EnumToken.BlockEndTokenType, - exports.EnumToken.StartParensTokenType, - exports.EnumToken.EndParensTokenType, - exports.EnumToken.ColonTokenType, - exports.EnumToken.EOFTokenType, - ]); - var TokenMap; - (function (TokenMap) { - TokenMap[TokenMap["EXCLAMATION"] = 33] = "EXCLAMATION"; - TokenMap[TokenMap["SLASH"] = 47] = "SLASH"; - TokenMap[TokenMap["LOWERTHAN"] = 60] = "LOWERTHAN"; - TokenMap[TokenMap["HASH"] = 35] = "HASH"; - TokenMap[TokenMap["REVERSE_SOLIDUS"] = 92] = "REVERSE_SOLIDUS"; - TokenMap[TokenMap["DOUBLE_QUOTE"] = 34] = "DOUBLE_QUOTE"; - TokenMap[TokenMap["SINGLE_QUOTE"] = 39] = "SINGLE_QUOTE"; - TokenMap[TokenMap["DOT"] = 46] = "DOT"; - TokenMap[TokenMap["AT"] = 64] = "AT"; - TokenMap[TokenMap["PIPE"] = 124] = "PIPE"; - TokenMap[TokenMap["EQUALS"] = 61] = "EQUALS"; - TokenMap[TokenMap["AMPERSAND"] = 38] = "AMPERSAND"; - TokenMap[TokenMap["STAR"] = 42] = "STAR"; - TokenMap[TokenMap["TILDA"] = 126] = "TILDA"; - TokenMap[TokenMap["CARET"] = 94] = "CARET"; - TokenMap[TokenMap["DOLLAR"] = 36] = "DOLLAR"; - TokenMap[TokenMap["COMMA"] = 44] = "COMMA"; - TokenMap[TokenMap["COLON"] = 58] = "COLON"; - TokenMap[TokenMap["SEMICOLON"] = 59] = "SEMICOLON"; - TokenMap[TokenMap["LEFT_PARENTHESIS"] = 40] = "LEFT_PARENTHESIS"; - TokenMap[TokenMap["RIGHT_PARENTHESIS"] = 41] = "RIGHT_PARENTHESIS"; - TokenMap[TokenMap["LEFT_BRACKETS"] = 91] = "LEFT_BRACKETS"; - TokenMap[TokenMap["RIGHT_BRACKETS"] = 93] = "RIGHT_BRACKETS"; - TokenMap[TokenMap["LEFT_BRACE"] = 123] = "LEFT_BRACE"; - TokenMap[TokenMap["RIGHT_BRACE"] = 125] = "RIGHT_BRACE"; - TokenMap[TokenMap["PLUS"] = 43] = "PLUS"; - TokenMap[TokenMap["MINUS"] = 45] = "MINUS"; - TokenMap[TokenMap["GREATERTHAN"] = 62] = "GREATERTHAN"; - TokenMap[TokenMap["PERCENTAGE"] = 37] = "PERCENTAGE"; - })(TokenMap || (TokenMap = {})); - function getSymbolHint(parseInfo, start, end) { - const len = end - start; - if (len <= 0) - return null; - const value = parseInfo.stream.slice(start, end); - // Direct lookup handles the hottest punctuation and keyword cases without - // scanning the entire symbol table on every token. - const match = SymbolsMapTokens[value] ?? SymbolsMapTokensLower[value.toLowerCase()]; - if (match != null) { - return match; + if (result.hasUpdates) { + hasUpdates = true; + } + acc.set(values.reduce((acc, t) => acc + printValue(t), ""), result.values); + return acc; + }, new Map()); + if (sections.has("all")) { + tokens.length = 0; } - const keysLength = SymbolsMapTokensKeys.length; - for (let i = 0; i < keysLength; i++) { - const key = SymbolsMapTokensKeys[i]; - if (key.length !== len) - continue; - let match = true; - for (let j = 0; j < len; j++) { - let ca = key.charCodeAt(j); - let cb = value.charCodeAt(j); - if (ca >= 65 && ca <= 90) - ca += 32; - if (cb >= 65 && cb <= 90) - cb += 32; - if (ca !== cb) { - match = false; - break; + else if (hasUpdates) { + tokens.length = 0; + tokens.push(...[...sections.values()].reduce((acc, t) => { + if (acc.length > 0) { + acc.push({ + typ: exports.EnumToken.CommaTokenType, + }); } - } - if (match) { - return SymbolsMapTokens[key]; - } + for (const token of t) { + acc.push(token); + } + return acc; + }, [])); } - return null; + // return ast; + return tokens; } - function searchArray(array, parseInfo, start, end) { - const len = end - start; - if (len <= 0) - return null; - let match; - let item; - for (let i = 0; i < array.length; i++) { - item = array[i]; - if (item.length !== len) - continue; - match = true; - for (let j = 0; j < len; j++) { - let ca = parseInfo.stream.charCodeAt(start + j); - let cb = item.charCodeAt(j); - if (ca >= 65 && ca <= 90) - ca += 32; - if (cb >= 65 && cb <= 90) - cb += 32; - if (ca !== cb) { - match = false; - break; + /** + * Reduce selectors + * @param acc + * @param curr + * + * @private + */ + function reduce(acc, curr) { + // trim :is() + if (curr[0] == "&") { + if (curr[1] == " " && !isIdent(curr[2]) && !isFunction(curr[2])) { + const all = new Array(Math.max(curr.length - 2, 0)); + let write = 0; + for (let read = 2; read < curr.length; read++) { + all[write++] = curr[read]; + } + curr.length = 0; + for (let index = 0; index < write; index++) { + curr[index] = all[index]; } - } - if (match) { - return item; } } - return null; + acc.push(curr.join("")); + return acc; } /** - * tokenizer class + * Apply minification rules to the ast tree + * @param ast + * @param options + * @param recursive + * @param errors + * @param nestingContent + * @param context + * + * @private */ - class Tokenizer { - parseInfo; - input; - /** - * token type - */ - typ = null; - /** - * token kind - */ - kin = null; - /** - * token name - */ - nam = null; - /** - * token value - */ - val = null; - /** - * token unit - */ - unit = null; - /** - * source id - */ - srcId = null; - /** - * token start - */ - sta = null; - /** - * token end - */ - end = null; - /** - * bytes in - */ - bytesIn = null; - /** - * decode string - */ - decodeString = null; - /** - * token slice - */ - slice = null; - /** - * source file - */ - source = null; - /** - * token hint - */ - hint = null; - state = null; - constructor(parseInfo, input = null) { - this.parseInfo = parseInfo; - this.input = input; - this.parseInfo = - typeof parseInfo == "string" - ? { - stream: parseInfo, - source: new SourceFile(parseInfo, [], ""), - offset: 0, - time: 0, - position: 0, - currentPosition: 0, - } - : parseInfo; + function doMinify(ast, options = {}, recursive = false, errors, nestingContent, context = {}) { + if (!("nodes" in context)) { + context.nodes = new Set(); } - /** - * - * @param parseInfo - * @returns - */ - consumeString(parseInfo) { - const quote = this.advance(parseInfo).charCodeAt(0); - let charCode; - let decodeSegments = false; - while ((charCode = parseInfo.stream.charCodeAt(parseInfo.currentPosition - parseInfo.offset)) == charCode) { - if (charCode == 92 /* TokenMap.REVERSE_SOLIDUS */) { - if (charCode == parseInfo.stream.charCodeAt(parseInfo.currentPosition - parseInfo.offset + 1)) { - this.advance(parseInfo, 2); + if (context.nodes.has(ast)) { + return ast; + } + context.nodes.add(ast); + // @ts-ignore + if ("chi" in ast && ast.chi.length > 0) { + const reducer = reduce.bind(ast); + if (!nestingContent) { + nestingContent = options.nestingRules && ast.typ == exports.EnumToken.RuleNodeType; + } + let i = 0; + let previous = null; + let node = null; + let nodeIndex = -1; + for (; i < ast.chi.length; i++) { + if (ast.chi[i].typ === exports.EnumToken.CommentNodeType) { + continue; + } + while (previous?.typ === exports.EnumToken.CommentNodeType) { + // @ts-ignore + previous = ast.chi[--nodeIndex]; + } + node = ast.chi[i]; + if (node.typ === exports.EnumToken.AtRuleNodeType && node.nam === "font-face") { + continue; + } + if (node.typ === exports.EnumToken.KeyframesAtRuleNodeType) { + if (previous?.typ === exports.EnumToken.KeyframesAtRuleNodeType && + node.nam === previous.nam && + node.val === previous.val) { + ast.chi?.splice(nodeIndex--, 1); + previous = ast?.chi?.[nodeIndex] ?? null; + i = nodeIndex; continue; } - charCode = parseInfo.stream.charCodeAt(parseInfo.currentPosition - parseInfo.offset + 1); - // \n \r \f \v - if (charCode == 0xa || - charCode == 0xb || - charCode == 0xc || - charCode == 0xd || - charCode == 0x2028 || - charCode == 0x2029) { - this.advance(parseInfo, 2); - decodeSegments = true; + } + else if (node.typ === exports.EnumToken.KeyframesRuleNodeType) { + if (previous?.typ === exports.EnumToken.KeyframesRuleNodeType && + node.sel === previous.sel) { + // do not merge keyframes + // https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@keyframes#resolving_duplicates + for (const child of node.chi) { + previous.chi.push(child); + } + // @ts-ignore + ast.chi.splice(i, 1); + previous = ast?.chi?.[nodeIndex] ?? null; + i = nodeIndex; continue; } - const sequence = this.peek(parseInfo, 7); - let escapeSequence = ""; - let codepoint; - let i; - for (i = 1; i < sequence.length; i++) { - codepoint = sequence.charCodeAt(i); - if (codepoint == 0x20 || - (codepoint >= 0x61 && codepoint <= 0x66) || - (codepoint >= 0x41 && codepoint <= 0x46) || - (codepoint >= 0x30 && codepoint <= 0x39)) { - escapeSequence += sequence[i]; - if (codepoint == 0x20) { + let k; + for (k = 0; k < node.chi.length; k++) { + if (node.chi[k].typ == exports.EnumToken.DeclarationNodeType) { + let l = node.chi[k].val.length; + while (l--) { + if (node.chi[k].val[l].typ == + exports.EnumToken.ImportantTokenType) { + node.chi.splice(k--, 1); + break; + } + if ([exports.EnumToken.WhitespaceTokenType, exports.EnumToken.CommentTokenType].includes(node.chi[k].val[l].typ)) { + continue; + } break; } - continue; } - break; - } - if (escapeSequence.trimEnd().length > 0) { - const length = escapeSequence.length + - 1 + - (isWhiteSpace(parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset)?.charCodeAt(0)) - ? 1 - : 0); - decodeSegments = true; - this.advance(parseInfo, length); - continue; } - this.advance(parseInfo, 2); - continue; - } - if (charCode == quote) { - this.advance(parseInfo); - return this.makeToken(parseInfo, - /* hasNewLine ? EnumToken.BadStringTokenType : */ exports.EnumToken.StringTokenType, decodeSegments ? { decodeSegments } : null); - } - if (isNewLine(charCode)) { - this.advance(parseInfo); - return this.makeToken(parseInfo, exports.EnumToken.BadStringTokenType); } - this.advance(parseInfo); - } - // EOF - 'Unclosed-string' fixed - return this.makeToken(parseInfo, exports.EnumToken.StringTokenType, decodeSegments ? { decodeSegments } : null); - // return result; - } - /** - * - * @param parseInfo - * @returns - */ - consumeStringAsURLToken(parseInfo) { - const quote = this.advance(parseInfo).charCodeAt(0); - let charCode; - let decodeSegments = false; - while ((charCode = parseInfo.stream.charCodeAt(parseInfo.currentPosition - parseInfo.offset)) == charCode) { - if (charCode == 92 /* TokenMap.REVERSE_SOLIDUS */) { - if (charCode == parseInfo.stream.charCodeAt(parseInfo.currentPosition - parseInfo.offset + 1)) { - this.advance(parseInfo, 2); - continue; + else if (node.typ == exports.EnumToken.AtRuleNodeType) { + if (node.nam == "media") { + if (Array.isArray(node[TOKENS])) { + const slice = node[TOKENS].slice(); + minifyAtRuleMedia(slice); + if (slice.length !== node[TOKENS].length) { + node[TOKENS].length = 0; + for (const token of slice) { + node[TOKENS].push(token); + } + node.val = slice.reduce((acc, curr, index, arr) => acc + + (curr.typ === exports.EnumToken.CommentTokenType || + (curr.typ === exports.EnumToken.WhitespaceTokenType && + arr[index + 1]?.typ === exports.EnumToken.CommentTokenType && + (index + 3 < arr.length || + arr[index + 2]?.typ === exports.EnumToken.WhitespaceTokenType)) + ? "" + : printValue(curr)), ""); + } + } + if (["all", "", null].includes(node.val)) { + ast.chi?.splice(i--, 1, ...node.chi); + continue; + } } - const sequence = this.peek(parseInfo, 7); - let escapeSequence = ""; - let codepoint; - let i; - for (i = 1; i < sequence.length; i++) { - codepoint = sequence.charCodeAt(i); - if (codepoint == 0x20 || - (codepoint >= 0x61 && codepoint <= 0x66) || - (codepoint >= 0x41 && codepoint <= 0x46) || - (codepoint >= 0x30 && codepoint <= 0x39)) { - escapeSequence += sequence[i]; - if (codepoint == 0x20) { + else if (node.nam === "import" && Array.isArray(node[TOKENS])) { + let l = 0; + let token; + for (; l < node[TOKENS].length; l++) { + token = node[TOKENS][l]; + if (token.typ === exports.EnumToken.ParensTokenType || + token.typ === exports.EnumToken.MediaQueryConditionTokenType || + (token.typ === exports.EnumToken.IdenTokenType && "layer" !== token.val)) { break; } - continue; } - break; + if (l < node[TOKENS].length) { + const slice = node[TOKENS]?.slice(l); + node[TOKENS].splice(l, slice.length, ...minifyAtRuleMedia(slice)); + node.val = trimArray(node[TOKENS]).reduce((acc, curr, index, arr) => acc + + (curr.typ === exports.EnumToken.CommentTokenType || + (curr.typ === exports.EnumToken.WhitespaceTokenType && + arr[index + 1]?.typ === exports.EnumToken.CommentTokenType && + (index + 3 < arr.length || arr[index + 2].typ === exports.EnumToken.WhitespaceTokenType)) + ? "" + : printValue(curr)), ""); + } } - if (escapeSequence.trimEnd().length > 0) { - const length = escapeSequence.length + - 1 + - (isWhiteSpace(parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset)?.charCodeAt(0)) - ? 1 - : 0); - decodeSegments = true; - this.advance(parseInfo, length); + else if (ast.typ === node.typ && + ast.nam === node.nam && + ast.val === node.val) { + // @ts-ignore + replaceNodeOrValue(ast, node, node.chi); + i--; continue; } - this.advance(parseInfo, 2); - continue; - } - if (charCode == quote) { - this.advance(parseInfo); - let k = 1; - let end = parseInfo.stream.length - parseInfo.offset; - let position = parseInfo.currentPosition - parseInfo.offset; - while (position + k < end) { - charCode = parseInfo.stream.charCodeAt(position); - // NaN != NaN - if (charCode != charCode) { - this.advance(parseInfo, k); - return this.makeToken(parseInfo, exports.EnumToken.BadUrlTokenType); - } - if (isWhiteSpace(charCode)) { - // this.advance(parseInfo, k); - k++; - continue; - } - if (charCode != 41 /* TokenMap.RIGHT_PARENTHESIS */) { - this.advance(parseInfo, k); - return this.makeToken(parseInfo, exports.EnumToken.BadUrlTokenType); + if (previous?.typ == exports.EnumToken.AtRuleNodeType && + node.nam != "font-face" && + previous.nam === node.nam && + previous.val === node.val) { + if ("chi" in node) { + for (const child of node.chi) { + previous.chi.push(child); + } + if (!hasDeclaration(previous)) { + context.nodes.delete(previous); + doMinify(previous, options, recursive, errors, nestingContent, context); + } } - break; + ast?.chi?.splice(i--, 1); + continue; } - // consume until the ')' - return this.makeToken(parseInfo, - /* hasNewLine ? EnumToken.BadStringTokenType : */ exports.EnumToken.StringTokenType, decodeSegments ? { decodeSegments } : null); - // return result; - } - if (isNewLine(charCode)) { - // bad string - this.advance(parseInfo); - while ((charCode = parseInfo.stream.charCodeAt(parseInfo.currentPosition - parseInfo.offset)) == charCode) { - if (charCode == 92 /* TokenMap.REVERSE_SOLIDUS */) { - this.advance(parseInfo, 2); - continue; - } - if (charCode == 41 /* TokenMap.RIGHT_PARENTHESIS */) { - return this.makeToken(parseInfo, exports.EnumToken.BadUrlTokenType); - } - this.advance(parseInfo); + // if (!hasDeclaration(node as AstAtRule)) { + // doMinify(node, options, recursive, errors, nestingContent, context); + // } + if ("chi" in node) { + doMinify(node, options, recursive, errors, nestingContent, context); } - return this.makeToken(parseInfo, exports.EnumToken.BadStringTokenType); - } - this.advance(parseInfo); - } - // EOF - bad url token - return this.makeToken(parseInfo, exports.EnumToken.BadUrlTokenType); - // return result; - } - /** - * consume number, dimension, or percentage - * @param parseInfo - * @returns - */ - consumeNumericToken(parseInfo) { - let position = parseInfo.currentPosition - parseInfo.offset; - let offset = position; - let hasDigits = false; - let hasLetter = false; - let hasPercent = false; - let codepoint = parseInfo.stream.charCodeAt(position); - this.slice = null; - this.hint = null; - // '+' '-' - if (codepoint == 0x2b || codepoint == 0x2d) { - position++; - } - // consume digits - while (position < parseInfo.stream.length) { - codepoint = parseInfo.stream.charCodeAt(position); - if (isDigit(codepoint)) { - hasDigits = true; - position++; + previous = node; + nodeIndex = i; continue; } - // '.' 'E' 'e' - if (codepoint == 0x2e || codepoint == 0x45 || codepoint == 0x65) { - position++; - break; - } - if (isWhiteSpace(codepoint) || - codepoint == 41 /* TokenMap.RIGHT_PARENTHESIS */ || - codepoint == 59 /* TokenMap.SEMICOLON */ || - codepoint == 125 /* TokenMap.RIGHT_BRACE */ || - codepoint == 47 /* TokenMap.SLASH */ || - codepoint == 42 /* TokenMap.STAR */ || - codepoint == 44 /* TokenMap.COMMA */) { - return !hasDigits ? 0 : position - offset; - } - if (codepoint == 37 /* TokenMap.PERCENTAGE */) { - hasPercent = true; - break; - } - if (isLetter(codepoint)) { - hasLetter = true; - break; - } - return 0; - } - if (!hasLetter && !hasPercent) { - // '.' - if (codepoint == 0x2e) { - codepoint = parseInfo.stream.charCodeAt(position); - if (codepoint != codepoint) { - return !hasDigits ? 0 : position - offset; - } - if (isWhiteSpace(codepoint) || - codepoint == 41 /* TokenMap.RIGHT_PARENTHESIS */ || - codepoint == 59 /* TokenMap.SEMICOLON */ || - codepoint == 125 /* TokenMap.RIGHT_BRACE */ || - codepoint == 47 /* TokenMap.SLASH */ || - codepoint == 42 /* TokenMap.STAR */ || - codepoint == 44 /* TokenMap.COMMA */) { - return !hasDigits ? 0 : position - offset; - } - if (!isDigit(codepoint)) { - if (!hasDigits) { - return 0; + // @ts-ignore + else if (node.typ === exports.EnumToken.RuleNodeType) { + reduceRuleSelector(node); + let wrapper = null; + let match; + if (options.nestingRules) { + if (previous?.typ == exports.EnumToken.RuleNodeType) { + reduceRuleSelector(previous); + // @ts-ignore + match = matchSelectors(previous[RAW], node[RAW]); + if (match != null) { + wrapper = wrapNodes(previous, node, match, ast, reducer, i, nodeIndex); + nodeIndex = i - 1; + previous = ast.chi[nodeIndex]; + } } - if (codepoint == 37 /* TokenMap.PERCENTAGE */) { - hasPercent = true; + if (wrapper != null) { + while (i < ast.chi.length) { + const nextNode = ast.chi[i]; + if (nextNode.typ != exports.EnumToken.RuleNodeType) { + break; + } + reduceRuleSelector(nextNode); + match = matchSelectors(wrapper[RAW], nextNode[RAW]); + if (match == null) { + break; + } + wrapper = wrapNodes(wrapper, nextNode, match, ast, reducer, i, nodeIndex); + } + nodeIndex = --i; + previous = ast.chi[nodeIndex]; + doMinify(wrapper, options, recursive, errors, nestingContent, context); + continue; } - else if (isLetter(codepoint)) { - hasLetter = true; + // @ts-ignore + else if (node[OPTIMIZED] != null && + // @ts-ignore + node[OPTIMIZED].match && + // @ts-ignore + node[OPTIMIZED].selector.length > 1) { + // @ts-ignore + wrapper = { + ...node, + chi: [], + sel: node[OPTIMIZED].optimized[0], + [RAW]: [[node[OPTIMIZED].optimized[0]]], + }; + // @ts-ignore + node.sel = node[OPTIMIZED].selector.reduce(reducer, []).join(","); + // @ts-ignore + node[RAW] = node[OPTIMIZED].selector.slice(); + node[TOKENS] = null; + // @ts-ignore + wrapper.chi.push(node); + // @ts-ignore + ast.chi.splice(i, 1, wrapper); + node = wrapper; } - else { - return 0; + else if (node[OPTIMIZED]?.reducible) { + if (node[OPTIMIZED].optimized.length === 1) { + const sel1 = node[OPTIMIZED].optimized[0] + + ":is(" + + node[OPTIMIZED].selector.reduce(reducer, []).join(",") + + ")"; + const sel2 = node[OPTIMIZED].selector.reduce((acc, curr) => + // @ts-ignore + (acc.length > 0 ? acc + "," : "") + node[OPTIMIZED].optimized[0] + curr.join(""), ""); + node.sel = sel1.length < sel2.length ? sel1 : sel2; + node[TOKENS] = null; + } + else if (node[OPTIMIZED].optimized.length === 0) { + const testIdent = /^[a-zA-Z]/; + node.sel = node[OPTIMIZED].selector.reduce((acc, curr) => (acc.length > 0 ? acc + "," : "") + + (nestingContent && testIdent.test(curr[0]) ? "& " : "") + + curr.join(""), ""); + node[TOKENS] = null; + } } } - else { - position++; - hasDigits = true; - } - } - if (!hasLetter && !hasPercent) { - while (position + 1 <= parseInfo.stream.length) { - codepoint = parseInfo.stream.charCodeAt(position); - if (isDigit(codepoint)) { - position++; - continue; + // @ts-ignore + else if (node[OPTIMIZED]?.match) { + let wrap = true; + // @ts-ignore + const selector = node[OPTIMIZED].selector.reduce((acc, curr) => { + if (curr[0] == "&" && curr.length > 1) { + if (curr[1] == " ") { + curr.splice(0, 2); + } + else { + curr.splice(0, 1); + } + } + else if (combinators.includes(curr[0])) { + curr.unshift("&"); + wrap = false; + } + acc.push(curr); + return acc; + }, []); + if (!wrap) { + wrap = selector.some((s) => s[0] != "&"); } - if (!hasDigits) { - return 0; + let rule = null; + const optimized = node[OPTIMIZED].optimized.slice(); + if (optimized.length > 1) { + const check = optimized.at(-2); + if (!combinators.includes(check)) { + const last = optimized[optimized.length - 1]; + const all = new Array(Math.max(optimized.length - 1, 0)); + let write = 0; + for (let read = 0; read < optimized.length - 1; read++) { + all[write++] = optimized[read]; + } + optimized.length = 0; + for (let index = 0; index < write; index++) { + optimized[index] = all[index]; + } + wrap = false; + rule = + optimized.join("") + + `:is(${selector + .map((s) => { + const next = s[0] == "&" ? new Array(s.length) : new Array(s.length + 1); + let write = 0; + if (s[0] == "&") { + next[write++] = last; + for (let read = 1; read < s.length; read++) { + next[write++] = s[read]; + } + } + else { + next[write++] = last; + for (let read = 0; read < s.length; read++) { + next[write++] = s[read]; + } + } + const rebuilt = new Array(write); + for (let index = 0; index < write; index++) { + rebuilt[index] = next[index]; + } + return rebuilt.join(""); + }) + .join(",")})`; + } } - // 'E' 'e' - if (codepoint == 0x45 || codepoint == 0x65) { - position++; - break; + if (rule == null) { + rule = selector + .map((s) => { + if (s[0] == "&") { + const replacement = node[OPTIMIZED].optimized; + const all = new Array(Math.max(s.length - 1 + replacement.length, 0)); + let write = 0; + for (let read = 0; read < replacement.length; read++) { + all[write++] = replacement[read]; + } + for (let read = 1; read < s.length; read++) { + all[write++] = s[read]; + } + const rebuilt = new Array(write); + for (let index = 0; index < write; index++) { + rebuilt[index] = all[index]; + } + return rebuilt.join(""); + } + return s.join(""); + }) + .join(","); } - if (isWhiteSpace(codepoint) || - codepoint == 41 /* TokenMap.RIGHT_PARENTHESIS */ || - codepoint == 59 /* TokenMap.SEMICOLON */ || - codepoint == 125 /* TokenMap.RIGHT_BRACE */ || - codepoint == 47 /* TokenMap.SLASH */ || - codepoint == 42 /* TokenMap.STAR */ || - codepoint == 44 /* TokenMap.COMMA */) { - return position - offset; + let sel = wrap ? node[OPTIMIZED].optimized.join("") + `:is(${rule})` : rule; + if (sel.length < node.sel.length) { + node.sel = sel; + node[TOKENS] = null; } - if (isLetter(codepoint)) { - hasLetter = true; - break; + } + else if (node[OPTIMIZED]?.reducible) { + if (node[OPTIMIZED].optimized.length === 1) { + const sel1 = node[OPTIMIZED].optimized[0] + + ":is(" + + node[OPTIMIZED].selector.reduce(reducer, []).join(",") + + ")"; + const sel2 = node[OPTIMIZED].selector.reduce((acc, curr) => + // @ts-ignore + (acc.length > 0 ? acc + "," : "") + node[OPTIMIZED].optimized[0] + curr.join(""), ""); + node.sel = sel1.length < sel2.length ? sel1 : sel2; + node[TOKENS] = null; } - if (codepoint == 37 /* TokenMap.PERCENTAGE */) { - hasPercent = true; - break; + else if (node[OPTIMIZED].optimized.length === 0) { + const testIdent = /^[a-zA-Z]/; + node.sel = node[OPTIMIZED].selector.reduce((acc, curr) => (acc.length > 0 ? acc + "," : "") + + (nestingContent && testIdent.test(curr[0]) ? "& " : "") + + curr.join(""), ""); + node[TOKENS] = null; } - return 0; + // @ts-ignore } - // 'E' 'e' - 'em' - if ((codepoint == 0x45 || codepoint == 0x65) && hasDigits && !hasLetter && !hasPercent) { - if (isLetter(parseInfo.stream.charCodeAt(position))) { - hasLetter = true; + else if (node[OPTIMIZED]?.optimized.length > 0) { + // @ts-ignore + const sel = node[OPTIMIZED].optimized.join(""); + if (sel.length < node.sel.length) { + node.sel = sel; + // @ts-ignore + node[RAW] = [node[OPTIMIZED].optimized.slice()]; + node[TOKENS] = null; } } - if (!hasLetter && !hasPercent) { - // 'E' 'e' - if (codepoint == 0x45 || codepoint == 0x65) { - codepoint = parseInfo.stream.charCodeAt(position + 1); - // '+' '-' - if (codepoint == 0x2b || codepoint == 0x2d) { - position++; - } - codepoint = position = parseInfo.stream.charCodeAt(position + 1); - if (!isDigit(codepoint)) { - if (!hasDigits) { - return 0; - } - if (isLetter(codepoint)) { - hasLetter = true; - } - else if (codepoint == 37 /* TokenMap.PERCENTAGE */) { - hasPercent = true; - } - else { - return 0; + doMinify(node, options, recursive, errors, nestingContent, context); + } + if (previous != null) { + if ("chi" in previous && "chi" in node) { + if (previous.typ === node.typ) { + let shouldMerge = true; + let k = previous.chi.length; + while (k-- > 0) { + if (previous.chi[k].typ === exports.EnumToken.CommentNodeType || + previous.chi[k].typ === exports.EnumToken.InvalidRuleNodeType || + previous.chi[k].typ === exports.EnumToken.InvalidRuleNodeType) { + continue; } + shouldMerge = previous.chi[k].typ === exports.EnumToken.DeclarationNodeType; + break; } - } - if (!hasLetter && !hasPercent) { - while (++position < parseInfo.stream.length) { - codepoint = parseInfo.stream.charCodeAt(position); - // eof - if (codepoint != codepoint) { - break; - } - if (isDigit(codepoint)) { - position++; + if (shouldMerge) { + if (((node.typ === exports.EnumToken.RuleNodeType || + node.typ === exports.EnumToken.KeyframesRuleNodeType) && + node.sel === previous.sel) || + // @ts-ignore + (node.typ == exports.EnumToken.AtRuleNodeType && + node.nam !== "font-face" && + // @ts-ignore + node.nam === previous.nam)) { + const array = []; + for (let i = 0; i < previous.chi.length; i++) { + array.push(previous.chi[i]); + } + for (let i = 0; i < node.chi.length; i++) { + array.push(node.chi[i]); + } + // @ts-ignore + node.chi = array; + doMinify(node, options, recursive, errors, nestingContent, context); + ast.chi.splice(nodeIndex, 1); + previous = ast.chi[--i]; + nodeIndex = i; continue; } - if (!hasDigits) { - return 0; - } - if (isWhiteSpace(codepoint) || - codepoint == 41 /* TokenMap.RIGHT_PARENTHESIS */ || - codepoint == 59 /* TokenMap.SEMICOLON */ || - codepoint == 125 /* TokenMap.RIGHT_BRACE */ || - codepoint == 47 /* TokenMap.SLASH */ || - codepoint == 42 /* TokenMap.STAR */ || - codepoint == 44 /* TokenMap.COMMA */) { - return position - offset; - } - else if (isLetter(codepoint)) { - hasLetter = true; - break; - } - else if (codepoint == 37 /* TokenMap.PERCENTAGE */) { - hasPercent = true; - break; - } - else { - return 0; + else if (node.typ == previous?.typ && + [exports.EnumToken.KeyframesRuleNodeType, exports.EnumToken.RuleNodeType].includes(node.typ)) { + const intersect = diff(previous, node, options); + if (intersect != null) { + if (intersect.node1.chi.length == 0) { + ast.chi.splice(i--, 1); + } + else { + ast.chi.splice(i--, 1, intersect.node1); + } + if (intersect.node2.chi.length == 0) { + if (intersect.result != null) { + ast.chi.splice(nodeIndex, 1, intersect.result); + } + else { + ast.chi.splice(nodeIndex, 1); + } + i--; + if (nodeIndex == i) { + nodeIndex = i; + } + } + else { + if (intersect.result != null) { + ast.chi.splice(nodeIndex, 1, intersect.result, intersect.node2); + } + else { + ast.chi.splice(nodeIndex, 1, intersect.node2); + } + i = (nodeIndex ?? 0) + 1; + } + if (node != ast.chi[i]) { + node = ast.chi[i]; + } + previous = intersect.result; + nodeIndex = i; + } } } - if (!hasLetter && !hasPercent) { - return position - offset; + } + if (recursive && previous != null && previous != node) { + if (!hasDeclaration(previous)) { + doMinify(previous, options, recursive, errors, nestingContent, context); } } } } - } - if (!hasDigits) { - return 0; - } - if (hasPercent) { - const slice = position; - codepoint = parseInfo.stream.charCodeAt(++position); - if (codepoint != codepoint || - isWhiteSpace(codepoint) || - codepoint == 41 /* TokenMap.RIGHT_PARENTHESIS */ || - codepoint == 59 /* TokenMap.SEMICOLON */ || - codepoint == 125 /* TokenMap.RIGHT_BRACE */ || - codepoint == 47 /* TokenMap.SLASH */ || - codepoint == 42 /* TokenMap.STAR */ || - codepoint == 44 /* TokenMap.COMMA */) { - this.slice = slice; - this.hint = exports.EnumToken.PercentageTokenType; - return position - offset; + if (!nestingContent && + previous != null && + previous.typ == exports.EnumToken.RuleNodeType && + previous.sel.includes("&")) { + fixSelector(previous); } - return 0; + previous = node; + nodeIndex = i; } - if (hasLetter) { - codepoint = parseInfo.stream.charCodeAt(position - 1); - // 'E' 'e' - const slice = codepoint == 0x45 || codepoint == 0x65 ? position - 1 : position; - while (position + 1 <= parseInfo.stream.length) { - codepoint = parseInfo.stream.charCodeAt(++position); - if (!isLetter(codepoint)) { - break; + // @ts-ignore + if (recursive && node != null && node.chi != null) { + if (node.typ == exports.EnumToken.KeyframesAtRuleNodeType || + !node.chi.some((n) => n.typ == exports.EnumToken.DeclarationNodeType)) { + if (!(node.typ == exports.EnumToken.AtRuleNodeType && node.nam != "font-face")) { + doMinify(node, options, recursive, errors, nestingContent, context); } } - if (codepoint != codepoint || - isWhiteSpace(codepoint) || - codepoint == 41 /* TokenMap.RIGHT_PARENTHESIS */ || - codepoint == 59 /* TokenMap.SEMICOLON */ || - codepoint == 125 /* TokenMap.RIGHT_BRACE */ || - codepoint == 43 /* TokenMap.PLUS */ || - codepoint == 47 /* TokenMap.SLASH */ || - codepoint == 42 /* TokenMap.STAR */ || - codepoint == 44 /* TokenMap.COMMA */) { - this.slice = slice; - this.hint = getSymbolHint(parseInfo, slice, position) ?? exports.EnumToken.DimensionTokenType; - return position - offset; - } - return 0; } - return 0; - } - /** - * - * @param parseInfo - * @returns - */ - consumeIdentToken(parseInfo) { - let position = parseInfo.currentPosition - parseInfo.offset; - let offset = position; - let codepoint = parseInfo.stream.charCodeAt(position); - if (!isIdentStart(codepoint) && codepoint != 45 /* TokenMap.MINUS */) { - return 0; + if (!nestingContent && + node != null && + node.typ == exports.EnumToken.RuleNodeType && + node.sel.includes("&")) { + fixSelector(node); } - if (codepoint == 45 /* TokenMap.MINUS */) { - position++; - codepoint = parseInfo.stream.charCodeAt(position); - if (!isIdentStart(codepoint) && codepoint != 45 /* TokenMap.MINUS */) { - return 0; - } + } + return ast; + } + /** + * Check if a rule has a declaration + * @param node + * + * @private + */ + function hasDeclaration(node) { + // @ts-ignore + for (let i = 0; i < node.chi?.length; i++) { + // @ts-ignore + if (node.chi[i].typ == exports.EnumToken.CommentNodeType) { + continue; } - while ((codepoint = parseInfo.stream.charCodeAt(position)) == codepoint) { - if (codepoint == 92 /* TokenMap.REVERSE_SOLIDUS */) { - // eof - if ((codepoint = parseInfo.stream.charCodeAt(position + 1)) != codepoint) { - // this.next(parseInfo, position); - return 0; - } - // \n \r \f \v - if (codepoint == 0xa || - codepoint == 0xb || - codepoint == 0xc || - codepoint == 0xd || - codepoint == 0x2028 || - codepoint == 0x2029) { - return 0; - } - position += 2; - continue; - } - if (codepoint == 0x2d || isIdentCodepoint(codepoint)) { - position++; - } - else { - switch (codepoint) { - case 58 /* TokenMap.COLON */: - case 123 /* TokenMap.LEFT_BRACE */: - case 125 /* TokenMap.RIGHT_BRACE */: - case 40 /* TokenMap.LEFT_PARENTHESIS */: - case 41 /* TokenMap.RIGHT_PARENTHESIS */: - case 91 /* TokenMap.LEFT_BRACKETS */: - case 93 /* TokenMap.RIGHT_BRACKETS */: - case 59 /* TokenMap.SEMICOLON */: - case 33 /* TokenMap.EXCLAMATION */: - case 47 /* TokenMap.SLASH */: - case 35 /* TokenMap.HASH */: - case 42 /* TokenMap.STAR */: - case 61 /* TokenMap.EQUALS */: - case 126 /* TokenMap.TILDA */: - case 124 /* TokenMap.PIPE */: - case 94 /* TokenMap.CARET */: - case 36 /* TokenMap.DOLLAR */: - case 44 /* TokenMap.COMMA */: - case 62 /* TokenMap.GREATERTHAN */: - case 46 /* TokenMap.DOT */: - case 43 /* TokenMap.PLUS */: - return position - offset; - } - if (codepoint != codepoint || isWhiteSpace(codepoint)) { - return position - offset; + // @ts-ignore + return node.chi[i].typ == exports.EnumToken.DeclarationNodeType; + } + return true; + } + /** + * Optimize selector + * @param selector + * + * @private + */ + function optimizeSelector(selector) { + const map = new Set(); + selector = selector + .reduce((acc, curr) => { + // @ts-ignore + if (curr.length > 0 && curr.at(-1).startsWith(":is(")) { + // @ts-ignore + const rules = splitRule(curr.at(-1).slice(4, -1)).map((x) => { + if (x[0] == "&" && x.length > 1) { + return x.slice(x[1] == " " ? 2 : 1); } - return 0; + return x; + }); + const part = curr.slice(0, -1); + for (const rule of rules) { + acc.push(part.concat(rule)); } + return acc; } - return position - offset; - } - /** - * - * @param parseInfo - * @returns - */ - consumeColor(parseInfo) { - let position = parseInfo.currentPosition - parseInfo.offset; - let offset = position; - let codepoint = parseInfo.stream.charCodeAt(position); - if (codepoint != 35 /* TokenMap.HASH */) { - return 0; + acc.push(curr); + return acc; + }, []) + .filter((x) => { + const str = x.join(""); + if (map.has(str)) { + return false; } - position++; - let count = 0; - while (true) { - codepoint = parseInfo.stream.charCodeAt(position); - // 'a-f0-9' 'A-F0-9' - if ((codepoint >= 0x30 && codepoint <= 0x39) || - (codepoint >= 0x61 && codepoint <= 0x66) || - (codepoint >= 0x41 && codepoint <= 0x46)) { - position++; - count++; - continue; + map.add(str); + return true; + }); + const optimized = []; + const k = selector.reduce((acc, curr) => acc == 0 ? curr.length : curr.length == 0 ? acc : Math.min(acc, curr.length), 0); + let i = 0; + let j; + let match; + for (; i < k; i++) { + const item = selector[0][i]; + match = true; + for (j = 1; j < selector.length; j++) { + if (item != selector[j][i]) { + match = false; + break; } - break; } - if (count != 3 && count != 4 && count != 6 && count != 8) { - return 0; + if (!match) { + break; } - codepoint = parseInfo.stream.charCodeAt(position); - if (codepoint != codepoint || - isWhiteSpace(codepoint) || - codepoint == 41 /* TokenMap.RIGHT_PARENTHESIS */ || - codepoint == 59 /* TokenMap.SEMICOLON */ || - codepoint == 125 /* TokenMap.RIGHT_BRACE */ || - codepoint == 44 /* TokenMap.COMMA */) { - return position - offset; + optimized.push(item); + } + while (optimized.length > 0) { + const last = optimized.at(-1); + if (last == " " || combinators.includes(last)) { + const all = new Array(Math.max(optimized.length - 1, 0)); + let write = 0; + for (let read = 0; read < optimized.length - 1; read++) { + all[write++] = optimized[read]; + } + optimized.length = 0; + for (let index = 0; index < write; index++) { + optimized[index] = all[index]; + } + continue; } - return 0; + break; } - consumeURLToken(parseInfo, endPosition) { - let charCode; - // consume an - while (isWhiteSpace(this.peekCharCode(parseInfo))) { - this.advance(parseInfo); + for (let i1 = 0; i1 < selector.length; i1++) { + const all = new Array(Math.max(selector[i1].length - optimized.length, 0)); + let write = 0; + for (let read = optimized.length; read < selector[i1].length; read++) { + all[write++] = selector[i1][read]; } - charCode = this.peekCharCode(parseInfo); - if (charCode == 34 /* TokenMap.DOUBLE_QUOTE */ || charCode == 39 /* TokenMap.SINGLE_QUOTE */) { - return this.consumeStringAsURLToken(parseInfo); + selector[i1].length = 0; + for (let index = 0; index < write; index++) { + selector[i1][index] = all[index]; } - do { - this.advance(parseInfo); - charCode = this.peekCharCode(parseInfo); - } while ( - // !(value === "/" && this.match(parseInfo, "/*") && - charCode !== 41 /* TokenMap.RIGHT_PARENTHESIS */ && - !isWhiteSpace(charCode) && - parseInfo.currentPosition < endPosition); - if (charCode !== 41 /* TokenMap.RIGHT_PARENTHESIS */) { - let k = 1; - while (k < endPosition) { - charCode = parseInfo.stream.charCodeAt(parseInfo.currentPosition - parseInfo.offset + k); - if (isWhiteSpace(charCode)) { - k++; - continue; - } - break; + } + let reducible = optimized.length == 1; + if (optimized[0] == "&") { + if (optimized[1] == " ") { + const all = new Array(Math.max(optimized.length - 2, 0)); + let write = 0; + for (let read = 2; read < optimized.length; read++) { + all[write++] = optimized[read]; } - if (charCode != charCode || (charCode != 41 /* TokenMap.RIGHT_PARENTHESIS */ && !isIdentStart(charCode))) { - this.advance(parseInfo, k); - return this.makeToken(parseInfo, exports.EnumToken.BadUrlTokenType); + optimized.length = 0; + for (let index = 0; index < write; index++) { + optimized[index] = all[index]; } } - return this.makeToken(parseInfo, - // parseInfo.position < parseInfo.currentPosition - (charCode = this.peekCharCode(parseInfo)) != charCode || !this.isURLToken(parseInfo) - ? exports.EnumToken.BadUrlTokenType - : exports.EnumToken.UrlTokenTokenType); } - /** - * - * @param parseInfo - * @param hint - * @param options - * @returns - */ - makeToken(parseInfo, hint, options) { - let val = null; - this.typ = null; - this.nam = null; - this.val = null; - this.unit = null; - this.kin = null; - this.decodeString = null; - this.slice = null; - this.hint = null; - if (options?.slice) { - this.slice = options.slice; + if (optimized.length == 0 || optimized[0].charAt(0) == "&" || selector.length == 1) { + return { + match: false, + optimized, + selector: selector.map((selector) => selector[0] == "&" && selector[1] == " " ? selector.slice(2) : selector), + reducible: selector.length > 1 && selector.every((selector) => !combinators.includes(selector[0])), + }; + } + return { + match: true, + optimized, + selector: selector.reduce((acc, curr) => { + let hasCompound = true; + if (hasCompound && curr.length > 0) { + hasCompound = !["&"].concat(combinators).includes(curr[0].charAt(0)); + } + // @ts-ignore + if (hasCompound && curr[0] == " ") { + hasCompound = false; + curr.unshift("&"); + } + if (curr.length == 0) { + curr.push("&"); + hasCompound = false; + } + if (reducible) { + const chr = curr[0].charAt(0); + // @ts-ignore + reducible = chr == "." || chr == ":" || isIdentStart(chr.charCodeAt(0)); + } + acc.push(hasCompound ? ["&"].concat(curr) : curr); + return acc; + }, []), + reducible: selector.every((selector) => ![">", "+", "~", "&"].includes(selector[0])), + }; + } + /** + * Split selector string + * @param buffer + * + * @internal + */ + function splitRule(buffer) { + const result = [[]]; + let str = ""; + for (let i = 0; i < buffer.length; i++) { + let chr = buffer.charAt(i); + if (isWhiteSpace(chr.charCodeAt(0))) { + if (str !== "") { + // @ts-ignore + result.at(-1).push(str); + str = ""; + } + // @ts-ignore + if (result.at(-1).length > 0) { + // @ts-ignore + result.at(-1).push(" "); + } + // i = k; + continue; } - if (options?.decodeSegments) { - this.decodeString = true; + if (chr == ",") { + if (str !== "") { + result.at(-1).push(str); + str = ""; + } + result.push([]); + continue; } - if (hint != null) { - let array = null; - let hasUnit = false; - switch (hint) { - case exports.EnumToken.TransformFunctionTokenDefType: - array = transformFunctions; - break; - case exports.EnumToken.ColorFunctionTokenDefType: - array = colorsFunc; - break; - case exports.EnumToken.ContainerFunctionTokenDefType: - array = containerFunc; - break; - case exports.EnumToken.UrlFunctionTokenDefType: - array = urlFunc; - break; - case exports.EnumToken.GridTemplateFuncTokenDefType: - array = gridTemplateFunc; - break; - case exports.EnumToken.ImageFunctionTokenDefType: - array = imageFunc; - break; - case exports.EnumToken.TimelineFunctionTokenDefType: - array = timelineFunc; - break; - // case EnumToken.GeneralEnclosedFunctionTokenDefType: - // searchArray = generalEnclosedFunc; - // break; - case exports.EnumToken.SupportsFunctionTokenDefType: - array = supportFunc; - break; - case exports.EnumToken.TimingFunctionTokenDefType: - array = timingFunc; - break; - case exports.EnumToken.MathFunctionTokenDefType: - array = mathFuncs; - break; - case exports.EnumToken.WhenElseFunctionTokenDefType: - array = whenElseFunc; - break; - case exports.EnumToken.WildCardFunctionTokenDefType: - array = wildCardFuncs; - break; - case exports.EnumToken.FrequencyTokenType: - array = frequencyUnits; - hasUnit = true; - break; - case exports.EnumToken.ResolutionTokenType: - array = resolutionUnits; - hasUnit = true; - break; - case exports.EnumToken.LengthTokenType: - array = dimensionUnits; - hasUnit = true; - break; - case exports.EnumToken.FlexTokenType: - array = flexUnits; - hasUnit = true; - break; - case exports.EnumToken.AngleTokenType: - array = angleUnits; - hasUnit = true; - break; - case exports.EnumToken.TimeTokenType: - array = timeUnits; - hasUnit = true; - break; - case exports.EnumToken.DimensionTokenType: - hasUnit = true; - break; + if (chr == ".") { + if (str !== "") { + result.at(-1).push(str); + str = ""; } - if (array != null) { - val = searchArray(array, parseInfo, hasUnit ? options?.slice : parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset); + str += chr; + continue; + } + if (combinators.includes(chr)) { + if (str !== "") { + result.at(-1).push(str); + str = ""; } - else if (!hintsEnum.has(hint)) { - val = parseInfo.stream.slice(options?.slice ?? parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset); + if (chr == "|" && buffer.charAt(i + 1) == "|") { + chr += buffer.charAt(++i); } - if (this.decodeString) { - val = decodeEscapeSequences(val); + result.at(-1).push(chr); + continue; + } + if (chr == ":") { + if (str !== "") { + result.at(-1).push(str); + str = ""; } - if (hintsEnum.has(hint)) { - this.typ = hint; + if (buffer.charAt(i + 1) == ":") { + chr += buffer.charAt(++i); } - else { - this.typ = hint; - if (hasUnit || hint == exports.EnumToken.PercentageTokenType || hint == exports.EnumToken.DimensionTokenType) { - this.val = parseFloat(parseInfo.stream.slice(parseInfo.position - parseInfo.offset, options?.slice)); - if (hint != exports.EnumToken.PercentageTokenType) { - this.unit = val; - } + str += chr; + continue; + } + str += chr; + if (chr == "\\") { + str += buffer.charAt(++i); + continue; + } + if (chr == "(" || chr == "[") { + const open = chr; + const close = chr == "(" ? ")" : "]"; + let inParens = 1; + let k = i; + while (++k < buffer.length) { + chr = buffer.charAt(k); + if (chr == "\\") { + str += buffer.slice(k, k + 2); + k++; + continue; } - else if (hint == exports.EnumToken.NumberTokenType) { - this.val = parseFloat(val); + str += chr; + if (chr == open) { + inParens++; } - else if (hint == exports.EnumToken.AtRuleTokenType) { - this.nam = val; + else if (chr == close) { + inParens--; } - else { - this.val = val; - if (hint == exports.EnumToken.ColorTokenType) { - this.kin = exports.ColorType.HEX; - } + if (inParens == 0) { + break; } } + i = k; } - else { - if (this.equalsIgnoreCase(parseInfo, "!important")) { - this.typ = exports.EnumToken.ImportantTokenType; - } - } - if (this.typ == null) { - val = parseInfo.stream.slice(parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset); - if (options?.decodeSegments) { - val = decodeEscapeSequences(val); - this.decodeString = true; - } - this.typ = exports.EnumToken.LiteralTokenType; - this.val = val; + } + if (str !== "") { + result.at(-1).push(str); + } + return result; + } + /** + * Reduce selector + * @param acc + * @param curr + * + * @private + */ + function reduceSelector(acc, curr) { + let hasCompoundSelector = true; + // @ts-ignore + curr = curr.slice(this.match[0].length); + while (curr.length > 0) { + if (curr[0] == " ") { + hasCompoundSelector = false; + curr.unshift("&"); + continue; } - this.srcId = parseInfo.source.id; - this.sta = parseInfo.position; - this.end = parseInfo.currentPosition; - this.bytesIn = parseInfo.currentPosition; - parseInfo.position = parseInfo.currentPosition; - return this; + break; } - /** - * - * @param parseInfo - * @param input - * @returns - */ - equalsIgnoreCase(parseInfo, input) { - let position = parseInfo.currentPosition - parseInfo.offset; - let ca; - let cb; - for (let i = 0; i < input.length; i++) { - ca = parseInfo.stream.charCodeAt(position + i); - cb = input.charCodeAt(i); - // Normalize A-Z to a-z - if (ca >= 65 && ca <= 90) - ca += 32; - if (cb >= 65 && cb <= 90) - cb += 32; - if (ca != cb) { - return false; + if (hasCompoundSelector && curr.length > 0) { + hasCompoundSelector = !["&"].concat(combinators).includes(curr[0].charAt(0)); + } + if (curr[0] == ":is(") { + let canReduce = true; + const isCompound = curr.reduce((acc, token, index) => { + if (index == 0) { + canReduce = curr[1] == "&"; + } + else if (token == ")") ; + else if (token == ",") { + if (!canReduce) { + canReduce = curr[index + 1] == "&"; + } + acc.push([]); } + else + acc.at(-1)?.push(token); + return acc; + }, [[]]); + if (canReduce) { + curr = isCompound.reduce((acc, curr) => { + if (acc.length > 0) { + acc.push(","); + } + for (const c of curr) { + acc.push(c); + } + return acc; + }, []); } - return true; } - /** - * - * @param parseInfo - * @param input - * @returns - */ - match(parseInfo, input) { - let position = parseInfo.currentPosition - parseInfo.offset; - for (let i = 0; i < input.length; i++) { - if (parseInfo.stream[position + i] != input.charAt(i)) { - return false; + acc.push( + // @ts-ignore + this.match.length == 0 + ? ["&"] + : hasCompoundSelector && curr[0] != "&" && (curr.length == 0 || !combinators.includes(curr[0].charAt(0))) + ? ["&"].concat(curr) + : curr); + return acc; + } + /** + * Match selectors + * @param selector1 + * @param selector2 + * + * @private + */ + function matchSelectors(selector1, selector2) { + let match = [[]]; + const j = Math.min(selector1.reduce((acc, curr) => Math.min(acc, curr.length), selector1.length > 0 ? selector1[0].length : 0), selector2.reduce((acc, curr) => Math.min(acc, curr.length), selector2.length > 0 ? selector2[0].length : 0)); + let i = 0; + let k; + let l; + let token; + let matching = true; + let matchFunction = 0; + let inAttr = 0; + const regEx = /^:is\(([:.][^\s,]+)\)$/; + for (const _1 of selector1) { + if (_1[0] !== "&") { + continue; + } + for (let i = 1; i < _1.length; i++) { + const token = _1[i]; + if (token.startsWith(":is(")) { + const match = regEx.exec(token); + if (match != null) { + _1[i] = match[1]; + } } } - return true; - } - /** - * Get the current character code without creating a string - * @param parseInfo - * @returns charCode at current position - */ - peekCharCode(parseInfo) { - return parseInfo.stream.charCodeAt(parseInfo.currentPosition - parseInfo.offset); } - /** - * - * @param parseInfo - * @param count - * @returns - */ - peek(parseInfo, count = 1) { - if (count == 1) { - return parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset); + for (const _1 of selector2) { + if (_1[0] !== "&") { + continue; } - const position = parseInfo.currentPosition - parseInfo.offset; - return parseInfo.stream.slice(position, position + count); - } - /** - * - * @param parseInfo - * @param count - * @returns - */ - advance(parseInfo, count = 1) { - let position = parseInfo.currentPosition - parseInfo.offset; - let char = count == 1 ? parseInfo.stream.charAt(position) : parseInfo.stream.slice(position, position + count); - let i = 0; - let codepoint; - const lineStarts = parseInfo.source.lineStarts.lineStarts; - for (; i < char.length; i++) { - codepoint = char.charCodeAt(i); - if (codepoint == 0xa || // \n - codepoint == 0xb || // \v - codepoint == 0xc || // \f - codepoint == 0xd || // \r - codepoint == 0x2028 || // \u2028 - codepoint == 0x2029 // \u2029 - ) { - // \r\n - if (codepoint == 0xa && i > 0 && char.charCodeAt(i - 1) == 0xd) ; - else { - lineStarts.push(position + parseInfo.offset + i); + for (let i = 1; i < _1.length; i++) { + const token = _1[i]; + if (token.startsWith(":is(")) { + const match = regEx.exec(token); + if (match != null) { + _1[i] = match[1]; } } } - parseInfo.currentPosition += char.length; - return char; } - /** - * - * @param parseInfo - * @returns - */ - isIdentToken(parseInfo /* , start?: number, end?: number */) { - let j = parseInfo.currentPosition - parseInfo.offset; - let i = parseInfo.position - parseInfo.offset; - // if (start != null) { - // if (end == null) { - // if (start < 0) { - // j += start; - // } else { - // i += start; - // } - // } else { - // if (end < 0) { - // j += end; - // } else { - // j = parseInfo.position + end; - // } - // } - // } - j--; - let codepoint = parseInfo.stream.charCodeAt(i); - // - - if (codepoint == 0x2d) { - let nextCodepoint; - // NaN != NaN - if ((nextCodepoint = parseInfo.stream.charCodeAt(i + 1)) != nextCodepoint) { - return false; - } - if (!isIdentStart(nextCodepoint) && nextCodepoint != 0x2d) { - return false; + for (; i < j; i++) { + k = 0; + token = selector1[0][i]; + for (; k < selector1.length; k++) { + if (selector1[k][i] != token) { + matching = false; + break; } - codepoint = nextCodepoint; - i++; } - if (codepoint !== 0x2d && !isIdentStart(codepoint)) { - return false; + if (matching) { + l = 0; + for (; l < selector2.length; l++) { + if (selector2[l][i] != token) { + matching = false; + break; + } + } } - if (codepoint == 92 /* TokenMap.REVERSE_SOLIDUS */) { - codepoint = parseInfo.stream.charCodeAt(i + 1); - i += String.fromCodePoint(codepoint).length; + if (!matching) { + break; } - while (i < j) { - i += codepoint < 0x80 ? 1 : String.fromCodePoint(codepoint).length; - codepoint = parseInfo.stream.charCodeAt(i); - if (codepoint == 92 /* TokenMap.REVERSE_SOLIDUS */) { - i += codepoint < 0x80 ? 1 : String.fromCodePoint(codepoint).length; - codepoint = parseInfo.stream.charCodeAt(i); - i += codepoint < 0x80 ? 1 : String.fromCodePoint(codepoint).length; - continue; - } - if (codepoint !== 0x2d && !isIdentCodepoint(codepoint)) { - return false; - } + if (token.endsWith("(")) { + matchFunction++; } - return true; + match.at(-1).push(token); } - /** - * - * @param parseInfo - * @param input - * @returns - */ - startsWith(parseInfo, input) { - let i = 0; - let j = input.length; - while (i < j) { - if (parseInfo.stream.charAt(parseInfo.position - parseInfo.offset + i) != input.charAt(i)) { - return false; - } - i++; - } - return true; + // invalid function + if (matchFunction != 0 || inAttr != 0) { + return null; } - /** - * - * @param parseInfo - * @returns - */ - isURLToken(parseInfo) { - let i = parseInfo.position - parseInfo.offset; - let c; - while (++i < parseInfo.currentPosition) { - c = parseInfo.stream.charCodeAt(i); - // single quote or double quote or start parenthesis or close parenthesis - if (isNonPrintable(c) || c == 0x27 || c == 0x22 || c == 0x28 || c == 0x29) { - return false; - } - // valid escape - if (c == 92 /* TokenMap.REVERSE_SOLIDUS */) { - i++; - if (i >= parseInfo.currentPosition) { - return false; - } - c = parseInfo.stream.charCodeAt(i); - // c is not '\n' or '\r' or '\f' - if (c == 0x6e || c == 0x72 || c == 0x66) { - return false; - } + for (const part of match) { + while (part.length > 0) { + const token = part.at(-1); + if (token == " " || combinators.includes(token) || notEndingWith.includes(token.at(-1))) { + part.pop(); continue; } - // is white space - if (c == 0x20 || c == 0x09) { - break; - } + break; } - return i == parseInfo.currentPosition; } - done() { - return this.typ === exports.EnumToken.EOF; + if (match.every((t) => t.length == 0)) { + return null; } - /** - * Tokenize CSS string - */ - next( /* parseInfo: ParseInfo | string, yieldEOFToken: boolean = true */) { - const parseInfo = this.parseInfo; - this.source = parseInfo.source; - let charCode; - let nextCharCode; - // const result: TokenizeResult[] = []; - // allow 10 characters buffer for the streaming parser to avoid incomplete tokens - const endPosition = parseInfo.stream.length - 1; // yieldEOFToken ? parseInfo.stream.length - 1 : parseInfo.stream.length - 10; - let tokensCount; - // NaN is not equal to NaN - while ((charCode = this.peekCharCode(parseInfo)) == charCode) { - if (this.state === exports.EnumToken.UrlFunctionTokenDefType) { - this.state = null; - return this.consumeURLToken(parseInfo, endPosition); - } - if (parseInfo.position == parseInfo.currentPosition) { - if (charCode == 45 /* TokenMap.MINUS */ || - charCode == 43 /* TokenMap.PLUS */ || - charCode == 46 /* TokenMap.DOT */ || - isDigit(charCode)) { - tokensCount = this.consumeNumericToken(parseInfo); - if (tokensCount > 0) { - this.advance(parseInfo, tokensCount); - return this.makeToken(parseInfo, this.hint ?? exports.EnumToken.NumberTokenType, { - slice: this.slice, - sign: charCode == 45 /* TokenMap.MINUS */ ? "-" : charCode == 43 /* TokenMap.PLUS */ ? "+" : null, - }); - } - } - if (isIdentStart(charCode) || charCode == 45 /* TokenMap.MINUS */) { - tokensCount = this.consumeIdentToken(parseInfo); - if (tokensCount > 0) { - this.advance(parseInfo, tokensCount); - charCode = this.peekCharCode(parseInfo); - // do not match function - if (40 /* TokenMap.LEFT_PARENTHESIS */ != charCode) { - return this.makeToken(parseInfo, this.startsWith(parseInfo, "--") - ? exports.EnumToken.DashedIdenTokenType - : exports.EnumToken.IdenTokenType); - } - } - } - if (charCode == 64 /* TokenMap.AT */) { - this.advance(parseInfo); - charCode = this.peekCharCode(parseInfo); - // match at-rule - if (charCode == 45 /* TokenMap.MINUS */ || isIdentStart(this.peekCharCode(parseInfo))) { - // consume '@' - parseInfo.position = parseInfo.currentPosition; - tokensCount = this.consumeIdentToken(parseInfo); - if (tokensCount > 0) { - this.advance(parseInfo, tokensCount); - return this.makeToken(parseInfo, exports.EnumToken.AtRuleTokenType); - } - } - } - if (charCode == 35 /* TokenMap.HASH */) { - tokensCount = this.consumeColor(parseInfo); - if (tokensCount > 0) { - this.advance(parseInfo, tokensCount); - return this.makeToken(parseInfo, exports.EnumToken.ColorTokenType); - } - this.advance(parseInfo); - tokensCount = this.consumeIdentToken(parseInfo); - if (tokensCount > 0) { - this.advance(parseInfo, tokensCount); - return this.makeToken(parseInfo, exports.EnumToken.HashTokenType); - } - } - } - // EOF - switch (charCode) { - case 61 /* TokenMap.EQUALS */: - // if (parseInfo.position < parseInfo.currentPosition) { - // return this.makeToken(parseInfo); - // } - this.advance(parseInfo); - return this.makeToken(parseInfo, exports.EnumToken.DelimTokenType); - // '+' or '-' - case 43 /* TokenMap.PLUS */: - // if (parseInfo.position < parseInfo.currentPosition) { - // return this.makeToken(parseInfo); - // } - this.advance(parseInfo); - charCode = parseInfo.stream.charCodeAt(parseInfo.currentPosition - parseInfo.offset); - // if (isDigit(charCode)) { - // tokensCount = this.consumeNumericToken(parseInfo); - // if (tokensCount > 0) { - // this.advance(parseInfo, tokensCount); - // return this.makeToken(parseInfo, this.hint ?? EnumToken.NumberTokenType, { - // slice: this.slice, - // sign: "+", - // }); - // } - // } - return this.makeToken(parseInfo, exports.EnumToken.Plus); - case 45 /* TokenMap.MINUS */: - if (parseInfo.position == parseInfo.currentPosition) { - nextCharCode = parseInfo.stream.charCodeAt(parseInfo.currentPosition - parseInfo.offset + 1); - // not a number - if (isWhiteSpace(nextCharCode)) { - this.advance(parseInfo); - return this.makeToken(parseInfo, exports.EnumToken.Sub); - } - // if ( - // charCode == TokenMap.MINUS && - // (nextCharCode == TokenMap.MINUS || isIdentStart(nextCharCode)) - // ) { - // this.advance(parseInfo); - // tokensCount = this.consumeIdentToken(parseInfo); - // if (tokensCount > 0) { - // this.advance(parseInfo, tokensCount); - // return this.makeToken(parseInfo, EnumToken.IdenTokenType); - // } - // } - } - this.advance(parseInfo); - break; - // '{' - case 123 /* TokenMap.LEFT_BRACE */: - if (parseInfo.position < parseInfo.currentPosition) { - return this.makeToken(parseInfo); - } - this.advance(parseInfo); - return this.makeToken(parseInfo, exports.EnumToken.BlockStartTokenType); - // '}' - case 125 /* TokenMap.RIGHT_BRACE */: - if (parseInfo.position < parseInfo.currentPosition) { - return this.makeToken(parseInfo); - } - this.advance(parseInfo); - return this.makeToken(parseInfo, exports.EnumToken.BlockEndTokenType); - // '(' - case 40 /* TokenMap.LEFT_PARENTHESIS */: - if (parseInfo.position < parseInfo.currentPosition) { - // if ( - // parseInfo.stream[parseInfo.position - parseInfo.offset] === ":" && - // this.isPseudo(parseInfo) - // ) { - // this.advance(parseInfo); - // return this.makeToken(parseInfo, EnumToken.PseudoClassFunctionTokenDefType); - // } else - if (this.isIdentToken(parseInfo)) { - const hint = this.startsWith(parseInfo, "--") - ? exports.EnumToken.CustomFunctionTokenDefType - : (getSymbolHint(parseInfo, parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset + 1) ?? exports.EnumToken.FunctionTokenDefType); - this.makeToken(parseInfo, hint); - this.advance(parseInfo); - // consume '(' - parseInfo.position = parseInfo.currentPosition; - if (hint === exports.EnumToken.UrlFunctionTokenDefType) { - this.state = hint; - } - return this; - } - } - this.advance(parseInfo); - return this.makeToken(parseInfo, exports.EnumToken.StartParensTokenType); - // ')' - case 41 /* TokenMap.RIGHT_PARENTHESIS */: - if (parseInfo.position < parseInfo.currentPosition) { - return this.makeToken(parseInfo); - } - this.advance(parseInfo); - return this.makeToken(parseInfo, exports.EnumToken.EndParensTokenType); - // '[' - case 91 /* TokenMap.LEFT_BRACKETS */: - // if (parseInfo.position < parseInfo.currentPosition) { - // return this.makeToken(parseInfo); - // } - this.advance(parseInfo); - return this.makeToken(parseInfo, exports.EnumToken.AttrStartTokenType); - // ']' - case 93 /* TokenMap.RIGHT_BRACKETS */: - // if (parseInfo.position < parseInfo.currentPosition) { - // return this.makeToken(parseInfo); - // } - this.advance(parseInfo); - return this.makeToken(parseInfo, exports.EnumToken.AttrEndTokenType); - case 59 /* TokenMap.SEMICOLON */: - // if (parseInfo.position < parseInfo.currentPosition) { - // return this.makeToken(parseInfo); - // } - this.advance(parseInfo); - return this.makeToken(parseInfo, exports.EnumToken.SemiColonTokenType); - case 58 /* TokenMap.COLON */: - // if (parseInfo.position < parseInfo.currentPosition) { - // return this.makeToken(parseInfo); - // } - this.advance(parseInfo); - if (this.peekCharCode(parseInfo) == 58 /* TokenMap.COLON */) { - this.advance(parseInfo); - return this.makeToken(parseInfo, exports.EnumToken.DoubleColonTokenType); - } - return this.makeToken(parseInfo, exports.EnumToken.ColonTokenType); - // \n \r \f \v \t space - case 0x9: - case 0x20: - case 0xa: - case 0xb: - case 0xc: - case 0xd: - case 0x2028: - case 0x2029: - if (parseInfo.position < parseInfo.currentPosition) { - return this.makeToken(parseInfo); - } - this.advance(parseInfo); - nextCharCode = parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset).charCodeAt(0); - while (nextCharCode == 0x20 || - (nextCharCode >= 0x9 && nextCharCode <= 0xd) || - nextCharCode == 0x2028 || - nextCharCode == 0x2029) { - this.advance(parseInfo); - nextCharCode = parseInfo.stream - .charAt(parseInfo.currentPosition - parseInfo.offset) - .charCodeAt(0); - } - return this.makeToken(parseInfo, exports.EnumToken.WhitespaceTokenType); - case 44 /* TokenMap.COMMA */: - // if (parseInfo.position < parseInfo.currentPosition) { - // return this.makeToken(parseInfo); - // } - this.advance(parseInfo); - return this.makeToken(parseInfo, exports.EnumToken.CommaTokenType); - case 36 /* TokenMap.DOLLAR */: - // if (parseInfo.position < parseInfo.currentPosition) { - // return this.makeToken(parseInfo); - // } - if (this.match(parseInfo, "$=")) { - this.advance(parseInfo, 2); - return this.makeToken(parseInfo, exports.EnumToken.EndMatchTokenType); - } - this.advance(parseInfo); - break; - case 126 /* TokenMap.TILDA */: - // if (parseInfo.position < parseInfo.currentPosition) { - // return this.makeToken(parseInfo); - // } - if (this.match(parseInfo, "~=")) { - this.advance(parseInfo, 2); - return this.makeToken(parseInfo, exports.EnumToken.IncludeMatchTokenType); - } - this.advance(parseInfo); - return this.makeToken(parseInfo, exports.EnumToken.Tilda); - // case '^': - case 94 /* TokenMap.CARET */: - // if (parseInfo.position < parseInfo.currentPosition) { - // return this.makeToken(parseInfo); - // } - if (this.match(parseInfo, "^=")) { - this.advance(parseInfo, 2); - return this.makeToken(parseInfo, exports.EnumToken.StartMatchTokenType); - } - this.advance(parseInfo); - break; - case 42 /* TokenMap.STAR */: - // if (parseInfo.position < parseInfo.currentPosition) { - // return this.makeToken(parseInfo); - // } - if (this.match(parseInfo, "*=")) { - this.advance(parseInfo, 2); - return this.makeToken(parseInfo, exports.EnumToken.ContainMatchTokenType); - } - this.advance(parseInfo); - return this.makeToken(parseInfo, exports.EnumToken.Star); - case 38 /* TokenMap.AMPERSAND */: - if (parseInfo.position < parseInfo.currentPosition) { - return this.makeToken(parseInfo); - } - this.advance(parseInfo); - return this.makeToken(parseInfo, exports.EnumToken.NestingSelectorTokenType); - case 124 /* TokenMap.PIPE */: - // if (parseInfo.position < parseInfo.currentPosition) { - // return this.makeToken(parseInfo); - // } - // '||' - if (this.match(parseInfo, "||")) { - this.advance(parseInfo, 2); - return this.makeToken(parseInfo, exports.EnumToken.ColumnCombinatorTokenType); - } - else if (this.match(parseInfo, "|=")) { - this.advance(parseInfo, 2); - return this.makeToken(parseInfo, exports.EnumToken.DashMatchTokenType); - } - this.advance(parseInfo); - return this.makeToken(parseInfo, exports.EnumToken.Pipe); - case 33 /* TokenMap.EXCLAMATION */: - if (parseInfo.position < parseInfo.currentPosition) { - return this.makeToken(parseInfo); - } - if (this.match(parseInfo, "!important")) { - this.advance(parseInfo, 10); - return this.makeToken(parseInfo, exports.EnumToken.ImportantTokenType); - } - this.advance(parseInfo); - break; - case 47 /* TokenMap.SLASH */: - // if (parseInfo.position < parseInfo.currentPosition) { - // return this.makeToken(parseInfo); - // } - if (!this.match(parseInfo, "/*")) { - this.advance(parseInfo); - return this.makeToken(parseInfo, getSymbolHint(parseInfo, parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset)); - } - this.advance(parseInfo, 2); - while ((charCode = this.advance(parseInfo).charCodeAt(0)) == charCode) { - if (charCode == 42 /* TokenMap.STAR */) { - if (this.match(parseInfo, "/")) { - this.advance(parseInfo); - return this.makeToken(parseInfo, exports.EnumToken.CommentTokenType); - } - } - } - if (parseInfo.position < parseInfo.currentPosition) { - return this.makeToken(parseInfo, exports.EnumToken.BadCommentTokenType); - } - break; - case 62 /* TokenMap.GREATERTHAN */: - // if (parseInfo.position < parseInfo.currentPosition) { - // return this.makeToken(parseInfo); - // } - if (this.match(parseInfo, ">=")) { - this.advance(parseInfo, 2); - return this.makeToken(parseInfo, exports.EnumToken.GteTokenType); - } - this.advance(parseInfo); - return this.makeToken(parseInfo, exports.EnumToken.GtTokenType); - case 60 /* TokenMap.LOWERTHAN */: - if (parseInfo.position < parseInfo.currentPosition) { - return this.makeToken(parseInfo); - } - if (this.match(parseInfo, "<=")) { - this.advance(parseInfo, 2); - return this.makeToken(parseInfo, exports.EnumToken.LteTokenType); - } - this.advance(parseInfo); - if (this.match(parseInfo, "!--")) { - this.advance(parseInfo, 3); - while ((charCode = this.advance(parseInfo).charCodeAt(0)) == charCode) { - if (charCode == 45 /* TokenMap.MINUS */ && this.match(parseInfo, "->")) { - break; - } - } - if (parseInfo.currentPosition >= endPosition) { - return this.makeToken(parseInfo, exports.EnumToken.BadCdoTokenType); - } - else { - this.advance(parseInfo, 2); - return this.makeToken(parseInfo, exports.EnumToken.CDOCOMMTokenType); - } + if (eq([["&"]], match)) { + return null; + } + const reducer = reduceSelector.bind({ match }); + // @ts-ignore + selector1 = selector1.reduce(reducer, []); + // @ts-ignore + selector2 = selector2.reduce(reducer, []); + return selector1 == null || selector2 == null + ? null + : { + eq: eq(selector1, selector2), + match, + selector1, + selector2, + }; + } + /** + * Fix selector + * @param node + * + * @private + */ + function fixSelector(node) { + if (node.sel.includes("&")) { + const attributes = parseString(node.sel); + for (const attr of walkValues(attributes)) { + if (attr.value.typ == exports.EnumToken.PseudoClassFuncTokenType && + attr.value.val == ":is") { + let i = attr.value.chi.length; + while (i--) { + if (attr.value.chi[i].typ == exports.EnumToken.NestingSelectorTokenType) { + attr.value.chi.splice(i, 1); } - break; - case 35 /* TokenMap.HASH */: - if (parseInfo.position < parseInfo.currentPosition) { - return this.makeToken(parseInfo); + } + } + } + node.sel = attributes.reduce((acc, curr) => acc + printValue(curr), ""); + node[TOKENS] = null; + } + } + /** + * Wrap nodes + * @param previous + * @param node + * @param match + * @param ast + * @param reducer + * @param i + * @param nodeIndex + * + * @private + */ + function wrapNodes(previous, node, match, ast, reducer, i, nodeIndex) { + // @ts-ignore + let pSel = match.selector1.reduce(reducer, []).join(","); + // @ts-ignore + let nSel = match.selector2.reduce(reducer, []).join(","); + const wrapper = { + ...previous, + chi: [], + // @ts-ignore + sel: match.match.reduce(reducer, []).join(","), + [RAW]: match.match.map((t) => t.slice()), + }; + if (pSel == "&" || pSel === "") { + for (const child of previous.chi) { + wrapper.chi.push(child); + } + if (nSel == "&" || nSel === "") { + for (const child of node.chi) { + wrapper.chi.push(child); + } + } + else { + wrapper.chi.push(node); + } + } + else { + wrapper.chi.push(previous, node); + } + ast.chi.splice(i, 1, wrapper); + ast.chi.splice(nodeIndex, 1); + previous.sel = pSel; + previous[RAW] = match.selector1; + previous[TOKENS] = null; + node.sel = nSel; + node[RAW] = match.selector2; + node[TOKENS] = null; + reduceRuleSelector(wrapper); + wrapper[TOKENS] = null; + return wrapper; + } + /** + * Diff nodes + * @param n1 + * @param n2 + * @param options + * + * @private + */ + function diff(n1, n2, options = {}) { + if (!("cache" in options)) { + options.cache = new WeakMap(); + } + let node1 = n1; + let node2 = n2; + let exchanged = false; + if (node1.chi.length > node2.chi.length) { + const t = node1; + node1 = node2; + node2 = t; + exchanged = true; + } + let i = node1.chi.length; + let j = node2.chi.length; + const raw1 = node1[RAW]; + const raw2 = node2[RAW]; + if (raw1 != null && raw2 != null) { + const prefixes1 = new Set(); + const prefixes2 = new Set(); + for (const token1 of raw1) { + for (const t of token1) { + if (t.includes(":")) { + const matches = t.match(/::?-([a-z]+)-/); + if (matches == null) { + continue; } - this.advance(parseInfo); - break; - case 92 /* TokenMap.REVERSE_SOLIDUS */: - this.advance(parseInfo); - // EOF - // end of stream ignore \\ - if (!this.peek(parseInfo) && parseInfo.position < parseInfo.currentPosition) { - return this.makeToken(parseInfo); + prefixes1.add(matches[1]); + if (prefixes1.size > 1) { + break; } - this.advance(parseInfo); - break; - case 39 /* TokenMap.SINGLE_QUOTE */: - case 34 /* TokenMap.DOUBLE_QUOTE */: - if (parseInfo.position < parseInfo.currentPosition) { - return this.makeToken(parseInfo); + } + } + if (prefixes1.size > 1) { + break; + } + } + for (const token2 of raw2) { + for (const t of token2) { + if (t.includes(":")) { + const matches = t.match(/::?-([a-z]+)-/); + if (matches == null) { + continue; } - return this.consumeString(parseInfo); - case 46 /* TokenMap.DOT */: - const codepoint = parseInfo.stream.charCodeAt(parseInfo.currentPosition - parseInfo.offset + 1); - if (isIdentStart(codepoint) || codepoint == 45 /* TokenMap.MINUS */) { - // if (parseInfo.position < parseInfo.currentPosition) { - // return this.makeToken(parseInfo); - // } - this.advance(parseInfo); - let tokensCount = this.consumeIdentToken(parseInfo); - if (tokensCount > 0) { - this.advance(parseInfo, tokensCount); - return this.makeToken(parseInfo, exports.EnumToken.ClassSelectorTokenType); - } + prefixes2.add(matches[1]); + if (prefixes2.size > 1) { + break; } - // if (!isDigit(codepoint) && parseInfo.position !== parseInfo.currentPosition) { - // this.makeToken(parseInfo); - // this.advance(parseInfo, 2); - // return this; - // } - this.advance(parseInfo); - break; - default: - this.advance(parseInfo); + } + } + if (prefixes2.size > 1) { + break; + } + } + if (prefixes1.size != prefixes2.size) { + return null; + } + for (const prefix of prefixes1) { + if (!prefixes2.has(prefix)) { + return null; + } + } + } + const css1 = options.cache.get(node1); + const css2 = options.cache.get(node2); + node1 = { ...node1, chi: node1.chi.slice() }; + node2 = { ...node2, chi: node2.chi.slice() }; + if (css1 != null) { + options.cache.set(node1, css1); + } + if (css2 != null) { + options.cache.set(node2, css2); + } + if (raw1 != null) { + node1[RAW] = raw1; + } + if (raw2 != null) { + node2[RAW] = raw2; + } + const intersect = []; + while (i--) { + if (node1.chi[i].typ == exports.EnumToken.CommentNodeType) { + continue; + } + j = node2.chi.length; + while (j--) { + if (node2.chi[j].typ == exports.EnumToken.CommentNodeType) { + continue; + } + if (node1.chi[i].nam == node2.chi[j].nam) { + if (node1.chi[i].typ == node2.chi[j].typ && eq(node1.chi[i], node2.chi[j])) { + intersect.push(node1.chi[i]); + node1.chi.splice(i, 1); + node2.chi.splice(j, 1); + options.cache.delete(node1); + options.cache.delete(node2); break; + } } } - if (parseInfo.position < parseInfo.currentPosition) { - return this.makeToken(parseInfo); + } + const result = intersect.length === 0 && (node1.chi.length > 0 || node2.chi.length > 0) + ? null + : { + ...node1, + // @ts-ignore + sel: [ + ...new Set(splitRule(node1.sel) + .concat(splitRule(node2.sel)) + .map((s) => s.join(""))), + ].join(","), + // @ts-ignore + chi: intersect.reverse(), + }; + let op = { level: 0, ...options }; + if (result == null || + [n1, n2].reduce((acc, curr) => { + let css = options.cache.get(curr); + if (css == null) { + let level = 0; + let parent = curr[PARENT]; + while (parent != null && parent.typ != exports.EnumToken.StyleSheetNodeType) { + level++; + parent = parent[PARENT]; + } + op.level = level; + css = doRender(curr, op).code; + options.cache.set(curr, css); + } + return curr.chi.length == 0 ? acc : acc + css.length; + }, 0) <= + [node1, node2, result].reduce((acc, curr) => { + let css = options.cache.get(curr); + if (css != null) { + return curr.chi.length == 0 ? acc : acc + css.length; + } + let level = 0; + let parent = curr[PARENT]; + while (parent != null && parent.typ != exports.EnumToken.StyleSheetNodeType) { + level++; + parent = parent[PARENT]; + } + op.level = level; + css = doRender(curr, op).code; + return curr.chi.length == 0 ? acc : acc + css.length; + }, 0)) { + if (node1.chi.length != 0 && node2.chi.length != 0) { + return null; } - return this.makeToken(parseInfo, exports.EnumToken.EOFTokenType); } - /** - * tokenize readable stream - */ - async tokenizeStream() { - const decoder = new TextDecoder("utf-8"); - const reader = this.input.getReader(); - let parseInfo = this.parseInfo; - parseInfo.stream = ""; - while (true) { - const { done, value } = await reader.read(); - const stream = ArrayBuffer.isView(value) ? decoder.decode(value, { stream: true }) : value; - if (!done) { - parseInfo.source.append(stream); + if (result != null) { + result[TOKENS] = null; + result[RAW] = null; + const optimized = optimizeSelector(splitRule(result.sel)); + if (optimized?.match) { + const rule = optimized.selector.reduce((acc, curr) => { + if (acc.length > 0) { + acc += ","; + } + if (curr.length > 2 && curr[0] === "&" && curr[1] === " ") { + return acc + curr.slice(2).join(""); + } + else if (curr.length > 1 && curr[0] === "&") { + return acc + curr.slice(1).join(""); + } + return acc + curr.join(""); + }, ""); + const match = optimized.optimized.join(""); + const sel = match + ":is(" + replaceCompound(rule, match) + ")"; + if (sel.length < result.sel.length) { + result.sel = sel; + result[TOKENS] = null; } - else { - break; + } + } + return { result, node1: exchanged ? node2 : node1, node2: exchanged ? node1 : node2 }; + } + /** + * Reduce rule selector + * @param node + * + * @private + */ + function reduceRuleSelector(node) { + if (node[RAW] == null) { + node[RAW] = splitRule(node.sel); + } + let optimized = optimizeSelector(node[RAW].reduce((acc, curr) => { + acc.push(curr.slice()); + return acc; + }, [])); + if (optimized != null) { + node[OPTIMIZED] = optimized; + } + if (optimized != null && optimized.match && optimized.reducible && optimized.selector.length > 1) { + for (const selector of optimized.selector) { + if (selector.length > 1 && + selector[0] == "&" && + (combinators.includes(selector[1]) || !/^[a-zA-Z:]/.test(selector[1]))) { + const all = new Array(Math.max(selector.length - 1, 0)); + let write = 0; + for (let read = 1; read < selector.length; read++) { + all[write++] = selector[read]; + } + selector.length = 0; + for (let index = 0; index < write; index++) { + selector[index] = all[index]; + } } } - parseInfo.stream = parseInfo.source.getContent(); - return this; // .next(); + const unique = new Set(); + const reduced = optimized.selector.reduce((acc, curr) => { + const sig = curr.join(""); + if (!unique.has(sig)) { + if (acc.length > 0) { + acc.push(","); + } + unique.add(sig); + for (const c of curr) { + acc.push(c); + } + } + return acc; + }, []); + const raw = [ + [optimized.optimized[0], reduced.length === 1 ? reduced.join("") : ":is("].concat(reduced).concat(")"), + ]; + const sel = raw[0].join(""); + if (sel.length < node.sel.length) { + node.sel = sel; + node[RAW] = raw; + node[TOKENS] = null; + } } } @@ -28527,33 +30354,39 @@ * parse selector */ function parseSelector(tokens, context, options, errors) { - if (context?.typ === exports.EnumToken.KeyframesAtRuleNodeType) { - const result = matchAllSyntaxes(getParsedSyntax(ValidationSyntaxGroupEnum.Syntaxes, "keyframe-selectors"), createValidationContext(tokens), options); - const parts = splitTokenList(tokens); + const arena = options.arena; + const contextTyp = arena.getType(context); + let typ; + if (arena.getType(context) === exports.EnumToken.KeyframesAtRuleNodeType) { + const result = matchAllSyntaxes(getParsedSyntax(ValidationSyntaxGroupEnum.Syntaxes, "keyframe-selectors"), createValidationContext(tokens, options), options); + const parts = splitTokenList(tokens, options); for (const part of parts) { - trimArray(part); + trimArray(part, options); if (options.minify) { - const filtered = part.filter((token) => token.typ !== exports.EnumToken.WhitespaceTokenType && token.typ !== exports.EnumToken.CommentTokenType); + const filtered = part.filter((token) => { + typ = arena.getType(token); + }); if (filtered.length === 1) { - if (filtered[0].typ === exports.EnumToken.IdenTokenType && - "from" === filtered[0].val.toLowerCase()) { - filtered[0] = { - typ: exports.EnumToken.PercentageTokenType, - val: 0, - [LOCSRCID]: filtered[0][LOCSRCID], - [LOCSTA]: filtered[0][LOCSTA], - [LOCEND]: filtered[0][LOCEND], - }; - } - else if (filtered[0].typ === exports.EnumToken.PercentageTokenType && - 100 === filtered[0].val) { - filtered[0] = { - typ: exports.EnumToken.IdenTokenType, - val: "to", - [LOCSRCID]: filtered[0][LOCSRCID], - [LOCSTA]: filtered[0][LOCSTA], - [LOCEND]: filtered[0][LOCEND], - }; + typ = arena.getType(filtered[0]); + if (typ === exports.EnumToken.IdenTokenType && arena.equalsIgnoreCase(filtered[0], "val", "from")) { + arena.set(filtered[0], exports.EnumToken.PercentageTokenType, 0, 0); + // filtered[0] = { + // typ: EnumToken.PercentageTokenType, + // val: 0, + // [LOCSRCID]: filtered[0][LOCSRCID], + // [LOCSTA]: filtered[0][LOCSTA], + // [LOCEND]: filtered[0][LOCEND], + // }; + } + else if (typ === exports.EnumToken.PercentageTokenType && 100 === arena.getNumerciValue(filtered[0])) { + arena.set(filtered[0], 0, exports.EnumToken.IdenTokenType, arena.internString("to"), 0); + // filtered[0] = { + // typ: EnumToken.IdenTokenType, + // val: "to", + // [LOCSRCID]: filtered[0][LOCSRCID], + // [LOCSTA]: filtered[0][LOCSTA], + // [LOCEND]: filtered[0][LOCEND], + // }; } part.splice(0, part.length, ...filtered); } @@ -28561,18 +30394,22 @@ } tokens.splice(0, tokens.length, ...parts.reduce((acc, curr) => { if (acc.length > 0) { - acc.push({ typ: exports.EnumToken.CommaTokenType }); + const id = arena.allocate(); + arena.set(id, exports.EnumToken.CommaTokenType); + acc.push(id); } for (const c of curr) { acc.push(c); } return acc; }, [])); + const node = arena.allocate(); + arena.set(node, exports.EnumToken.KeyframesRuleNodeType); return { typ: exports.EnumToken.KeyframesRuleNodeType, sel: [ - ...splitTokenList(trimArray(tokens)).reduce((acc, curr) => { - acc.add(curr.reduce((acc, curr) => acc + renderValue(curr, { minify: false }), "")); + ...splitTokenList(trimArray(tokens, options), options).reduce((acc, curr) => { + acc.add(curr.reduce((acc, curr) => acc + printValue$1(curr, { minify: false }), "")); return acc; }, new Set()), ].join(), @@ -28592,10 +30429,10 @@ let parent = context; let nested = false; let val; - if (context?.typ !== exports.EnumToken.StyleSheetNodeType && context?.typ !== exports.EnumToken.RuleNodeType) { + if (contextTyp !== exports.EnumToken.StyleSheetNodeType && contextTyp !== exports.EnumToken.RuleNodeType) { allowed = false; - if (context?.typ === exports.EnumToken.AtRuleNodeType) { - const syntaxRule = getSyntaxRule(ValidationSyntaxGroupEnum.AtRules, "@" + context.nam); + if (contextTyp === exports.EnumToken.AtRuleNodeType) { + const syntaxRule = getSyntaxRule(ValidationSyntaxGroupEnum.AtRules, "@" + arena.getName(context)); allowed = syntaxRule?.acceptAnyRule ?? false; if (!allowed) { const rules = syntaxRule?.getBlockRules?.(); @@ -28614,129 +30451,201 @@ } } do { - if (parent?.typ === exports.EnumToken.AtRuleNodeType && "media" === parent.nam) { - parent = parent[PARENT]; + if (contextTyp === exports.EnumToken.AtRuleNodeType && arena.equalsIgnoreCase(parent, "nam", "media")) { + parent = arena.getParent(parent); continue; } - nested = parent?.typ == exports.EnumToken.RuleNodeType; - parent = parent?.[PARENT]; - } while (!nested && parent != null); + nested = arena.getType(parent) == exports.EnumToken.RuleNodeType; + parent = arena.getParent(parent); + } while (!nested && parent != 0); const all = new Array(tokens.length); let write = 0; + let token; + let next; + let newToken; for (let read = 0; read < tokens.length; read++) { - let token = tokens[read]; - if (token.typ == exports.EnumToken.ColonTokenType) { - const next = tokens[read + 1]; - if (next?.typ == exports.EnumToken.IdenTokenType) { - all[write++] = { - ...token, - typ: exports.EnumToken.PseudoElementTokenType, - val: ":" + next.val, - [LOCEND]: next[LOCEND], - }; + token = tokens[read]; + typ = arena.getType(token); + if (typ == exports.EnumToken.ColonTokenType) { + next = tokens[read + 1]; + if (next != null && arena.getType(next) == exports.EnumToken.IdenTokenType) { + newToken = arena.allocate(); + all[write++] = newToken; + arena.set(newToken, exports.EnumToken.PseudoClassTokenType, arena.interString(":" + arena.getValue(next))); + arena.setSpan(newToken, arena.getSourceId(token), arena.getStart(token), arena.getEnd(next)); read++; continue; } - if (next?.typ == exports.EnumToken.FunctionTokenDefType) { - val = ":" + next.val; - all[write++] = { - ...token, - typ: val + "()" in getSyntaxConfig().selectors - ? exports.EnumToken.PseudoClassFunctionTokenDefType - : next.typ, - val, - [LOCEND]: next[LOCEND], - }; + if (next != null && arena.getType(next) == exports.EnumToken.FunctionTokenDefType) { + val = ":" + arena.getValue(next); + // all[write++] = { + // ...token, + // typ: + // val + "()" in getSyntaxConfig().selectors + // ? EnumToken.PseudoClassFunctionTokenDefType + // : next.typ, + // val, + // [LOCEND]: next[LOCEND], + // } as Token; + newToken = arena.allocate(); + all[write++] = newToken; + arena.set(newToken, val + "()" in getSyntaxConfig().selectors + ? exports.EnumToken.PseudoClassFunctionTokenDefType + : arena.getType(next), arena.interString(":" + arena.getValue(next))); + arena.setSpan(newToken, arena.getSourceId(token), arena.getStart(token), arena.getEnd(next)); read++; continue; } } - if (token.typ == exports.EnumToken.DoubleColonTokenType) { + if (arena.getType(token) == exports.EnumToken.DoubleColonTokenType) { const next = tokens[read + 1]; - val = ":" + next.val; - if (next?.typ == exports.EnumToken.IdenTokenType) { - all[write++] = { - ...token, - typ: exports.EnumToken.PseudoClassTokenType, - val: (pseudoElements.includes(val) ? "" : ":") + val, - [LOCEND]: next[LOCEND], - }; + val = ":" + arena.getValue(next); + if (arena.getType(next) == exports.EnumToken.IdenTokenType) { + // all[write++] = { + // ...token, + // typ: EnumToken.PseudoClassTokenType, + // val: (pseudoElements.includes(val) ? "" : ":") + val, + // [LOCEND]: next[LOCEND], + // } as Token; + newToken = arena.allocate(); + all[write++] = newToken; + arena.set(newToken, exports.EnumToken.PseudoClassTokenType, arena.interString((pseudoElements.includes(val) ? "" : ":") + val)); + arena.setSpan(newToken, arena.getSourceId(token), arena.getStart(token), arena.getEnd(next)); read++; continue; } - if (next?.typ == exports.EnumToken.FunctionTokenDefType) { - val = "::" + next.val; - all[write++] = { - ...token, - typ: val + "()" in getSyntaxConfig().selectors - ? exports.EnumToken.PseudoClassFunctionTokenDefType - : exports.EnumToken.FunctionTokenDefType, - val, - [LOCEND]: next[LOCEND], - }; + if (arena.getType(next) == exports.EnumToken.FunctionTokenDefType) { + val = "::" + arena.getValue(next); + // all[write++] = { + // ...token, + // typ: + // val + "()" in getSyntaxConfig().selectors + // ? EnumToken.PseudoClassFunctionTokenDefType + // : EnumToken.FunctionTokenDefType, + // val, + // [LOCEND]: next[LOCEND], + // } as Token; + newToken = arena.allocate(); + all[write++] = newToken; + arena.set(newToken, val + "()" in getSyntaxConfig().selectors + ? exports.EnumToken.PseudoClassFunctionTokenDefType + : exports.EnumToken.FunctionTokenDefType, arena.interString(val)); + arena.setSpan(newToken, arena.getSourceId(token), arena.getStart(token), arena.getEnd(next)); read++; continue; } } - if (token.typ == exports.EnumToken.ColorTokenType) { - if (isHash(token.val)) { - token.typ = exports.EnumToken.HashTokenType; + if (arena.getType(token) == exports.EnumToken.ColorTokenType) { + if (isHash(arena.getValue(token))) { + arena.set(token, exports.EnumToken.HashTokenType); all[write++] = token; continue; } - return { - typ: exports.EnumToken.RuleNodeType, - sel: [ - ...tokens - .reduce((acc, curr, index, array) => { - if (curr.typ == exports.EnumToken.WhitespaceTokenType) { - if (trimWhiteSpace.includes(array[index - 1]?.typ) || - trimWhiteSpace.includes(array[index + 1]?.typ) || - combinators.includes(array[index - 1]?.val) || - combinators.includes(array[index + 1]?.val)) { - return acc; - } - } - let t = renderValue(curr, { minify: false }); - if (t == ",") { - acc.push([]); - } - else { - acc[acc.length - 1].push(t); + const node = arena.allocate(); + node.set(node, exports.EnumToken.RuleNodeType, arena.internString([ + ...all + .reduce((acc, curr, index, array) => { + if (arena.getType(curr) == exports.EnumToken.WhitespaceTokenType) { + if (trimWhiteSpace.includes(arena.getType(array[index - 1])) || + trimWhiteSpace.includes(arena.getType(array[index + 1])) || + combinators.includes(arena.getValue(array[index - 1])) || + combinators.includes(arena.getValue(array[index + 1]))) { + return acc; } - return acc; - }, [[]]) - .reduce((acc, curr) => { - let i = 0; - for (; i < curr.length; i++) { - if (i + 1 < curr.length && curr[i] == "*") { - if (curr[i] == "*") { - let index = curr[i + 1] == " " ? 2 : 1; - if (![">", "~", "+"].includes(curr[index])) { - curr.splice(i, index); - } + } + let t = printValue$1(curr, { minify: false, arena }); + if (t == ",") { + acc.push([]); + } + else { + acc[acc.length - 1].push(t); + } + return acc; + }, [[]]) + .reduce((acc, curr) => { + let i = 0; + for (; i < curr.length; i++) { + if (i + 1 < curr.length && curr[i] == "*") { + if (curr[i] == "*") { + let index = curr[i + 1] == " " ? 2 : 1; + if (![">", "~", "+"].includes(curr[index])) { + curr.splice(i, index); } } } - acc.set(curr.join(""), curr); - return acc; - }, uniq) - .keys(), - ].join(","), - chi: [], - [LOCSRCID]: tokens[0][LOCSRCID], - [LOCSTA]: tokens[0][LOCSTA], - [LOCEND]: tokens[tokens.length - 1][LOCEND], - [TOKENS]: tokens, - [STATE]: exports.EnumAstNodeStatus.Invalid, - [ERRORS]: [ - { - action: "drop", - node: token, - message: "invalid hash id", - }, - ], - }; + } + acc.set(curr.join(""), curr); + return acc; + }, uniq) + .keys(), + ].join(","))); + arena.setTokens(node, all); + arena.setFlags(node, exports.EnumAstNodeStatus.HasChildren | exports.EnumAstNodeStatus.Invalid); + arena.setErrors(node, [ + { + action: "drop", + node: node, + message: "invalid hash id", + }, + ]); + return node; + // return { + // typ: EnumToken.RuleNodeType, + // sel: [ + // ...tokens + // .reduce( + // (acc: string[][], curr: Token, index: number, array: Token[]) => { + // if (curr.typ == EnumToken.WhitespaceTokenType) { + // if ( + // trimWhiteSpace.includes(array[index - 1]?.typ) || + // trimWhiteSpace.includes(array[index + 1]?.typ) || + // combinators.includes((array[index - 1])?.val) || + // combinators.includes((array[index + 1])?.val) + // ) { + // return acc; + // } + // } + // let t: string = printValue(curr, { minify: false }); + // if (t == ",") { + // acc.push([]); + // } else { + // acc[acc.length - 1].push(t); + // } + // return acc; + // }, + // [[]], + // ) + // .reduce((acc: Map, curr: string[]) => { + // let i: number = 0; + // for (; i < curr.length; i++) { + // if (i + 1 < curr.length && curr[i] == "*") { + // if (curr[i] == "*") { + // let index: number = curr[i + 1] == " " ? 2 : 1; + // if (![">", "~", "+"].includes(curr[index])) { + // curr.splice(i, index); + // } + // } + // } + // } + // acc.set(curr.join(""), curr); + // return acc; + // }, uniq) + // .keys(), + // ].join(","), + // chi: [], + // [LOCSRCID]: tokens[0][LOCSRCID], + // [LOCSTA]: tokens[0][LOCSTA], + // [LOCEND]: tokens[tokens.length - 1][LOCEND], + // [TOKENS]: tokens, + // [STATE]: EnumAstNodeStatus.Invalid, + // [ERRORS]: [ + // { + // action: "drop", + // node: token, + // message: "invalid hash id", + // }, + // ], + // } as AstRule; } all[write++] = token; } @@ -28747,194 +30656,211 @@ tokens.length = write; } const result = matchSelectorSyntax(tokens, errors, options, nested); - trimArray(tokens); - if (result.success) { - for (let i = 0; i < tokens.length; i++) { - const token = tokens[i]; - switch (token.typ) { - case exports.EnumToken.AttrStartTokenType: - stack.push(token); - break; - case exports.EnumToken.AttrEndTokenType: - if (stack.at(-1)?.typ == exports.EnumToken.AttrStartTokenType) { - index = tokens.indexOf(stack.at(-1)); - // @ts-expect-error - const { val, ...attr } = stack.at(-1); - attr[LOCSRCID] = stack.at(-1)[LOCSRCID]; - attr[LOCSTA] = stack.at(-1)[LOCSTA]; - attr[LOCEND] = token[LOCEND]; - tokens.splice(i, 1); - Object.assign(attr, { - typ: exports.EnumToken.AttrTokenType, - chi: tokens.splice(index + 1, i - index - 1), - }); - tokens[index] = attr; - i = index; - stack.pop(); - } - break; - case exports.EnumToken.PseudoClassFunctionTokenDefType: - stack.push(token); - break; - case exports.EnumToken.EndParensTokenType: - if (stack.at(-1)?.typ == exports.EnumToken.PseudoClassFunctionTokenDefType) { - const func = stack.at(-1); - index = tokens.indexOf(func); - stack.at(-1)[LOCEND] = token[LOCEND]; - tokens.splice(i, 1); - if (tokensfuncDefMap.has(func.typ)) { - // @ts-expect-error - func.typ = tokensfuncDefMap.get(func.typ); - func.chi = trimArray(tokens.splice(index + 1, i - index - 1)); - } - if (result.success && options.minify) { - // parse an+b - // an+b produces an ident such as 'n-0', a literal such as '+2n-0' or a list of tokens such as[2n\s?[+-]\s?3] - if (func.val == ":nth-child" || - func.val == ":nth-last-child" || - func.val == ":nth-of-type" || - func.val == ":nth-last-of-type") { - const list = []; - let index; - for (index = 0; index < func.chi.length; index++) { - if (func.chi[index].typ == exports.EnumToken.CommentTokenType || - func.chi[index].typ == exports.EnumToken.WhitespaceTokenType) { - continue; - } - if (func.chi[index].typ == exports.EnumToken.IdenTokenType && - equalsIgnoreCase("of", func.chi[index].val)) { - index--; - break; - } - list.push(func.chi[index]); + trimArray(tokens, options); + // if (result.success) { + for (let i = 0; i < tokens.length; i++) { + token = tokens[i]; + typ = arena.getType(token); + switch (token) { + case exports.EnumToken.AttrStartTokenType: + stack.push(token); + break; + case exports.EnumToken.AttrEndTokenType: + if (arena.getType(stack.at(-1)) == exports.EnumToken.AttrStartTokenType) { + index = tokens.indexOf(stack.at(-1)); + // const { val, ...attr } = stack.at(-1) as AttrStartToken; + // attr[LOCSRCID] = stack.at(-1)![LOCSRCID]; + // attr[LOCSTA] = stack.at(-1)![LOCSTA]; + // attr[LOCEND] = token[LOCEND]; + arena.setSpanEnd(tokens[index], arena.getSpanEnd(token)); + arena.setChildren(tokens[index], ...tokens.splice(index + 1, i - index - 1)); + tokens.splice(i, 1); + // Object.assign(attr, { + // typ: EnumToken.AttrTokenType, + // chi: tokens.splice(index + 1, i - index - 1), + // }); + // tokens[index] = attr; + i = index; + stack.pop(); + } + break; + case exports.EnumToken.PseudoClassFunctionTokenDefType: + stack.push(token); + break; + case exports.EnumToken.EndParensTokenType: + if (stack.length > 0 && arena.getType(stack.at(-1)) == exports.EnumToken.PseudoClassFunctionTokenDefType) { + const func = stack.at(-1); + const funcType = arena.getType(func); + index = tokens.indexOf(func); + arena.setSpanEnd(tokens[index], arena.getSpanEnd(token)); + // (stack.at(-1) as AttrStartToken)[LOCEND] = token[LOCEND]; + tokens.splice(i, 1); + if (tokensfuncDefMap.has(funcType)) { + arena.setType(func, tokensfuncDefMap.get(funcType)); + arena.setChildren(func, ...trimArray(tokens.splice(index + 1, i - index - 1), options)); + } + if (result.success && options.minify) { + val = arena.getValue(func); + // parse an+b + // an+b produces an ident such as 'n-0', a literal such as '+2n-0' or a list of tokens such as[2n\s?[+-]\s?3] + if (val == ":nth-child" || + val == ":nth-last-child" || + val == ":nth-of-type" || + val == ":nth-last-of-type") { + const list = []; + const children = arena.children(func); + let index = 0; + let child = 0; + for (index = 0; index < children.length; index++) { + child = children[index]; + typ = arena.getType(child); + if (typ == exports.EnumToken.CommentTokenType || typ == exports.EnumToken.WhitespaceTokenType) { + continue; } - if (list.length == 2) { - if (list[1].typ == exports.EnumToken.NumberTokenType) { - if (list[1].val == 0) { - list.length = 1; - if (list[0].typ == exports.EnumToken.DimensionTokenType && - list[0].val == -2) { - list[0].val = 2; - } - } - else { - const sign = Math.sign(list[1].val); - // @ts-ignore - list[1].val *= sign; - list.splice(1, 0, { - typ: exports.EnumToken.LiteralTokenType, - val: sign > 0 ? "+" : "-", - }); + if (typ == exports.EnumToken.IdenTokenType && arena.equalsIgnoreCase(child, "val", "of")) { + index--; + break; + } + list.push(child); + } + if (list.length == 2) { + if (arena.getType(list[1]) == exports.EnumToken.NumberTokenType) { + if (arena.getNumericValue(list[1]) == 0) { + list.length = 1; + if (arena.getType(list[0]) == exports.EnumToken.DimensionTokenType && + arena.getNumericValue(list[0]) == -2) { + arena.setNumericValue(list[0], 2); } } - if (list.length == 3 && - list[2].typ == exports.EnumToken.NumberTokenType && - list[0].typ == exports.EnumToken.DimensionTokenType && - (list[0].val == 2 || - list[0].val == -2)) { - if (1 == list[2].val) { - list.splice(0, 3, { - typ: exports.EnumToken.IdenTokenType, - val: "odd", - [LOCSRCID]: list[0][LOCSRCID], - [LOCSTA]: list[0][LOCSTA], - [LOCEND]: list[0][LOCEND], - }); + else { + const sign = Math.sign(arena.getNumericValue(list[1])); + // @ts-ignore + list[1].val *= sign; + const literal = arena.allocate(); + arena.setType(literal, exports.EnumToken.LiteralTokenType, arena.internString(sign < 0 ? "-" : "+")); + list.splice(1, 0, literal); + } + } + if (list.length == 3 && + arena.getType(list[2]) == exports.EnumToken.NumberTokenType && + arena.getType(list[0]) == exports.EnumToken.DimensionTokenType) { + const value = arena.getNumericValue(list[0]); + if (value == 2 || value == -2) { + const value0 = arena.getNumericValue(list[2]); + if (1 == value0) { + const literal = arena.allocate(); + arena.setType(literal, exports.EnumToken.IdenTokenType, arena.internString("odd")); + arena.setSpan(literal, arena.getSourceId(list[0]), arena.getSpanStart(list[0]), arena.getSpanEnd(list[0])); + list.splice(0, 3, literal); } - else if (0 == list[2].val) { - list.splice(0, 3, { - typ: exports.EnumToken.IdenTokenType, - val: "even", - [LOCSRCID]: list[0][LOCSRCID], - [LOCSTA]: list[0][LOCSTA], - [LOCEND]: list[0][LOCEND], - }); + else if (0 == value0) { + const literal = arena.allocate(); + arena.setType(literal, exports.EnumToken.IdenTokenType, arena.internString("even")); + arena.setSpan(literal, arena.getSourceId(list[0]), arena.getSpanStart(list[0]), arena.getSpanEnd(list[0])); + list.splice(0, 3, literal); } } - func.chi.splice(0, index, ...list); } - if (list.length == 1) { - if (list[0].typ == exports.EnumToken.IdenTokenType && - equalsIgnoreCase("-n", list[0].val)) { - list[0].val = "n"; - } + arena.setChildren(func, arena.children(func).splice(0, index, ...list)); + } + if (list.length == 1) { + if (arena.getType(list[0]) == exports.EnumToken.IdenTokenType && + arena.equalsIgnoreCase(list[0], "val", "-n")) { + arena.setValue(list[0], arena.interString("n")); } - if (list.length == 3) { - if (list[0].typ == exports.EnumToken.IdenTokenType && - ("n" == list[0].val || - "-n" == list[0].val || - "+n" == list[0].val)) { - if (list[1].typ == exports.EnumToken.NextSiblingCombinatorTokenType) { - if (list[2].typ == exports.EnumToken.NumberTokenType && - 0 == list[2].val) { - list[0].val = "n"; - func.chi.splice(0, index, list[0]); + } + if (list.length == 3) { + if (arena.getType(list[0]) == exports.EnumToken.IdenTokenType) { + val = arena.getValue(list[0]); + if ("n" == val || "-n" == val || "+n" == val) { + if (arena.getType(list[1]) == exports.EnumToken.NextSiblingCombinatorTokenType) { + if (arena.getType(list[2]) == exports.EnumToken.NumberTokenType && + 0 == arena.getNumericValue(list[2])) { + arena.setValue(list[0], "n"); + arena.setChildren(func, arena.children(func).splice(0, index, list[0])); + // func.chi.splice(0, index, list[0]); break; } } } } - const token = func.chi.find((t) => t.typ != exports.EnumToken.WhitespaceTokenType && t.typ != exports.EnumToken.CommentTokenType); - if (token?.typ == exports.EnumToken.IdenTokenType || token?.typ == exports.EnumToken.LiteralTokenType) { - if (token.typ == exports.EnumToken.IdenTokenType && - (token.val == "odd" || token.val == "even")) { - if (token.val == "even") { - Object.assign(token, { - typ: exports.EnumToken.DimensionTokenType, - val: 2, - unit: "n", - }); + } + const token = arena.findChild(func, (t) => { + typ = arena.getType(t); + return typ != exports.EnumToken.WhitespaceTokenType && typ != exports.EnumToken.CommentTokenType; + }); + if (token != null) { + typ = arena.getType(token); + if (typ == exports.EnumToken.IdenTokenType || typ == exports.EnumToken.LiteralTokenType) { + val = arena.getValue(token); + if (typ == exports.EnumToken.IdenTokenType) { + if (val == "even") { + arena.set(token, exports.EnumToken.DimensionTokenType, 0, 2, arena.internString("n")); } } else { - const matches = /^(([+-]?[0-9]*)?n)?([+-]?[0-9]+)?$/.exec(token.val); + const matches = /^(([+-]?[0-9]*)?n)?([+-]?[0-9]+)?$/.exec(val); if (matches != null) { const a1 = matches[2] === "" ? 1 : matches[2] === "-" ? -1 : +matches[2]; const b1 = +matches[3]; if (b1 === 0) { - Object.assign(token, Math.abs(a1) === 1 - ? { - typ: exports.EnumToken.IdenTokenType, - val: "n", - } - : { - typ: exports.EnumToken.DimensionTokenType, - val: a1 * Math.sign(a1), - unit: "n", - }); + if (Math.abs(a1) === 1) { + arena.set(token, exports.EnumToken.IdenTokenType, arena.internString("n")); + } + else { + arena.set(token, exports.EnumToken.DimensionTokenType, 0, a1 * Math.sign(a1), arena.internString("n")); + } + // Object.assign( + // token, + // Math.abs(a1) === 1 + // ? { + // typ: EnumToken.IdenTokenType, + // val: "n", + // } + // : { + // typ: EnumToken.DimensionTokenType, + // val: a1 * Math.sign(a1), + // unit: "n", + // }, + // ); } } } } - else if (token?.typ === exports.EnumToken.DimensionTokenType) { - const index = func.chi.indexOf(token); + else if (typ === exports.EnumToken.DimensionTokenType) { + const children = arena.children(func); + const index = children.indexOf(token); let num = null; let i = index + 1; - for (; i < func.chi.length; i++) { - if (func.chi[i].typ == exports.EnumToken.WhitespaceTokenType || - func.chi[i].typ == exports.EnumToken.CommentTokenType) { + for (; i < children.length; i++) { + typ = arena.getType(children[i]); + if (typ == exports.EnumToken.WhitespaceTokenType || + typ == exports.EnumToken.CommentTokenType) { continue; } - if (func.chi[i].typ == exports.EnumToken.NumberTokenType) { - num = func.chi[i]; + if (typ == exports.EnumToken.NumberTokenType) { + num = children[i]; break; } } if (num != null) { - if (num.val === 0) { - func.chi.splice(index + 1, i - index); - if (token.val < 0) { - token.val *= -1; + if (arena.getNumericValue(num) === 0) { + children.splice(index + 1, i - index); + if (arena.getNumericValue(token) < 0) { + arena.setNumericValue(token, arena.getNumericValue(token) * -1); } + arena.setChildren(func, children); break; } - else if (token.val === 2 && - Math.abs(num.val) === 1) { - Object.assign(token, { - typ: exports.EnumToken.IdenTokenType, - val: "odd", - }); - func.chi.splice(index + 1, i - index); + else if (arena.getNumericValue(token) === 2 && + Math.abs(arena.getNumericValue(num)) === 1) { + arena.set(token, exports.EnumToken.IdenTokenType, arena.internString("n")); + // Object.assign(token, { + // typ: EnumToken.IdenTokenType, + // val: "odd", + // }); + children.splice(index + 1, i - index); + arena.setChildren(func, children); break; } // if (((token as DimensionToken).val as number) === 1) { @@ -28959,67 +30885,117 @@ } } } - i = index; - stack.pop(); } - break; - } + i = index; + stack.pop(); + } + break; } } - return { - typ: exports.EnumToken.RuleNodeType, - sel: [ - ...tokens - .reduce((acc, curr, index, array) => { - // if (curr.typ == EnumToken.CommentTokenType) { - // return acc; - // } - if (curr.typ == exports.EnumToken.WhitespaceTokenType) { - if (trimWhiteSpace.includes(array[index - 1]?.typ) || - trimWhiteSpace.includes(array[index + 1]?.typ) || - combinators.includes(array[index - 1]?.val) || - combinators.includes(array[index + 1]?.val)) { - return acc; - } - } - let t = renderValue(curr, { minify: false }); - if (t == ",") { - acc.push([]); - } - else { - acc[acc.length - 1].push(t); + // } + const node = arena.allocate(); + arena.set(node, exports.EnumToken.RuleNodeType, arena.internString([ + ...trimArray(all, options) + .reduce((acc, curr, index, array) => { + // if (curr.typ == EnumToken.CommentTokenType) { + // return acc; + // } + if (arena.getType(curr) == exports.EnumToken.WhitespaceTokenType) { + if (trimWhiteSpace.includes(arena.getType(array[index - 1])) || + trimWhiteSpace.includes(arena.getType(array[index + 1])) || + combinators.includes(arena.getValue(array[index - 1])) || + combinators.includes(arena.getValue(array[index + 1]))) { + return acc; } - return acc; - }, [[]]) - .reduce((acc, curr) => { - let i = 0; - for (; i < curr.length; i++) { - if (i + 1 < curr.length && curr[i] == "*") { - if (curr[i] == "*") { - let index = curr[i + 1] == " " ? 2 : 1; - if (![">", "~", "+"].includes(curr[index])) { - curr.splice(i, index); - } + } + let t = printValue$1(curr, arena, { minify: false, arena }); + if (t == ",") { + acc.push([]); + } + else { + acc[acc.length - 1].push(t); + } + return acc; + }, [[]]) + .reduce((acc, curr) => { + let i = 0; + for (; i < curr.length; i++) { + if (i + 1 < curr.length && curr[i] == "*") { + if (curr[i] == "*") { + let index = curr[i + 1] == " " ? 2 : 1; + if (![">", "~", "+"].includes(curr[index])) { + curr.splice(i, index); } } } - acc.set(curr.join(""), curr); - return acc; - }, uniq) - .keys(), - ].join(","), - chi: [], - [LOCSRCID]: tokens[0][LOCSRCID], - [LOCSTA]: tokens[0][LOCSTA], - [LOCEND]: tokens[tokens.length - 1][LOCEND], - [TOKENS]: tokens, - [STATE]: result.success && allowed - ? exports.EnumAstNodeStatus.Validated - : allowed - ? exports.EnumAstNodeStatus.Invalid - : exports.EnumAstNodeStatus.Disallowed, - [ERRORS]: result.success ? [] : result.errors, - }; + } + acc.set(curr.join(""), curr); + return acc; + }, uniq) + .keys(), + ].join(","))); + arena.setSpan(node, arena.getSourceId(tokens[0]), arena.getSpanStart(tokens[0]), arena.getSpanEnd(tokens[tokens.length - 1])); + arena.setFlags(node, exports.EnumAstNodeStatus.HasChildren | exports.EnumAstNodeStatus.Validated); + return node; + // return { + // typ: EnumToken.RuleNodeType, + // sel: [ + // ...tokens + // .reduce( + // (acc: string[][], curr: Token, index: number, array: Token[]) => { + // // if (curr.typ == EnumToken.CommentTokenType) { + // // return acc; + // // } + // if (curr.typ == EnumToken.WhitespaceTokenType) { + // if ( + // trimWhiteSpace.includes(array[index - 1]?.typ) || + // trimWhiteSpace.includes(array[index + 1]?.typ) || + // combinators.includes((array[index - 1])?.val) || + // combinators.includes((array[index + 1])?.val) + // ) { + // return acc; + // } + // } + // let t: string = printValue(curr, { minify: false }); + // if (t == ",") { + // acc.push([]); + // } else { + // acc[acc.length - 1].push(t); + // } + // return acc; + // }, + // [[]], + // ) + // .reduce((acc: Map, curr: string[]) => { + // let i: number = 0; + // for (; i < curr.length; i++) { + // if (i + 1 < curr.length && curr[i] == "*") { + // if (curr[i] == "*") { + // let index: number = curr[i + 1] == " " ? 2 : 1; + // if (![">", "~", "+"].includes(curr[index])) { + // curr.splice(i, index); + // } + // } + // } + // } + // acc.set(curr.join(""), curr); + // return acc; + // }, uniq) + // .keys(), + // ].join(","), + // chi: [], + // [LOCSRCID]: tokens[0][LOCSRCID], + // [LOCSTA]: tokens[0][LOCSTA], + // [LOCEND]: tokens[tokens.length - 1][LOCEND], + // [TOKENS]: tokens, + // [STATE]: + // result.success && allowed + // ? EnumAstNodeStatus.Validated + // : allowed + // ? EnumAstNodeStatus.Invalid + // : EnumAstNodeStatus.Disallowed, + // [ERRORS]: result.success ? [] : result.errors, + // } as AstRule; } /** @@ -29064,69 +31040,91 @@ */ function parseDeclaration(tokens, parent, options, errors) { const name = tokens.shift(); + const arena = options.arena; let i; let rules = null; let syntaxRules = null; let success = true; + let val = arena.getValue(name); + let token; + let typ; let hasImportantFlag = false; let validate = typeof options.validation === "boolean" ? options.validation : !!options.validation; - if (renamedStandardProperties.has(name.val)) { - name.val = renamedStandardProperties.get(name.val); + if (renamedStandardProperties.has(val)) { + arena.setValue(name, arena.internString(renamedStandardProperties.get(val))); } for (i = 0; i < tokens.length; i++) { - const token = tokens[i]; - if (token.typ == exports.EnumToken.WhitespaceTokenType || - token.typ == exports.EnumToken.CommentTokenType || - token.typ == exports.EnumToken.InvalidCommentTokenType) { + token = tokens[i]; + typ = arena.getType(token); + if (typ == exports.EnumToken.WhitespaceTokenType || + typ == exports.EnumToken.CommentTokenType || + typ == exports.EnumToken.InvalidCommentTokenType) { continue; } break; } - if ((name.typ !== exports.EnumToken.IdenTokenType && name.typ !== exports.EnumToken.DashedIdenTokenType) || - tokens[i]?.typ !== exports.EnumToken.ColonTokenType) { - if (tokens[tokens.length - 1]?.[LOCEND] != null) { - name[LOCEND] = tokens[tokens.length - 1]?.[LOCEND]; - } - name[STATE] = exports.EnumAstNodeStatus.Unparsed; - name[ERRORS] = [ + typ = arena.getType(name); + if ((typ !== exports.EnumToken.IdenTokenType && typ !== exports.EnumToken.DashedIdenTokenType) || + arena.getType(tokens[i]) !== exports.EnumToken.ColonTokenType) { + // if (tokens[tokens.length - 1]?.[LOCEND] != null) { + // name[LOCEND] = tokens[tokens.length - 1]?.[LOCEND]; + // } + const node = arena.allocate(); + arena.setType(node, exports.EnumToken.RawNodeTokenType); + arena.setFlags(node, exports.EnumAstNodeStatus.Unparsed); + arena.setErrors(node, [ { action: "drop", node: name, - location: options.source.getSourceLocation(name[LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(name)), message: "invalid declaration", }, - ]; - return Object.assign({ - typ: exports.EnumToken.RawNodeTokenType, - val: tokens, - }); - } - tokens = trimArray(tokens.slice(i + 1)); - if (validate && name.typ === exports.EnumToken.IdenTokenType) { + ]); + arena.setTokens(node, ...tokens); + arena.setSpan(node, arena.getSourceId(name), arena.getSpanStart(name), arena.getSpanEnd(tokens.at(-1) ?? name)); + return node; + // name[STATE] = EnumAstNodeStatus.Unparsed; + // name[ERRORS] = [ + // { + // action: "drop", + // node: name, + // location: options.source!.getSourceLocation(name[LOCSTA]!), + // message: "invalid declaration", + // } as ErrorDescription, + // ]; + // return Object.assign({ + // typ: EnumToken.RawNodeTokenType, + // val: tokens, + // }) as RawNodeToken; + } + tokens = trimArray(tokens.slice(i + 1), options); + if (validate && typ === exports.EnumToken.IdenTokenType) { + typ = arena.getType(parent); + val = arena.getValue(parent).toLowerCase(); if (parent != null && - parent.typ === exports.EnumToken.AtRuleNodeType && - parent.nam != "import" && - parent.nam != "supports" && - parent.nam != "media") { + typ === exports.EnumToken.AtRuleNodeType && + val != "import" && + val != "supports" && + val != "media") { // check @at-rule description // pick up the syntax or reject immediately // else do this - rules = getSyntaxRule(ValidationSyntaxGroupEnum.AtRules, "@" + parent.nam); + rules = getSyntaxRule(ValidationSyntaxGroupEnum.AtRules, "@" + val); if (rules != null) { const propertyDescriptors = rules.getPropertyDescriptors(); if (propertyDescriptors != null) { - syntaxRules = propertyDescriptors[name.val.toLowerCase()]; + syntaxRules = propertyDescriptors[val.toLowerCase()]; } else { syntaxRules = rules.acceptAnyDeclaration && rules.acceptAnyRule - ? getParsedSyntax(ValidationSyntaxGroupEnum.Declarations, name.val.toLowerCase()) + ? getParsedSyntax(ValidationSyntaxGroupEnum.Declarations, val) : rules.getBlockRules(); } } } else { - syntaxRules = getParsedSyntax(ValidationSyntaxGroupEnum.Declarations, name.val.toLowerCase()); + syntaxRules = getParsedSyntax(ValidationSyntaxGroupEnum.Declarations, val); } // or // else @@ -29135,61 +31133,73 @@ syntaxRules[0].typ === ValidationTokenEnum.PropertyType && (syntaxRules[0].val === "declaration-list" || syntaxRules[0].val === "declaration-rule-list"))) { - if (parent?.typ === exports.EnumToken.AtRuleNodeType && - (parent.nam === "swash" || - parent.nam === "annotation" || - parent.nam === "ornaments" || - parent.nam === "stylistic" || - parent.nam === "historical-forms")) { + typ = arena.getType(parent); + val = arena.getValue(parent).toLowerCase(); + if (parent != null && + typ === exports.EnumToken.AtRuleNodeType && + (val === "swash" || + val === "annotation" || + val === "ornaments" || + val === "stylistic" || + val === "historical-forms")) { syntaxRules = getParsedSyntax(ValidationSyntaxGroupEnum.Syntaxes, "font-feature-custom-ident"); } - else if (parent?.typ === exports.EnumToken.AtRuleNodeType && parent.nam === "styleset") { + else if (typ === exports.EnumToken.AtRuleNodeType && val === "styleset") { syntaxRules = getParsedSyntax(ValidationSyntaxGroupEnum.Syntaxes, "font-feature-custom-ident-list"); } - else if (parent?.typ === exports.EnumToken.AtRuleNodeType && parent.nam === "character-variant") { + else if (typ === exports.EnumToken.AtRuleNodeType && val === "character-variant") { syntaxRules = getParsedSyntax(ValidationSyntaxGroupEnum.Syntaxes, "font-feature-custom-ident-character-variant"); } else { - syntaxRules = getParsedSyntax(ValidationSyntaxGroupEnum.Declarations, name.val.toLowerCase()); + syntaxRules = getParsedSyntax(ValidationSyntaxGroupEnum.Declarations, val); } } } - if (tokens.length === 0 && name.typ !== exports.EnumToken.DashedIdenTokenType) { + typ = arena.getType(name); + if (tokens.length === 0 && typ !== exports.EnumToken.DashedIdenTokenType) { errors.push({ action: "drop", message: "declaration value missing", node: name, - location: options.source.getSourceLocation(name[LOCSTA]), - }); - if (tokens[tokens.length - 1]?.[LOCEND] != null) { - name[LOCEND] = tokens[tokens.length - 1][LOCEND]; - } - name[STATE] = exports.EnumAstNodeStatus.Invalid; - name[ERRORS] = [errors[errors.length - 1]]; - // @ts-expect-error - return Object.assign(name, { - typ: exports.EnumToken.DeclarationNodeType, - nam: name.val, - val: tokens, + location: options.source.getSourceLocation(arena.getSpanStart(name)), }); + // if (tokens[tokens.length - 1]?.[LOCEND] != null) { + // name[LOCEND] = tokens[tokens.length - 1][LOCEND]; + // } + arena.setFlags(name, exports.EnumAstNodeStatus.Invalid | exports.EnumAstNodeStatus.HasValues); + arena.setErrors(name, [errors[errors.length - 1]]); + arena.setName(name, arena.internString(arena.getValue(name))); + arena.set(name, exports.EnumToken.DeclarationNodeType, 0); + arena.setChildren(name, ...tokens); + return name; + // name[STATE] = EnumAstNodeStatus.Invalid; + // name[ERRORS] = [errors[errors.length - 1]]; + // return Object.assign(name, { + // typ: EnumToken.DeclarationNodeType, + // nam: name.val, + // val: tokens, + // }) as AstDeclaration; } const stack = []; let result = null; - let token; + // let token: Token; let index; if (syntaxRules != null) { const doNotValidate = options.validation === false; - result = doNotValidate ? null : matchAllSyntaxes(syntaxRules, createValidationContext(tokens), options); + result = doNotValidate + ? null + : matchAllSyntaxes(syntaxRules, createValidationContext(tokens, options), options); if (doNotValidate || result != null) { success = doNotValidate || result?.success; if (success) { - for (const { value } of walkValues(tokens)) { - if (value.typ === exports.EnumToken.IdenTokenType && isColor(value)) { - parseColor(value); - } - else if (value.typ === exports.EnumToken.ColorTokenType) { - if (isColor(value)) { - parseColor(value); + for (const value of arena.walkValues(tokens)) { + typ = arena.getType(value); + if (typ === exports.EnumToken.IdenTokenType && isColor(value, arena)) { + parseColor(value, arena); + } + else if (typ === exports.EnumToken.ColorTokenType) { + if (isColor(value, arena)) { + parseColor(value, arena); } } } @@ -29203,66 +31213,83 @@ } for (i = 0; i < tokens.length; i++) { token = tokens[i]; - if (tokensfuncDefMap.has(token.typ) || - token.typ == exports.EnumToken.StartParensTokenType || - token.typ == exports.EnumToken.BlockStartTokenType) { + typ = arena.getType(token); + val = arena.getValue(token); + if (tokensfuncDefMap.has(typ) || + typ == exports.EnumToken.StartParensTokenType || + typ == exports.EnumToken.BlockStartTokenType) { stack.push(token); continue; } - if (token.typ === exports.EnumToken.ColonTokenType && stack.length == 0) { + if (typ === exports.EnumToken.ColonTokenType && stack.length == 0) { stack.push(token); continue; } - switch (token.typ) { + const stackType = arena.getType(stack.at(-1)); + switch (typ) { case exports.EnumToken.Literal: - if (token.val === "/" && stack.at(-1)?.typ == exports.EnumToken.MathFunctionTokenDefType) { - Object.assign(token, { - typ: exports.EnumToken.Div, - }); - } - else if (token.val === "-" && - stack.at(-1)?.typ == exports.EnumToken.MathFunctionTokenDefType) { - Object.assign(token, { - typ: exports.EnumToken.Sub, - }); + if (arena.equalsIgnoreCase(token, "val", "/") && stackType == exports.EnumToken.MathFunctionTokenDefType) { + arena.setType(token, exports.EnumToken.Div); + // Object.assign(token, { + // typ: EnumToken.Div, + // }); + } + else if (arena.equalsIgnoreCase(token, "val", "-") && + stackType == exports.EnumToken.MathFunctionTokenDefType) { + arena.setType(token, exports.EnumToken.Sub); + // Object.assign(token, { + // typ: EnumToken.Sub, + // }); } break; case exports.EnumToken.Star: case exports.EnumToken.Plus: - Object.assign(token, { - typ: tokensMap.get(token.typ), - }); + arena.setType(token, tokensMap.get(typ)); + // Object.assign(token, { + // typ: tokensMap.get(typ), + // }); break; case exports.EnumToken.BlockEndTokenType: - if (stack.at(-1)?.typ === exports.EnumToken.BlockStartTokenType) { + if (stackType === exports.EnumToken.BlockStartTokenType) { // stack.pop();index = tokens.indexOf(stack.at(-1)!); index = tokens.indexOf(stack.at(-1)); tokens.splice(i, 1); - Object.assign(tokens[index], { - typ: exports.EnumToken.WrappedValuesTokenType, - chi: trimArray(tokens.splice(index + 1, i - index - 1)), - }); + arena.setType(tokens[index], exports.EnumToken.WrappedValuesTokenType); + arena.setChildren(tokens[index], ...trimArray(tokens.splice(index + 1, i - index - 1), options)); + // Object.assign(tokens[index], { + // typ: EnumToken.WrappedValuesTokenType, + // chi: trimArray(tokens.splice(index + 1, i - index - 1), options), + // }); i = index; stack.pop(); } break; case exports.EnumToken.EndParensTokenType: - if (stack.at(-1)?.typ === exports.EnumToken.StartParensTokenType || tokensfuncDefMap.has(stack.at(-1)?.typ)) { + if (stackType === exports.EnumToken.StartParensTokenType || tokensfuncDefMap.has(stackType)) { index = tokens.indexOf(stack.at(-1)); tokens.splice(i, 1); - Object.assign(tokens[index], { - typ: stack.at(-1)?.typ === exports.EnumToken.StartParensTokenType - ? exports.EnumToken.ParensTokenType - : tokensfuncDefMap.get(stack.at(-1)?.typ), - chi: trimArray(tokens.splice(index + 1, i - index - 1)), - }); - if (tokens[index].typ === exports.EnumToken.WildCardFunctionTokenType && - equalsIgnoreCase(tokens[index].val, "if")) { - if (tokens[index].chi.at(-1)?.typ === exports.EnumToken.SemiColonTokenType) { - tokens[index].chi.pop(); + arena.setType(tokens[index], stackType === exports.EnumToken.StartParensTokenType + ? exports.EnumToken.ParensTokenType + : tokensfuncDefMap.get(stackType)); + arena.setChildren(tokens[index], ...trimArray(tokens.splice(index + 1, i - index - 1), options)); + // Object.assign(tokens[index], { + // typ: + // stack.at(-1)?.typ === EnumToken.StartParensTokenType + // ? EnumToken.ParensTokenType + // : (tokensfuncDefMap.get(stack.at(-1)?.typ) as EnumToken), + // chi: trimArray(tokens.splice(index + 1, i - index - 1)), + // }); + if (arena.getType(tokens[index]) === exports.EnumToken.WildCardFunctionTokenType && + arena.equalsIgnoreCase(tokens[index], "val", "if")) { + const lastChild = arena.getLastChild(tokens[index]); + if (lastChild != 0 && arena.getType(lastChild) === exports.EnumToken.SemiColonTokenType) { + arena.detach(lastChild); } + // if ((tokens[index] as FunctionToken).chi.at(-1)?.typ === EnumToken.SemiColonTokenType) { + // (tokens[index] as FunctionToken).chi.pop(); + // } let foundElse = false; - tokens[index].chi = splitTokenList(tokens[index].chi, [exports.EnumToken.SemiColonTokenType], true).reduce((acc, curr) => { + splitTokenList(arena.children(tokens[index]), options, [exports.EnumToken.SemiColonTokenType], true).reduce((acc, curr) => { if (foundElse) { // else already found // ignore everything after else @@ -29270,70 +31297,107 @@ return acc; } let index = -1; + let typ = arena.getType(curr[index]); while (++index < curr.length) { - if (curr[index].typ == exports.EnumToken.IdenTokenType && - equalsIgnoreCase("else", curr[index].val)) { + if (typ == exports.EnumToken.IdenTokenType && + arena.equalsIgnoreCase(curr[index], "val", "else")) { foundElse = true; } - if (curr[index].typ === exports.EnumToken.ColonTokenType) { + if (typ === exports.EnumToken.ColonTokenType) { break; } } if (foundElse && acc.length > 0) { - acc[acc.length - 1] = { - typ: exports.EnumToken.IfElseConditionTokenType, - l: acc[acc.length - 1], - r: { - typ: exports.EnumToken.IfConditionTokenType, - l: trimArray(curr.slice(0, index)), - r: trimArray(curr.at(-1)?.typ === exports.EnumToken.SemiColonTokenType - ? curr.slice(index + 1, -1) - : curr.slice(index + 1)), - }, - }; + const leftChild = arena.allocate(); + arena.setType(leftChild, exports.EnumToken.ListTokenType); + arena.setChildren(leftChild, ...trimArray(curr.slice(0, index), options)); + const rightChild = arena.allocate(); + arena.setType(rightChild, exports.EnumToken.ListTokenType); + arena.setChildren(rightChild, ...trimArray(arena.getType(curr.at(-1)) === exports.EnumToken.SemiColonTokenType + ? curr.slice(index + 1, -1) + : curr.slice(index + 1), options)); + const ifCondition = arena.allocate(); + arena.setType(ifCondition, exports.EnumToken.IfConditionTokenType); + arena.setLeft(ifCondition, leftChild); + arena.setRight(ifCondition, rightChild); + const ifElseLeftCondition = arena.allocate(); + arena.setType(ifElseLeftCondition, exports.EnumToken.IfElseConditionTokenType); + arena.setLeft(ifElseLeftCondition, acc[acc.length - 1]); + arena.setRight(ifElseLeftCondition, ifCondition); + acc[acc.length - 1] = ifElseLeftCondition; + // acc[acc.length - 1] = { + // typ: EnumToken.IfElseConditionTokenType, + // l: acc[acc.length - 1] as IfConditionToken, + // r: { + // typ: EnumToken.IfConditionTokenType, + // l: trimArray(curr.slice(0, index)), + // r: trimArray( + // curr.at(-1)?.typ === EnumToken.SemiColonTokenType + // ? curr.slice(index + 1, -1) + // : curr.slice(index + 1), + // ), + // } as IfConditionToken, + // } as IfElseConditionToken; } else { - acc.push({ - typ: exports.EnumToken.IfConditionTokenType, - l: trimArray(curr.slice(0, index)), - r: trimArray(curr.slice(index + 1)), - }); + const ifCondition = arena.allocate(); + arena.setType(ifCondition, exports.EnumToken.IfConditionTokenType); + const leftChild = arena.allocate(); + arena.setType(leftChild, exports.EnumToken.ListTokenType); + arena.setChildren(leftChild, ...trimArray(curr.slice(0, index), options)); + const rightChild = arena.allocate(); + arena.setType(rightChild, exports.EnumToken.ListTokenType); + arena.setChildren(rightChild, ...trimArray(curr.slice(index + 1), options)); + arena.setLeft(ifCondition, leftChild); + arena.setRight(ifCondition, rightChild); + acc.push(ifCondition); + // acc.push({ + // typ: EnumToken.IfConditionTokenType, + // l: trimArray(curr.slice(0, index)), + // r: trimArray(curr.slice(index + 1)), + // } as IfConditionToken); } return acc; }, []); } - else if (tokens[index].typ === exports.EnumToken.UrlFunctionTokenType) { + else if (arena.getType(tokens[index]) === exports.EnumToken.UrlFunctionTokenType) { let l = -1; - while (++l < tokens[index].chi.length) { - if (tokens[index].chi[l].typ === exports.EnumToken.StringTokenType) { + const children = arena.children(tokens[index]); + while (++l < children.length) { + if (arena.getType(children[l]) === exports.EnumToken.StringTokenType) { break; } } - if (tokens[index].chi[l]?.typ === exports.EnumToken.StringTokenType && - /^[a-zA-Z0-0/_.-]+$/.test(tokens[index].chi[l].val.slice(1, -1))) { - Object.assign(tokens[index].chi[l], { - typ: exports.EnumToken.UrlTokenTokenType, - val: tokens[index].chi[l].val.slice(1, -1), - }); + if (l < children.length && arena.getType(children[l]) === exports.EnumToken.StringTokenType) { + const val = arena.getValue(children[l]).slice(1, -1); + if (/^[a-zA-Z0-0/_.-]+$/.test(val)) { + arena.setValue(children[l], arena.internString(val)); + arena.setType(children[l], exports.EnumToken.UrlTokenTokenType); + // Object.assign((tokens[index] as FunctionToken).chi[l], { + // typ: EnumToken.UrlTokenTokenType, + // val: ((tokens[index] as FunctionToken).chi[l] as StringToken).val.slice(1, -1), + // }); + } } } i = index; - if (tokens[index].typ === exports.EnumToken.MathFunctionTokenType && - equalsIgnoreCase("calc", tokens[index].val)) { - tokens[index].chi = [buildExpression(tokens[index].chi)]; - } - else if (tokens[index].typ === exports.EnumToken.ColorTokenType) { - if (isColor(tokens[index])) { - parseColor(tokens[index]); + if (arena.getType(tokens[index]) === exports.EnumToken.MathFunctionTokenType && + arena.equalsIgnoreCase(tokens[index], "val", "calc")) { + arena.setChildren(tokens[index], ...arena.children(tokens[index])); + // (tokens[index] as FunctionToken).chi = [buildExpression((tokens[index] as FunctionToken).chi)]; + } + else if (arena.getType(tokens[index]) === exports.EnumToken.ColorTokenType) { + if (isColor(tokens[index], arena)) { + parseColor(tokens[index], arena); } else { success = false; - tokens[index].typ = exports.EnumToken.FunctionTokenType; + arena.setType(tokens[index], exports.EnumToken.FunctionTokenType); errors.push({ action: "drop", message: `invalid color`, node: tokens[index], - location: options.source.getSourceLocation(tokens[index][LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(tokens[index])), }); } } @@ -29342,31 +31406,39 @@ break; case exports.EnumToken.IdenTokenType: { - const val = token.val.toLowerCase(); - if (val in COLORS_NAMES || val === "currentcolor") { - Object.assign(token, { - val, - typ: exports.EnumToken.ColorTokenType, - kin: exports.ColorType.LIT, - }); + const val = arena.getValue(token).toLowerCase(); + if (COLORS_NAMES[val] != null || val === "currentcolor") { + arena.setType(token, exports.EnumToken.ColorTokenType); + arena.setColorType(token, exports.ColorType.LIT); + // Object.assign(token, { + // val, + // typ: EnumToken.ColorTokenType, + // kin: ColorType.LIT, + // }); } else if (nonStandardColors.has(val)) { - Object.assign(token, { - typ: exports.EnumToken.ColorTokenType, - kin: exports.ColorType.NON_STD, - }); + arena.setType(token, exports.EnumToken.ColorTokenType); + arena.setColorType(token, exports.ColorType.NON_STD); + // Object.assign(token, { + // typ: EnumToken.ColorTokenType, + // kin: ColorType.NON_STD, + // }); } else if (systemColors.has(val)) { - Object.assign(token, { - typ: exports.EnumToken.ColorTokenType, - kin: exports.ColorType.SYS, - }); + arena.setType(token, exports.EnumToken.ColorTokenType); + arena.setColorType(token, exports.ColorType.SYS); + // Object.assign(token, { + // typ: EnumToken.ColorTokenType, + // kin: ColorType.SYS, + // }); } else if (deprecatedSystemColors.has(val)) { - Object.assign(token, { - typ: exports.EnumToken.ColorTokenType, - kin: exports.ColorType.DPSYS, - }); + arena.setType(token, exports.EnumToken.ColorTokenType); + arena.setColorType(token, exports.ColorType.DPSYS); + // Object.assign(token, { + // typ: EnumToken.ColorTokenType, + // kin: ColorType.DPSYS, + // }); } } break; @@ -29374,8 +31446,9 @@ hasImportantFlag = true; break; } - if (trimTokenSpace$1.has(token.typ)) { - if (tokens[i + 1]?.typ === exports.EnumToken.WhitespaceTokenType) { + typ = arena.getType(token); + if (trimTokenSpace$1.has(typ)) { + if (tokens.length > i + 1 && arena.getType(tokens[i + 1]) === exports.EnumToken.WhitespaceTokenType) { for (let index = i + 1; index < tokens.length - 1; index++) { tokens[index] = tokens[index + 1]; } @@ -29383,7 +31456,7 @@ i--; continue; } - if (tokens[i - 1]?.typ == exports.EnumToken.WhitespaceTokenType) { + if (tokens.length >= i && arena.getType(tokens[i - 1]) == exports.EnumToken.WhitespaceTokenType) { for (let index = i - 1; index < tokens.length - 1; index++) { tokens[index] = tokens[index + 1]; } @@ -29397,97 +31470,156 @@ action: "drop", message: "unbalanced token", node: stack[stack.length - 1], - location: options.source.getSourceLocation(stack[stack.length - 1][LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(stack[stack.length - 1])), }); - if (tokens[tokens.length - 1][LOCEND] != null) { - name[LOCEND] = tokens[tokens.length - 1][LOCEND]; - } - name[STATE] = exports.EnumAstNodeStatus.Invalid; - name[ERRORS] = result?.errors ?? []; - //@ts-expect-error - return Object.assign(name, { - typ: exports.EnumToken.DeclarationNodeType, - nam: name.val, - val: tokens, - }); - } - for (const { value } of walkValues(tokens)) { - if (value.typ === exports.EnumToken.IdenTokenType && isColor(value)) { - parseColor(value); - } - } - if (name.val === "grid" || - name.val === "grid-template-areas" || - name.val === "grid-template-rows" || - name.val === "grid-template-columns") { + if (tokens.length > 0) { + arena.setSpanEnd(name, arena.getSpanEnd(tokens[tokens.length - 1])); + // name[LOCEND] = tokens[tokens.length - 1][LOCEND]; + } + arena.setName(name, arena.internString(arena.getValue(name))); + arena.set(name, exports.EnumToken.DeclarationNodeType, 0); + arena.setFlags(name, exports.EnumAstNodeStatus.Invalid | exports.EnumAstNodeStatus.HasValues); + arena.setErrors(name, result?.errors ?? []); + arena.setChildren(name, ...tokens); + return name; + // name[STATE] = EnumAstNodeStatus.Invalid; + // name[ERRORS] = result?.errors ?? []; + // return Object.assign(name, { + // typ: EnumToken.DeclarationNodeType, + // nam: name.val, + // val: tokens, + // }) as AstDeclaration; + } + for (const value of arena.walkValues(tokens)) { + if (arena.getType(value) === exports.EnumToken.IdenTokenType && isColor(value, arena)) { + parseColor(value, arena); + } + } + val = arena.getValue(name); + if (val === "grid" || + val === "grid-template-areas" || + val === "grid-template-rows" || + val === "grid-template-columns") { let i = 0; for (; i < tokens.length; i++) { - if (tokens[i].typ === exports.EnumToken.StringTokenType) { - if (/[\s.]/.test(tokens[i].val)) { - tokens[i].val = parseGridTemplate(tokens[i].val); - } - if (tokens[i + 1]?.typ === exports.EnumToken.WhitespaceTokenType && - tokens[i + 2]?.typ === exports.EnumToken.StringTokenType) { + typ = arena.getType(tokens[i]); + if (typ === exports.EnumToken.StringTokenType) { + val = arena.getValue(tokens[i]); + if (/[\s.]/.test(val)) { + arena.setValue(tokens[i], arena.internString(parseGridTemplate(val))); + // (tokens[i] as StringToken).val = parseGridTemplate(val); + } + if (arena.getType(tokens[i + 1]) === exports.EnumToken.WhitespaceTokenType && + arena.getType(tokens[i + 2]) === exports.EnumToken.StringTokenType) { tokens.splice(i + 1, 1); } } } } - if (validate && syntaxRules == null && name.typ === exports.EnumToken.IdenTokenType) { - if (tokens[tokens.length - 1]?.[LOCEND] != null) { - name[LOCEND] = tokens[tokens.length - 1][LOCEND]; - } - name[STATE] = exports.EnumAstNodeStatus.Unknown; - name[ERRORS] = result?.errors ?? []; - if (hasImportantFlag) { - name[STATE] |= exports.EnumAstNodeStatus.ImportantDeclaration; - } - // @ts-expect-error - return Object.assign(name, { - typ: exports.EnumToken.DeclarationNodeType, - nam: name.val, - val: tokens, - }); - } - if (equalsIgnoreCase("composes", name.val)) { + if (validate && syntaxRules == null && arena.getType(name) === exports.EnumToken.IdenTokenType) { + if (tokens.length > 0 != null) { + arena.setSpanEnd(name, arena.getSpanEnd(tokens[tokens.length - 1])); + // name[LOCEND] = tokens[tokens.length - 1][LOCEND]; + } + arena.setName(name, arena.internString(arena.getValue(name))); + arena.set(name, exports.EnumToken.DeclarationNodeType, 0); + arena.setFlags(name, hasImportantFlag ? exports.EnumAstNodeStatus.ImportantDeclaration | exports.EnumAstNodeStatus.Unknown : exports.EnumAstNodeStatus.Unknown); + arena.setErrors(name, result?.errors ?? []); + arena.setChildren(name, ...tokens); + return name; + // name[STATE] = EnumAstNodeStatus.Unknown; + // name[ERRORS] = result?.errors ?? []; + // // @ts-expect-error + // return Object.assign(name, { + // typ: EnumToken.DeclarationNodeType, + // nam: name.val, + // val: tokens, + // }) as AstDeclaration; + // if (hasImportantFlag) { + // name[STATE] |= EnumAstNodeStatus.ImportantDeclaration; + // } + // // @ts-expect-error + // return Object.assign(name, { + // typ: EnumToken.DeclarationNodeType, + // nam: name.val, + // val: tokens, + // }) as AstDeclaration; + } + if (arena.equalsIgnoreCase(name, "val", "composes")) { let index = -1; for (let i = 0; i < tokens.length; i++) { - if (tokens[i].typ === exports.EnumToken.IdenTokenType && equalsIgnoreCase("from", tokens[i].val)) { + if (arena.getType(tokens[i]) === exports.EnumToken.IdenTokenType && + arena.equalsIgnoreCase(tokens[i], "val", "from")) { index = i; break; } } - const left = trimArray(index == -1 ? tokens : tokens.slice(0, index)); - const right = index == -1 ? null : trimArray(tokens.slice(index + 1)); - tokens = [ - { - typ: exports.EnumToken.ComposesSelectorNodeType, - l: left, - r: right?.[0] ?? null, - [LOCSRCID]: tokens[0][LOCSRCID], - [LOCSTA]: tokens[0][LOCSTA], - [LOCEND]: index != -1 ? right[right.length - 1]?.[LOCEND] : left[left.length - 1][LOCEND], - }, - ]; + const left = trimArray(index == -1 ? tokens : tokens.slice(0, index), options); + const right = index == -1 ? null : trimArray(tokens.slice(index + 1), options); + const composeSelector = arena.allocate(); + arena.set(composeSelector, exports.EnumToken.ComposesSelectorNodeType); + arena.setSpan(composeSelector, arena.getSourceId(tokens[0]), arena.getSpanStart(tokens[0]), arena.getSpanEnd(right?.[right.length - 1] ?? left[left.length - 1])); + const leftSide = arena.allocate(); + arena.set(leftSide, exports.EnumToken.ListTokenType); + arena.setChildren(leftSide, ...left); + arena.setLeft(composeSelector, leftSide); + if (right != null) { + const rightSide = arena.allocate(); + arena.set(rightSide, exports.EnumToken.ListTokenType); + arena.setChildren(rightSide, ...right); + arena.setRight(composeSelector, rightSide); + } + tokens.length = 0; + tokens.push(composeSelector); + // tokens = [ + // { + // typ: EnumToken.ComposesSelectorNodeType, + // l: left, + // r: right?.[0] ?? null, + // [LOCSRCID]: tokens[0][LOCSRCID], + // [LOCSTA]: tokens[0][LOCSTA], + // [LOCEND]: index != -1 ? right![right!.length - 1]?.[LOCEND] : left[left.length - 1][LOCEND], + // } as ComposesSelectorToken, + // ]; } - if (tokens[tokens.length - 1]?.[LOCEND] != null) { - name[LOCEND] = tokens[tokens.length - 1][LOCEND]; + if (tokens.length > 0) { + arena.setSpanEnd(name, arena.getSpanEnd(tokens[tokens.length - 1])); + // name[LOCEND] = tokens[tokens.length - 1][LOCEND]; } - name[STATE] = success + let flags = exports.EnumAstNodeStatus.HasValues | (success ? result == null ? exports.EnumAstNodeStatus.Unvalidated : exports.EnumAstNodeStatus.Validated - : exports.EnumAstNodeStatus.ValidationFailed; - name[ERRORS] = result?.errors ?? []; + : exports.EnumAstNodeStatus.ValidationFailed); if (hasImportantFlag) { - name[STATE] |= exports.EnumAstNodeStatus.ImportantDeclaration; - } - // @ts-expect-error - return Object.assign(name, { - typ: exports.EnumToken.DeclarationNodeType, - nam: name.val, - val: tokens, - }); + flags |= exports.EnumAstNodeStatus.ImportantDeclaration; + } + arena.setName(name, arena.internString(arena.getValue(name))); + arena.set(name, exports.EnumToken.DeclarationNodeType, 0); + arena.setFlags(name, flags); + arena.setErrors(name, result?.errors ?? []); + arena.setChildren(name, ...tokens); + return name; + // name[STATE] = success + // ? result == null + // ? EnumAstNodeStatus.Unvalidated + // : EnumAstNodeStatus.Validated + // : EnumAstNodeStatus.ValidationFailed; + // name[ERRORS] = result?.errors ?? []; + // return Object.assign(name, { + // typ: EnumToken.DeclarationNodeType, + // nam: name.val, + // val: tokens, + // }) as AstDeclaration; + // if (hasImportantFlag) { + // name[STATE] |= EnumAstNodeStatus.ImportantDeclaration; + // } + // // @ts-expect-error + // return Object.assign(name, { + // typ: EnumToken.DeclarationNodeType, + // nam: name.val, + // val: tokens, + // }) as AstDeclaration; } // https://drafts.csswg.org/mediaqueries/#media-descriptor-table:~:text=It%20is%20invalid%20to%20mix%20and%20and%20or%20and%20not%20at%20the%20same%20%E2%80%9Clevel%E2%80%9D%20of%20a%20media%20query%2E @@ -30903,8 +33035,8 @@ if (tokens == null || tokens.length === 0) return ""; if (options != null) - return tokens.map((t) => renderValue(t, options)).join(""); - return tokens.map((t) => renderValue(t)).join(""); + return tokens.map((t) => printValue(t, options)).join(""); + return tokens.map((t) => printValue(t)).join(""); } const trimWhiteSpace = [ exports.EnumToken.CommentTokenType, @@ -31365,140 +33497,159 @@ imports: [], }; const invalidNodes = []; + const arena = options.arena; let ast = { typ: exports.EnumToken.StyleSheetNodeType, chi: [], }; let tokens = []; - let context = ast; - let item; + let context = arena.root; let node; // @ts-ignore ignore error let parensMatch = 0; let curlyBracketMatch = 0; + let tokenId; // let currentItemIndex: number; ast[LOCSRCID] = options.source.id; ast[LOCSTA] = 0; while (!tokenizer.done()) { tokenizer.next(); // item = (iter as Array)[currentItemIndex]; + tokenId = arena.allocate(); if (tokenizer.unit != null) { - item = { - typ: tokenizer.typ, - val: tokenizer.val, - unit: tokenizer.unit, - }; + arena.set(tokenId, tokenizer.typ, 0, tokenizer.val, arena.internString(tokenizer.unit)); + // item = { + // typ: tokenizer.typ as EnumToken, + // val: tokenizer.val, + // unit: tokenizer.unit, + // } as DimensionToken; } else if (tokenizer.nam != null) { - item = { - typ: tokenizer.typ, - nam: tokenizer.nam, - }; + arena.set(tokenId, tokenizer.typ); + arena.setName(tokenId, arena.internString(tokenizer.nam)); + // item = { + // typ: tokenizer.typ as EnumToken, + // nam: tokenizer.nam, + // } as Token; } else if (tokenizer.val === null) { - item = { - typ: tokenizer.typ, - }; + arena.set(tokenId, tokenizer.typ); + // item = { + // typ: tokenizer.typ as EnumToken, + // } as Token; } else if (tokenizer.kin != null) { - item = { - typ: tokenizer.typ, - val: tokenizer.val, - kin: tokenizer.kin, - }; + arena.set(tokenId, tokenizer.typ, arena.internString(tokenizer.val)); + arena.setColorType(tokenId, tokenizer.kin); + // item = { + // typ: tokenizer.typ as EnumToken, + // val: tokenizer.val, + // kin: tokenizer.kin, + // } as ColorToken; } else { - item = { - typ: tokenizer.typ, - val: tokenizer.val, - }; - } - item[LOCSRCID] = tokenizer.srcId; - item[LOCSTA] = tokenizer.sta; - item[LOCEND] = tokenizer.end; + arena.set(tokenId, tokenizer.typ, typeof tokenizer.val === "string" ? arena.internString(tokenizer.val) : 0, typeof tokenizer.val === "number" ? tokenizer.val : 0); + // item = { + // typ: tokenizer.typ as EnumToken, + // val: tokenizer.val, + // } as Token; + } + arena.setSpan(tokenId, tokenizer.srcId, tokenizer.sta, tokenizer.end); + // item[LOCSRCID] = tokenizer.srcId as number; + // item[LOCSTA] = tokenizer.sta as number; + // item[LOCEND] = tokenizer.end as number; stats.bytesIn = tokenizer.bytesIn; stats.tokensCount++; - if (BadTokensTypes.includes(item.typ)) { - tokens.push(item); + if (BadTokensTypes.includes(tokenizer.typ)) { + tokens.push(tokenId); errors.push({ action: "drop", message: "Bad token", syntax: null, - node: item, - location: options.source.getSourceLocation(item[LOCSTA]), + node: tokenId, + location: options.source.getSourceLocation(tokenizer.sta), }); // bad token continue; } - if (item.typ === exports.EnumToken.StartParensTokenType || tokensfuncDefMap.has(item.typ)) { + if (tokenizer.typ === exports.EnumToken.StartParensTokenType || tokensfuncDefMap.has(tokenizer.typ)) { parensMatch++; } - else if (item.typ === exports.EnumToken.EndParensTokenType && parensMatch > 0) { + else if (tokenizer.typ === exports.EnumToken.EndParensTokenType && parensMatch > 0) { parensMatch--; } - if (item.typ === exports.EnumToken.BlockStartTokenType) { + if (tokenizer.typ === exports.EnumToken.BlockStartTokenType) { curlyBracketMatch++; } - else if (item.typ === exports.EnumToken.BlockEndTokenType && curlyBracketMatch > 0) { + else if (tokenizer.typ === exports.EnumToken.BlockEndTokenType && curlyBracketMatch > 0) { curlyBracketMatch--; } - tokens.push(item); + tokens.push(tokenId); if (parensMatch === 0 && - (item.typ === exports.EnumToken.SemiColonTokenType || - item.typ === exports.EnumToken.BlockStartTokenType || - item.typ === exports.EnumToken.EOFTokenType)) { + (tokenizer.typ === exports.EnumToken.SemiColonTokenType || + tokenizer.typ === exports.EnumToken.BlockStartTokenType || + tokenizer.typ === exports.EnumToken.EOFTokenType)) { node = parseNode(tokens, context, options, errors, stats, invalidNodes); if (node != null) { - if ("chi" in node) { + if (arena.hasFlags(node, exports.EnumAstNodeStatus.HasChildren)) { stack.push(node); context = node; } } - else if (item.typ == exports.EnumToken.BlockStartTokenType) { + else if (tokenizer.typ == exports.EnumToken.BlockStartTokenType) { let inBlock = 1; tokens.length = 0; - tokens.push(item); + tokens.push(tokenId); do { tokenizer.next(); + tokenId = arena.allocate(); if (tokenizer.unit != null) { - item = { - typ: tokenizer.typ, - val: tokenizer.val, - unit: tokenizer.unit, - }; + arena.set(tokenId, tokenizer.typ, 0, tokenizer.val, arena.internString(tokenizer.unit)); + // item = { + // typ: tokenizer.typ as EnumToken, + // val: tokenizer.val, + // unit: tokenizer.unit, + // } as DimensionToken; } else if (tokenizer.nam != null) { - item = { - typ: tokenizer.typ, - nam: tokenizer.nam, - }; + arena.set(tokenId, tokenizer.typ); + arena.setName(tokenId, arena.internString(tokenizer.nam)); + // item = { + // typ: tokenizer.typ as EnumToken, + // nam: tokenizer.nam, + // } as Token; } else if (tokenizer.val === null) { - item = { - typ: tokenizer.typ, - }; + arena.set(tokenId, tokenizer.typ); + // item = { + // typ: tokenizer.typ as EnumToken, + // } as Token; } else if (tokenizer.kin != null) { - item = { - typ: tokenizer.typ, - val: tokenizer.val, - kin: tokenizer.kin, - }; + arena.set(tokenId, tokenizer.typ, arena.internString(tokenizer.val)); + arena.setColorType(tokenId, tokenizer.kin); + // item = { + // typ: tokenizer.typ as EnumToken, + // val: tokenizer.val, + // kin: tokenizer.kin, + // } as ColorToken; } else { - item = { - typ: tokenizer.typ, - val: tokenizer.val, - }; - } - item[LOCSRCID] = tokenizer.srcId; - item[LOCSTA] = tokenizer.sta; - item[LOCEND] = tokenizer.end; - tokens.push(item); - if (item.typ === exports.EnumToken.BlockStartTokenType) { + arena.set(tokenId, tokenizer.typ, arena.internString(tokenizer.val)); + // item = { + // typ: tokenizer.typ as EnumToken, + // val: tokenizer.val, + // } as Token; + } + arena.setSpan(tokenId, tokenizer.srcId, tokenizer.sta, tokenizer.end); + // item[LOCSRCID] = tokenizer.srcId as number; + // item[LOCSTA] = tokenizer.sta as number; + // item[LOCEND] = tokenizer.end as number; + tokens.push(tokenId); + if (tokenizer.typ === exports.EnumToken.BlockStartTokenType) { inBlock++; } - else if (item.typ === exports.EnumToken.BlockEndTokenType) { + else if (tokenizer.typ === exports.EnumToken.BlockEndTokenType) { inBlock--; } } while (inBlock != 0 && !tokenizer.done()); @@ -31506,22 +33657,22 @@ errors.push({ action: "drop", message: "invalid block", - location: options.source.getSourceLocation(tokens[0][LOCSTA]), + location: options.source.getSourceLocation(tokenizer.sta), }); } } tokens.length = 0; } - else if ((parensMatch === 0 || curlyBracketMatch === 0) && item.typ === exports.EnumToken.BlockEndTokenType) { + else if ((parensMatch === 0 || curlyBracketMatch === 0) && tokenizer.typ === exports.EnumToken.BlockEndTokenType) { parseNode(tokens, context, options, errors, stats, invalidNodes); - context[LOCEND] = item[LOCEND]; + // context[LOCEND] = tokenizer.end as number; const previousNode = stack.pop(); - context = (stack[stack.length - 1] ?? ast); + context = stack[stack.length - 1] ?? arena.root; if (options.removeEmpty && previousNode != null && - previousNode.chi.length == 0 && - context.chi[context.chi.length - 1] == previousNode) { - context.chi.pop(); + arena.getFirstChild(previousNode) == 0 && + arena.getLastChild(context) == previousNode) { + arena.detach(previousNode); } tokens.length = 0; parensMatch = 0; @@ -31532,7 +33683,7 @@ if (tokens.length > 0) { node = parseNode(tokens, context, options, errors, stats, invalidNodes); if (node != null) { - if ("chi" in node /* && node.typ != EnumToken.InvalidRuleNodeType */) { + if (arena.hasFlags(node, exports.EnumAstNodeStatus.HasChildren)) { stack.push(node); context = node; } @@ -31738,6 +33889,7 @@ stats.bytesIn += stats.importedBytesIn; let endTime = performance.now(); const result = { + arena, ast, errors, stats: { @@ -32176,7 +34328,7 @@ } node.sel = ""; for (const token of node[TOKENS]) { - node.sel += renderValue(token); + node.sel += printValue(token); } } else if (node.typ == exports.EnumToken.AtRuleNodeType || node.typ == exports.EnumToken.KeyframesAtRuleNodeType) { @@ -33282,7 +35434,7 @@ } node.sel = ""; for (const token of node[TOKENS]) { - node.sel += renderValue(token); + node.sel += printValue(token); } } else if (node.typ == exports.EnumToken.AtRuleNodeType || node.typ == exports.EnumToken.KeyframesAtRuleNodeType) { @@ -33341,104 +35493,128 @@ } function parseNode(tokens, context, options, errors, stats, invalidNodes) { let i = 0; - if (tokens.at(-1)?.typ === exports.EnumToken.EOFTokenType) { + const arena = options.arena; + const contextType = arena.getType(context); + let token; + let typ; + if (arena.getType(tokens.at(-1)) === exports.EnumToken.EOFTokenType) { tokens.pop(); // check parenthesis are balanced let matchCount = 0; for (let i = 0; i < tokens.length; i++) { - const token = tokens[i]; - if (token.typ === exports.EnumToken.StartParensTokenType || tokensfuncDefMap.has(token.typ)) { + token = tokens[i]; + typ = arena.getType(token); + if (typ === exports.EnumToken.StartParensTokenType || tokensfuncDefMap.has(typ)) { matchCount++; } - else if (token.typ === exports.EnumToken.EndParensTokenType) { + else if (typ === exports.EnumToken.EndParensTokenType) { matchCount--; } } if (matchCount > 0) { let k = tokens.length; - while (k-- > 0 && - (tokens[k].typ === exports.EnumToken.WhitespaceTokenType || tokens[k].typ === exports.EnumToken.CommentTokenType)) - ; - if (tokens[k]?.typ == exports.EnumToken.SemiColonTokenType) { - matchCount = 0; - } - while (matchCount > 0) { - tokens.push({ - typ: exports.EnumToken.EndParensTokenType, - [LOCSRCID]: tokens[k]?.[LOCSRCID], - [LOCSTA]: tokens[k]?.[LOCSTA], - [LOCEND]: tokens[k]?.[LOCEND], - }); - matchCount--; + if (k > 0) { + typ = arena.getType(tokens[k - 1]); + while ((k-- > 0 && typ === exports.EnumToken.WhitespaceTokenType) || typ === exports.EnumToken.CommentTokenType) { + typ = arena.getType(tokens[k - 1]); + } + if (typ == exports.EnumToken.SemiColonTokenType) { + matchCount = 0; + } + let tokenId; + let sourceId = arena.getSourceId(tokens[k]); + let spanStart = arena.getSpanStart(tokens[k]); + let spanEnd = arena.getSpanEnd(tokens[k]); + while (matchCount > 0) { + tokenId = arena.allocate(); + arena.set(tokenId, exports.EnumToken.EndParensTokenType); + arena.setSpan(tokenId, sourceId, spanStart, spanEnd); + // tokens.push({ + // typ: EnumToken.EndParensTokenType, + // [LOCSRCID]: tokens[k]?.[LOCSRCID], + // [LOCSTA]: tokens[k]?.[LOCSTA], + // [LOCEND]: tokens[k]?.[LOCEND], + // }); + matchCount--; + } } } } for (; i < tokens.length; i++) { - if (tokens[i].typ === exports.EnumToken.CDOCOMMTokenType && context.typ !== exports.EnumToken.StyleSheetNodeType) { + if (arena.getType(tokens[i]) === exports.EnumToken.CDOCOMMTokenType && contextType !== exports.EnumToken.StyleSheetNodeType) { errors.push({ action: "drop", message: `CDOCOMM not allowed here ${JSON.stringify(tokens[i], null, 1)}`, node: tokens[i], - location: options.source.getSourceLocation(tokens[i][LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(tokens[i])), }); - tokens[i].typ = exports.EnumToken.InvalidCommentTokenType; + arena.set(tokens[i], exports.EnumToken.InvalidCommentTokenType); continue; } - if (tokens[i].typ === exports.EnumToken.CommentTokenType || - tokens[i].typ === exports.EnumToken.CDOCOMMTokenType || - tokens[i].typ === exports.EnumToken.WhitespaceTokenType) { + typ = arena.getType(tokens[i]); + if (typ === exports.EnumToken.CommentTokenType || + typ === exports.EnumToken.CDOCOMMTokenType || + typ === exports.EnumToken.WhitespaceTokenType) { continue; } break; } if (i > 0) { - context.chi.push(...tokens.splice(0, i).filter((n) => n.typ !== exports.EnumToken.WhitespaceTokenType)); + arena.appendChildren(context, ...tokens.splice(0, i).filter((n) => arena.getType(n) !== exports.EnumToken.WhitespaceTokenType)); i = 0; } for (; i < tokens.length; i++) { - if (tokens[i].typ == exports.EnumToken.CommentTokenType || tokens[i].typ == exports.EnumToken.CDOCOMMTokenType) { - if (tokens[i].typ == exports.EnumToken.CDOCOMMTokenType && context.typ != exports.EnumToken.StyleSheetNodeType) { + typ = arena.getType(tokens[i]); + if (typ == exports.EnumToken.CommentTokenType || typ == exports.EnumToken.CDOCOMMTokenType) { + if (typ == exports.EnumToken.CDOCOMMTokenType && contextType != exports.EnumToken.StyleSheetNodeType) { errors.push({ action: "drop", message: `CDOCOMM not allowed here ${JSON.stringify(tokens[i], null, 1)}`, node: tokens[i], - location: options.source.getSourceLocation(tokens[i][LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(tokens[i])), }); - tokens[i].typ = exports.EnumToken.InvalidCommentTokenType; + arena.setType(tokens[i], exports.EnumToken.InvalidCommentTokenType); continue; } - tokens[i][ROOT] = context[ROOT]; - context.chi.push(tokens[i]); + // tokens[i][ROOT] = context[ROOT]; + arena.appendChildren(context, tokens[i]); + // context.chi!.push(tokens[i] as AstNode); stats.nodesCount++; } - else if (tokens[i].typ != exports.EnumToken.WhitespaceTokenType) { + else if (typ != exports.EnumToken.WhitespaceTokenType) { break; } } if (tokens.length == 0) { return null; } + // for (const token of tokens.slice(1)) { + // console.error(EnumToken[arena.getType(token)]); + // } let delim = tokens.at(-1); - if (delim.typ == exports.EnumToken.SemiColonTokenType || - delim.typ == exports.EnumToken.BlockStartTokenType || - delim.typ == exports.EnumToken.BlockEndTokenType) { + typ = arena.getType(delim); + if (typ == exports.EnumToken.SemiColonTokenType || + typ == exports.EnumToken.BlockStartTokenType || + typ == exports.EnumToken.BlockEndTokenType) { tokens.pop(); } if (tokens.length == 0) { return null; } - if (tokens[0]?.typ == exports.EnumToken.AtRuleTokenType) { + typ = arena.getType(tokens[0]); + if (typ == exports.EnumToken.AtRuleTokenType) { let nestedRule = false; let parent = context; let node; - while (parent != null) { - if (parent.typ == exports.EnumToken.RuleNodeType) { + while (parent != 0) { + typ = arena.getType(parent); + if (typ == exports.EnumToken.RuleNodeType) { nestedRule = true; break; } - parent = parent[PARENT]; + parent = arena.getParent(parent); } - node = parseAtRule(tokens, context, { ...options, nestedRule }, errors, delim.typ == exports.EnumToken.BlockStartTokenType); + node = parseAtRule(tokens, context, { ...options, nestedRule }, errors, arena.getType(delim) == exports.EnumToken.BlockStartTokenType); if (node == null) { return null; } @@ -33451,46 +35627,48 @@ invalidNodes.push(node); } stats.nodesCount++; - context.chi.push(node); - node[ROOT] = context[ROOT]; - node[PARENT] = context; + arena.appendChildren(context, node); + // context.chi!.push(node); + // arena.setParent(node, context); + // node[ROOT] = context[ROOT]; + // node[PARENT] = context; // @ts-ignore return node; } else { stats.nodesCount++; // rule - if (delim.typ == exports.EnumToken.BlockStartTokenType) { + if (arena.getType(delim) == exports.EnumToken.BlockStartTokenType) { const node = parseSelector(tokens, context, options, errors); - context.chi.push(node); - node[PARENT] = context; - node[ROOT] = context[ROOT]; - if (node[STATE] & exports.EnumAstNodeStatus.Invalid || - node[STATE] & exports.EnumAstNodeStatus.Disallowed || - node[STATE] & exports.EnumAstNodeStatus.Unknown || - node[STATE] & exports.EnumAstNodeStatus.Unparsed || - node[STATE] & exports.EnumAstNodeStatus.Malformed) { + arena.appendChildren(context, node); + // context.chi!.push(node); + // node[PARENT] = context; + // node[ROOT] = context[ROOT]; + if (arena.hasFlags(node, exports.EnumAstNodeStatus.Invalid | + exports.EnumAstNodeStatus.Disallowed | + exports.EnumAstNodeStatus.Unknown | + exports.EnumAstNodeStatus.Unparsed | + exports.EnumAstNodeStatus.Malformed)) { invalidNodes.push(node); } return node; } else { const node = parseDeclaration(tokens, context, options, errors); - node[PARENT] = context; - node[ROOT] = context[ROOT]; - if (context.typ === exports.EnumToken.StyleSheetNodeType && node.typ === exports.EnumToken.DeclarationNodeType) { - node[STATE] |= - exports.EnumAstNodeStatus.Invalid & - ~(exports.EnumAstNodeStatus.Validated | exports.EnumAstNodeStatus.Unvalidated | exports.EnumAstNodeStatus.Unparsed); + // node[PARENT] = context; + // node[ROOT] = context[ROOT]; + if (contextType === exports.EnumToken.StyleSheetNodeType && node.typ === exports.EnumToken.DeclarationNodeType) { + node[STATE] = exports.EnumAstNodeStatus.Invalid; errors.push({ message: " not allowed in ", action: "drop", node, - location: options.source.getSourceLocation(node[LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(node)), }); } else if (options.lenient || node.typ === exports.EnumToken.DeclarationNodeType) { - context.chi.push(node); + // context.chi!.push(node); + arena.appendChildren(context, node); } if (node[STATE] & (exports.EnumAstNodeStatus.Invalid | @@ -33825,7 +36003,7 @@ stream[index + 1]?.typ === exports.EnumToken.CommentTokenType && (stream.length < index + 3 || stream[index + 2]?.typ === exports.EnumToken.WhitespaceTokenType)) ? "" - : renderValue(t, options)), ""), + : printValue(t, options)), ""), ...(parseAsBlock ? { chi: [] } : {}), }); } @@ -33855,7 +36033,7 @@ stream[index + 1]?.typ === exports.EnumToken.CommentTokenType && (stream.length < index + 3 || stream[index + 2]?.typ === exports.EnumToken.WhitespaceTokenType)) ? "" - : renderValue(t, options)), ""), + : printValue(t, options)), ""), }); } case "supports": @@ -34571,148 +36749,6 @@ return tokens; } - /** - * response type - */ - exports.ResponseType = void 0; - (function (ResponseType) { - /** - * return text - */ - ResponseType[ResponseType["Text"] = 0] = "Text"; - /** - * return a readable stream - */ - ResponseType[ResponseType["ReadableStream"] = 1] = "ReadableStream"; - /** - * return an arraybuffer - */ - ResponseType[ResponseType["ArrayBuffer"] = 2] = "ArrayBuffer"; - /** - * return a json object - */ - ResponseType[ResponseType["JSON"] = 3] = "JSON"; - })(exports.ResponseType || (exports.ResponseType = {})); - - /** - * parse result. process input sourcemap - * @param result - * @param options - * @returns - * @private - */ - function parseResult(result, options) { - if (options.sourcemap != null && options.source.getInputSourceMap() == null) { - if (options.inputSourceMap != null) { - options.source.setInputSourceMap(options.inputSourceMap); - } - else { - // extract inline source map from the input CSS - const token = result.ast.chi.at(-1); - if (token?.typ == exports.EnumToken.CommentTokenType && - token.val.startsWith("/*# sourceMappingURL=")) { - let data = token.val.slice(21, -2).trim(); - if (data.endsWith(".map")) { - if (options.load == null) { - data = ""; - } - else { - options - .load(options.resolve(data, dirname(options.src)).absolute, ".", exports.ResponseType.JSON) - .catch((error) => console.error({ error })) - .then((res) => { - if (res != null) { - // @ts-expect-error - options.source.setInputSourceMap(res); - } - }); - } - } - else { - options.source.setInputSourceMap(data); - } - } - } - } - if (options.module) { - const { revMapping, ...res } = result; - return res; - } - return result; - } - /** - * - * @param options - * @param prefix - * @private - */ - function validateSyncArguments(options, prefix = "options.") { - const args = Object.entries(options); - let i; - for (i = 0; i < args.length; i++) { - const [key, value] = args[i]; - if (typeof value == "function") { - if (value instanceof Promise || Object.getPrototypeOf(value).constructor.name == "AsyncFunction") { - throw new Error(`[${prefix + key}]: Async functions are not supported in sync mode. Use parse() or transform() instead.`); - } - } - else if (value != null && typeof value == "object") { - validateSyncArguments(value, prefix + key + "."); - } - } - } - - /** - * - * @param node - * @param key - * @returns - */ - function getNodeProperty(node, key) { - switch (key) { - case "parent": - return node[PARENT]; - case "location": - return node[LOCSRCID] == null && node[LOCSTA] == null && node[LOCEND] == null ? null : { srcId: node[LOCSRCID], sta: node[LOCSTA], end: node[LOCEND] }; - case "state": - return node[STATE]; - case "errors": - return node[ERRORS]; - case "tokens": - return node[TOKENS]; - } - return undefined; - } - /** - * - * @param node - * @param key - * @param value - */ - function setNodeProperty(node, key, value) { - switch (key) { - case "parent": - node[PARENT] = value; - break; - case "location": - node[LOCSRCID] = value.srcId; - node[LOCSTA] = value.sta; - node[LOCEND] = value.end; - break; - case "state": - node[STATE] = value; - break; - case "errors": - node[ERRORS] = value; - break; - case "tokens": - node[TOKENS] = value; - break; - } - } - - var version = "1.6.7"; - /** * Load file or url * @param url @@ -34856,6 +36892,7 @@ options ??= {}; options.src ??= ""; options.sourcesMap ??= new Map(); + options.arena ??= new Arena(2 << Math.ceil(2 + Math.log2(stream.length))); Object.assign(options, { resolve, dirname, @@ -34864,6 +36901,7 @@ : dirname(self.location.pathname), }); options.src = resolve(options.src, options.cwd).relative; + options.arena ??= new Arena(typeof stream == "string" ? 2 << Math.ceil(2 + Math.log2(stream.length)) : 4096); if (options.source == null) { const source = new SourceFile(typeof stream == "string" ? stream : "", [], options.src); options.sourcesMap.set(source.id, source); @@ -35118,7 +37156,6 @@ }); } - exports.PropertyList = PropertyList; exports.SourceMap = SourceMap; exports.cloneNode = cloneNode; exports.convertColor = convertColor; @@ -35138,8 +37175,8 @@ exports.parseFile = parseFile; exports.parseString = parseString; exports.parseSync = parseSync; + exports.printValue = printValue$1; exports.render = render; - exports.renderToken = renderValue; exports.replaceNodeOrValue = replaceNodeOrValue; exports.resolve = resolve; exports.setNodeProperty = setNodeProperty; diff --git a/dist/index.cjs b/dist/index.cjs index 5759b16a8..659c5b9ce 100644 --- a/dist/index.cjs +++ b/dist/index.cjs @@ -57,49 +57,26 @@ exports.EnumAstNodeStatus = void 0; */ EnumAstNodeStatus[EnumAstNodeStatus["Malformed"] = 128] = "Malformed"; /** - * Important declaration - */ - EnumAstNodeStatus[EnumAstNodeStatus["ImportantDeclaration"] = 256] = "ImportantDeclaration"; - /** - * Color component without unit - */ - EnumAstNodeStatus[EnumAstNodeStatus["PowerlessColorComponent"] = 512] = "PowerlessColorComponent"; -})(exports.EnumAstNodeStatus || (exports.EnumAstNodeStatus = {})); -/** - * Enum of validation levels - * @deprecated - */ -exports.ValidationLevel = void 0; -(function (ValidationLevel) { - /** - * disable validation - */ - ValidationLevel[ValidationLevel["None"] = 0] = "None"; - /** - * validate selectors + * Node has children */ - ValidationLevel[ValidationLevel["Selector"] = 1] = "Selector"; + EnumAstNodeStatus[EnumAstNodeStatus["HasChildren"] = 256] = "HasChildren"; /** - * validate at-rules + * Node has values */ - ValidationLevel[ValidationLevel["AtRule"] = 2] = "AtRule"; + EnumAstNodeStatus[EnumAstNodeStatus["HasValues"] = 512] = "HasValues"; /** - * validate declarations + * Node has fraction value */ - ValidationLevel[ValidationLevel["Declaration"] = 4] = "Declaration"; + EnumAstNodeStatus[EnumAstNodeStatus["HasFractionValue"] = 1024] = "HasFractionValue"; /** - * validate selectors and at-rules - */ - ValidationLevel[ValidationLevel["Default"] = 3] = "Default"; - /** - * validate selectors, at-rules and declarations + * Important declaration */ - ValidationLevel[ValidationLevel["All"] = 7] = "All"; + EnumAstNodeStatus[EnumAstNodeStatus["ImportantDeclaration"] = 2048] = "ImportantDeclaration"; /** - * Report only. Apply validation and report nodes that are marked as invalid + * Color component without unit */ - ValidationLevel[ValidationLevel["ReportOnly"] = 8] = "ReportOnly"; -})(exports.ValidationLevel || (exports.ValidationLevel = {})); + EnumAstNodeStatus[EnumAstNodeStatus["PowerlessColorComponent"] = 4096] = "PowerlessColorComponent"; +})(exports.EnumAstNodeStatus || (exports.EnumAstNodeStatus = {})); /** * Enum of all token types */ @@ -344,7 +321,7 @@ exports.EnumToken = void 0; /** * token list token type */ - EnumToken[EnumToken["ListToken"] = 59] = "ListToken"; + EnumToken[EnumToken["ListTokenType"] = 59] = "ListTokenType"; /* arithmetic tokens */ /** * addition token type @@ -807,135 +784,135 @@ exports.ColorType = void 0; /** * deprecated system colors */ - ColorType[ColorType["SYS"] = 0] = "SYS"; + ColorType[ColorType["SYS"] = 1] = "SYS"; /** * deprecated system colors */ - ColorType[ColorType["DPSYS"] = 1] = "DPSYS"; + ColorType[ColorType["DPSYS"] = 2] = "DPSYS"; /** * named colors */ - ColorType[ColorType["LIT"] = 2] = "LIT"; + ColorType[ColorType["LIT"] = 3] = "LIT"; /** * colors as hex values */ - ColorType[ColorType["HEX"] = 3] = "HEX"; + ColorType[ColorType["HEX"] = 4] = "HEX"; /** * colors as rgb values */ - ColorType[ColorType["RGBA"] = 4] = "RGBA"; + ColorType[ColorType["RGBA"] = 5] = "RGBA"; /** * colors using rgb */ - ColorType[ColorType["HSLA"] = 5] = "HSLA"; + ColorType[ColorType["HSLA"] = 6] = "HSLA"; /** * colors using hwb */ - ColorType[ColorType["HWB"] = 6] = "HWB"; + ColorType[ColorType["HWB"] = 7] = "HWB"; /** * colors using cmyk */ - ColorType[ColorType["CMYK"] = 7] = "CMYK"; + ColorType[ColorType["CMYK"] = 8] = "CMYK"; /** * colors using oklab * */ - ColorType[ColorType["OKLAB"] = 8] = "OKLAB"; + ColorType[ColorType["OKLAB"] = 9] = "OKLAB"; /** * colors using oklch * */ - ColorType[ColorType["OKLCH"] = 9] = "OKLCH"; + ColorType[ColorType["OKLCH"] = 10] = "OKLCH"; /** * colors using lab */ - ColorType[ColorType["LAB"] = 10] = "LAB"; + ColorType[ColorType["LAB"] = 11] = "LAB"; /** * colors using lch */ - ColorType[ColorType["LCH"] = 11] = "LCH"; + ColorType[ColorType["LCH"] = 12] = "LCH"; /** * colors using color() function */ - ColorType[ColorType["COLOR"] = 12] = "COLOR"; + ColorType[ColorType["COLOR"] = 13] = "COLOR"; /** * color using srgb */ - ColorType[ColorType["SRGB"] = 13] = "SRGB"; + ColorType[ColorType["SRGB"] = 14] = "SRGB"; /** * color using prophoto-rgb */ - ColorType[ColorType["PROPHOTO_RGB"] = 14] = "PROPHOTO_RGB"; + ColorType[ColorType["PROPHOTO_RGB"] = 15] = "PROPHOTO_RGB"; /** * color using a98-rgb */ - ColorType[ColorType["A98_RGB"] = 15] = "A98_RGB"; + ColorType[ColorType["A98_RGB"] = 16] = "A98_RGB"; /** * color using rec2020 */ - ColorType[ColorType["REC2020"] = 16] = "REC2020"; + ColorType[ColorType["REC2020"] = 17] = "REC2020"; /** * color using display-p3 */ - ColorType[ColorType["DISPLAY_P3"] = 17] = "DISPLAY_P3"; + ColorType[ColorType["DISPLAY_P3"] = 18] = "DISPLAY_P3"; /** * color using srgb-linear */ - ColorType[ColorType["SRGB_LINEAR"] = 18] = "SRGB_LINEAR"; + ColorType[ColorType["SRGB_LINEAR"] = 19] = "SRGB_LINEAR"; /** * color using xyz-d50 */ - ColorType[ColorType["XYZ_D50"] = 19] = "XYZ_D50"; + ColorType[ColorType["XYZ_D50"] = 20] = "XYZ_D50"; /** * color using xyz-d65 */ - ColorType[ColorType["XYZ_D65"] = 20] = "XYZ_D65"; + ColorType[ColorType["XYZ_D65"] = 21] = "XYZ_D65"; /** * light-dark() color function */ - ColorType[ColorType["LIGHT_DARK"] = 21] = "LIGHT_DARK"; + ColorType[ColorType["LIGHT_DARK"] = 22] = "LIGHT_DARK"; /** * color-mix() color function */ - ColorType[ColorType["COLOR_MIX"] = 22] = "COLOR_MIX"; + ColorType[ColorType["COLOR_MIX"] = 23] = "COLOR_MIX"; /** * non-standard color */ - ColorType[ColorType["NON_STD"] = 23] = "NON_STD"; + ColorType[ColorType["NON_STD"] = 24] = "NON_STD"; /** * custom color */ - ColorType[ColorType["CUSTOM_COLOR"] = 24] = "CUSTOM_COLOR"; + ColorType[ColorType["CUSTOM_COLOR"] = 25] = "CUSTOM_COLOR"; /** * alpha() color function */ - ColorType[ColorType["ALPHA"] = 25] = "ALPHA"; + ColorType[ColorType["ALPHA"] = 26] = "ALPHA"; /** * color using display-p3-linear */ - ColorType[ColorType["DISPLAY_P3_LINEAR"] = 26] = "DISPLAY_P3_LINEAR"; + ColorType[ColorType["DISPLAY_P3_LINEAR"] = 27] = "DISPLAY_P3_LINEAR"; /** * Contrast color */ - ColorType[ColorType["CONTRAST_COLOR"] = 27] = "CONTRAST_COLOR"; + ColorType[ColorType["CONTRAST_COLOR"] = 28] = "CONTRAST_COLOR"; /** * color layers */ - ColorType[ColorType["COLOR_LAYERS"] = 28] = "COLOR_LAYERS"; + ColorType[ColorType["COLOR_LAYERS"] = 29] = "COLOR_LAYERS"; /** * alias for rgba */ - ColorType[ColorType["RGB"] = 4] = "RGB"; + ColorType[ColorType["RGB"] = 5] = "RGB"; /** * alias for hsl */ - ColorType[ColorType["HSL"] = 5] = "HSL"; + ColorType[ColorType["HSL"] = 6] = "HSL"; /** * alias for xyz-d65 */ - ColorType[ColorType["XYZ"] = 20] = "XYZ"; + ColorType[ColorType["XYZ"] = 21] = "XYZ"; /** * alias for cmyk */ - ColorType[ColorType["DEVICE_CMYK"] = 7] = "DEVICE_CMYK"; + ColorType[ColorType["DEVICE_CMYK"] = 8] = "DEVICE_CMYK"; })(exports.ColorType || (exports.ColorType = {})); /** * Supported module case transform @@ -6389,7 +6366,6 @@ const LOCEND = Symbol.for("locEnd"); const LOC = Symbol.for("loc"); const RAW = Symbol.for("raw"); const STATE = Symbol.for("state"); -const ROOT = Symbol.for("root"); const ERRORS = Symbol.for("errors"); const TOKENS = Symbol.for("tokens"); const PARENT = Symbol.for("parent"); @@ -9907,7 +9883,6 @@ function* walkValues(values, root = null, filter, reverse) { let isNumeric = false; let value; let option; - let node; let i = -1; const eventType = filter.event ?? exports.WalkerEvent.Enter; // @ts-ignore @@ -9941,16 +9916,7 @@ function* walkValues(values, root = null, filter, reverse) { (typeof filter.type == "function" && filter.type(value)); if (isValid) { // @ts-expect-error - option = filter.fn(value, value[PARENT] ?? root, exports.WalkerEvent.Enter, - // @ts-expect-error - function* () { - // @ts-expect-error - let parent = node[PARENT]; - while (parent != null) { - yield parent; - parent = parent[PARENT] ?? null; - } - }); + option = filter.fn(value); isNumeric = typeof option == "number"; if (isNumeric && option & exports.WalkerOptionEnum.Stop) { return; @@ -10253,7 +10219,7 @@ function evaluate(tokens) { if (token.typ == exports.EnumToken.Sub) { if (!isScalarToken(nodes[i + 1])) { token = { - typ: exports.EnumToken.ListToken, + typ: exports.EnumToken.ListTokenType, chi: [nodes[i], nodes[i + 1]], [LOCSRCID]: nodes[i][LOCSRCID], [LOCSTA]: nodes[i][LOCSTA], @@ -10301,7 +10267,7 @@ function evaluate(tokens) { return acc; } } - if (acc.length > 0 && curr[0] != exports.EnumToken.ListToken) { + if (acc.length > 0 && curr[0] != exports.EnumToken.ListTokenType) { acc.push({ typ: exports.EnumToken.Add, [LOCSRCID]: token[LOCSRCID], @@ -10992,7 +10958,7 @@ function factor(tokens, ops) { return [factorToken(tokens[0])]; } for (let i = 0; i < tokens.length; i++) { - if (tokens[i].typ == exports.EnumToken.ListToken) { + if (tokens[i].typ == exports.EnumToken.ListTokenType) { // @ts-ignore tokens.splice(i, 1, ...tokens[i].chi); } @@ -12396,11 +12362,12 @@ funcTypes.push(exports.EnumToken.FunctionTokenType, exports.EnumToken.PseudoClas * @param tokens * @returns */ -function trimArray(tokens) { - while (tokens[0]?.typ === exports.EnumToken.WhitespaceTokenType) { +function trimArray(tokens, options) { + const arena = options.arena; + while (arena.getType(tokens[0]) === exports.EnumToken.WhitespaceTokenType) { tokens.shift(); } - while (tokens[tokens.length - 1]?.typ === exports.EnumToken.WhitespaceTokenType) { + while (arena.getType(tokens[tokens.length - 1]) === exports.EnumToken.WhitespaceTokenType) { tokens.pop(); } return tokens; @@ -12488,8 +12455,7 @@ function isMFValue(featureName, tokens, isMFRange) { case MediaFeatureType.RatioType: return { valid: true, - success: (tokens.length == 1 && - tokens[0].typ == exports.EnumToken.NumberTokenType) || + success: (tokens.length == 1 && tokens[0].typ == exports.EnumToken.NumberTokenType) || (tokens.length === 3 && tokens[0].typ == exports.EnumToken.NumberTokenType && typeof tokens[0].val === "number" && @@ -12511,9 +12477,10 @@ function isMFValue(featureName, tokens, isMFRange) { * @param tokens * @returns */ -function createValidationContext(tokens) { - tokens = trimArray(tokens.filter((t) => t.typ !== exports.EnumToken.CommentTokenType)); - if (tokens.at(-1)?.typ === exports.EnumToken.ImportantTokenType) { +function createValidationContext(tokens, options) { + const arena = options.arena; + tokens = trimArray(tokens.filter((t) => arena.getType(t) !== exports.EnumToken.CommentTokenType)); + if (arena.getType(tokens.at(-1)) === exports.EnumToken.ImportantTokenType) { tokens.pop(); trimArray(tokens); } @@ -12668,15 +12635,22 @@ function createValidationContext(tokens) { function matchSelectorSyntax(stream, errors, options, nested = true) { const stack = []; const tokens = []; + const arena = options.arena; let token; let i = 0; let success = true; - while (i < stream.length && - (stream[i].typ === exports.EnumToken.WhitespaceTokenType || stream[i].typ === exports.EnumToken.CommentTokenType)) { + let typ; + let val; + while (i < stream.length) { + typ = arena.getType(stream[i]); + if (typ !== exports.EnumToken.WhitespaceTokenType && typ !== exports.EnumToken.CommentTokenType) { + break; + } i++; } if (i < stream.length) { - switch (stream[i].typ) { + typ = arena.getType(stream[i]); + switch (typ) { case exports.EnumToken.Plus: case exports.EnumToken.Tilda: case exports.EnumToken.GtTokenType: @@ -12690,10 +12664,10 @@ function matchSelectorSyntax(stream, errors, options, nested = true) { errors: [ { action: "drop", - message: `Unexpected token ${exports.EnumToken[stream[i].typ]}`, + message: `Unexpected token ${exports.EnumToken[typ]}`, node: stream[i], // @ts-expect-error - location: options.source.getSourceLocation(stream[i][LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(stream[i])), }, ], }; @@ -12702,29 +12676,31 @@ function matchSelectorSyntax(stream, errors, options, nested = true) { } for (; i < stream.length; i++) { token = stream[i]; - if (token.typ === exports.EnumToken.Star) { - token.typ = exports.EnumToken.UniversalSelectorTokenType; + typ = arena.getType(token); + if (typ === exports.EnumToken.Star) { + typ = exports.EnumToken.UniversalSelectorTokenType; } tokens.push(token); - if (tokensfuncDefMap.has(token.typ)) { - if (stack.length > 0 && selectorNodeTypes.includes(stack.at(-1).typ)) { + if (tokensfuncDefMap.has(typ)) { + if (stack.length > 0 && selectorNodeTypes.includes(arena.getType(stack.at(-1)))) { stack.pop(); } stack.push(token); continue; } if (stack.length > 0 && - selectorNodeTypes.includes(stack.at(-1).typ) && - !selectorNodeTypes.includes(token.typ) && - token.typ !== exports.EnumToken.WhitespaceTokenType && - token.typ !== exports.EnumToken.CommentTokenType && - token.typ !== exports.EnumToken.CDOCOMMTokenType) { + selectorNodeTypes.includes(arena.getType(stack.at(-1))) && + !selectorNodeTypes.includes(typ) && + typ !== exports.EnumToken.WhitespaceTokenType && + typ !== exports.EnumToken.CommentTokenType && + typ !== exports.EnumToken.CDOCOMMTokenType) { stack.pop(); } - switch (token.typ) { + switch (typ) { case exports.EnumToken.PseudoClassFuncTokenType: { - const result = matchAllSyntaxes(getParsedSyntax(ValidationSyntaxGroupEnum.Selectors, token.val + "()")?.[0]?.chi ?? [], createValidationContext(token.chi), options); + val = arena.getValue(token); + const result = matchAllSyntaxes(getParsedSyntax(ValidationSyntaxGroupEnum.Selectors, val + "()")?.[0]?.chi ?? [], createValidationContext(arena.children(token), options), options); if (!result.success) { success = false; if (result.errors.length > 0) { @@ -12749,41 +12725,43 @@ function matchSelectorSyntax(stream, errors, options, nested = true) { message: `Nesting selector is not allowed`, node: token, // @ts-expect-error - location: options.source.getSourceLocation(token[LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(token)), }, ], }; } break; case exports.EnumToken.Plus: - Object.assign(token, { typ: exports.EnumToken.NextSiblingCombinatorTokenType }); + arena.set(token, exports.EnumToken.SubsequentSiblingCombinatorTokenType, 0); + // Object.assign(token, { typ: EnumToken.NextSiblingCombinatorTokenType }); stack.push(token); break; case exports.EnumToken.Tilda: case exports.EnumToken.GtTokenType: - Object.assign(token, { typ: selectorEnumMap.get(token.typ) }); + arena.set(token, selectorEnumMap.get(typ)); + // Object.assign(token, { typ: selectorEnumMap.get(token.typ)! }); case exports.EnumToken.ColumnCombinatorTokenType: case exports.EnumToken.ChildCombinatorTokenType: case exports.EnumToken.UniversalSelectorTokenType: case exports.EnumToken.DescendantCombinatorTokenType: case exports.EnumToken.NextSiblingCombinatorTokenType: case exports.EnumToken.SubsequentSiblingCombinatorTokenType: - if (stream[i + 1]?.typ === exports.EnumToken.WhitespaceTokenType) { + if (arena.getType(stream[i + 1]) === exports.EnumToken.WhitespaceTokenType) { i++; } - if (stack.length > 0 && stack.at(-1)?.typ === exports.EnumToken.UniversalSelectorTokenType) { + if (stack.length > 0 && arena.getType(stack.at(-1)) === exports.EnumToken.UniversalSelectorTokenType) { stack.pop(); } - if (stack.length > 0 && selectorNodeTypes.includes(stack.at(-1)?.typ)) { + if (stack.length > 0 && selectorNodeTypes.includes(arena.getType(stack.at(-1)))) { return { success: false, errors: [ { action: "drop", - message: `Unexpected combinator ${exports.EnumToken[token.typ]}`, + message: `Unexpected combinator ${exports.EnumToken[typ]}`, node: token, // @ts-expect-error - location: options.source.getSourceLocation(token[LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(token)), }, ], }; @@ -12791,10 +12769,20 @@ function matchSelectorSyntax(stream, errors, options, nested = true) { stack.push(token); break; case exports.EnumToken.CommaTokenType: - if (stack.length > 0 && stack.at(-1).typ === exports.EnumToken.UniversalSelectorTokenType) { + if (stack.length > 0 && arena.getType(stack.at(-1)) === exports.EnumToken.UniversalSelectorTokenType) { stack.pop(); } stack.push(token); + if (!nested) { + let k = i + 1; + while (k < stream.length) { + typ = arena.getType(stream[k]); + if (typ !== exports.EnumToken.WhitespaceTokenType && typ !== exports.EnumToken.CommentTokenType) { + break; + } + k++; + } + } break; case exports.EnumToken.Pipe: case exports.EnumToken.DelimTokenType: @@ -12815,19 +12803,19 @@ function matchSelectorSyntax(stream, errors, options, nested = true) { stack.push(token); break; case exports.EnumToken.AttrEndTokenType: - if (stack.length > 0 && stack.at(-1).typ === exports.EnumToken.UniversalSelectorTokenType) { + if (stack.length > 0 && arena.getType(stack.at(-1)) === exports.EnumToken.UniversalSelectorTokenType) { stack.pop(); } - if (stack.at(-1)?.typ !== exports.EnumToken.AttrStartTokenType) { + if (arena.getType(stack.at(-1)) !== exports.EnumToken.AttrStartTokenType) { return { success: false, errors: [ { action: "drop", - message: `Unexpected token ${exports.EnumToken[token.typ]}`, + message: `Unexpected token ${exports.EnumToken[typ]}`, node: token, // @ts-expect-error - location: options.source.getSourceLocation(token[LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(token)), }, ], }; @@ -12837,17 +12825,18 @@ function matchSelectorSyntax(stream, errors, options, nested = true) { let index = tokens.indexOf(stack.at(-1)); const slice = []; for (let n = index + 1; n < k; n++) { - if (tokens[n].typ === exports.EnumToken.WhitespaceTokenType || - tokens[n].typ === exports.EnumToken.CommentTokenType || - tokens[n].typ === exports.EnumToken.CDOCOMMTokenType) { + typ = arena.getType(tokens[n]); + if (typ === exports.EnumToken.WhitespaceTokenType || + typ === exports.EnumToken.CommentTokenType || + typ === exports.EnumToken.CDOCOMMTokenType) { continue; } slice.push(tokens[n]); } - if (slice[1]?.typ === exports.EnumToken.Pipe) { + if (arena.getType(slice[1]) === exports.EnumToken.Pipe) { slice.shift(); } - if (slice[0].typ === exports.EnumToken.Pipe) { + if (arena.getType(slice[0]) === exports.EnumToken.Pipe) { slice.shift(); } if (slice.length === 1) { @@ -12856,29 +12845,29 @@ function matchSelectorSyntax(stream, errors, options, nested = true) { } slice.shift(); slice.shift(); - if (slice[0]?.typ === exports.EnumToken.StringTokenType && - /^[a-zA-Z0-9_-]+$/.test(slice[0].val.slice(1, -1))) { - Object.assign(slice[0], { - typ: exports.EnumToken.IdenTokenType, - val: slice[0].val.slice(1, -1), - }); + if (arena.getType(slice[0]) === exports.EnumToken.StringTokenType) { + val = arena.getValue(slice[0]); + if (/^[a-zA-Z0-9_-]+$/.test(val.slice(1, -1))) { + arena.set(slice[0], exports.EnumToken.IdenTokenType, val.slice(1, -1)); + } } slice.shift(); if (slice.length === 0) { stack.pop(); break; } - if (slice[0].typ !== exports.EnumToken.IdenTokenType || - (slice[0].val != "i" && slice[0].val != "s")) { + val = arena.getValue(slice[0]); + if (arena.getType(slice[0]) !== exports.EnumToken.IdenTokenType || + (val != "i" && val != "s")) { return { success: false, errors: [ { action: "drop", - message: `Unexpected token ${exports.EnumToken[slice[0].typ]}`, + message: `Unexpected token ${exports.EnumToken[arena.getType(slice[0])]}`, node: slice[0], // @ts-expect-error - location: options.source.getSourceLocation(slice[0][LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(slice[0])), }, ], }; @@ -12893,19 +12882,19 @@ function matchSelectorSyntax(stream, errors, options, nested = true) { errors: [ { action: "drop", - message: `Unexpected token ${exports.EnumToken[token.typ]}`, + message: `Unexpected token ${exports.EnumToken[typ]}`, node: token, // @ts-expect-error - location: options.source.getSourceLocation(token[LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(token)), }, ], }; case exports.EnumToken.EndParensTokenType: - if (stack.length > 0 && stack.at(-1)?.typ === exports.EnumToken.UniversalSelectorTokenType) { + if (stack.length > 0 && arena.getType(stack.at(-1)) === exports.EnumToken.UniversalSelectorTokenType) { stack.pop(); } - if (stack.at(-1)?.typ === exports.EnumToken.PseudoClassFunctionTokenDefType || - stack.at(-1)?.typ === exports.EnumToken.PseudoElementTokenType) { + if (arena.getType(stack.at(-1)) === exports.EnumToken.PseudoClassFunctionTokenDefType || + arena.getType(stack.at(-1)) === exports.EnumToken.PseudoElementTokenType) { const token = stack.at(-1); const index = tokens.indexOf(token); const result = matchAllSyntaxes(getParsedSyntax(ValidationSyntaxGroupEnum.Selectors, token.val + "()")?.[0]?.chi ?? [], createValidationContext(tokens.slice(index + 1, tokens.length - 1)), options); @@ -12925,10 +12914,10 @@ function matchSelectorSyntax(stream, errors, options, nested = true) { errors: [ { action: "drop", - message: `Unexpected token ${exports.EnumToken[token.typ]}`, + message: `Unexpected token ${exports.EnumToken[typ]}`, node: token, // @ts-expect-error - location: options.source.getSourceLocation(token[LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(token)), }, ], }; @@ -12938,7 +12927,7 @@ function matchSelectorSyntax(stream, errors, options, nested = true) { // if (stack.at(-1)?.typ === EnumToken.CommaTokenType) { // stack.pop(); // } - if (stack.at(-1)?.typ === exports.EnumToken.PseudoClassFunctionTokenDefType) { + if (arena.getType(stack.at(-1)) === exports.EnumToken.PseudoClassFunctionTokenDefType) { break; } default: @@ -12947,10 +12936,10 @@ function matchSelectorSyntax(stream, errors, options, nested = true) { errors: [ { action: "drop", - message: `Unsupported selector token ${exports.EnumToken[token.typ]}`, + message: `Unsupported selector token ${exports.EnumToken[typ]}`, node: token, // @ts-expect-error - location: options.source.getSourceLocation(token[LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(token)), }, ], }; @@ -12964,7 +12953,7 @@ function matchSelectorSyntax(stream, errors, options, nested = true) { tokens.splice(tokens.length - 2, 1); } } - if (stack.length > 0 && stack.at(-1).typ === exports.EnumToken.UniversalSelectorTokenType) { + if (stack.length > 0 && arena.getType(stack.at(-1)) === exports.EnumToken.UniversalSelectorTokenType) { stack.pop(); } if (stack.length > 0) { @@ -12973,10 +12962,10 @@ function matchSelectorSyntax(stream, errors, options, nested = true) { errors: [ { action: "drop", - message: `Unmatched token ${exports.EnumToken[stack.at(-1).typ]}`, + message: `Unmatched token ${exports.EnumToken[arena.getType(stack.at(-1))]}`, node: stack.at(-1), // @ts-expect-error - location: options.source.getSourceLocation(stack.at(-1)[LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(stack.at(-1))), }, ], }; @@ -14057,17 +14046,15 @@ function matchProperty(property, context, options) { case "display-internal": case "display-outside": case "display-legacy": - case "content-position": - { - let values = propertyKeywordValues.get(property.val); - if (values == null) { - values = new Set(config$3.syntaxes[property.val].syntax.split(/[\s|]+/)); - propertyKeywordValues.set(property.val, values); - } - success = - token?.typ == exports.EnumToken.IdenTokenType && values.has(token.val.toLowerCase()); - break; + case "content-position": { + let values = propertyKeywordValues.get(property.val); + if (values == null) { + values = new Set(config$3.syntaxes[property.val].syntax.split(/[\s|]+/)); + propertyKeywordValues.set(property.val, values); } + success = token?.typ == exports.EnumToken.IdenTokenType && values.has(token.val.toLowerCase()); + break; + } case "mf-name": { const token = context.peek(); @@ -14087,8 +14074,7 @@ function matchProperty(property, context, options) { case "custom-ident": case "counter-name": case "counter-style-name": - success = - token?.typ == exports.EnumToken.IdenTokenType || token?.typ == exports.EnumToken.DashedIdenTokenType; + success = token?.typ == exports.EnumToken.IdenTokenType || token?.typ == exports.EnumToken.DashedIdenTokenType; if (success && token?.typ === exports.EnumToken.IdenTokenType) { const val = token.val.toLowerCase(); success = "none" !== val && !allValues.includes(val); @@ -15315,29 +15301,28 @@ function getNumber(token) { * convert angle to turn * @param token */ -function getAngle(token) { - if (token.typ == exports.EnumToken.IdenTokenType) { - if (token.val == "none") { +function getAngle(token, arena) { + const typ = arena.getType(token); + if (typ == exports.EnumToken.IdenTokenType) { + if (arena.equalsIgnoreCase(token, 'val', "none")) { return 0; } } - if (token.typ == exports.EnumToken.AngleTokenType) { - switch (token.unit) { + const val = arena.getNumericValue(token); + if (typ == exports.EnumToken.AngleTokenType) { + switch (arena.getUnit(token)) { case "deg": - // @ts-ignore - return token.val / 360; + return val / 360; case "rad": - // @ts-ignore - return token.val / (2 * Math.PI); + return val / (2 * Math.PI); case "grad": - // @ts-ignore - return token.val / 400; + return val / 400; case "turn": - // @ts-ignore - return +token.val; + // @ts-ign + return +val; } } - return token.val / 360; + return val / 360; } /** @@ -15421,19 +15406,23 @@ function toDegrees(angle) { return angle; } -function stripCommaToken(tokenList) { +function stripCommaToken(tokenList, options) { let result = []; + let typ; + const arena = options.arena; for (let i = 0; i < tokenList.length; i++) { - if (tokenList[i].typ == exports.EnumToken.CommentTokenType || tokenList[i].typ == exports.EnumToken.CommaTokenType) { + typ = arena.getType(tokenList[i]); + if (typ == exports.EnumToken.CommentTokenType || typ == exports.EnumToken.CommaTokenType) { continue; } result.push(tokenList[i]); } return result; } -function splitTokenList(tokenList, split = [exports.EnumToken.CommaTokenType], includeSplitToken = false) { +function splitTokenList(tokenList, options, split = [exports.EnumToken.CommaTokenType], includeSplitToken = false) { + const arena = options.arena; return tokenList.reduce((acc, curr) => { - if (split.includes(curr.typ)) { + if (split.includes(arena.getType(curr))) { if (includeSplitToken && Array.isArray(acc[acc.length - 1])) { acc[acc.length - 1].push(curr); } @@ -15993,7 +15982,7 @@ function isFrequency(dimension) { */ // export function isColorspace(token: Token): boolean { // return ( -// token.typ === EnumToken.IdenTokenType && +// typ === EnumToken.IdenTokenType && // [ // "srgb", // "srgb-linear", @@ -16199,7 +16188,7 @@ function reduceConicColorStops(stops) { * @returns */ function isRectangularOrthogonalColorspace(token) { - return (token.typ === exports.EnumToken.IdenTokenType && + return (typ === exports.EnumToken.IdenTokenType && colorFuncColorSpace.some((t) => equalsIgnoreCase(t, token.val))); } /** @@ -16208,7 +16197,7 @@ function isRectangularOrthogonalColorspace(token) { * @returns */ function isPolarColorspace(token) { - return (token.typ === exports.EnumToken.IdenTokenType && + return (typ === exports.EnumToken.IdenTokenType && ["hsl", "hwb", "lch", "oklch"].some((t) => equalsIgnoreCase(t, token.val))); } /** @@ -16218,7 +16207,7 @@ function isPolarColorspace(token) { */ // export function isHueInterpolationMethod(token: Token | Token[]): boolean { // if (!Array.isArray(token)) { -// return token.typ == EnumToken.IdenTokenType && "hue" === (token as IdentToken).val?.toLowerCase?.(); +// return typ == EnumToken.IdenTokenType && "hue" === (token as IdentToken).val?.toLowerCase?.(); // } // // if (token.length != 2 || token[0].typ != EnumToken.IdenTokenType || token[1].typ != EnumToken.IdenTokenType) { @@ -16237,26 +16226,27 @@ function isPolarColorspace(token) { * @returns */ function isIdentColor(token) { - return (token.typ == exports.EnumToken.ColorTokenType && + return (typ == exports.EnumToken.ColorTokenType && [exports.ColorType.SYS, exports.ColorType.DPSYS, exports.ColorType.LIT].includes(token.kin) && isIdent(token.val)); } // export function isPercentageToken(token: Token): boolean { // return ( -// token.typ == EnumToken.PercentageTokenType || -// (token.typ == EnumToken.NumberTokenType && (token as NumberToken).val == 0) +// typ == EnumToken.PercentageTokenType || +// (typ == EnumToken.NumberTokenType && (token as NumberToken).val == 0) // ); // } -function isColor(token, errors) { - if (token.typ == exports.EnumToken.WildCardFunctionTokenType) { +function isColor(token, arena) { + const typ = arena.getType(token); + if (typ == exports.EnumToken.WildCardFunctionTokenType) { return true; } - if (token.typ == exports.EnumToken.ColorTokenType) { + if (typ == exports.EnumToken.ColorTokenType) { if ("kin" in token && !("chi" in token)) { return true; } } - if (token.typ == exports.EnumToken.IdenTokenType) { + if (typ == exports.EnumToken.IdenTokenType) { const val = token.val.toLowerCase(); if (systemColors.has(val) || deprecatedSystemColors.has(val) || nonStandardColors.has(val)) { return true; @@ -16264,7 +16254,7 @@ function isColor(token, errors) { // named color return val in COLORS_NAMES || "currentcolor" === val || "transparent" === val; } - if (token.typ === exports.EnumToken.FunctionTokenType || token.typ === exports.EnumToken.ColorTokenType) { + if (typ === exports.EnumToken.FunctionTokenType || typ === exports.EnumToken.ColorTokenType) { // if (!colorsFunc.includes((token as FunctionToken).val.toLowerCase())) { // return false; // } @@ -16544,8 +16534,9 @@ function isColor(token, errors) { } return false; } -function parseColor(token) { - if (token.typ === exports.EnumToken.IdenTokenType) { +function parseColor(token, arena) { + const typ = arena.getType(token); + if (typ === exports.EnumToken.IdenTokenType) { const val = token.val.toLowerCase(); if (nonStandardColors.has(val)) { Object.assign(token, { @@ -16577,7 +16568,7 @@ function parseColor(token) { return token; } } - if (token.typ === exports.EnumToken.ColorTokenType) { + if (typ === exports.EnumToken.ColorTokenType) { if (!("kin" in token) && "val" in token) { // @ts-expect-error token.kin = exports.ColorType[token.val.replaceAll("-", "_").toUpperCase()]; @@ -16923,24 +16914,24 @@ function isWhiteSpace(codepoint) { // } // // return ( -// token.typ === EnumToken.IdenTokenType || -// token.typ === EnumToken.DimensionTokenType || -// token.typ === EnumToken.LengthTokenType || -// token.typ === EnumToken.AngleTokenType || -// token.typ === EnumToken.FlexTokenType || -// token.typ === EnumToken.TimeTokenType || -// token.typ === EnumToken.ResolutionTokenType || -// token.typ === EnumToken.FrequencyTokenType || -// token.typ === EnumToken.NumberTokenType || -// token.typ === EnumToken.ColorTokenType || -// token.typ === EnumToken.FunctionTokenType || -// token.typ === EnumToken.UrlFunctionTokenType || -// token.typ === EnumToken.GridTemplateFuncTokenType || -// token.typ === EnumToken.ImageFunctionTokenType || -// token.typ === EnumToken.TimelineFunctionTokenType || -// token.typ === EnumToken.TimingFunctionTokenType || -// token.typ === EnumToken.MathFunctionTokenType || -// token.typ === EnumToken.TransformFunctionTokenType +// typ === EnumToken.IdenTokenType || +// typ === EnumToken.DimensionTokenType || +// typ === EnumToken.LengthTokenType || +// typ === EnumToken.AngleTokenType || +// typ === EnumToken.FlexTokenType || +// typ === EnumToken.TimeTokenType || +// typ === EnumToken.ResolutionTokenType || +// typ === EnumToken.FrequencyTokenType || +// typ === EnumToken.NumberTokenType || +// typ === EnumToken.ColorTokenType || +// typ === EnumToken.FunctionTokenType || +// typ === EnumToken.UrlFunctionTokenType || +// typ === EnumToken.GridTemplateFuncTokenType || +// typ === EnumToken.ImageFunctionTokenType || +// typ === EnumToken.TimelineFunctionTokenType || +// typ === EnumToken.TimingFunctionTokenType || +// typ === EnumToken.MathFunctionTokenType || +// typ === EnumToken.TransformFunctionTokenType // ); // } // https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Values_and_units#absolute_length_units @@ -17068,9844 +17059,10824 @@ function eq(a, b) { } /** - * feature walk mode + * expand css nesting ast nodes + * @param ast * * @private */ -exports.FeatureWalkMode = void 0; -(function (FeatureWalkMode) { - /** - * pre process - */ - FeatureWalkMode[FeatureWalkMode["Pre"] = 1] = "Pre"; - /** - * post process - */ - FeatureWalkMode[FeatureWalkMode["Post"] = 2] = "Post"; -})(exports.FeatureWalkMode || (exports.FeatureWalkMode = {})); - -const config$2 = getSyntaxConfig(); -function replacePseudo(tokens) { - return tokens.map((raw) => raw.map((r) => { - if (r.includes("(")) { - const index = r.indexOf("("); - const name = r.slice(0, index) + "()"; - if (name in pseudoAliasMap) { - return pseudoAliasMap[name] + r.slice(index); +function expand(ast) { + if (ast[STATE] & + (exports.EnumAstNodeStatus.Invalid | + exports.EnumAstNodeStatus.Disallowed | + exports.EnumAstNodeStatus.Unknown | + exports.EnumAstNodeStatus.Unparsed | + exports.EnumAstNodeStatus.Malformed)) { + return ast; + } + const result = Object.assign(cloneNode(ast), { chi: [] }); + let children; + for (let i = 0; i < ast.chi.length; i++) { + let node = ast.chi[i]; + if (node.typ === exports.EnumToken.RuleNodeType) { + children = expandRule(node); + for (const child of children) { + child[PARENT] = result; + result.chi.push(child); } - return r; } - return r in pseudoAliasMap && pseudoAliasMap[r] in config$2[ValidationSyntaxGroupEnum.Selectors] - ? pseudoAliasMap[r] - : r; - })); -} -function replaceAstNodes(tokens, root) { - let result = false; - for (const { value, parent } of walkValues(tokens, root)) { - if (value.typ == exports.EnumToken.MediaQueryConditionTokenType) { - const token = value.l.find((t) => t.typ == exports.EnumToken.IdenTokenType); - if (token != null) { - if (token.val in pseudoAliasMap) { - token.val = pseudoAliasMap[token.val]; - if ((equalsIgnoreCase(token.val, "min-resolution") || - equalsIgnoreCase(token.val, "max-resolution")) && - value.r?.[0]?.typ == exports.EnumToken.NumberTokenType) { - Object.assign(value.r?.[0], { - typ: exports.EnumToken.ResolutionTokenType, - unit: "x", - }); - result = true; - } - let isMin = token.val.startsWith("min-"); - let isMax = token.val.startsWith("max-"); - if (isMin || isMax) { - token.val = token.val.slice(4); - value.op.typ = isMax - ? exports.EnumToken.LteTokenType - : exports.EnumToken.GteTokenType; - } + else if (node.typ == exports.EnumToken.AtRuleNodeType && "chi" in node) { + let hasRule = false; + let j = node.chi.length; + while (j--) { + // @ts-ignore + if (node.chi[j].typ == exports.EnumToken.RuleNodeType || node.chi[j].typ == exports.EnumToken.AtRuleNodeType) { + hasRule = true; + break; } } - } - else if (value.typ == exports.EnumToken.IdenTokenType || - value.typ == exports.EnumToken.PseudoClassFuncTokenType || - value.typ == exports.EnumToken.PseudoClassTokenType || - value.typ == exports.EnumToken.PseudoElementTokenType) { - let key = value.val + - (value.typ == exports.EnumToken.PseudoClassFuncTokenType ? "()" : ""); - if (key in pseudoAliasMap) { - const isPseudClass = pseudoAliasMap[key].startsWith("::"); - value.val = pseudoAliasMap[key]; - // if ( - // value.typ == EnumToken.IdenTokenType && - // ["min-resolution", "max-resolution"].includes((value as IdentToken).val) && - // parent?.typ == EnumToken.MediaQueryConditionTokenType && - // (parent as MediaQueryConditionToken).r?.[0]?.typ == EnumToken.NumberTokenType - // ) { - // Object.assign((parent as MediaQueryConditionToken).r?.[0], { - // typ: EnumToken.ResolutionTokenType, - // unit: "x", - // }); - // } - // else - if (isPseudClass && value.typ == exports.EnumToken.PseudoElementTokenType) { - // @ts-ignore - value.typ = exports.EnumToken.PseudoClassTokenType; + if (hasRule) { + node = expand(node); + for (const child of node.chi) { + child[PARENT] = result; } - result = true; - } - } - } - // remove duplicate tokens - if (tokens.find((t) => t.typ == exports.EnumToken.CommaTokenType) != null) { - const set = new Set(); - const split = splitTokenList(tokens, [exports.EnumToken.CommaTokenType]); - tokens.length = 0; - for (const token of split.reduce((acc, curr) => { - const str = curr.reduce((acc, curr) => acc + renderValue(curr), ""); - if (set.has(str)) { - return acc; + node[PARENT] = result; + // @ts-ignore + result.chi.push(node); } - set.add(str); - if (acc.length > 0) { - acc.push({ - typ: exports.EnumToken.CommaTokenType, - }); + else { + node[PARENT] = result; + // @ts-ignore + result.chi.push(node); } - return acc.concat(curr); - }, [])) { - tokens.push(token); + } + else { + node[PARENT] = result; + // @ts-ignore + result.chi.push(node); } } return result; } -class ComputePrefixFeature { - get ordering() { - return 0; - } - get processMode() { - return exports.FeatureWalkMode.Pre; - } - static register(options) { - if (options.removePrefix) { - // @ts-ignore - options.features.push(new ComputePrefixFeature(options)); - } +function expandRule(node) { + if (node[STATE] & + (exports.EnumAstNodeStatus.Invalid | + exports.EnumAstNodeStatus.Disallowed | + exports.EnumAstNodeStatus.Unknown | + exports.EnumAstNodeStatus.Unparsed | + exports.EnumAstNodeStatus.Malformed)) { + return [node]; } - run(node) { - if (node[STATE] & (exports.EnumAstNodeStatus.Disallowed | exports.EnumAstNodeStatus.Invalid | exports.EnumAstNodeStatus.Malformed)) { - return null; - } - if (node.typ == exports.EnumToken.RuleNodeType) { - node.sel = replacePseudo(splitRule(node.sel)).reduce((acc, curr, index) => acc + (index > 0 ? "," : "") + curr.join(""), ""); - if (node[TOKENS] != null) { - replaceAstNodes(node[TOKENS]); - } - } - else if (node.typ == exports.EnumToken.DeclarationNodeType) { - if (node.nam.charAt(0) == "-") { - const match = node.nam.match(/^-([^-]+)-(.+)$/); - if (match != null) { - let nam = match[2]; - if (!(nam in config$2.declarations)) { - if (node.nam in pseudoAliasMap) { - nam = pseudoAliasMap[node.nam]; - } + const ast = Object.assign(cloneNode(node), { chi: node.chi.slice() }); + const result = []; + if (ast.typ == exports.EnumToken.RuleNodeType) { + let i = 0; + for (; i < ast.chi.length; i++) { + if (ast.chi[i].typ == exports.EnumToken.RuleNodeType) { + const rule = ast.chi[i]; + if (!rule.sel.includes("&")) { + const selRule = splitRule(rule.sel); + const arSelf = splitRule(ast.sel) + .filter((r) => r.every((t) => t != ":before" && t != ":after" && !t.startsWith("::"))) + .reduce((acc, curr) => acc.concat([curr.join("")]), []) + .join(","); + if (arSelf.length == 0) { + ast.chi.splice(i--, 1); + continue; } - if (nam in config$2.declarations) { - node.nam = nam; + for (let i1 = 0; i1 < selRule.length; i1++) { + const arr = selRule[i1]; + combinators.includes(arr[0].charAt(0)) ? arr.unshift(arSelf) : arr.unshift(arSelf, " "); } + rule.sel = selRule + .reduce((acc, curr) => { + acc.push(curr.join("")); + return acc; + }, []) + .join(","); } - } - let hasPrefix = false; - for (const { value } of walkValues(node.val)) { - if ((value.typ == exports.EnumToken.IdenTokenType || - (value.typ != exports.EnumToken.ParensTokenType && funcLike.includes(value.typ))) && - value.val.match(/^-([^-]+)-(.+)$/) != null) { - if (value.val.endsWith("-gradient")) { - if (regMatchLinearGradient.test(value.val)) { - this.webkitLinearToLinearGradient(value); + else { + let childSelectorCompound = []; + let withCompound = []; + let withoutCompound = []; + // pseudo elements cannot be used with '&' + // https://www.w3.org/TR/css-nesting-1/#example-7145ff1e + const rules = splitRule(ast.sel).filter((r) => r.every((t) => t != ":before" && t != ":after" && !t.startsWith("::"))); + const parentSelector = !node.sel.includes("&"); + if (rules.length == 0) { + ast.chi.splice(i--, 1); + continue; + } + for (const sel of rule[RAW] ?? splitRule(rule.sel)) { + const s = sel.join(""); + if (s.includes("&") || parentSelector) { + if (s.indexOf("&", 1) == -1) { + if (s.at(0) == "&") { + if (s.at(1) == " ") { + childSelectorCompound.push(s.slice(2)); + } + else { + if (s == "&" || parentSelector) { + withCompound.push(s); + } + } + } + else { + withoutCompound.push(s); + } + } + else { + withCompound.push(s); + } } - else if (regMatchRadialGradient.test(value.val)) { - this.webkitRadialToRadialGradient(value); + } + const selectors = []; + const selector = rules.length > 1 ? ":is(" + rules.map((a) => a.join("")).join(",") + ")" : rules[0].join(""); + if (childSelectorCompound.length > 0) { + if (childSelectorCompound.length == 1) { + selectors.push(replaceCompound("& " + childSelectorCompound[0].trim(), selector)); } - else if (equalsIgnoreCase(value.val, "-webkit-gradient")) { - this.webkitGradientToGradient(value); + else { + selectors.push(replaceCompound("& :is(" + + childSelectorCompound.reduce((acc, curr) => acc + (acc.length > 0 ? "," : "") + curr.trim(), "") + + ")", selector)); } - // not supported yet - break; } - hasPrefix = true; - break; - } - } - if (hasPrefix) { - const nodes = structuredClone(node.val); - for (const { value } of walkValues(nodes)) { - if (value.typ == exports.EnumToken.IdenTokenType || funcLike.includes(value.typ)) { - const match = value.val.match(/^-([^-]+)-(.+)$/); - if (match != null) { - value.val = match[2]; + if (withCompound.length > 0) { + if (withCompound.every((t) => t[0] == "&" && t.indexOf("&", 1) == -1)) { + // for (const w of withCompound) { + // for (let m = 0; m < w.length; m++) { + // // for (let n = 0; n < w[m].length; n++) { + // withoutCompound.push(w[m].slice(1)); + // // } + // } + // } + withoutCompound.push(...withCompound.map((t) => t.slice(1))); + withCompound.length = 0; + } + } + if (withoutCompound.length > 0) { + if (withoutCompound.length == 1) { + const useIs = rules.length == 1 && + selector.match(/^[a-zA-Z.:]/) != null && + selector.includes(" ") && + withoutCompound.length == 1 && + withoutCompound[0].match(/^[a-zA-Z]+$/) != null; + const compound = useIs ? ":is(&)" : "&"; + selectors.push(replaceCompound(rules.length == 1 + ? useIs + ? withoutCompound[0] + ":is(&)" + : selector.match(/^[.:]/) && withoutCompound[0].match(/^[a-zA-Z]+$/) + ? withoutCompound[0] + compound + : compound + withoutCompound[0] + : withoutCompound[0].match(/^[a-zA-Z:]+$/) + ? withoutCompound[0].trim() + compound + : "&" + + (withoutCompound[0].match(/^\S+$/) + ? withoutCompound[0].trim() + : ":is(" + withoutCompound[0].trim() + ")"), selector)); + } + else { + selectors.push(replaceCompound("&:is(" + + withoutCompound.reduce((acc, curr) => acc + (acc.length > 0 ? "," : "") + curr.trim(), "") + + ")", selector)); + } + } + if (withCompound.length > 0) { + if (withCompound.length == 1) { + selectors.push(replaceCompound(withCompound[0], selector)); } } + rule.sel = selectors.reduce((acc, curr) => (curr.length == 0 ? acc : acc + (acc.length > 0 ? "," : "") + curr), ""); } - node.val = nodes; - } - } - else if (node.typ == exports.EnumToken.AtRuleNodeType || node.typ == exports.EnumToken.KeyframesAtRuleNodeType) { - if (node.nam.startsWith("-")) { - const match = node.nam.match(/^-([^-]+)-(.+)$/); - if (match != null && "@" + match[2] in config$2.atRules) { - node.nam = match[2]; + ast.chi.splice(i--, 1); + for (const s of expandRule(rule)) { + result.push(s); } } - if (node.typ == exports.EnumToken.AtRuleNodeType && node.val !== "") { - if (replaceAstNodes(node[TOKENS])) { - node.val = node[TOKENS].reduce((acc, curr) => acc + renderValue(curr), ""); + else if (ast.chi[i].typ == exports.EnumToken.AtRuleNodeType) { + let astAtRule = ast.chi[i]; + const values = []; + if (astAtRule.nam === "scope") { + if (astAtRule.val.includes("&")) { + astAtRule.val = replaceCompound(astAtRule.val, ast.sel); + } + const slice = astAtRule.chi + .slice() + .filter((t) => t.typ == exports.EnumToken.RuleNodeType && t.sel.includes("&")); + if (slice.length > 0) { + expandRule({ ...node, chi: astAtRule.chi.slice() }); + } } - } - } - return node; - } - /** - * Convert -webkit-linear-gradient to linear-gradient syntax - * @param tokens - * @returns - */ - webkitLinearToLinearGradient(token) { - let i; - let k; - let to; - let val; - let key; - const tokens = token.chi; - // conversion rules - // translation: - // - left -> to right - // - right -> to left - // - top -> to bottom - // - top right -> to bottom left - // - top left -> to bottom right - // - bottom -> to top - // - bottom right -> to top left - // - bottom left -> to top right - // https://drafts.csswg.org/css-images-3/#linear-gradients - // final angle: - // - to top -> 0deg - // - to right -> 90deg - // - to bottom -> 180deg - // - to left -> 270deg - for (i = 0; i < tokens.length; i++) { - if (tokens[i].typ == exports.EnumToken.AngleTokenType || - (tokens[i].typ == exports.EnumToken.NumberTokenType && tokens[i].val == 0)) { - tokens[i].val = - (90 - - // @ts-expect-error - (tokens[i].typ == exports.EnumToken.NumberTokenType - ? tokens[i] - : toDegrees(tokens[i]).val)) % - 360; - } - else if (tokens[i].typ == exports.EnumToken.IdenTokenType) { - key = tokens[i].val.toLowerCase(); - switch (key) { - case "right": - case "left": - tokens.splice(i, 1, { typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { - typ: exports.EnumToken.IdenTokenType, - val: key == "right" ? "left" : "right", - }); - i += 2; - break; - case "top": - case "bottom": - k = i + 1; - to = key == "top" ? "bottom" : "top"; - while (k < tokens.length && - (tokens[k].typ === exports.EnumToken.WhitespaceTokenType || - tokens[k].typ === exports.EnumToken.CommentTokenType)) { - k++; - } - if (tokens[k]?.typ === exports.EnumToken.IdenTokenType) { - val = ""; - if (equalsIgnoreCase(tokens[k].val, "left")) { - val = "right"; - } - if (equalsIgnoreCase(tokens[k].val, "right")) { - val = "left"; + else { + // @ts-ignore + const clone = { ...ast, chi: astAtRule.chi.slice() }; + // @ts-ignore + astAtRule.chi.length = 0; + for (const r of expandRule(clone)) { + if (r.typ == exports.EnumToken.AtRuleNodeType && "chi" in r) { + if (astAtRule.val !== "" && r.val !== "") { + if (astAtRule.nam === "media" && r.nam === "media") { + r.val = astAtRule.val + " and " + r.val; + } + else if (astAtRule.nam == "layer" && r.nam == "layer") { + r.val = astAtRule.val + "." + r.val; + } } - if (val !== "") { - tokens.splice(i, k - i + 1, { typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: to }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val }); - i += 4; - break; + // @ts-ignore + values.push(r); + } + else if (r.typ == exports.EnumToken.RuleNodeType) { + for (const rule of expandRule(r)) { + // @ts-ignore + astAtRule.chi.push(rule); } } - tokens.splice(i, 1, { typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: to }); - i += 2; - break; + } + } + if (astAtRule.chi.length > 0) { + result.push(astAtRule); + } + for (const r of values) { + result.push(r); } + ast.chi.splice(i--, 1); } } - token.val = equalsIgnoreCase(token.val, "-webkit-repeating-linear-gradient") - ? "repeating-linear-gradient" - : "linear-gradient"; } - /** - * Convert -webkit-gradient(linear) to linear-gradient or linear-gradient syntax - * @param token - * @returns - */ - webkitGradientToGradient(token) { - let i = 0; - let k; - let key = ""; - let commaCount; - let type = ""; - let tokens = token.chi.slice(); - // linear or radial - if (equalsIgnoreCase(tokens[i].val, "linear")) { - type = "linear-gradient"; - i++; - } - tokens.splice(0, i + 1); - commaCount = 0; - for (i = 0; i < tokens.length; i++) { - if (tokens[i].typ === exports.EnumToken.CommaTokenType) { - commaCount++; - if (commaCount > 1) { - break; + // @ts-ignore + return ast.chi.length > 0 ? [ast].concat(result) : result; +} +/** + * replace compound selector + * @param input + * @param replace + */ +function replaceCompound(input, replace) { + const tokens = parseString(input); + let replacement = null; + for (const t of walkValues(tokens)) { + if (t.value.typ == exports.EnumToken.NestingSelectorTokenType) { + if (tokens.length == 2) { + if (replacement == null) { + replacement = parseString(replace); } + Object.assign(t.value, { + typ: exports.EnumToken.LiteralTokenType, + val: replaceCompoundLiteral(t.value.val, replace), + }); + continue; } - if (tokens[i].typ === exports.EnumToken.IdenTokenType) { - key += (key.length > 0 ? " " : "") + tokens[i].val; - } - } - // mapping keywords - // left top → left bottom to bottom - // left bottom → left top to top - // left top → right top to right - // right top → left top to left - // left top → right bottom to bottom right - // right top → left bottom to bottom left - // left bottom → right top to top right - // right bottom → left top to top left - const replacements = []; - if (key === "left top left bottom") { - replacements.push({ typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "bottom" }); - } - else if (key === "left bottom left top") { - replacements.push({ typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "top" }); - } - else if (key === "left top right top") { - replacements.push({ typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "right" }); + const rule = splitRule(replace); + Object.assign(t.value, { + typ: exports.EnumToken.LiteralTokenType, + val: rule.length > 1 ? ":is(" + replace + ")" : replace, + }); } - else if (key === "right top left top") { - replacements.push({ typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "left" }); + } + return tokens.reduce((acc, curr) => acc + printValue(curr), ""); +} +function replaceCompoundLiteral(selector, replace) { + const tokens = [""]; + let i = 0; + for (; i < selector.length; i++) { + if (selector.charAt(i) == "&") { + tokens.push("&", ""); } - else if (key === "left top right bottom") { - replacements.push({ typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "bottom" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "right" }); + } + return tokens + .sort((a, b) => { + if (a == "&") { + return 1; } - else if (key === "right top left bottom") { - replacements.push({ typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "bottom" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "left" }); + return b == "&" ? -1 : 0; + }) + .reduce((acc, curr) => acc + (curr == "&" ? replace : curr), ""); +} + +/** + * + * @param value + * @returns + */ +function toBase64(value) { + let result = ''; + for (const c of new TextEncoder().encode(value)) { + result += String.fromCharCode(c); + } + return btoa(result); +} + +// from https://github.com/Rich-Harris/vlq/tree/master +// credit: Rich Harris +const integer_to_char = {}; +const char_to_integer = {}; +let i = 0; +for (const char of 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=') { + char_to_integer[char] = i; + integer_to_char[i++] = char; +} +/** + * @param {string} str + */ +function decode(str) { + /** @type {number[]} */ + let result = []; + let shift = 0; + let value = 0; + for (let i = 0; i < str.length; i += 1) { + let integer = char_to_integer[str[i]]; + // if (integer === undefined) { + // throw new Error('Invalid character (' + str[i] + ')'); + // } + const has_continuation_bit = integer & 32; + integer &= 31; + value += integer << shift; + if (has_continuation_bit) { + shift += 5; } - else if (key === "left bottom right top") { - replacements.push({ typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "top" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "right" }); + else { + const should_negate = value & 1; + value >>>= 1; + if (should_negate) { + result.push(value === 0 ? -2147483648 : -value); + } + else { + result.push(value); + } + // reset + value = shift = 0; } - else if (key === "right bottom left top") { - replacements.push({ typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "top" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "left" }); + } + return result; +} +/** + * + * @param value + * @returns + */ +function encode(value) { + // if (typeof value === 'number') { + // return encode_integer(value); + // } + let result = ''; + for (let i = 0; i < value.length; i += 1) { + result += encode_integer(value[i]); + } + return result; +} +/** + * + * @param num + * @returns + */ +function encode_integer(num) { + let result = ''; + if (num < 0) { + num = (-num << 1) | 1; + } + else { + num <<= 1; + } + do { + let clamped = num & 31; + num >>>= 5; + if (num > 0) { + clamped |= 32; } - tokens.splice(0, i, ...replacements); - let checkStop = true; - let checkStopIndex = replacements.length + 1; - let colorStop = []; - for (i = replacements.length + 1; i < tokens.length; i++) { - if (tokens[i].typ === exports.EnumToken.CommaTokenType && checkStop) { - checkStopIndex = i; - continue; - } - if (tokens[i].typ === exports.EnumToken.FunctionTokenType) { - if (equalsIgnoreCase(tokens[i].val, "to")) { - colorStop.push(tokens[checkStopIndex]); - for (const token of tokens[i].chi) { - colorStop.push(token); - } - tokens.splice(checkStopIndex, i - checkStopIndex + 1); - i = checkStopIndex; - checkStop = false; + result += integer_to_char[clamped]; + } while (num > 0); + return result; +} + +/** + * Generate and parse source map + * + * ```ts + * const sourcemap = new SourceMap(); + * + * // you can also pass an input sourcemap + * // const sourcemap = new SourceMap(sourcemapObjectOrJSONString); + * + * sourcemap.addSourceContent(0, '/css/styles.css', null); + * sourcemap.addSourceContent(1, '/css/typography.css', null); + * + * const maps = []; + * + * maps.add([ + 1, 1, 0, 1, 1]); + * maps.add([ + 2, 1, 1, 1, 1 + ]); + + sourcemap.add(maps); + + // convert to inline sourcemap + sourcemap.toUrl(); + + // convert to JSON + sourcemap.toJSON(); + + // find the original file, line and column + sourcemap.find(2, 1); + ``` + */ +class SourceMap { + /** + * + * @private + */ + keys = new Set(); + /** + * Last location + */ + lastLocation = null; + /** + * Version + * @private + */ + version = 3; + /** + * Sources map + * @private + */ + sourcesMap = []; + /** + * Sources content + * @private + */ + sourcesContent = []; + /** + * Sources + * @private + */ + sources = []; + /** + * Map + * @private + * + */ + map = new Map(); + /** + * Map + * @private + * + */ + reverseMap = new Map(); + /** + * Line + * @private + */ + line = -1; + /** + * + * @param sourcemaps input sourcemap + */ + constructor(sourcemaps) { + if (typeof sourcemaps === "string") { + if (sourcemaps.startsWith("data:")) { + let encoding = ""; + let offset = sourcemaps.indexOf(",") + 1; + if (offset == 0) { + offset = sourcemaps.lastIndexOf(";") + 1; } - else if (equalsIgnoreCase(tokens[i].val, "from")) { - k = tokens[i].chi.length + 1; - tokens.splice(i, 1, ...tokens[i].chi, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.PercentageTokenType, val: 0 }); - i += k; + else { + encoding = sourcemaps.slice(sourcemaps.lastIndexOf(";") + 1, offset - 1); } - else if (equalsIgnoreCase(tokens[i].val, "color-stop")) { - k = tokens[i].chi.length - 1; - tokens.splice(i, 1, - // @ts-expect-error - ...tokens[i].chi.reverse().map((t) => t.typ === exports.EnumToken.CommaTokenType - ? { typ: exports.EnumToken.WhitespaceTokenType } - : t.typ === exports.EnumToken.NumberTokenType - ? { - typ: exports.EnumToken.PercentageTokenType, - // @ts-expect-error - val: t.val * 100, - } - : t)); - i += k; + if (encoding == "base64") { + sourcemaps = atob(sourcemaps.slice(offset)); + } + else { + sourcemaps = decodeURIComponent(sourcemaps.slice(offset)); } } + sourcemaps = JSON.parse(sourcemaps); } - if (colorStop.length > 0) { - for (const t of colorStop) { - tokens.push(t); - } - } - if (type !== "") { - token.val = type; - token.chi.length = 0; - for (const t of tokens) { - token.chi.push(t); + if (sourcemaps != null) { + this.sources = sourcemaps.sources?.slice() ?? []; + this.sourcesContent = sourcemaps.sourcesContent?.slice() ?? []; + const decodedMappings = sourcemaps.mappings + .split(";") + .map((mapping) => mapping.split(",").map((mapping) => decode(mapping))); + this.line = decodedMappings.length - 1; + for (let index = 0; index < decodedMappings.length; index++) { + if (decodedMappings[index].length == 0 || + (decodedMappings[index].length == 1 && decodedMappings[index][0].length == 0)) { + continue; + } + this.map.set(index, decodedMappings[index]); } + this.computePositions(); } } /** - * Convert -webkit-radial-gradient to radial-gradient syntax - * @param tokens + * add source + * @param id source id + * @param fileName source file + * @param content source content * @returns */ - webkitRadialToRadialGradient(token) { - let i = 0; - const tokens = token.chi; - while (i < tokens.length && - (tokens[i].typ === exports.EnumToken.WhitespaceTokenType || tokens[i].typ === exports.EnumToken.CommentTokenType)) { - i++; - } - const positions = []; - const form = []; - const size = []; - const colorStops = []; - if (tokens[i]?.typ === exports.EnumToken.PercentageTokenType || - (tokens[i]?.typ === exports.EnumToken.NumberTokenType && 0 === tokens[i].val) || - (tokens[i]?.typ === exports.EnumToken.IdenTokenType && - (equalsIgnoreCase(tokens[i].val, "center") || - equalsIgnoreCase(tokens[i].val, "top") || - equalsIgnoreCase(tokens[i].val, "bottom") || - equalsIgnoreCase(tokens[i].val, "left") || - equalsIgnoreCase(tokens[i].val, "right")))) { - do { - positions.push(tokens[i]); - i++; - } while (tokens[i]?.typ !== exports.EnumToken.CommaTokenType); - if (tokens[i]?.typ === exports.EnumToken.CommaTokenType) { - i++; - } + addSourceContent(id, fileName, content) { + if (this.sourcesMap.includes(id)) { + return; } - if (tokens[i]?.typ === exports.EnumToken.IdenTokenType) { - if (equalsIgnoreCase(tokens[i].val, "closest-side") || - equalsIgnoreCase(tokens[i].val, "closest-corner") || - equalsIgnoreCase(tokens[i].val, "farthest-side") || - equalsIgnoreCase(tokens[i].val, "farthest-corner")) { - size.push(tokens[i++]); - } - else if (equalsIgnoreCase(tokens[i].val, "circle") || - equalsIgnoreCase(tokens[i].val, "ellipse")) { - form.push(tokens[i++]); - } - while (tokens[i]?.typ === exports.EnumToken.WhitespaceTokenType || tokens[i]?.typ === exports.EnumToken.CommentTokenType) { - i++; + this.sourcesMap[this.sourcesMap.length] = id; + this.sources[this.sources.length] = fileName || null; + this.sourcesContent[this.sourcesContent.length] = content || null; + } + /** + * Add multiple sourcemaps + * @param maps + * @throws + */ + add(maps) { + let srcIndex; + for (let [newLine, newColumn, srcId, ln, col] of maps) { + const key = `${srcId}:${ln}:${col}:${newLine}:${newColumn}`; + if (this.keys.has(key)) { + continue; } - if (tokens[i]?.typ === exports.EnumToken.CommaTokenType) { - i++; + this.keys.add(key); + const line = newLine - 1; + let record; + if (line > this.line) { + this.line = line; } - } - if (tokens[i]?.typ === exports.EnumToken.IdenTokenType) { - if (equalsIgnoreCase(tokens[i].val, "closest-side") || - equalsIgnoreCase(tokens[i].val, "closest-corner") || - equalsIgnoreCase(tokens[i].val, "farthest-side") || - equalsIgnoreCase(tokens[i].val, "farthest-corner")) { - size.push(tokens[i++]); + srcIndex = this.sourcesMap.indexOf(srcId); + if (srcIndex == -1) { + throw new Error(`Source file ${srcId} not added to sourcemap`); } - else if (equalsIgnoreCase(tokens[i].val, "circle") || - equalsIgnoreCase(tokens[i].val, "ellipse")) { - form.push(tokens[i++]); + if (!this.map.has(line)) { + record = [Math.max(0, newColumn - 1), srcIndex, ln - 1, col - 1]; + this.map.set(line, [record]); } - while (tokens[i]?.typ === exports.EnumToken.WhitespaceTokenType || tokens[i]?.typ === exports.EnumToken.CommentTokenType) { - i++; + else { + const arr = this.map.get(line); + record = [Math.max(0, newColumn - 1) - arr[0][0], srcIndex - arr[0][1], ln - 1, col - 1]; + arr.push(record); } - if (tokens[i]?.typ === exports.EnumToken.CommaTokenType) { - i++; + if (this.lastLocation != null) { + record[2] -= this.lastLocation.ln - 1; + record[3] -= this.lastLocation.col - 1; } + this.lastLocation ??= { ln, col }; + this.lastLocation.ln = ln; + this.lastLocation.col = col; } - for (let m = i; m < tokens.length; m++) { - colorStops.push(tokens[m]); - } - tokens.length = 0; - if (form.length > 0 || size.length > 0) { - if (form.length === 0) { - form.push({ typ: exports.EnumToken.IdenTokenType, val: "ellipse" }); + } + /** + * compute original positions + */ + computePositions() { + this.reverseMap.clear(); + let sourceFileIndex = 0; // second field + let sourceCodeLine = 0; // third field + let sourceCodeColumn = 0; // fourth field + // let nameIndex: number = 0; // fifth field + let generatedCodeColumn; + let result; + // mappings to original source + for (let [i, line] of this.map.entries()) { + if (line.length === 0 || (line.length === 1 && line[0].length === 0)) { + continue; } - if (size.length > 0) { - form.push({ typ: exports.EnumToken.WhitespaceTokenType }); - for (const token of size) { - form.push(token); + generatedCodeColumn = line[0][0]; // first field - reset each time + line = line + .map((segment, index, array) => { + if (segment.length === 0) { + return []; } - } - if (positions.length > 0) { - form.push({ typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "at" }, { typ: exports.EnumToken.WhitespaceTokenType }); - for (const position of positions) { - form.push(position); + generatedCodeColumn = index == 0 ? segment[0] : segment[0] + array[0][0]; + result = [generatedCodeColumn]; + if (segment.length <= 1) { + return result; } + sourceFileIndex = index == 0 ? segment[1] : segment[1] + array[0][1]; + sourceCodeLine += segment[2]; + sourceCodeColumn += segment[3]; + result.push(sourceFileIndex, sourceCodeLine, sourceCodeColumn); + // nameIndex not needed + // if (segment.length === 5) { + // nameIndex += segment[4]; + // result.push(nameIndex); + // } + return result; + }) + .sort((a, b) => { + if (a[1] !== b[1]) { + return a[1] - b[1]; + } + return a[0] - b[0]; + }); + if (line.length == 0 || (line.length == 1 && line[0].length == 0)) { + continue; } - for (const token of form) { - tokens.push(token); - } - tokens.push({ typ: exports.EnumToken.CommaTokenType }); - } - token.val = equalsIgnoreCase(token.val, "-webkit-repeating-radial-gradient") - ? "repeating-radial-gradient" - : "radial-gradient"; - for (const colorStop of colorStops) { - tokens.push(colorStop); + this.reverseMap.set(i, line); } - return tokens; } -} - -function inlineExpression(token) { - const result = []; - if (token.typ == exports.EnumToken.BinaryExpressionTokenType) { - const chi = inlineExpression(token.l); - chi.push({ typ: token.op }); - for (const child of inlineExpression(token.r)) { - chi.push(child); + /** + * retrieve original sources, lines and columns + * @param line generated line + * @param column generated column + */ + find(line, column) { + if (this.reverseMap.size == 0) { + this.computePositions(); } - result.push({ - typ: exports.EnumToken.ParensTokenType, - chi, - }); - } - else { - result.push(token); - } - return result; -} -function replace(node, variableScope) { - for (const { value, parent: parentValue } of walkValues(node.val)) { - if (value.typ == exports.EnumToken.BinaryExpressionTokenType && parentValue != null && "chi" in parentValue) { - // @ts-ignore - parentValue.chi.splice(parentValue.chi.indexOf(value), 1, ...inlineExpression(value)); + if (!this.reverseMap.has(--line)) { + return null; } - } - for (const { value, parent: parentValue } of walkValues(node.val)) { - if (value.typ == exports.EnumToken.MathFunctionTokenDefType || - (value.typ == exports.EnumToken.WildCardFunctionTokenType && value.val == "var")) { - if (value.chi.length == 1 && - value.chi[0].typ == exports.EnumToken.DashedIdenTokenType) { - const info = (variableScope.get(value.chi[0].val)); - if (info?.replaceable) { - if (parentValue != null) { - let i = 0; - for (; i < parentValue.chi.length; i++) { - if (parentValue.chi[i] == value) { - parentValue.chi.splice(i, 1, ...info.node.val); - break; - } - } - } - else { - node.val = info.node.val.slice(); - } - } + column--; + const result = []; + for (const record of this.reverseMap.get(line)) { + if (record.length == 0 || record[0] < column) { + continue; } + if (record[0] > column) { + break; + } + result.push([ + this.sources?.[record[1]] ?? null, + record[2] + 1, + record[3] + 1, + this.sourcesContent?.[record[1]] ?? null, + ]); } + return result.length == 0 ? null : result; } -} -class InlineCssVariablesFeature { - accept = new Set([exports.EnumToken.RuleNodeType, exports.EnumToken.AtRuleNodeType]); - get ordering() { - return 2; - } - get processMode() { - return exports.FeatureWalkMode.Post; - } - static register(options) { - if (options.inlineCssVariables) { - // @ts-ignore - options.features.push(new InlineCssVariablesFeature()); - } + /** + * Convert to URL encoded string + */ + toUrl() { + // /*# sourceMappingURL = ${url} */ + return `data:application/json;charset=utf-8;base64,${toBase64(JSON.stringify(this.toJSON()))}`; } - run(ast, options = {}, parent, context) { - if (!("chi" in ast)) { - return null; - } - if (!("variableScope" in context)) { - context.variableScope = new Map(); - } - // [':root', 'html'] - const isRoot = parent.typ == exports.EnumToken.StyleSheetNodeType && - ast.typ == exports.EnumToken.RuleNodeType && - (ast[RAW] ?? splitRule(ast.sel)).some((segment) => segment.some((s) => s == ":root" || s == "html")); - const variableScope = context.variableScope; - // @ts-ignore - for (const node of ast.chi) { - if (node.typ !== exports.EnumToken.DeclarationNodeType) { - continue; - } - // css variable - if (node.nam.startsWith("--")) { - if (!variableScope.has(node.val)) { - const info = { - globalScope: isRoot, - // @ts-ignore - parent: new Set(), - declarationCount: 1, - replaceable: isRoot, - node: node, - values: structuredClone(node.val), - }; - info.parent.add(ast); - variableScope.set(node.nam, info); - let recursive = false; - for (const { value } of walkValues(node.val)) { - if (value?.typ == exports.EnumToken.MathFunctionTokenDefType || - (value?.typ == exports.EnumToken.WildCardFunctionTokenType && - (mathFuncs.includes(value.val) || - value.val === "var"))) { - recursive = true; - break; - } - } - if (recursive) { - replace(node, variableScope); - } - } + /** + * Convert to JSON object + */ + toJSON() { + const mappings = []; + let i = 0; + for (; i <= this.line; i++) { + if (!this.map.has(i)) { + mappings.push(""); } else { - replace(node, variableScope); + mappings.push(this.map.get(i).reduce((acc, curr) => acc + (acc === "" ? "" : ",") + encode(curr), "")); } } - return null; + return { + version: this.version, + sources: this.sources.slice(), + sourcesContent: this.sourcesContent?.slice(), + mappings: mappings.join(";"), + }; } - cleanup(ast, options = {}, context) { - const variableScope = context.variableScope; - if (variableScope == null) { - return; +} + +/** + * Compute line and column of the offset + */ +class LineMap { + /** + * line starts + */ + lineStarts; + /** + * Constructor + * @param lines + */ + constructor(lines = []) { + if (lines.length === 0) { + lines.push(0); } - for (const info of variableScope.values()) { - if (info.replaceable) { - let i; - // drop declarations from :root{} - for (const parent of info.parent) { - i = parent.chi?.length ?? 0; - while (i--) { - if (parent.chi[i] == info.node) { - // @ts-ignore - parent.chi.splice(i, 1, { - typ: exports.EnumToken.CommentTokenType, - val: `/* ${info.node.nam}: ${info.values.reduce((acc, curr) => acc + renderValue(curr, { convertColor: false }), "")} */`, - }); - break; - } - } - } + this.lineStarts = lines; + } + /** + * Compute line and column of the offset + * @param offset + * @returns + */ + getOffsets(offset) { + const line = this.search(offset); + const column = offset - this.lineStarts[line]; + // [line, column] + return [line + 1, line == 0 ? column + 1 : column]; + } + /** + * search the greatest index of the value less than or equal to offset + * @param offset + * @returns + */ + search(offset) { + // search lineStarts using binary search + let start = 0; + let end = this.lineStarts.length - 1; + let mid = 0; + let result = -1; + while (start <= end) { + mid = start + ((end - start) >>> 1); + if (this.lineStarts[mid] <= offset) { + result = mid; + start = mid + 1; + } + else if (this.lineStarts[mid] > offset) { + end = mid - 1; } } + return result; + } + /** + * get line starts + * @returns + */ + getLineStarts() { + return this.lineStarts; + } + /** + * add line start + */ + addLineStart(lineStart) { + this.lineStarts.push(lineStart); } } -function dedup(values) { - for (const value of values) { - let i = value.length; - while (i-- > 1) { - const t = value[i]; - const k = value[i == 1 ? 0 : i % 2]; - if (t.val == k.val && t.val == 0) { - if ((t.typ == exports.EnumToken.NumberTokenType && isLength(k)) || - (k.typ == exports.EnumToken.NumberTokenType && isLength(t)) || - isLength(k) || - isLength(t)) { - value.splice(i, 1); - continue; - } - } - if (eq(t, k)) { - value.splice(i, 1); - continue; - } - break; +/** + * match url + */ +const matchUrl = /^(https?:)?\/\//; +const windowsPathnameRegexp = /^\/?[a-zA-Z]:/; +/** + * return the directory name of a path + * @param path + * + * @private + */ +function dirname(path) { + if (path === "") { + return ""; + } + if (path.startsWith("data:")) { + return path; + } + let i = 0; + let parts = [""]; + for (; i < path.length; i++) { + const chr = path.charAt(i); + if (chr == "/") { + parts.push(""); + } + else { + parts[parts.length - 1] += chr; } } - return values; + parts.pop(); + return parts.join("/"); } -class PropertySet { - config; - declarations; - constructor(config) { - this.config = config; - this.declarations = new Map(); +/** + * split path + * @param result + * @private + */ +function splitPath(result) { + if (result.length == 0) { + return { parts: [], i: 0 }; } - add(declaration) { - if (declaration.nam == this.config.shorthand) { - this.declarations = new Map(); + const parts = result == "/" ? [] : [""]; + let i = 0; + for (; i < result.length; i++) { + const chr = result.charAt(i); + if (chr == "/") { + parts.push(""); } + // else if (chr == "?" || chr == "#") { + // break; + // } else { - // expand shorthand - if (this.declarations.has(this.config.shorthand)) { - let isValid = true; - let current = -1; - const tokens = []; - // @ts-ignore - for (let token of this.declarations.get(this.config.shorthand).val) { - if ( - // @ts-expect-error - this.config.types.some((t) => token.typ == exports.EnumToken[t]) || - (token.typ == exports.EnumToken.NumberTokenType && - token.val == 0 && - (this.config.types.includes("Length") || - this.config.types.includes("Angle") || - this.config.types.includes("Dimension")))) { - if (tokens.length == 0) { - tokens.push([]); - current++; - } - tokens[current].push(token); - continue; - } - if (token.typ != exports.EnumToken.WhitespaceTokenType && token.typ != exports.EnumToken.CommentTokenType) { - if (token.typ == exports.EnumToken.IdenTokenType && - this.config.keywords.includes(token.val)) { - if (tokens.length == 0) { - tokens.push([]); - current++; - } - tokens[current].push(token); - } - // @ts-ignore - if (token.typ == exports.EnumToken.LiteralTokenType && token.val == this.config.separator?.val) { - tokens.push([]); - current++; - continue; - } - isValid = false; - break; - } - } - if (isValid && tokens.length > 0) { - this.declarations.delete(this.config.shorthand); - for (const values of tokens) { - this.config.properties.forEach((property, index) => { - if (!this.declarations.has(property)) { - this.declarations.set(property, { - typ: exports.EnumToken.DeclarationNodeType, - nam: property, - val: [], - }); - } - while (index > 0 && index >= values.length) { - if (index > 1) { - index %= 2; - } - else { - index = 0; - break; - } - } - // @ts-ignore - const val = this.declarations.get(property).val; - if (val.length > 0) { - val.push({ typ: exports.EnumToken.WhitespaceTokenType }); - } - val.push({ ...values[index] }); - }); - } - } - this.declarations.set(declaration.nam, declaration); - return this; - } + parts[parts.length - 1] += chr; } - this.declarations.set(declaration.nam, declaration); - return this; } - isShortHand() { - if (this.declarations.has(this.config.shorthand)) { - return this.declarations.size == 1; - } - return this.config.properties.length == this.declarations.size; + // let k: number = -1; + // while (++k < parts.length) { + // if (parts[k] == ".") { + // parts.splice(k--, 1); + // } else if (parts[k] == "..") { + // parts.splice(k - 1, 2); + // k -= 2; + // } + // } + return { parts, i }; +} +/** + * Nomalize path + * @param path + * @private + */ +const normalize$1 = memoize(function (path) { + let parts = []; + let i = 0; + if (path.includes("\\")) { + path = path.replace(/(\\)/g, "/"); } - [Symbol.iterator]() { - let iterator; - const declarations = this.declarations; - if (declarations.size < this.config.properties.length) { - const values = [...declarations.values()]; - if (this.isShortHand()) { - const val = values[0].val.reduce((acc, curr) => { - if (![exports.EnumToken.WhitespaceTokenType, exports.EnumToken.CommentTokenType].includes(curr.typ)) { - acc.push(curr); - } - return acc; - }, []); - values[0].val = val.reduce((acc, curr) => { - if (acc.length > 0) { - acc.push({ typ: exports.EnumToken.WhitespaceTokenType }); - } - acc.push(curr); - return acc; - }, []); + if (windowsPathnameRegexp.test(path)) { + path = path.replace(windowsPathnameRegexp, ""); + } + for (; i < path.length; i++) { + const chr = path.charAt(i); + if (chr == "/") { + if (parts.length == 0 || parts[parts.length - 1] !== "") { + parts.push(""); } - return values[Symbol.iterator](); + } + else if (chr == "?" || chr == "#") { + break; } else { - const values = []; - this.config.properties.forEach((property) => { - let index = 0; - // @ts-ignore - for (const token of this.declarations.get(property)?.val ?? []) { - if (token == null) { - return this.declarations.values()[Symbol.iterator](); - } - if (token.typ == exports.EnumToken.WhitespaceTokenType) { - continue; - } - if (values.length == index) { - values.push([]); - } - values[index].push(token); - index++; - } - }); - if (this.config.valueSeparator == null) { - dedup(values); + if (parts.length == 0) { + parts.push(""); } - iterator = [ - { - typ: exports.EnumToken.DeclarationNodeType, - nam: this.config.shorthand, - val: values.reduce((acc, curr) => { - if (curr.length > 1) { - const k = curr.length * 2 - 1; - let i = 1; - while (i < k) { - curr.splice(i, 0, { typ: exports.EnumToken.WhitespaceTokenType }); - i += 2; - } - } - if (acc.length > 0) { - // @ts-ignore - acc.push({ ...this.config.separator, typ: exports.EnumToken.LiteralTokenType }); - } - for (const token of curr) { - acc.push(token); - } - return acc; - }, []), - }, - ][Symbol.iterator](); + parts[parts.length - 1] += chr; } - return iterator; } -} - -var properties = { - gap: { - shorthand: "gap", - properties: [ - "row-gap", - "column-gap" - ], - types: [ - "Length", - "Perc" - ], - multiple: false, - separator: null, - keywords: [ - "normal" - ] - }, - "row-gap": { - shorthand: "gap" - }, - "column-gap": { - shorthand: "gap" - }, - inset: { - shorthand: "inset", - properties: [ - "top", - "right", - "bottom", - "left" - ], - types: [ - "Length", - "Perc" - ], - multiple: false, - separator: null, - keywords: [ - "auto" - ] - }, - top: { - shorthand: "inset" - }, - right: { - shorthand: "inset" - }, - bottom: { - shorthand: "inset" - }, - left: { - shorthand: "inset" - }, - margin: { - shorthand: "margin", - properties: [ - "margin-top", - "margin-right", - "margin-bottom", - "margin-left" - ], - types: [ - "Length", - "Perc" - ], - multiple: false, - separator: null, - keywords: [ - "auto" - ] - }, - "margin-top": { - shorthand: "margin" - }, - "margin-right": { - shorthand: "margin" - }, - "margin-bottom": { - shorthand: "margin" - }, - "margin-left": { - shorthand: "margin" - }, - padding: { - shorthand: "padding", - properties: [ - "padding-top", - "padding-right", - "padding-bottom", - "padding-left" - ], - types: [ - "Length", - "Perc" - ], - keywords: [ - ] - }, - "padding-top": { - shorthand: "padding" - }, - "padding-right": { - shorthand: "padding" - }, - "padding-bottom": { - shorthand: "padding" - }, - "padding-left": { - shorthand: "padding" - }, - "scroll-margin": { - shorthand: "scroll-margin", - properties: [ - "scroll-margin-top", - "scroll-margin-right", - "scroll-margin-bottom", - "scroll-margin-left" - ], - types: [ - "Length", - "Perc" - ], - "default": [ - "0" - ], - keywords: [ - "auto" - ] - }, - "scroll-margin-top": { - shorthand: "scroll-margin" - }, - "scroll-margin-right": { - shorthand: "scroll-margin" - }, - "scroll-margin-bottom": { - shorthand: "scroll-margin" - }, - "scroll-margin-left": { - shorthand: "scroll-margin" - }, - "scroll-padding": { - shorthand: "scroll-padding", - properties: [ - "scroll-padding-top", - "scroll-padding-right", - "scroll-padding-bottom", - "scroll-padding-left" - ], - types: [ - "Length", - "Perc" - ], - keywords: [ - "auto" - ] - }, - "scroll-padding-top": { - shorthand: "scroll-padding" - }, - "scroll-padding-right": { - shorthand: "scroll-padding" - }, - "scroll-padding-bottom": { - shorthand: "scroll-padding" - }, - "scroll-padding-left": { - shorthand: "scroll-padding" - }, - "border-radius": { - shorthand: "border-radius", - properties: [ - "border-top-left-radius", - "border-top-right-radius", - "border-bottom-right-radius", - "border-bottom-left-radius" - ], - types: [ - "Length", - "Perc" - ], - multiple: true, - separator: { - typ: "Literal", - val: "/" - }, - keywords: [ - ] - }, - "border-top-left-radius": { - shorthand: "border-radius" - }, - "border-top-right-radius": { - shorthand: "border-radius" - }, - "border-bottom-right-radius": { - shorthand: "border-radius" - }, - "border-bottom-left-radius": { - shorthand: "border-radius" - }, - "border-width": { - shorthand: "border-width", - map: "border", - properties: [ - "border-top-width", - "border-right-width", - "border-bottom-width", - "border-left-width" - ], - types: [ - "Length", - "Perc" - ], - "default": [ - "medium" - ], - keywords: [ - "thin", - "medium", - "thick" - ] - }, - "border-top-width": { - map: "border", - shorthand: "border-width" - }, - "border-right-width": { - map: "border", - shorthand: "border-width" - }, - "border-bottom-width": { - map: "border", - shorthand: "border-width" - }, - "border-left-width": { - map: "border", - shorthand: "border-width" - }, - "border-style": { - shorthand: "border-style", - map: "border", - properties: [ - "border-top-style", - "border-right-style", - "border-bottom-style", - "border-left-style" - ], - types: [ - ], - "default": [ - "none" - ], - keywords: [ - "none", - "hidden", - "dotted", - "dashed", - "solid", - "double", - "groove", - "ridge", - "inset", - "outset" - ] - }, - "border-top-style": { - map: "border", - shorthand: "border-style" - }, - "border-right-style": { - map: "border", - shorthand: "border-style" - }, - "border-bottom-style": { - map: "border", - shorthand: "border-style" - }, - "border-left-style": { - map: "border", - shorthand: "border-style" - }, - "border-color": { - shorthand: "border-color", - map: "border", - properties: [ - "border-top-color", - "border-right-color", - "border-bottom-color", - "border-left-color" - ], - types: [ - "Color" - ], - "default": [ - "currentcolor" - ], - keywords: [ - ] - }, - "border-top-color": { - map: "border", - shorthand: "border-color" - }, - "border-right-color": { - map: "border", - shorthand: "border-color" - }, - "border-bottom-color": { - map: "border", - shorthand: "border-color" - }, - "border-left-color": { - map: "border", - shorthand: "border-color" - } -}; -var map = { - "flex-flow": { - shorthand: "flex-flow", - pattern: "flex-direction flex-wrap", - keywords: [ - ], - "default": [ - "row", - "nowrap" - ], - properties: { - "flex-direction": { - keywords: [ - "row", - "row-reverse", - "column", - "column-reverse" - ], - "default": [ - "row" - ], - types: [ - ] - }, - "flex-wrap": { - keywords: [ - "wrap", - "nowrap", - "wrap-reverse" - ], - "default": [ - "nowrap" - ], - types: [ - ] - } - } - }, - "flex-direction": { - shorthand: "flex-flow" - }, - "flex-wrap": { - shorthand: "flex-flow" - }, - "scroll-timeline": { - shorthand: "scroll-timeline", - pattern: "scroll-timeline-name scroll-timeline-axis", - keywords: [ - "none", - "block", - "inline", - "x", - "y" - ], - "default": [ - "none", - "block" - ], - properties: { - "scroll-timeline-name": { - keywords: [ - "none" - ], - "default": [ - "none" - ], - types: [ - "DashedIden" - ] - }, - "scroll-timeline-axis": { - keywords: [ - "block", - "inline", - "x", - "y" - ], - "default": [ - "block" - ], - types: [ - ] - } - } - }, - "scroll-timeline-name": { - shorthand: "scroll-timeline" - }, - "scroll-timeline-axis": { - shorthand: "scroll-timeline" - }, - container: { - shorthand: "container", - pattern: "container-name container-type", - keywords: [ - ], - "default": [ - ], - properties: { - "container-name": { - required: true, - multiple: true, - keywords: [ - "none" - ], - "default": [ - "none" - ], - types: [ - "Iden", - "DashedIden" - ] - }, - "container-type": { - previous: "container-name", - prefix: { - typ: "Literal", - val: "/" - }, - keywords: [ - "size", - "inline-size", - "normal" - ], - "default": [ - "normal" - ], - types: [ - ] - } - } - }, - "container-name": { - shorthand: "container" - }, - "container-type": { - shorthand: "container" - }, - flex: { - shorthand: "flex", - pattern: "flex-grow flex-shrink flex-basis", - keywords: [ - "auto", - "none", - "initial" - ], - "default": [ - ], - mapping: { - "0 1 auto": "initial", - "0 0 auto": "none", - "1 1 auto": "auto" - }, - properties: { - "flex-grow": { - required: true, - keywords: [ - ], - "default": [ - ], - types: [ - "Number" - ] - }, - "flex-shrink": { - keywords: [ - ], - "default": [ - ], - types: [ - "Number" - ] - }, - "flex-basis": { - keywords: [ - "max-content", - "min-content", - "fit-content", - "fit-content", - "content", - "auto" - ], - "default": [ - ], - types: [ - "Length", - "Perc" - ] - } - } - }, - "flex-grow": { - shorthand: "flex" - }, - "flex-shrink": { - shorthand: "flex" - }, - "flex-basis": { - shorthand: "flex" - }, - columns: { - shorthand: "columns", - pattern: "column-count column-width", - keywords: [ - "auto" - ], - "default": [ - "auto", - "auto auto" - ], - properties: { - "column-count": { - keywords: [ - "auto" - ], - "default": [ - "auto" - ], - types: [ - "Number" - ] - }, - "column-width": { - keywords: [ - "auto" - ], - "default": [ - "auto" - ], - types: [ - "Length" - ] - } - } - }, - "column-count": { - shorthand: "columns" - }, - "column-width": { - shorthand: "columns" - }, - transition: { - shorthand: "transition", - multiple: true, - separator: { - typ: "Comma" - }, - pattern: "transition-property transition-duration transition-timing-function transition-delay transition-behavior", - keywords: [ - "none", - "all" - ], - "default": [ - "0s", - "0ms", - "all", - "ease", - "none", - "normal" - ], - mapping: { - "cubic-bezier(.25,.1,.25,1)": "ease", - "cubic-bezier(0,0,1,1)": "linear", - "cubic-bezier(.42,0,1,1)": "ease-in", - "cubic-bezier(0,0,.58,1)": "ease-out", - "cubic-bezier(.42,0,.58,.42)": "ease-in-out" - }, - properties: { - "transition-property": { - keywords: [ - "none", - "all" - ], - "default": [ - "all" - ], - types: [ - "Iden" - ] - }, - "transition-duration": { - keywords: [ - ], - "default": [ - "0s", - "0ms", - "normal" - ], - types: [ - "Time" - ] - }, - "transition-timing-function": { - keywords: [ - "ease", - "ease-in", - "ease-out", - "ease-in-out", - "linear", - "step-start", - "step-end" - ], - "default": [ - "ease" - ], - types: [ - "TimingFunction" - ], - mapping: { - "cubic-bezier(.25,.1,.25,1)": "ease", - "cubic-bezier(0,0,1,1)": "linear", - "cubic-bezier(.42,0,1,1)": "ease-in", - "cubic-bezier(0,0,.58,1)": "ease-out", - "cubic-bezier(.42,0,.58,.42)": "ease-in-out" - } - }, - "transition-delay": { - keywords: [ - ], - "default": [ - "0s" - ], - types: [ - "Time" - ] - }, - "transition-behavior": { - keywords: [ - "normal", - "allow-discrete" - ], - "default": [ - "normal" - ], - types: [ - ] - } - } - }, - "transition-property": { - shorthand: "transition" - }, - "transition-duration": { - shorthand: "transition" - }, - "transition-timing-function": { - shorthand: "transition" - }, - "transition-delay": { - shorthand: "transition" - }, - "transition-behavior": { - shorthand: "transition" - }, - animation: { - shorthand: "animation", - separator: { - typ: "Comma" - }, - pattern: "animation-name animation-duration animation-timing-function animation-delay animation-iteration-count animation-direction animation-fill-mode animation-play-state animation-timeline", - "default": [ - "1", - "0s", - "0ms", - "none", - "ease", - "normal", - "running", - "auto" - ], - properties: { - "animation-name": { - keywords: [ - "none" - ], - "default": [ - "none" - ], - types: [ - "Iden" - ] - }, - "animation-duration": { - keywords: [ - "auto" - ], - "default": [ - "0s", - "0ms", - "auto" - ], - types: [ - "Time" - ], - mapping: { - auto: "0s" - } - }, - "animation-timing-function": { - keywords: [ - "ease", - "ease-in", - "ease-out", - "ease-in-out", - "linear", - "step-start", - "step-end" - ], - "default": [ - "ease" - ], - types: [ - "TimingFunction" - ], - mapping: { - "cubic-bezier(.25,.1,.25,1)": "ease", - "cubic-bezier(0,0,1,1)": "linear", - "cubic-bezier(.42,0,1,1)": "ease-in", - "cubic-bezier(0,0,.58,1)": "ease-out", - "cubic-bezier(.42,0,.58,.42)": "ease-in-out" - } - }, - "animation-delay": { - keywords: [ - ], - "default": [ - "0s", - "0ms" - ], - types: [ - "Time" - ] - }, - "animation-iteration-count": { - keywords: [ - "infinite" - ], - "default": [ - "1" - ], - types: [ - "Number" - ] - }, - "animation-direction": { - keywords: [ - "normal", - "reverse", - "alternate", - "alternate-reverse" - ], - "default": [ - "normal" - ], - types: [ - ] - }, - "animation-fill-mode": { - keywords: [ - "none", - "forwards", - "backwards", - "both" - ], - "default": [ - "none" - ], - types: [ - ] - }, - "animation-play-state": { - keywords: [ - "running", - "paused" - ], - "default": [ - "running" - ], - types: [ - ] - }, - "animation-timeline": { - keywords: [ - "none", - "auto" - ], - "default": [ - "auto" - ], - types: [ - "DashedIden", - "TimelineFunction" - ] - } - } - }, - "animation-name": { - shorthand: "animation" - }, - "animation-duration": { - shorthand: "animation" - }, - "animation-timing-function": { - shorthand: "animation" - }, - "animation-delay": { - shorthand: "animation" - }, - "animation-iteration-count": { - shorthand: "animation" - }, - "animation-direction": { - shorthand: "animation" - }, - "animation-fill-mode": { - shorthand: "animation" - }, - "animation-play-state": { - shorthand: "animation" - }, - "animation-timeline": { - shorthand: "animation" - }, - "text-emphasis": { - shorthand: "text-emphasis", - pattern: "text-emphasis-color text-emphasis-style", - "default": [ - "none", - "currentcolor" - ], - properties: { - "text-emphasis-style": { - keywords: [ - "none", - "filled", - "open", - "dot", - "circle", - "double-circle", - "triangle", - "sesame" - ], - "default": [ - "none" - ], - types: [ - "String" - ] - }, - "text-emphasis-color": { - "default": [ - "currentcolor" - ], - types: [ - "Color" - ] - } - } - }, - "text-emphasis-style": { - shorthand: "text-emphasis" - }, - "text-emphasis-color": { - shorthand: "text-emphasis" - }, - border: { - shorthand: "border", - pattern: "border-color border-style border-width", - keywords: [ - "none" - ], - "default": [ - "0", - "none" - ], - properties: { - "border-color": { - types: [ - "Color" - ], - "default": [ - "currentcolor" - ], - keywords: [ - ] - }, - "border-style": { - types: [ - ], - "default": [ - "none" - ], - keywords: [ - "none", - "hidden", - "dotted", - "dashed", - "solid", - "double", - "groove", - "ridge", - "inset", - "outset" - ] - }, - "border-width": { - types: [ - "Length", - "Perc" - ], - "default": [ - "medium" - ], - keywords: [ - "thin", - "medium", - "thick" - ] - } - } - }, - "border-color": { - shorthand: "border" - }, - "border-style": { - shorthand: "border" - }, - "border-width": { - shorthand: "border" - }, - "list-style": { - shorthand: "list-style", - pattern: "list-style-type list-style-position list-style-image", - keywords: [ - "none", - "outside" - ], - "default": [ - "none", - "outside" - ], - properties: { - "list-style-position": { - types: [ - ], - "default": [ - "outside" - ], - keywords: [ - "inside", - "outside" - ] - }, - "list-style-image": { - "default": [ - "none" - ], - keywords: [ - "node" - ], - types: [ - "UrlFunc", - "ImageFunc" - ] - }, - "list-style-type": { - types: [ - "String", - "Iden", - "Symbols" - ], - "default": [ - "disc" - ], - keywords: [ - "disc", - "circle", - "square", - "decimal", - "decimal-leading-zero", - "lower-roman", - "upper-roman", - "lower-greek", - "lower-latin", - "upper-latin", - "none" - ] - } - } - }, - "list-style-position": { - shorthand: "list-style" - }, - "list-style-image": { - shorthand: "list-style" - }, - "list-style-type": { - shorthand: "list-style" - }, - overflow: { - shorthand: "overflow", - pattern: "overflow-x overflow-y", - keywords: [ - "auto", - "visible", - "hidden", - "clip", - "scroll" - ], - "default": [ - ], - mapping: { - "visible visible": "visible", - "auto auto": "auto", - "hidden hidden": "hidden", - "scroll scroll": "scroll" - }, - properties: { - "overflow-x": { - "default": [ - ], - types: [ - ], - keywords: [ - "auto", - "visible", - "hidden", - "clip", - "scroll" - ] - }, - "overflow-y": { - "default": [ - ], - types: [ - ], - keywords: [ - "auto", - "visible", - "hidden", - "clip", - "scroll" - ] - } - } - }, - "overflow-x": { - shorthand: "overflow" - }, - "overflow-y": { - shorthand: "overflow" - }, - outline: { - shorthand: "outline", - pattern: "outline-color outline-style outline-width", - keywords: [ - "none" - ], - "default": [ - "0", - "none", - "currentcolor" - ], - properties: { - "outline-color": { - types: [ - "Color" - ], - "default": [ - "currentcolor" - ], - keywords: [ - "currentcolor" - ] - }, - "outline-style": { - types: [ - ], - "default": [ - "none" - ], - keywords: [ - "auto", - "none", - "dotted", - "dashed", - "solid", - "double", - "groove", - "ridge", - "inset", - "outset" - ] - }, - "outline-width": { - types: [ - "Length", - "Perc" - ], - "default": [ - "medium" - ], - keywords: [ - "thin", - "medium", - "thick" - ] - } - } - }, - "outline-color": { - shorthand: "outline" - }, - "outline-style": { - shorthand: "outline" - }, - "outline-width": { - shorthand: "outline" - }, - font: { - shorthand: "font", - pattern: "font-weight font-style font-size line-height font-stretch font-variant font-family", - keywords: [ - "caption", - "icon", - "menu", - "message-box", - "small-caption", - "status-bar", - "-moz-window, ", - "-moz-document, ", - "-moz-desktop, ", - "-moz-info, ", - "-moz-dialog", - "-moz-button", - "-moz-pull-down-menu", - "-moz-list", - "-moz-field" - ], - "default": [ - ], - properties: { - "font-weight": { - types: [ - "Number" - ], - "default": [ - "400", - "normal" - ], - keywords: [ - "normal", - "bold", - "lighter", - "bolder" - ], - constraints: { - value: { - min: "1", - max: "1000" - } - }, - mapping: { - thin: "100", - hairline: "100", - "extra light": "200", - "ultra light": "200", - light: "300", - normal: "400", - regular: "400", - medium: "500", - "semi bold": "600", - "demi bold": "600", - bold: "700", - "extra bold": "800", - "ultra bold": "800", - black: "900", - heavy: "900", - "extra black": "950", - "ultra black": "950" - } - }, - "font-style": { - types: [ - "Angle" - ], - "default": [ - "normal" - ], - keywords: [ - "normal", - "italic", - "oblique" - ] - }, - "font-size": { - types: [ - "Length", - "Perc" - ], - "default": [ - ], - keywords: [ - "xx-small", - "x-small", - "small", - "medium", - "large", - "x-large", - "xx-large", - "xxx-large", - "larger", - "smaller" - ], - required: true - }, - "line-height": { - types: [ - "Length", - "Perc", - "Number" - ], - "default": [ - "normal" - ], - keywords: [ - "normal" - ], - previous: "font-size", - prefix: { - typ: "Literal", - val: "/" - } - }, - "font-stretch": { - types: [ - "Perc" - ], - "default": [ - "normal" - ], - keywords: [ - "ultra-condensed", - "extra-condensed", - "condensed", - "semi-condensed", - "normal", - "semi-expanded", - "expanded", - "extra-expanded", - "ultra-expanded" - ], - mapping: { - "ultra-condensed": "50%", - "extra-condensed": "62.5%", - condensed: "75%", - "semi-condensed": "87.5%", - normal: "100%", - "semi-expanded": "112.5%", - expanded: "125%", - "extra-expanded": "150%", - "ultra-expanded": "200%" - } - }, - "font-variant": { - types: [ - ], - "default": [ - "normal" - ], - keywords: [ - "normal", - "none", - "common-ligatures", - "no-common-ligatures", - "discretionary-ligatures", - "no-discretionary-ligatures", - "historical-ligatures", - "no-historical-ligatures", - "contextual", - "no-contextual", - "historical-forms", - "small-caps", - "all-small-caps", - "petite-caps", - "all-petite-caps", - "unicase", - "titling-caps", - "ordinal", - "slashed-zero", - "lining-nums", - "oldstyle-nums", - "proportional-nums", - "tabular-nums", - "diagonal-fractions", - "stacked-fractions", - "ordinal", - "slashed-zero", - "ruby", - "jis78", - "jis83", - "jis90", - "jis04", - "simplified", - "traditional", - "full-width", - "proportional-width", - "ruby", - "sub", - "super", - "text", - "emoji", - "unicode" - ] - }, - "font-family": { - types: [ - "String", - "Iden" - ], - "default": [ - ], - keywords: [ - "serif", - "sans-serif", - "monospace", - "cursive", - "fantasy", - "system-ui", - "ui-serif", - "ui-sans-serif", - "ui-monospace", - "ui-rounded", - "math", - "emoji", - "fangsong" - ], - required: true, - multiple: true, - separator: { - typ: "Comma" - } - } - } - }, - "font-weight": { - shorthand: "font" - }, - "font-style": { - shorthand: "font" - }, - "font-size": { - shorthand: "font" - }, - "line-height": { - shorthand: "font" - }, - "font-stretch": { - shorthand: "font" - }, - "font-variant": { - shorthand: "font" - }, - "font-family": { - shorthand: "font" - }, - background: { - shorthand: "background", - pattern: "background-attachment background-origin background-clip background-color background-image background-repeat background-position background-size", - keywords: [ - "none" - ], - "default": [ - "0 0", - "none", - "auto", - "repeat", - "transparent", - "#0000", - "scroll", - "padding-box", - "border-box" - ], - multiple: true, - set: { - "background-origin": [ - "background-clip" - ] - }, - separator: { - typ: "Comma" - }, - properties: { - "background-repeat": { - types: [ - ], - "default": [ - "repeat" - ], - multiple: true, - keywords: [ - "repeat-x", - "repeat-y", - "repeat", - "space", - "round", - "no-repeat" - ], - mapping: { - "repeat no-repeat": "repeat-x", - "no-repeat repeat": "repeat-y", - "repeat repeat": "repeat", - "space space": "space", - "round round": "round", - "no-repeat no-repeat": "no-repeat" - } - }, - "background-color": { - types: [ - "Color" - ], - "default": [ - "#0000", - "transparent" - ], - multiple: true, - keywords: [ - ] - }, - "background-image": { - types: [ - "UrlFunc", - "ImageFunc" - ], - "default": [ - "none" - ], - keywords: [ - "none" - ] - }, - "background-attachment": { - types: [ - ], - "default": [ - "scroll" - ], - multiple: true, - keywords: [ - "scroll", - "fixed", - "local" - ] - }, - "background-clip": { - types: [ - ], - "default": [ - "border-box" - ], - multiple: true, - keywords: [ - "border-box", - "padding-box", - "content-box", - "text" - ] - }, - "background-origin": { - types: [ - ], - "default": [ - "padding-box" - ], - multiple: true, - keywords: [ - "border-box", - "padding-box", - "content-box" - ] - }, - "background-position": { - multiple: true, - types: [ - "Perc", - "Length" - ], - "default": [ - "0 0", - "top left", - "left top" - ], - keywords: [ - "top", - "left", - "center", - "bottom", - "right" - ], - mapping: { - left: "0", - top: "0", - center: "50%", - "center center": "50%", - "50% 50%": "50%", - bottom: "100%", - right: "100%" - }, - constraints: { - mapping: { - max: 2 - } - } - }, - "background-size": { - multiple: true, - previous: "background-position", - prefix: { - typ: "Literal", - val: "/" - }, - types: [ - "Perc", - "Length" - ], - "default": [ - "auto", - "auto auto" - ], - keywords: [ - "auto", - "cover", - "contain" - ], - mapping: { - "auto auto": "auto" - } - } - } - }, - "background-repeat": { - shorthand: "background" - }, - "background-color": { - shorthand: "background" - }, - "background-image": { - shorthand: "background" - }, - "background-attachment": { - shorthand: "background" - }, - "background-clip": { - shorthand: "background" - }, - "background-origin": { - shorthand: "background" - }, - "background-position": { - shorthand: "background" - }, - "background-size": { - shorthand: "background" - }, - "grid-area": { - shorthand: "grid-area", - pattern: "grid-row-start grid-column-start grid-row-end grid-column-end", - keywords: [ - "auto" - ], - "default": [ - "auto" - ], - multiple: false, - expandShorthands: true, - separator: { - typ: "Literal", - val: "/" - }, - mapping: { - "auto/auto": "auto", - "auto/auto/auto": "auto", - "auto/auto/auto/auto": "auto" - }, - expandedProperties: { - "grid-row": [ - "grid-row-start", - "grid-row-end" - ], - "grid-column": [ - "grid-column-start", - "grid-column-end" - ] - }, - properties: { - "grid-row-start": { - map: "grid-area", - shorthand: "grid-row", - types: [ - "Iden", - "Number" - ], - "default": [ - "auto" - ], - keywords: [ - "auto" - ] - }, - "grid-column-start": { - map: "grid-area", - shorthand: "grid-column", - types: [ - "Iden", - "Number" - ], - prefix: { - typ: "Literal", - val: "/" - }, - "default": [ - "auto" - ], - keywords: [ - "auto" - ] - }, - "grid-row-end": { - map: "grid-area", - shorthand: "grid-row", - types: [ - "Iden", - "Number" - ], - prefix: { - typ: "Literal", - val: "/" - }, - "default": [ - "auto" - ], - keywords: [ - "auto" - ] - }, - "grid-column-end": { - map: "grid-area", - shorthand: "grid-column", - types: [ - "Iden", - "Number" - ], - prefix: { - typ: "Literal", - val: "/" - }, - "default": [ - "auto" - ], - keywords: [ - "auto" - ] - } - } - }, - "grid-row-start": { - shorthand: "grid-row" - }, - "grid-column-start": { - shorthand: "grid-column" - }, - "grid-row-end": { - shorthand: "grid-row" - }, - "grid-column-end": { - shorthand: "grid-column" - }, - "grid-row": { - shorthand: "grid-row", - map: "grid-area", - pattern: "grid-row-start grid-row-end", - types: [ - "Iden", - "Number" - ], - keywords: [ - "auto" - ], - "default": [ - "auto" - ], - multiple: false, - mapping: { - "auto auto": "auto", - "auto/auto": "auto" - }, - properties: { - "grid-row-start": { - map: "grid-area", - types: [ - "Iden", - "Number" - ], - "default": [ - "auto" - ], - multiple: true, - keywords: [ - "auto" - ] - }, - "grid-row-end": { - map: "grid-area", - types: [ - "Iden", - "Number" - ], - "default": [ - "auto" - ], - prefix: { - typ: "Literal", - val: "/" - }, - multiple: true, - keywords: [ - "auto" - ] - } - } - }, - "grid-column": { - shorthand: "grid-column", - map: "grid-area", - pattern: "grid-column-start grid-column-end", - types: [ - "Iden", - "Number" - ], - keywords: [ - "auto" - ], - "default": [ - "auto" - ], - multiple: false, - mapping: { - "auto/auto": "auto" - }, - properties: { - "grid-column-start": { - map: "grid-area", - types: [ - "Iden", - "Number" - ], - "default": [ - "auto" - ], - multiple: true, - keywords: [ - "auto" - ] - }, - "grid-column-end": { - map: "grid-area", - types: [ - "Iden", - "Number" - ], - "default": [ - "auto" - ], - multiple: true, - prefix: { - typ: "Literal", - val: "/" - }, - keywords: [ - "auto" - ] - } - } - }, - "grid-template": { - shorthand: "grid-template", - pattern: "grid-template-areas grid-template-rows grid-template-columns", - keywords: [ - "none" - ], - "default": [ - "none" - ], - multiple: false, - mapping: { - "none none": "none", - "none/none": "none", - "none none/none": "none" - }, - properties: { - "grid-template-areas": { - types: [ - "String" - ], - "default": [ - "none" - ], - multiple: true, - keywords: [ - "none", - "auto" - ], - mapping: { - } - }, - "grid-template-rows": { - types: [ - "Perc", - "Iden", - "Length", - "Flex" - ], - "default": [ - "none" - ], - multiple: true, - keywords: [ - "none" - ] - }, - "grid-template-columns": { - types: [ - "Perc", - "Iden", - "Length", - "Flex" - ], - "default": [ - "none" - ], - prefix: { - typ: "Literal", - val: "/" - }, - multiple: true, - keywords: [ - "none" - ] - } - } - }, - "grid-template-areas": { - shorthand: "grid-template" - }, - "grid-template-rows": { - shorthand: "grid-template" - }, - "grid-template-columns": { - shorthand: "grid-template" - } -}; -var property = { - "transform-origin": { - pattern: [ - [ - "left|center|right", - "top|center|bottom", - "" - ], - [ - "left|center|right", - "top|center|bottom" - ], - [ - "center|left|right|center|top|bottom|" - ] - ], - mapping: { - center: { - typ: "Perc", - val: 50 - }, - left: { - typ: "Perc", - val: 0 - }, - right: { - typ: "Perc", - val: 100 - }, - top: { - typ: "Perc", - val: 0 - }, - bottom: { - typ: "Perc", - val: 100 - } - } - } -}; -var config$1 = { - properties: properties, - map: map, - property: property -}; + let k = -1; + while (++k < parts.length) { + // if (parts[k] == ".") { + // parts.splice(k--, 1); + // } else + if (k > 0 && parts[k] == "..") { + parts.splice(k - 1, 2); + k -= 2; + } + } + return (path.charAt(0) == "/" ? "/" : "") + parts.join("/"); +}); +/** + * diff path + * @param path1 + * @param path2 + * @private + */ +const diff$1 = memoize(function (path1, path2) { + let { parts } = splitPath(path1); + const { parts: dirs } = splitPath(path2); + for (const p of dirs) { + if (parts[0] == p) { + parts.shift(); + } + else { + parts.unshift(".."); + } + } + return parts.join("/"); +}); +/** + * resolve path + * @param url url or path to resolve + * @param currentDirectory directory used to resolve the path + * @param cwd current working directory + * + * @private + */ +const resolve = memoize(function (url, currentDirectory, cwd) { + if (matchUrl.test(url)) { + return { + absolute: url, + relative: url, + }; + } + cwd ??= ""; + currentDirectory ??= ""; + url = normalize$1(url); + if (cwd !== "") { + cwd = normalize$1(cwd); + } + if (currentDirectory !== "") { + currentDirectory = normalize$1(currentDirectory); + } + let dir = cwd || currentDirectory; + if (windowsPathnameRegexp.test(dir)) { + dir = dir.replace(windowsPathnameRegexp, ""); + } + const absolute = dir == "" || url.startsWith("/") || url.startsWith(dir) || windowsPathnameRegexp.test(url) + ? resolvePath(url) + : resolvePath(dir, url); + return { + absolute, + relative: dir === "" ? absolute : diff$1(absolute, dir), + }; +}); +/** + * + * @param parts + * @returns + * @private + */ +function resolvePath(...parts) { + const path = parts.filter(Boolean).join("/"); + const isAbsolute = /^[\\/]/.test(path); + const segments = path.split(/[\\/]+/); + const resolved = []; + for (const segment of segments) { + if (!segment || segment === ".") { + continue; + } + if (segment === "..") { + if (resolved.length && resolved[resolved.length - 1] !== "..") { + resolved.pop(); + } + else if (!isAbsolute) { + resolved.push(".."); + } + } + else { + resolved.push(segment); + } + } + let result = resolved.join("/"); + if (isAbsolute) { + result = "/" + result; + } + return result || (isAbsolute ? "/" : "."); +} -Object.freeze(config$1); -// @ts-expect-error -const getConfig = () => config$1; +/** + * Source file ID + */ +let sourceId = 0; +/** + * Source file helper class + */ +class SourceFile { + /** + * Source file ID + */ + id; + /** + * Source file path + */ + file; + /** + * Line map + */ + lineStarts; + /** + * Source file content + */ + content; + inputSourceMap = null; + /** + * Constructor + * @param content + * @param lines + * @param file + */ + constructor(content, lines, file = null) { + this.id = sourceId++; + this.content = content; + this.file = file; + this.lineStarts = new LineMap(lines); + } + /** + * Update source content + * @param content + */ + append(content) { + this.content += content; + } + /** + * get file name + * @returns + */ + getFileName() { + return this.file; + } + /** + * get content + * @returns + */ + getContent() { + return this.content; + } + /** + * get text + * @param start + * @param length + * @returns + */ + getText(start, length) { + return this.content.slice(start, start + length); + } + /** + * Compute line and column of the offset + * @param offset + * @returns + */ + getOffsets(offset) { + return this.lineStarts.getOffsets(offset); + } + /** + * get source location + * @param offset + * @returns + */ + getSourceLocation(offset) { + return [this.file, ...this.getOffsets(offset)]; + } + /** + * get line starts + * @returns + */ + getLineStarts() { + return this.lineStarts.getLineStarts(); + } + /** + * add line start + * @param lineStart + */ + addLineStart(lineStart) { + this.lineStarts.addLineStart(lineStart); + } + /** + * set input source map + * @param inputSourceMap + */ + setInputSourceMap(inputSourceMap) { + this.inputSourceMap = inputSourceMap == null ? null : new SourceMap(inputSourceMap); + } + /** + * return input source map + * @returns + */ + getInputSourceMap() { + return this.inputSourceMap; + } +} -function matchType(val, properties) { - if ((val.typ === exports.EnumToken.IdenTokenType && properties.keywords.includes(val.val)) || - properties.types.some( - // @ts-expect-error - (t) => - // @ts-expect-error - (val.typ === exports.EnumToken.IdenTokenType && exports.EnumToken[t] === exports.EnumToken.ColorTokenType && isColor(val)) || - // @ts-expect-error - exports.EnumToken[t] === val.typ)) { - return true; +/** + * render ast + * @param data + * @param options + * @param mapping + * @private + */ +function doRender(data, options = {}, mapping) { + const minify = options.minify ?? true; + const beautify = options.beautify ?? !minify; + options = { + ...(beautify + ? { + indent: " ", + newLine: "\n", + } + : { + indent: "", + newLine: "", + }), + ...(minify + ? { + removeEmpty: true, + removeComments: true, + minify: true, + } + : { + removeEmpty: false, + removeComments: false, + }), + minifyValues: true, + sourcemap: false, + convertColor: true, + expandNestingRules: false, + preserveLicense: false, + ...options, + }; + if (options.withParents) { + let parent; + while (data[PARENT] != null) { + // @ts-ignore + // parent = { ...data[PARENT], chi: [data] }; + parent = cloneNode(data[PARENT]); + // @ts-ignore + parent.chi = [data]; + // @ts-ignore + parent[PARENT] = data[PARENT][PARENT]; + // @ts-ignore + data = parent; + } } - if (val.typ === exports.EnumToken.NumberTokenType && val.val === 0) { + const startTime = performance.now(); + const errors = []; + const sourcemap = options.sourcemap ? new SourceMap() : null; + const sourcemaps = options.sourcemap ? { sources: [], maps: [] } : null; + const cache = Object.create(null); + const sourceLocation = { + end: 0, + }; + const lineMap = options.sourcemap ? new LineMap() : null; + let code = ""; + if (mapping != null) { + if (mapping.importMapping != null) { + const absolutePath = options.resolve(options.output != null ? dirname(options.output) : dirname(options.src), options.cwd).absolute; + for (let [key, value] of Object.entries(mapping.importMapping)) { + key = options.resolve(options.resolve(key, options.cwd).absolute, absolutePath).relative; + if (!key.startsWith("/") && !key.startsWith(".")) { + key = "./" + key; + } + code += `:import("${key}")${options.indent}{${options.newLine}${Object.entries(value).reduce((acc, [k, v]) => acc + (acc.length > 0 ? options.newLine : "") + `${options.indent}${v}:${options.indent}${k};`, "")}${options.newLine}}${options.newLine}`; + } + } + code += `:export${options.indent}{${options.newLine}${Object.entries(mapping.mapping).reduce((acc, [k, v]) => acc + (acc.length > 0 ? options.newLine : "") + `${options.indent}${k}:${options.indent}${v};`, "")}${options.newLine}}${options.newLine}`; + if (sourcemap != null) { + move(sourceLocation, lineMap, code); + } + } + if (options.output != null) { // @ts-ignore - return properties.types.some((type) => { - // @ts-ignore - const typ = exports.EnumToken[type]; - return typ == exports.EnumToken.LengthTokenType || typ == exports.EnumToken.AngleTokenType; - }); + options.output = options.resolve(options.output, options.cwd).absolute; } - if (tokensfuncSet.has(val.typ) && val.typ === exports.EnumToken.MathFunctionTokenType) { - return val.chi.every((t) => [ - exports.EnumToken.Add, - exports.EnumToken.Mul, - exports.EnumToken.Div, - exports.EnumToken.Sub, - exports.EnumToken.LiteralTokenType, - exports.EnumToken.CommaTokenType, - exports.EnumToken.WhitespaceTokenType, - exports.EnumToken.DimensionTokenType, - exports.EnumToken.NumberTokenType, - exports.EnumToken.LengthTokenType, - exports.EnumToken.AngleTokenType, - exports.EnumToken.PercentageTokenType, - exports.EnumToken.ResolutionTokenType, - exports.EnumToken.TimeTokenType, - exports.EnumToken.BinaryExpressionTokenType, - ].includes(t.typ) || matchType(t, properties)); + const result = { + code: code + + renderAstNode(options.expandNestingRules && + [exports.EnumToken.StyleSheetNodeType, exports.EnumToken.AtRuleNodeType, exports.EnumToken.RuleNodeType].includes(data.typ) && + "chi" in data + ? expand(data) + : data, options, sourcemaps, sourceLocation, lineMap, errors, function reducer(acc, curr) { + if (curr.typ == exports.EnumToken.CommentTokenType && options.removeComments) { + if (!options.preserveLicense || !curr.val.startsWith("/*!")) { + return acc; + } + return acc + curr.val; + } + return acc + renderValue$1(curr, options, cache, reducer); + }, cache), + errors, + stats: { + total: `${(performance.now() - startTime).toFixed(2)}ms`, + }, + }; + if (sourcemap != null) { + let source; + for (const sourceId of sourcemaps.sources) { + source = options.sourcesMap.get(sourceId); + sourcemap.addSourceContent(source.id, source.getFileName(), source.getContent()); + } + sourcemap.add(sourcemaps.maps); + result.map = sourcemap; + if (options.sourcemap === "inline") { + result.code += `\n/*# sourceMappingURL=${result.map.toUrl()} */`; + } + } + return result; +} +/** + * Update source map + * @param node + * @param options + * @param cache + * @param sourcemaps + * @param sourceLocation + * @param lineMap + * @param str + * + * @internal + */ +function updateSourceMap(node, options, cache, sourcemaps, sourceLocation, lineMap, str) { + let offset = 0; + // eat leanding whitespace + while (offset < str.length && isWhiteSpace(str.charCodeAt(offset))) { + offset++; + } + if (offset > 0) { + move(sourceLocation, lineMap, str, 0, offset + 1); + } + if (node[LOCSTA] != null) { + const source = options.sourcesMap.get(node[LOCSRCID]); + const inputSourceMap = source.getInputSourceMap(); + const offsets = source.getOffsets(node[LOCSTA]); + const [newLine, newColumn] = lineMap.getOffsets(sourceLocation.end); + let records = null; + let srcId = node[LOCSRCID]; + let sourceFileName = source.getFileName() || null; + let sourceContent; // = (source.getContent() as string) || null; + if (inputSourceMap != null && (records = inputSourceMap.find(offsets[0], offsets[1])) != null) { + let newId = null; + for (const record of records) { + newId = null; + // @ts-ignore + sourceFileName = record[0] || null; + // @ts-ignore + offsets[0] = record[1]; + // @ts-ignore + offsets[1] = record[2]; + sourceContent = record[3] || null; + if (sourceFileName != null && options.output != null && !sourceFileName.startsWith("data:")) { + if (cache[sourceFileName] == null) { + const absolute = options.resolve(dirname(options.output), options.cwd) + .absolute; + const absoluteSourcePath = options.resolve(dirname(options.src || ""), options.cwd).absolute; + // resolution is relative to the source file + const absoluteSourceFileName = options.resolve(sourceFileName, absoluteSourcePath) + .absolute; + cache[sourceFileName] = options.resolve(absoluteSourceFileName, absolute).relative; + } + sourceFileName = cache[sourceFileName]; + } + for (const [id, file] of options.sourcesMap.entries()) { + if (file.getFileName() === sourceFileName) { + newId = id; + break; + } + if (sourceFileName == null && file.getContent() === sourceContent) { + newId = id; + break; + } + } + if (newId == null) { + const source = new SourceFile(sourceContent, [], sourceFileName); + options.sourcesMap.set(source.id, source); + newId = source.id; + } + srcId = newId; + if (!sourcemaps.sources.includes(srcId)) { + sourcemaps.sources.push(srcId); + } + sourcemaps.maps.push([newLine, newColumn, srcId, offsets[0], offsets[1]]); + } + } + else { + // if (sourceFileName != null && options.output != null && !sourceFileName.startsWith("data:")) { + // if (cache[sourceFileName] == null) { + // const absolute = options.resolve!(dirname(options.output as string), options.cwd as string) + // .absolute as string; + // const absoluteSourceFileName = options.resolve!(sourceFileName, options.cwd as string) + // .absolute as string; + // cache[sourceFileName] = options.resolve!(absoluteSourceFileName, absolute).relative as string; + // } + // sourceFileName = cache[sourceFileName] as string; + // } + if (!sourcemaps.sources.includes(srcId)) { + sourcemaps.sources.push(srcId); + } + sourcemaps.maps.push([newLine, newColumn, srcId, offsets[0], offsets[1]]); + } + } + move(sourceLocation, lineMap, str, offset); +} +/** + * Update position + * @param sourceLocation + * @param lineMap + * @param str + * @param start + * @param end + */ +function move(sourceLocation, lineMap, str, start, end) { + let i = start ?? 0; + let j = end ?? str.length; + let codepoint; + let char; + for (; i < j; i++) { + char = str.charAt(i); + codepoint = char.charCodeAt(0); + sourceLocation.end += char.length; + if (codepoint == 0xa || // \n + codepoint == 0xb || // \v + codepoint == 0xc || // \f + codepoint == 0xd || // \r + codepoint == 0x2028 || // \u2028 + codepoint == 0x2029 // \u2029 + ) { + if (codepoint == 0xd && i + 1 < str.length && str.charCodeAt(i + 1) == 0xa) ; + else if (codepoint == 0xa && i > 0 && str.charCodeAt(i - 1) == 0xd) ; + else { + lineMap.addLineStart(sourceLocation.end); + } + } } - return false; } - -const propertiesConfig = getConfig(); -class PropertyMap { - declarations; - config; - requiredCount; - pattern; - constructor(config) { - const values = Object.values(config.properties); - this.requiredCount = - values.reduce((acc, curr) => (curr.required ? ++acc : acc), 0) || - values.length; - this.config = config; - this.declarations = new Map(); - this.pattern = config.pattern.split(/\s/); +/** + * render ast node + * @param data + * @param options + * @param sourcemaps + * @param sourceLocation + * @param lineMap + * @param errors + * @param reducer + * @param cache + * @param level + * @param indents + * + * @internal + */ +function renderAstNode(data, options, sourcemaps, sourceLocation, lineMap, errors, reducer, cache, level = 0, indents = []) { + if (indents.length < level + 1) { + indents.push(options.indent.repeat(level)); } - add(declaration) { - if (declaration.nam == this.config.shorthand) { - this.declarations.clear(); - this.declarations.set(declaration.nam, declaration); - this.matchTypes(declaration); - } - else { - const separator = this.config.separator != null - ? { - ...this.config.separator, - // @ts-ignore - typ: exports.EnumToken[this.config.separator.typ], + if (indents.length < level + 2) { + indents.push(options.indent.repeat(level + 1)); + } + // @ts-ignore + let children = ""; + let str = ""; + const indent = indents[level]; + const indentSub = indents[level + 1]; + switch (data.typ) { + case exports.EnumToken.DeclarationNodeType: + return `${data.nam}:${options.indent}${(options.minifyValues + ? filterValues$1(data.val) + : data.val).reduce(reducer, "")}`; + case exports.EnumToken.CommentNodeType: + case exports.EnumToken.CDOCOMMNodeType: + if (data.val.startsWith("/*# sourceMappingURL=")) { + // ignore sourcemap comment + return ""; + } + return !options.removeComments || (options.preserveLicense && data.val.startsWith("/*!")) + ? data.val + : ""; + case exports.EnumToken.StyleSheetNodeType: + for (const node of data.chi) { + str = renderAstNode(node, options, sourcemaps, sourceLocation, lineMap, errors, reducer, cache, level, indents); + if (str === "") { + continue; } - : null; - // expand shorthand - if (this.declarations.has(this.config.shorthand)) { - const tokens = {}; - const values = []; - const val = [[]]; - for (const curr of this.declarations.get(this.config.shorthand).val) { - // @ts-ignore - // @ts-ignore - if (separator != null && separator.typ == curr.typ && separator.val == curr.val) { - val.push([]); + if (children.length > 0) { + str = options.newLine + str; + } + children += str; + if (sourcemaps != null && str !== "" && options.newLine) { + move(sourceLocation, lineMap, options.newLine); + } + } + return children; + case exports.EnumToken.AtRuleNodeType: + case exports.EnumToken.RuleNodeType: + case exports.EnumToken.KeyframesRuleNodeType: + case exports.EnumToken.KeyframesAtRuleNodeType: + if ([exports.EnumToken.AtRuleNodeType, exports.EnumToken.KeyframesAtRuleNodeType].includes(data.typ) && !("chi" in data)) { + return `${indent}@${data.nam}${data.val === "" ? "" : options.indent || " "}${data.val};`; + } + const lineMapLength = lineMap ? lineMap.getLineStarts().length : 0; + const prelude = (indent.length > 0 ? options.newLine : "") + + indent + + ([exports.EnumToken.AtRuleNodeType, exports.EnumToken.KeyframesAtRuleNodeType].includes(data.typ) + ? `@${data.nam}${data.val === "" ? "" : options.indent || " "}${data.val}${options.indent}{` + : data.sel + `${options.indent}{`); + if (sourcemaps != null) { + updateSourceMap(data, options, cache, sourcemaps, sourceLocation, lineMap, prelude); + } + let node; + let recordDeclarationSourceMap = data.typ == exports.EnumToken.AtRuleNodeType; + for (let i = 0; i < data.chi.length; i++) { + node = data.chi[i]; + if (node.typ == exports.EnumToken.CommentNodeType) { + str = + options.removeComments && + (!options.preserveLicense || !node.val.startsWith("/*!")) + ? "" + : node.val; + } + else if (node.typ == exports.EnumToken.DeclarationNodeType) { + str = `${node.nam}:${options.indent}${(options.minifyValues + ? filterValues$1(node.val) + : node.val) + .reduce(reducer, "") + .trimEnd()};`; + } + else { + str = renderAstNode(node, options, sourcemaps, sourceLocation, lineMap, errors, reducer, cache, level + 1, indents); + if (str === "") { continue; } - // @ts-ignore - val.at(-1).push(curr); + children += str; + str = ""; + continue; } - for (let current = 0; current < val.length; current++) { - values.push(...this.pattern.reduce((acc, property) => { - // let current: number = 0; - const props = this.config.properties[property]; - for (let i = 0; i < acc.length; i++) { - if (acc[i].typ == exports.EnumToken.CommentTokenType || - acc[i].typ == exports.EnumToken.WhitespaceTokenType) { - acc.splice(i, 1); - i--; - continue; + if (str === "") { + continue; + } + str = options.newLine + indentSub + str; + children += str; + if (sourcemaps != null && str !== "") { + if (node.typ == exports.EnumToken.DeclarationNodeType && recordDeclarationSourceMap) { + // if declaration is child of at-rule, then record it + // .rule { + // @media screen { + // color: red; + // } + // } + // @ts-ignore + updateSourceMap(node, options, cache, sourcemaps, sourceLocation, lineMap, str); + } + else { + move(sourceLocation, lineMap, str); + } + } + } + if (children.endsWith(";")) { + children = children.slice(0, -1); + sourceLocation.end--; + } + if (options.removeEmpty && children === "") { + if (sourcemaps != null) { + sourceLocation.end -= prelude.length; + lineMap.getLineStarts().length = lineMapLength; + } + return ""; + } + const end = options.newLine + indent + `}`; + if (sourcemaps != null) { + move(sourceLocation, lineMap, end); + } + return prelude + children + end; + default: + return ""; + } +} +/** + * render ast token + * @param token + * @param options + * @param cache + * @param reducer + * @param errors + * @private + */ +function renderValue$1(token, options = {}, cache = Object.create(null), reducer, errors) { + if (token.typ === exports.EnumToken.WhenElseFunctionTokenType && + equalsIgnoreCase(token.val, "supports")) { + options = { ...options, minifyValues: false, minify: false, convertColor: false }; + reducer = null; + } + options.minifyValues ??= options.minify; + if (reducer == null) { + reducer = function (acc, curr) { + if (curr.typ == exports.EnumToken.CommentTokenType && options.removeComments) { + if (!options.preserveLicense || !curr.val.startsWith("/*!")) { + return acc; + } + return acc + curr.val; + } + return acc + renderValue$1(curr, options, cache, reducer); + }; + } + switch (token.typ) { + case exports.EnumToken.UrlFunctionTokenDefType: + case exports.EnumToken.FunctionTokenDefType: + case exports.EnumToken.MathFunctionTokenDefType: + case exports.EnumToken.ImageFunctionTokenDefType: + case exports.EnumToken.ColorFunctionTokenDefType: + case exports.EnumToken.TimingFunctionTokenDefType: + case exports.EnumToken.WhenElseFunctionTokenDefType: + case exports.EnumToken.SupportsFunctionTokenDefType: + case exports.EnumToken.TimelineFunctionTokenDefType: + case exports.EnumToken.GridTemplateFuncTokenDefType: + case exports.EnumToken.TransformFunctionTokenDefType: + case exports.EnumToken.ContainerFunctionTokenDefType: + case exports.EnumToken.PseudoClassFunctionTokenDefType: + case exports.EnumToken.GeneralEnclosedFunctionTokenDefType: + case exports.EnumToken.CustomFunctionTokenDefType: + case exports.EnumToken.WildCardFunctionTokenDefType: + return token.val + "("; + case exports.EnumToken.ListTokenType: + return token.chi.reduce((acc, curr) => acc + renderValue$1(curr, options, cache), ""); + case exports.EnumToken.BinaryExpressionTokenType: + if (exports.EnumToken.Mul === token.op || + exports.EnumToken.Div === token.op) { + let result = ""; + if (token.l.typ == exports.EnumToken.BinaryExpressionTokenType && + [exports.EnumToken.Add, exports.EnumToken.Sub].includes(token.l.op)) { + result = "(" + renderValue$1(token.l, options, cache) + ")"; + } + else { + result = renderValue$1(token.l, options, cache); + } + result += token.op == exports.EnumToken.Mul ? "*" : "/"; + if (token.r.typ == exports.EnumToken.BinaryExpressionTokenType && + [exports.EnumToken.Add, exports.EnumToken.Sub].includes(token.r.op)) { + result += "(" + renderValue$1(token.r, options, cache) + ")"; + } + else { + result += renderValue$1(token.r, options, cache); + } + return result; + } + return (renderValue$1(token.l, options, cache) + + (token.op == exports.EnumToken.Add ? " + " : " - ") + + renderValue$1(token.r, options, cache)); + case exports.EnumToken.FractionTokenType: + const fraction = renderValue$1(token.l) + "/" + renderValue$1(token.r); + if (+token.r.val != 0) { + const value = minifyNumber(+token.l.val / +token.r.val); + if (value.length <= fraction.length) { + return value; + } + } + return fraction; + case exports.EnumToken.Add: + return " + "; + case exports.EnumToken.Sub: + return " - "; + case exports.EnumToken.UniversalSelectorTokenType: + case exports.EnumToken.Star: + case exports.EnumToken.Mul: + return "*"; + case exports.EnumToken.Div: + return "/"; + case exports.EnumToken.WrappedValuesTokenType: + return ("{" + + token.chi.reduce((acc, curr) => acc + renderValue$1(curr, options, cache), "") + + "}"); + case exports.EnumToken.ColorTokenType: + if (token.kin == exports.ColorType.LIGHT_DARK || + ("chi" in token && options.convertColor === false && token.chi.length == 2)) { + return (token.val + + "(" + + token.chi.reduce((acc, curr) => { + if (curr.typ === exports.EnumToken.IdenTokenType) { + parseColor(curr); + } + return acc + renderValue$1(curr, options, cache); + }, "") + + ")"); + } + if (options.convertColor !== false) { + const value = convertColor(token, typeof options.convertColor === "boolean" + ? exports.ColorType.HEX + : (exports.ColorType[exports.ColorType[options.convertColor ?? "HEX"] + ?.toUpperCase?.() + .replaceAll?.("-", "_")] ?? exports.ColorType.HEX)); + if (value != null) { + token = value; + } + if (token.kin == exports.ColorType.HEX || token.kin == exports.ColorType.LIT) { + return reduceHexValue(token.val); + } + } + if (token.kin === exports.ColorType.SYS || + token.kin === exports.ColorType.DPSYS || + token.kin === exports.ColorType.NON_STD) { + return token.val; + } + if (token.kin == exports.ColorType.HEX || token.kin == exports.ColorType.LIT) { + return token.val; + } + if (Array.isArray(token.chi)) { + const fnName = token.val; + const isLegacy = ["rgb", "rgba", "hsl", "hsla"].includes(token.val); + const hasAlpha = [ + "rgb", + "rgba", + "hsl", + "hsla", + "hwb", + "oklab", + "oklch", + "lab", + "lch", + ].includes(fnName); + const useAlpha = (hasAlpha && token.chi.length == (isLegacy ? 7 : 4)) || + ("color" == token.val && token.chi.length == 5); + return ((hasAlpha && token.val.endsWith("a") ? fnName.slice(0, -1) : fnName) + + "(" + + token + .chi.reduce((acc, curr, index, array) => { + if (/[,/]\s*$/.test(acc)) { + if (curr.typ == exports.EnumToken.WhitespaceTokenType) { + return acc.trimEnd(); + } + return acc.trimStart() + renderValue$1(curr, options, cache); + } + if (isLegacy && curr.typ == exports.EnumToken.CommaTokenType) { + return acc.trimEnd() + " "; + } + if (curr.typ == exports.EnumToken.WhitespaceTokenType) { + return acc.trimEnd() + " "; + } + if (curr.typ == exports.EnumToken.CommaTokenType || + (curr.typ == exports.EnumToken.LiteralTokenType && curr.val == "/")) { + return acc.trimEnd() + (curr.typ == exports.EnumToken.CommaTokenType ? "," : "/"); + } + return (acc.trimEnd() + + (useAlpha && index == array.length - 1 ? "/" : " ") + + renderValue$1(curr, options, cache)); + }, "") + .trimStart() + + ")"); + } + case exports.EnumToken.UrlFunctionTokenType: + if (options.minifyValues && token.typ === exports.EnumToken.UrlFunctionTokenType) { + if (token.chi[0]?.typ === exports.EnumToken.BadUrlTokenType) { + return "url()"; + } + for (const child of token.chi) { + if (child.typ === exports.EnumToken.StringTokenType) { + if (child.val.slice(1, 5) !== "data:" && + urlTokenMatcher.test(child.val)) { + Object.assign(child, { + typ: exports.EnumToken.UrlTokenTokenType, + val: child.val.slice(1, -1), + }); + } + break; + } + } + } + case exports.EnumToken.ParensTokenType: + case exports.EnumToken.FunctionTokenType: + case exports.EnumToken.MathFunctionTokenType: + case exports.EnumToken.ImageFunctionTokenType: + if (options.minifyValues && token.typ === exports.EnumToken.ImageFunctionTokenType) { + const slice = token.chi.slice(); + switch (token.val) { + case "linear-gradient": + case "repeating-linear-gradient": + { + let i; + let k; + let j; + let key; + for (i = 0; i < slice.length; i++) { + if (slice[i].typ == exports.EnumToken.ColorTokenType || + slice[i].typ === exports.EnumToken.CommaTokenType) { + break; + } + if (slice[i].typ == exports.EnumToken.IdenTokenType && + equalsIgnoreCase(slice[i].val, "to")) { + k = i + 1; + while (k < slice.length && + (slice[k].typ === exports.EnumToken.WhitespaceTokenType || + slice[k].typ === exports.EnumToken.CommentTokenType)) { + k++; + } + if (k < slice.length && slice[k].typ === exports.EnumToken.IdenTokenType) { + // convert keyword to angle + key = slice[k].val.toLowerCase(); + switch (key) { + case "right": + slice.splice(i, k - i + 1, { + typ: exports.EnumToken.AngleTokenType, + val: 90, + }); + break; + case "left": + slice.splice(i, k - i + 1, { + typ: exports.EnumToken.AngleTokenType, + val: 270, + }); + break; + case "top": + case "bottom": + j = k + 1; + while (j < slice.length && + (slice[j].typ === exports.EnumToken.WhitespaceTokenType || + slice[j].typ === exports.EnumToken.CommentTokenType)) { + j++; + } + if (j < slice.length && slice[j].typ !== exports.EnumToken.CommaTokenType) { + i = j - 1; + break; + } + // https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/gradient/linear-gradient#:~:text=If%20unspecified%2C%20it%20defaults%20to%20to%20bottom + if (key === "bottom") { + // to bottom -> 180deg is the default angle and will be removed + while (k < slice.length && + slice[k].typ != exports.EnumToken.CommaTokenType) { + k++; + } + slice.splice(i, k - i + 1); + i--; + continue; + } + slice.splice(i, k - i + 1, { + typ: exports.EnumToken.AngleTokenType, + val: key === "top" ? 0 : 180, + }); + i--; + break; + } + } + } + else if (slice[i].typ == exports.EnumToken.AngleTokenType) { + if ((180 - toDegrees(slice[i]).val) % 360 === 0) { + k = i + 1; + // while (k < slice.length && slice[k].typ !== EnumToken.CommaTokenType) { + // k++; + // } + slice.splice(i, k - i + 1); + } + } + } + if (slice[i]?.typ === exports.EnumToken.CommaTokenType) { + i++; + // while ( + // i < slice.length && + // (slice[i].typ === EnumToken.WhitespaceTokenType || + // slice[i].typ === EnumToken.CommentTokenType) + // ) { + // i++; + // } + } + if (slice[i]?.typ === exports.EnumToken.ColorTokenType) { + for (const token of reduceColorStops(slice.splice(i, slice.length - i))) { + slice.push(token); + } + } + } + break; + case "radial-gradient": + case "repeating-radial-gradient": + { + let i = 0; + // while ( + // i < slice.length && + // (slice[i].typ === EnumToken.WhitespaceTokenType || + // slice[i].typ === EnumToken.CommentTokenType) + // ) { + // i++; + // } + const positions = []; + const form = []; + const size = []; + const colorSpaceDef = []; + if (slice[i]?.typ === exports.EnumToken.IdenTokenType) { + if (equalsIgnoreCase(slice[i].val, "farthest-corner") || + equalsIgnoreCase(slice[i].val, "ellipse")) { + i++; + } + else if (equalsIgnoreCase(slice[i].val, "closest-side") || + equalsIgnoreCase(slice[i].val, "closest-corner") || + equalsIgnoreCase(slice[i].val, "farthest-side")) { + size.push(slice[i++]); + } + else if (equalsIgnoreCase(slice[i].val, "circle")) { + form.push(slice[i++]); + } + while (slice[i]?.typ === exports.EnumToken.WhitespaceTokenType || + slice[i]?.typ === exports.EnumToken.CommentTokenType) { + i++; + } + } + if (slice[i]?.typ === exports.EnumToken.IdenTokenType) { + if (equalsIgnoreCase(slice[i].val, "farthest-corner") || + equalsIgnoreCase(slice[i].val, "ellipse")) { + i++; + } + else if (equalsIgnoreCase(slice[i].val, "closest-side") || + equalsIgnoreCase(slice[i].val, "closest-corner") || + equalsIgnoreCase(slice[i].val, "farthest-side")) { + size.push(slice[i++]); + } + else if (equalsIgnoreCase(slice[i].val, "circle")) { + form.push(slice[i++]); + } + while (slice[i]?.typ === exports.EnumToken.WhitespaceTokenType || + slice[i]?.typ === exports.EnumToken.CommentTokenType) { + i++; + } + } + if (slice[i]?.typ === exports.EnumToken.IdenTokenType && + equalsIgnoreCase(slice[i].val, "at")) { + i++; + } + while (slice[i]?.typ === exports.EnumToken.WhitespaceTokenType || + slice[i]?.typ === exports.EnumToken.CommentTokenType) { + i++; + } + if (slice[i]?.typ === exports.EnumToken.PercentageTokenType || + (slice[i]?.typ === exports.EnumToken.NumberTokenType && 0 === slice[i].val) || + (slice[i]?.typ === exports.EnumToken.IdenTokenType && + (equalsIgnoreCase(slice[i].val, "center") || + equalsIgnoreCase(slice[i].val, "top") || + equalsIgnoreCase(slice[i].val, "bottom") || + equalsIgnoreCase(slice[i].val, "left") || + equalsIgnoreCase(slice[i].val, "right")))) { + do { + if (slice[i]?.typ === exports.EnumToken.IdenTokenType && + "in" === slice[i].val) { + break; + } + positions.push(slice[i]); + i++; + } while (slice[i]?.typ !== exports.EnumToken.CommaTokenType); } - if ( - // @ts-ignore - acc[i][PROPERTYNAME] == property || - matchType(acc[i], props)) { - if ("prefix" in props && props.previous != null && !(props.previous in tokens)) { - return acc; - } - if (!(property in tokens)) { - tokens[property] = [[acc[i]]]; - } - else { - if (current == tokens[property].length) { - tokens[property].push([acc[i]]); + while (slice[i]?.typ !== exports.EnumToken.CommaTokenType) { + colorSpaceDef.push(slice[i++]); + } + if (slice[i]?.typ === exports.EnumToken.CommaTokenType) { + i++; + } + if (positions.length > 0) { + let k; + let position1 = ""; + let position2 = ""; + for (k = 0; k < positions.length; k++) { + if (positions[k].typ === exports.EnumToken.IdenTokenType) { + if (position1.length === 0) { + position1 = positions[k].val; + } + else { + position2 = positions[k].val; + } } - else { - tokens[property][current].push({ typ: exports.EnumToken.WhitespaceTokenType }, acc[i]); + else if (positions[k].typ === exports.EnumToken.PercentageTokenType || + positions[k].typ === exports.EnumToken.NumberTokenType) { + if (position1.length === 0) { + position1 = positions[k].val + "%"; + } + else { + position2 = positions[k].val + "%"; + } } - } - acc.splice(i, 1); - i--; - // @ts-ignore - if ("prefix" in props && acc[i]?.typ == exports.EnumToken[props.prefix.typ]) { - if ( - // @ts-ignore - acc[i].typ == exports.EnumToken[props.prefix.typ] && - // @ts-ignore - acc[i].val == this.config.properties[property].prefix.val) { - acc.splice(i, 1); - i--; + if (position2.length > 0) { + break; } } - if (props.multiple) { - continue; + if (position1.length > 0) { + let key = `${position1} ${position2}`.trim(); + if (key === "50% 50%" || + key === "center center" || + key === "50%" || + key === "center") { + positions.length = 0; + } + else if (key === "0% 0%" || + key === "0 0" || + key === "top left" || + key === "left top") { + positions.splice(0, positions.length, { + typ: exports.EnumToken.PercentageTokenType, + val: 0, + }); + } + else if (key === "50% 0%" || + key === "50% 0" || + key === "center top" || + key === "top center") { + positions.splice(0, positions.length, { + typ: exports.EnumToken.IdenTokenType, + val: "top", + }); + } + else if (key === "top right" || key === "right top") { + positions.splice(0, positions.length, { + typ: exports.EnumToken.PercentageTokenType, + val: 100, + }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.PercentageTokenType, val: 0 }); + } + else if (key === "0 50%" || + key === "0% 50%" || + key === "left center" || + key === "center left") { + positions.splice(0, positions.length, { + typ: exports.EnumToken.IdenTokenType, + val: "left", + }); + } + else if (key === "100% 50%" || key === "right center" || key === "center right") { + positions.splice(0, positions.length, { + typ: exports.EnumToken.IdenTokenType, + val: "right", + }); + } + else if (key === "0 100%" || + key === "0% 100%" || + key === "left bottom" || + key === "bottom left") { + positions.splice(0, positions.length, { + typ: exports.EnumToken.IdenTokenType, + val: "bottom", + }); + } + else if (key === "50% 100%" || + key === "center bottom" || + key === "bottom center") { + positions.splice(0, positions.length, { + typ: exports.EnumToken.IdenTokenType, + val: "bottom", + }); + } + else if (key === "100% 100%" || + key === "right bottom" || + key === "bottom right") { + positions.splice(0, positions.length, { + typ: exports.EnumToken.PercentageTokenType, + val: 100, + }); + } + else if (position1 === position2) { + positions.splice(0, positions.length, positions[k]); + } } - return acc; } - else { - if (property in tokens && tokens[property].length > current) { - return acc; + const result = []; + if (form.length > 0) { + for (const token of form) { + result.push(token); } } - } - if (property in tokens && tokens[property].length > current) { - return acc; - } - // default - if (props.default.length > 0) { - const defaults = parseString(props.default[0]); - if (!(property in tokens)) { - tokens[property] = [[...defaults]]; - } - else { - if (current == tokens[property].length) { - tokens[property].push([]); - for (let i = 0; i < defaults.length; i++) { - tokens[property][current].push(defaults[i]); - } + if (size.length > 0) { + if (result.length > 0) { + result.push({ typ: exports.EnumToken.WhitespaceTokenType }); } - else { - tokens[property][current].push({ - typ: exports.EnumToken.WhitespaceTokenType, - }); - for (let i = 0; i < defaults.length; i++) { - tokens[property][current].push(defaults[i]); - } + for (const token of size) { + result.push(token); } } - } - return acc; - }, val[current])); - } - if (values.length == 0) { - this.declarations = Object.entries(tokens).reduce((acc, curr) => { - acc.set(curr[0], { - typ: exports.EnumToken.DeclarationNodeType, - nam: curr[0], - val: curr[1].reduce((acc, curr) => { - if (acc.length > 0) { - acc.push({ ...separator }); + if (positions.length > 0) { + if (result.length > 0) { + result.push({ typ: exports.EnumToken.WhitespaceTokenType }); } - for (let i = 0; i < curr.length; i++) { - acc.push(curr[i]); + result.push({ typ: exports.EnumToken.IdenTokenType, val: "at" }, { typ: exports.EnumToken.WhitespaceTokenType }); + for (const token of positions) { + result.push(token); } - return acc; - }, []), - }); - return acc; - }, new Map()); - } - } - // @ts-ignore - const config = propertiesConfig.properties[declaration.nam]; - let property = declaration.nam; - if (config != null) { - property = config.shorthand; - let value = this.declarations.get(property); - if (!(value instanceof PropertySet)) { - // @ts-ignore - this.declarations.set(property, new PropertySet(propertiesConfig.properties[config.shorthand])); - // Token[] - if (value != null) { - // @ts-ignore - this.declarations.get(property).add(value); - } - } - this.declarations.get(property).add(declaration); - } - else { - this.declarations.set(declaration.nam, declaration); - } - } - return this; - } - [Symbol.iterator]() { - const propertiesMapping = { ...this.config.properties }; - const patterns = this.config.pattern.split(" "); - let hasMapping = false; - let iterable; - let requiredCount = 0; - let property; - let isShorthand = true; - let declarations = this.declarations; - for (const key of declarations.keys()) { - if (declarations.has(key) && declarations.get(key) instanceof PropertyMap) { - hasMapping = true; - break; - } - } - if (hasMapping) { - const mapped = {}; - let key; - for (const value of declarations.values()) { - key = value.nam; - if (value instanceof PropertyMap) { - for (const [k, v] of value.declarations) { - if (k == value.config.shorthand) { - for (const [key, val] of Object.entries(value.config.properties)) { - mapped[key] = cloneNode(v); - for (let i = 0; i < v.val.length; i++) { - // @ts-ignore - if (key == v.val[i][PROPERTYNAME]) { - if (mapped[key].val.length > 0) { - mapped[key].val.push({ - typ: exports.EnumToken.WhitespaceTokenType, - }); - } - mapped[key].val.push(v.val[i]); - } + } + if (colorSpaceDef.length > 0) { + if (result.length > 0) { + result.push({ typ: exports.EnumToken.WhitespaceTokenType }); } - if (mapped[key].val.length == 0) { - mapped[key].val.push(...parseString(value.config.properties[key].default[0])); + for (const token of colorSpaceDef) { + result.push(token); } } - } - else { - mapped[k] = v; - } - } - } - else { - mapped[key] = value; - } - } - // console.error(mapped); - if (patterns.length === Object.keys(mapped).length) { - declarations = new Map(); - for (const key of patterns) { - if (mapped[key] != null) { - declarations.set(key, mapped[key]); - } - } - } - } - for (property of Object.keys(propertiesMapping)) { - if (propertiesMapping[property].required) { - if (!declarations.has(property)) { - isShorthand = false; - break; - } - else { - const val = declarations.get(property); - if (val instanceof PropertySet && !val.isShortHand()) { - isShorthand = false; - break; - } - else { - requiredCount++; - } - } - } - } - if (requiredCount === 0) { - requiredCount = declarations.size; - } - if (!isShorthand || requiredCount < this.requiredCount) { - if (isShorthand && declarations.has(this.config.shorthand)) { - const cache = new Map(); - const removeDefaults = (declaration) => { - let i; - let t; - let map = new Map(); - let value = []; - let values = []; - // @ts-ignore - let typ = (exports.EnumToken[this.config.separator?.typ] ?? exports.EnumToken.CommaTokenType); - // @ts-ignore - const sep = this.config.separator == null - ? null - : { - ...this.config.separator, - typ: exports.EnumToken[this.config.separator.typ], - }; - // @ts-ignore - const separator = this.config.separator - ? renderValue({ - ...this.config.separator, - typ: exports.EnumToken[this.config.separator.typ], - }) - : ","; - this.matchTypes(declaration); - values.push(value); - for (i = 0; i < declaration.val.length; i++) { - t = declaration.val[i]; - if (!cache.has(t)) { - cache.set(t, renderValue(t, { minify: true })); - } - if (t.typ == typ && separator == cache.get(t)) { - this.removeDefaults(map, value); - value = []; - values.push(value); - map.clear(); - continue; - } - value.push(t); - // @ts-ignore - if (t[PROPERTYNAME] != null) { - // @ts-ignore - if (!map.has(t[PROPERTYNAME])) { - // @ts-ignore - map.set(t[PROPERTYNAME], { t: [t], value: [cache.get(t)] }); - } - else { - // @ts-ignore - const v = map.get(t[PROPERTYNAME]); - v.t.push(t); - v.value.push(cache.get(t)); - } - } - } - this.removeDefaults(map, value); - declaration.val = values.reduce((acc, curr) => { - if (sep != null && acc.length > 0) { - acc.push({ ...sep }); - } - for (const cr of curr) { - if (cr.typ == exports.EnumToken.WhitespaceTokenType && acc.at(-1)?.typ == cr.typ) { - continue; - } - acc.push(cr); - } - return acc; - }, []); - while (declaration.val.at(-1)?.typ == exports.EnumToken.WhitespaceTokenType) { - declaration.val.pop(); - } - while (declaration.val.at(0)?.typ == exports.EnumToken.WhitespaceTokenType) { - declaration.val.shift(); - } - return declaration; - }; - const values = [...declarations.values()].reduce((acc, curr) => { - if (curr instanceof PropertySet || curr instanceof PropertyMap) { - for (const declaration of curr) { - acc.push(declaration); - } - } - else { - acc.push(curr); - } - return acc; - }, []); - let isImportant = false; - let dec; - const filtered = []; - for (const declaration of values) { - dec = removeDefaults(declaration); - for (const t of dec.val) { - if (t.typ == exports.EnumToken.ImportantTokenType) { - isImportant = true; - } - if (filtered.length == 0 && - t.typ != exports.EnumToken.WhitespaceTokenType && - t.typ != exports.EnumToken.ImportantTokenType) { - filtered.push(dec); - } - } - } - if (filtered.length == 0 && this.config.default.length > 0) { - filtered.push({ - typ: exports.EnumToken.DeclarationNodeType, - nam: this.config.shorthand, - val: parseString(this.config.default[0]), - }); - if (isImportant) { - filtered[0].val.push({ - typ: exports.EnumToken.ImportantTokenType, - }); - } - } - return (filtered.length > 0 ? filtered : values)[Symbol.iterator](); - } - for (const declaration of declarations.values()) { - if (declaration instanceof PropertySet || declaration instanceof PropertyMap) { - continue; - } - const config = declaration.nam == this.config.shorthand - ? this.config - : (propertiesMapping[declaration.nam] ?? this.config); - if (!("mapping" in config)) { - continue; - } - // @ts-ignore - for (const [key, val] of Object.entries(config.mapping)) { - const keys = parseString(key); - if (keys.length != declaration.val.length) { - continue; - } - if (eq(declaration.val, keys)) { - declaration.val = parseString(val); - break; - } - } - } - // @ts-ignore - iterable = declarations.values(); - } - else { - let count = 0; - let match; - const separator = this.config.separator != null - ? { - ...this.config.separator, - // @ts-ignore - typ: exports.EnumToken[this.config.separator.typ], - } - : null; - const tokens = {}; - // @ts-ignore - Object.entries(propertiesMapping).reduce((acc, curr) => { - if (!declarations.has(curr[0])) { - if (curr[1].required) { - acc.push(curr[0]); - } - return acc; - } - let current = 0; - const props = propertiesMapping[curr[0]]; - const properties = (declarations.get(curr[0])); - for (const declaration of properties instanceof PropertySet || properties instanceof PropertyMap - ? [...properties].flat() - : [properties]) { - // @ts-ignore - for (const val of declaration.val) { - // @ts-ignore - if (separator != null && separator.typ == val.typ && separator.val == val.val) { - current++; - if (tokens[curr[0]].length == current) { - tokens[curr[0]].push([]); - } - continue; - } - if (val.typ == exports.EnumToken.WhitespaceTokenType || val.typ == exports.EnumToken.CommentTokenType) { - continue; - } - // @ts-ignore - if (props.multiple && - props.separator != null && - // @ts-ignore - exports.EnumToken[props.separator.typ] === val.typ && - // @ts-ignore - (val.typ === exports.EnumToken.CommaTokenType || props.separator.val == val.val)) { - continue; - } - // @ts-ignore - match = val.typ == exports.EnumToken.CommentTokenType || matchType(val, curr[1]); - if (isShorthand) { - isShorthand = match; - } - // @ts-ignore - if (val[PROPERTYNAME] == property || match) { - if (!(curr[0] in tokens)) { - tokens[curr[0]] = [[]]; + if (result.length > 0) { + result.push({ typ: exports.EnumToken.CommaTokenType }); + } + for (const token of reduceColorStops(slice.slice(i))) { + result.push(token); + } + slice.length = 0; + for (const token of result) { + slice.push(token); } - // is default value - tokens[curr[0]][current].push(val); - } - else { - acc.push(curr[0]); - break; } - } - } - if (count === 0) { - count = current; - } - return acc; - }, []); - // grid-template - if (this.config.shorthand == "grid-template" && patterns.length == Object.keys(tokens).length) { - const k = tokens[patterns[0]][0].length; - const l = tokens[patterns[1]][0].length; - const j = k < l ? k : l; - const result = []; - let i; - for (i = 0; i < j; i++) { - if (i < k) { - result.push(tokens[patterns[0]][0][i]); - } - if (i < l) { - result.push(tokens[patterns[1]][0][i]); - } - } - if (j < k) { - result.push(...tokens[patterns[0]][0].slice(j)); - } - if (j < l) { - result.push(...tokens[patterns[1]][0].slice(j)); - } - const separator = this.config.properties[patterns[2]]?.prefix; - result.push( - // @ts-ignore - { ...separator, typ: exports.EnumToken[separator.typ] }, ...tokens[patterns[2]][0].reduce((acc, curr) => { - if (acc.length > 0) { - acc.push({ typ: exports.EnumToken.WhitespaceTokenType }); - } - acc.push(curr); - return acc; - }, [])); - const declaration = cloneNode(declarations.get(patterns[0])); - declaration.nam = this.config.shorthand; - declaration.val = result; - return [declaration][Symbol.iterator](); - } - count++; - if (!isShorthand || - Object.entries(propertiesMapping).some((entry) => { - // missing required property - return entry[1].required && !(entry[0] in tokens); - }) || - // @ts-ignore - !Object.values(tokens).every((v) => - // @ts-expect-error - v.filter((t) => t.typ != exports.EnumToken.CommentTokenType).length === count)) { - // @ts-ignore - iterable = declarations.values(); - } - else if (hasMapping) { - if (patterns.length == Object.keys(tokens).length) { - const declaration = cloneNode(declarations.get(patterns[0])); - declaration.nam = this.config.shorthand; - for (let i = 0; i < patterns.length; i++) { - if (declaration.val.length > 0) { - declaration.val.push( - // @ts-ignore - this.config.separator != null - ? { - ...this.config.separator, - typ: exports.EnumToken[this.config.separator.typ], + break; + case "conic-gradient": + case "repeating-conic-gradient": + { + let i = 0; + const angles = []; + const positions = []; + const colorSpaceDef = []; + // while ( + // i < slice.length && + // (slice[i].typ === EnumToken.WhitespaceTokenType || + // slice[i].typ === EnumToken.CommentTokenType) + // ) { + // i++; + // } + if (slice[i]?.typ === exports.EnumToken.IdenTokenType && + equalsIgnoreCase(slice[i].val, "from")) { + angles.push(slice[i++]); + while (i < slice.length && + (slice[i].typ === exports.EnumToken.WhitespaceTokenType || + slice[i].typ === exports.EnumToken.CommentTokenType)) { + angles.push(slice[i++]); + } + if ((slice[i]?.typ === exports.EnumToken.NumberTokenType || + slice[i]?.typ === exports.EnumToken.AngleTokenType) && + 0 === toDegrees(slice[i]).val) { + angles.length = 0; + i++; + } + else if (slice[i]?.typ !== exports.EnumToken.CommaTokenType && + slice[i].typ != exports.EnumToken.IdenTokenType) { + angles.push(slice[i++]); + } + while (i < slice.length && + (slice[i].typ === exports.EnumToken.WhitespaceTokenType || + slice[i].typ === exports.EnumToken.CommentTokenType)) { + i++; } - : { typ: exports.EnumToken.WhitespaceTokenType }); - } - declaration.val.push(...tokens[patterns[i]].reduce((acc, curr) => { - if (acc.length > 0) { - acc.push({ typ: exports.EnumToken.WhitespaceTokenType }); } - acc.push(...curr.reduce((acc, curr) => { - if (acc.length > 0) { - acc.push({ typ: exports.EnumToken.WhitespaceTokenType }); + if (slice[i]?.typ === exports.EnumToken.IdenTokenType && + equalsIgnoreCase(slice[i].val, "at")) { + i++; + while (i < slice.length && + (slice[i].typ === exports.EnumToken.WhitespaceTokenType || + slice[i].typ === exports.EnumToken.CommentTokenType)) { + i++; + } + let position1 = ""; + let position2 = ""; + if (slice[i]?.typ === exports.EnumToken.IdenTokenType && + !equalsIgnoreCase("in", slice[i].val)) { + position1 = slice[i].val; + positions.push(slice[i++]); + } + else if (slice[i]?.typ === exports.EnumToken.PercentageTokenType || + slice[i]?.typ === exports.EnumToken.NumberTokenType) { + position1 = slice[i].val + "%"; + positions.push(slice[i++]); + } + while (i < slice.length && + (slice[i].typ === exports.EnumToken.WhitespaceTokenType || + slice[i].typ === exports.EnumToken.CommentTokenType)) { + positions.push(slice[i++]); + } + if (slice[i]?.typ === exports.EnumToken.IdenTokenType && + !equalsIgnoreCase("in", slice[i].val)) { + position2 = slice[i].val; + positions.push(slice[i++]); + } + else if (slice[i]?.typ === exports.EnumToken.PercentageTokenType || + slice[i]?.typ === exports.EnumToken.NumberTokenType) { + position2 = slice[i].val + "%"; + positions.push(slice[i++]); + } + while (i < slice.length && + (slice[i].typ === exports.EnumToken.WhitespaceTokenType || + slice[i].typ === exports.EnumToken.CommentTokenType)) { + i++; + } + if (position1.length > 0) { + reducegradientBackgroundPosition(positions, `${position1} ${position2}`.trim()); } - acc.push(curr); - return acc; - }, [])); - return acc; - }, [])); - } - return [declaration][Symbol.iterator](); - } - iterable = declarations.values(); - } - else { - let values = Object.entries(tokens) - .reduce((acc, curr) => { - const props = propertiesMapping[curr[0]]; - for (let i = 0; i < curr[1].length; i++) { - if (acc.length == i) { - acc.push([]); - } - // if (acc[acc.length - 1].length > 0) { - // acc[acc.length - 1].push( - // // @ts-ignore - // this.config.separator != null ? - // { - // ...this.config.separator, - // typ: EnumToken[this.config.separator.typ as keyof typeof EnumToken], - // } : { typ: EnumToken.WhitespaceTokenType }, - // ) - // } - let values = curr[1][i]; - // .reduce( - // (acc, curr) => { - // // if (acc.length > 0) { - // // acc.push({ typ: EnumToken.WhitespaceTokenType }); - // // } - // acc.push(curr); - // return acc; - // }, - // [], - // ); - if (props.default.includes(curr[1][i] - .reduce((acc, curr) => acc + renderValue(curr) + " ", "") - .trimEnd())) { - if (!propertiesMapping[curr[0]].required) { - continue; } - } - // remove default values - let doFilterDefault = true; - if (curr[0] in propertiesConfig.properties) { - for (let v of values) { - if (![ - exports.EnumToken.WhitespaceTokenType, - exports.EnumToken.CommentTokenType, - exports.EnumToken.IdenTokenType, - ].includes(v.typ) || - (v.typ == exports.EnumToken.IdenTokenType && - !propertiesMapping[curr[0]].default.includes(v.val))) { - doFilterDefault = false; - break; + while (i < slice.length && + (slice[i].typ === exports.EnumToken.WhitespaceTokenType || + slice[i].typ === exports.EnumToken.CommentTokenType)) { + i++; + } + if (slice[i]?.typ === exports.EnumToken.IdenTokenType && + equalsIgnoreCase("in", slice[i].val)) { + while (i < slice.length && slice[i].typ !== exports.EnumToken.CommaTokenType) { + colorSpaceDef.push(slice[i++]); } } - } - // remove default values - const filtered = values.filter((val) => { - if (val.typ == exports.EnumToken.WhitespaceTokenType || - val.typ == exports.EnumToken.CommentTokenType) { - return false; + if (slice[i]?.typ === exports.EnumToken.CommaTokenType) { + i++; } - return (!doFilterDefault || - !(val.typ == exports.EnumToken.IdenTokenType && - props.default.includes(val.val))); - }); - if (filtered.length > 0 || - !(this.requiredCount == requiredCount && propertiesMapping[curr[0]].required)) { - values = filtered; - } - if (values.length > 0) { - if ("mapping" in props) { - if (!("constraints" in props) || - // @ts-ignore - !("max" in props.constraints) || - values.length <= props.constraints.mapping.max) { - let i = values.length; - while (i--) { - if (values[i].typ == exports.EnumToken.IdenTokenType && - // @ts-expect-error - values[i].val in props.mapping) { - // @ts-ignore - values.splice(i, 1, ...parseString(props.mapping[values[i].val])); - } + const result = []; + if (positions.length > 0) { + if (positions.length > 0) { + if (angles.length > 0) { + angles.push({ typ: exports.EnumToken.WhitespaceTokenType }); + } + angles.push({ typ: exports.EnumToken.IdenTokenType, val: "at" }, { typ: exports.EnumToken.WhitespaceTokenType }); + for (const position of positions) { + angles.push(position); } } } - if ("prefix" in props) { - // @ts-ignore - acc[i].push({ ...props.prefix, typ: exports.EnumToken[props.prefix.typ] }); - } - else if (acc[i].length > 0) { - // @ts-ignore - acc[i].push({ typ: exports.EnumToken.WhitespaceTokenType }); + if (angles.length > 0) { + for (const angle of angles) { + result.push(angle); + } + result.push({ typ: exports.EnumToken.CommaTokenType }); } - for (const v of values.reduce((acc, curr) => { - if (acc.length > 0) { - // @ts-ignore - acc.push({ - ...((props.separator && { - ...props.separator, - // @ts-ignore - typ: exports.EnumToken[props.separator.typ], - }) ?? { typ: exports.EnumToken.WhitespaceTokenType }), - }); + if (colorSpaceDef.length > 0) { + if (colorSpaceDef.length > 0) { + if (result.length > 0) { + result.push({ typ: exports.EnumToken.WhitespaceTokenType }); + } + for (const token of colorSpaceDef) { + result.push(token); + } } - // @ts-ignore - acc.push(curr); - return acc; - }, [])) { - acc[i].push(v); + result.push({ typ: exports.EnumToken.CommaTokenType }); } - } - } - return acc; - }, []) - .reduce((acc, curr) => { - if (acc.length > 0) { - acc.push({ ...separator }); - } - if (curr.length == 0 && this.config.default.length > 0) { - curr.push(...parseString(this.config.default[0]).reduce((acc, curr) => { - if (acc.length > 0) { - acc.push({ typ: exports.EnumToken.WhitespaceTokenType }); + for (const token of reduceConicColorStops(slice.slice(i))) { + result.push(token); } - acc.push(curr); - return acc; - }, [])); - } - for (const c of curr) { - acc.push(c); - } - return acc; - }, []); - if (this.config.mapping != null) { - const val = values.reduce((acc, curr) => acc + - renderValue(curr, { - removeComments: true, - minify: true, - }), ""); - if (val in this.config.mapping) { - values.length = 0; - values.push({ - typ: ['"', "'"].includes(val.charAt(0)) - ? exports.EnumToken.StringTokenType - : exports.EnumToken.IdenTokenType, - // @ts-ignore - val: this.config.mapping[val], - }); - } - } - // @ts-ignore - if (values.length == 1 && - // @ts-ignore - typeof values[0].val == "string" && - this.config.default.includes(values[0].val.toLowerCase()) && - this.config.default[0] != values[0].val.toLowerCase()) { - // @ts-ignore/ - values = parseString(this.config.default[0]); + slice.length = 0; + for (let j = 0; j < result.length; j++) { + slice.push(result[j]); + } + } + break; } - iterable = [ - { - typ: exports.EnumToken.DeclarationNodeType, - nam: this.config.shorthand, - val: values, - }, - ][Symbol.iterator](); + return token.val + "(" + slice.reduce(reducer, "") + ")"; } - } - const iterators = []; - // @ts-ignore - return { - // @ts-ignore - next() { - let v = iterable.next(); + case exports.EnumToken.TimingFunctionTokenType: + case exports.EnumToken.PseudoClassFuncTokenType: + case exports.EnumToken.WhenElseFunctionTokenType: + case exports.EnumToken.TimelineFunctionTokenType: + case exports.EnumToken.GridTemplateFuncTokenType: + case exports.EnumToken.SupportsFunctionTokenType: + case exports.EnumToken.ContainerFunctionTokenType: + case exports.EnumToken.TransformFunctionTokenType: + case exports.EnumToken.GeneralEnclosedFunctionTokenType: + case exports.EnumToken.CustomFunctionTokenType: + case exports.EnumToken.WildCardFunctionTokenType: + if (token.typ == exports.EnumToken.MathFunctionTokenType && + token.chi.length == 1 && + ![exports.EnumToken.BinaryExpressionTokenType, exports.EnumToken.FractionTokenType, exports.EnumToken.IdenTokenType].includes(token.chi[0].typ) && // @ts-ignore - while (v.done || v.value instanceof PropertySet) { - if (v.value instanceof PropertySet) { - // @ts-ignore - iterators.push(iterable); - iterable = v.value[Symbol.iterator](); - v = iterable.next(); + token.chi[0].val + ?.typ != exports.EnumToken.FractionTokenType) { + return (token.val + + "(" + + token.chi.reduce((acc, curr) => acc + + renderValue$1(curr, token.typ == exports.EnumToken.FunctionTokenType ? { minify: false } : options, cache, reducer), "") + + ")"); + } + return (token.val ?? "") + "(" + token.chi.reduce(reducer, "") + ")"; + case exports.EnumToken.BlockStartTokenType: + return "{"; + case exports.EnumToken.BlockEndTokenType: + return "}"; + case exports.EnumToken.StartParensTokenType: + return "("; + case exports.EnumToken.DelimTokenType: + case exports.EnumToken.EqualMatchTokenType: + return "="; + case exports.EnumToken.IncludeMatchTokenType: + return "~="; + case exports.EnumToken.DashMatchTokenType: + return "|="; + case exports.EnumToken.StartMatchTokenType: + return "^="; + case exports.EnumToken.EndMatchTokenType: + return "$="; + case exports.EnumToken.ContainMatchTokenType: + return "*="; + case exports.EnumToken.LtTokenType: + return "<"; + case exports.EnumToken.LteTokenType: + return "<="; + case exports.EnumToken.Tilda: + case exports.EnumToken.SubsequentSiblingCombinatorTokenType: + return "~"; + case exports.EnumToken.Plus: + case exports.EnumToken.NextSiblingCombinatorTokenType: + return "+"; + case exports.EnumToken.GtTokenType: + case exports.EnumToken.ChildCombinatorTokenType: + return ">"; + case exports.EnumToken.GteTokenType: + return ">="; + case exports.EnumToken.ColumnCombinatorTokenType: + return "||"; + case exports.EnumToken.EndParensTokenType: + return ")"; + case exports.EnumToken.AttrStartTokenType: + return "["; + case exports.EnumToken.AttrEndTokenType: + return "]"; + case exports.EnumToken.DescendantCombinatorTokenType: + case exports.EnumToken.WhitespaceTokenType: + return " "; + case exports.EnumToken.ColonTokenType: + return ":"; + case exports.EnumToken.DoubleColonTokenType: + return "::"; + case exports.EnumToken.SemiColonTokenType: + return ";"; + case exports.EnumToken.CommaTokenType: + return ","; + case exports.EnumToken.ImportantTokenType: + return "!important"; + case exports.EnumToken.Pipe: + return "|"; + case exports.EnumToken.AttrTokenType: + case exports.EnumToken.IdenListTokenType: + return "[" + token.chi.reduce(reducer, "") + "]"; + case exports.EnumToken.TimeTokenType: + case exports.EnumToken.AngleTokenType: + case exports.EnumToken.LengthTokenType: + case exports.EnumToken.DimensionTokenType: + case exports.EnumToken.FrequencyTokenType: + case exports.EnumToken.ResolutionTokenType: + let val = token.val.typ == exports.EnumToken.FractionTokenType + ? renderValue$1(token.val, options, cache) + : minifyNumber(token.val); + let unit = token.unit; + if (token.typ == exports.EnumToken.AngleTokenType && !val.includes("/")) { + const angle = getAngle(token); + let v; + let value = val + unit; + for (const u of ["deg", "turn", "rad", "grad"]) { + if (token.unit == u) { + continue; } - if (v.done) { - if (iterators.length > 0) { - // @ts-ignore - iterable = iterators.pop(); - v = iterable.next(); - } - if (v.done && iterators.length == 0) { + switch (u) { + case "deg": + v = minifyNumber(toPrecisionAngle(angle * 360, anglePrecision, false).toFixed(anglePrecision)); + if (v.length + 3 < value.length) { + val = v; + unit = u; + value = v + u; + } + break; + case "turn": + v = minifyNumber(toPrecisionAngle(angle, anglePrecision, false).toFixed(anglePrecision)); + if (v.length + 4 < value.length) { + val = v; + unit = u; + value = v + u; + } + break; + case "rad": + v = minifyNumber(toPrecisionAngle(angle * (2 * Math.PI), anglePrecision, false).toFixed(anglePrecision)); + if (v.length + 3 < value.length) { + val = v; + unit = u; + value = v + u; + } + break; + case "grad": + v = minifyNumber(toPrecisionAngle(angle * 400, anglePrecision, false).toFixed(anglePrecision)); + if (v.length + 4 < value.length) { + val = v; + unit = u; + value = v + u; + } break; - } } } - return v; - }, - }; - } - matchTypes(declaration) { - const patterns = this.pattern.slice(); - const values = []; - let i; - let j; - const map = new Map(); - for (i = 0; i < declaration.val.length; i++) { - values.push(declaration.val[i]); - } - for (i = 0; i < patterns.length; i++) { - for (j = 0; j < values.length; j++) { - if (!map.has(patterns[i])) { - // @ts-ignore - map.set(patterns[i], this.config.properties?.[patterns[i]]?.constraints?.mapping?.max ?? 1); + } + if (val === "0") { + if (token.typ == exports.EnumToken.TimeTokenType) { + return "0s"; } - let count = map.get(patterns[i]); - if (count > 0 && matchType(values[j], this.config.properties[patterns[i]])) { - // @ts-expect-error - values[j][PROPERTYNAME] = patterns[i]; - map.set(patterns[i], --count); - values.splice(j--, 1); + if (token.typ == exports.EnumToken.FrequencyTokenType) { + return "0Hz"; + } + // @ts-ignore + if (token.typ == exports.EnumToken.ResolutionTokenType) { + return "0x"; } + return "0"; } - } - if (this.config.set != null) { - for (const [key, val] of Object.entries(this.config.set)) { - if (map.has(key)) { - for (const v of val) { - // missing - if (map.get(v) == 1) { - let i = declaration.val.length; - while (i--) { - // @ts-expect-error - if (declaration.val[i][PROPERTYNAME] == key) { - const val = { ...declaration.val[i] }; - // @ts-expect-error - val[PROPERTYNAME] = v; - declaration.val.splice(i, 0, val, { typ: exports.EnumToken.WhitespaceTokenType }); - } - } - } + if (token.typ == exports.EnumToken.TimeTokenType) { + if (unit == "ms") { + // @ts-ignore + const v = minifyNumber(val / 1000); + if (v.length + 1 <= val.length) { + return v + "s"; } + return val + "ms"; } + return val + "s"; } - } - } - removeDefaults(map, value) { - for (const [key, val] of map) { - const config = this.config.properties[key]; - if (config == null) { - continue; + if (token.typ == exports.EnumToken.ResolutionTokenType && unit == "dppx") { + unit = "x"; } - const v = val.value.join(" "); - if (config.default.includes(v) || (value.length == 1 && this.config.default.includes(v))) { - for (const token of value) { - if (val.t.includes(token)) { - let index = value.indexOf(token); - value.splice(index, 1); - if (config.prefix != null) { - while (index-- > 0) { - if (value[index].typ == exports.EnumToken.WhitespaceTokenType) { - continue; - } - if ( - // @ts-expect-error - value[index].typ == exports.EnumToken[config.prefix.typ] && - // @ts-ignore - value[index].val == config.prefix.val) { - value.splice(index, 1); - break; - } - } - } - } - } + return val.includes("/") ? val.replace("/", unit + "/") : minifyNumber(toPrecisionValue(val)) + unit; + case exports.EnumToken.FlexTokenType: + case exports.EnumToken.PercentageTokenType: + const uni = token.typ == exports.EnumToken.PercentageTokenType ? "%" : "fr"; + const perc = token.val.typ == exports.EnumToken.FractionTokenType + ? renderValue$1(token.val, options, cache) + : minifyNumber(token.val); + return options.minifyValues && perc == "0" + ? "0" + : perc.includes("/") + ? perc.replace("/", uni + "/") + : perc + uni; + case exports.EnumToken.NumberTokenType: + return token.val.typ == exports.EnumToken.FractionTokenType + ? renderValue$1(token.val, options, cache) + : minifyNumber(token.val); + case exports.EnumToken.InfinityTokenType: + return "1/0"; + case exports.EnumToken.NegativeInfinityTokenType: + return "-1/0"; + case exports.EnumToken.NaNTokenType: + return "NaN"; + case exports.EnumToken.AtRuleTokenType: + return "@" + token.nam; + case exports.EnumToken.CommentTokenType: + case exports.EnumToken.CDOCOMMNodeType: + if (options.removeComments && + (!options.preserveLicense || !token.val.startsWith("/*!"))) { + return ""; + } + case exports.EnumToken.PseudoClassTokenType: + case exports.EnumToken.PseudoElementTokenType: + // https://www.w3.org/TR/selectors-4/#single-colon-pseudos + if (token.typ == exports.EnumToken.PseudoElementTokenType && + pseudoElements.includes(token.val.slice(1))) { + return token.val.slice(1); } + case exports.EnumToken.UrlTokenTokenType: + case exports.EnumToken.HashTokenType: + case exports.EnumToken.IdenTokenType: + case exports.EnumToken.StringTokenType: + case exports.EnumToken.LiteralTokenType: + case exports.EnumToken.DashedIdenTokenType: + case exports.EnumToken.PseudoPageTokenType: + case exports.EnumToken.ClassSelectorTokenType: + return token.val; + case exports.EnumToken.NestingSelectorTokenType: + return "&"; + case exports.EnumToken.InvalidAttrTokenType: + return ("[" + + token.chi.reduce((acc, curr) => acc + renderValue$1(curr, options, cache), "")); + case exports.EnumToken.InvalidClassSelectorTokenType: + return token.val; + case exports.EnumToken.SupportsQueryUnaryConditionTokenType: + case exports.EnumToken.WhenElseUnaryConditionTokenType: + return (renderValue$1(token.l, options, cache, reducer) + + " " + + token.r.reduce((acc, curr) => acc + renderValue$1(curr, options, cache, reducer), "")); + case exports.EnumToken.SupportsQueryConditionTokenType: + case exports.EnumToken.WhenElseQueryConditionTokenType: + return (token.l.reduce((acc, curr) => acc + renderValue$1(curr, options, cache, reducer), "") + + " " + + renderValue$1(token.op, options, cache, reducer) + + " " + + token.r.reduce((acc, curr) => acc + renderValue$1(curr, options, cache, reducer), "")); + case exports.EnumToken.IfConditionTokenType: + return token.l.length == 0 + ? "" + : token.l.reduce((acc, curr) => acc + renderValue$1(curr, options, cache, reducer), "") + + ":" + + (options.indent ?? "") + + token.r.reduce((acc, curr) => acc + renderValue$1(curr, options, cache, reducer), ""); + case exports.EnumToken.IfElseConditionTokenType: + return (renderValue$1(token.l, options, cache, reducer) + + (options.indent ?? "") + + renderValue$1(token.r, options, cache, reducer)); + case exports.EnumToken.DeclarationNodeType: + return (token.nam + + ":" + + (options.minifyValues ? filterValues$1(token.val) : token.val).reduce((acc, curr) => acc + renderValue$1(curr, options, cache, reducer), "")); + case exports.EnumToken.MediaQueryUnaryFeatureTokenType: + return (renderValue$1(token.l, options, cache, reducer) + + " " + + token.r.reduce((acc, curr) => acc + renderValue$1(curr, options, cache, reducer), "")); + case exports.EnumToken.MediaQueryConditionTokenType: { + const indent = token.op.typ == exports.EnumToken.LtTokenType || + token.op.typ == exports.EnumToken.GtTokenType || + token.op.typ == exports.EnumToken.ColonTokenType || + token.op.typ == exports.EnumToken.DelimTokenType || + token.op.typ == exports.EnumToken.LteTokenType || + token.op.typ == exports.EnumToken.GteTokenType + ? "" + : " "; + return (token.l.reduce((acc, curr) => acc + renderValue$1(curr, options, cache, reducer), "") + + indent + + renderValue$1(token.op, options, cache, reducer) + + indent + + token.r.reduce((acc, curr) => acc + renderValue$1(curr, options, cache, reducer), "")); } + case exports.EnumToken.MediaRangeQueryTokenType: + return (token.l.reduce((acc, curr) => acc + renderValue$1(curr, options, cache, reducer), "") + + renderValue$1(token.op1, options, cache, reducer) + + token.val.reduce((acc, curr) => acc + renderValue$1(curr, options, cache, reducer), "") + + renderValue$1(token.op2, options, cache, reducer) + + token.r.reduce((acc, curr) => acc + renderValue$1(curr, options, cache, reducer), "")); + case exports.EnumToken.MediaFeatureTokenType: + return token.val; + case exports.EnumToken.NotTokenType: + return "not"; + case exports.EnumToken.OnlyTokenType: + return "only"; + case exports.EnumToken.AndTokenType: + return "and"; + case exports.EnumToken.OrTokenType: + return "or"; + case exports.EnumToken.InvalidMediaQueryTokenType: + case exports.EnumToken.InvalidCommentTokenType: + case exports.EnumToken.BadCommentTokenType: + case exports.EnumToken.BadCdoTokenType: + case exports.EnumToken.BadStringTokenType: + case exports.EnumToken.BadUrlTokenType: + case exports.EnumToken.EOFTokenType: + return ""; + default: + console.debug({ token }); + throw new Error(`Unsupported token type for ${exports.EnumToken[token.typ]}`); } + // errors?.push({ action: "ignore", message: `render: unexpected token ${JSON.stringify(token, null, 1)}` }); + // return ""; } - -// Alphabet: a-z, A-Z, 0-9, _, - -const LOWER = "abcdefghijklmnopqrstuvwxyz"; -const DIGITS = "0123456789"; -const FULL_ALPHABET = (LOWER + DIGITS).split(""); // 64 chars -const FIRST_ALPHABET = LOWER.split(""); // 54 chars (no digits) /** - * supported hash algorithms + * Remove whitespace tokens that are not needed + * @param values + * + * @internal */ -const hashAlgorithms = ["hex", "base64", "base64url", "sha1", "sha256", "sha384", "sha512"]; -// simple deterministic hash → number -function hashCode(str) { - let hash = 0; - let l = str.length; +function filterValues$1(values) { let i = 0; - while (i < l) { - hash = (hash * 31 + str.charCodeAt(i++)) >>> 0; - } - return hash; -} -/** - * generate a hash id - * @param input - * @param length - */ -function hashId(input, length = 6) { - let n = hashCode(input); - const chars = []; - // First character: must not be a digit - chars.push(FIRST_ALPHABET[n % FIRST_ALPHABET.length]); - // Remaining characters - for (let i = 1; i < length; i++) { - n = (n + chars.length * i) % FULL_ALPHABET.length; - chars.push(FULL_ALPHABET[n]); - } - return chars.join(""); -} -/** - * Object to string - * @param input - * @returns - */ -function toSortedString(input) { - if (input == null) { - return "null"; - } - if (typeof input !== "object") { - return String(input); - } - if (Array.isArray(input)) { - const result = new Array(input.length); - for (const value of input) { - result.push(toSortedString(value)); - } - return JSON.stringify(result); - } - const keys = Object.keys(input).sort(); - for (let i = 0; i < keys.length; i++) { - keys[i] = `${keys[i]}:${toSortedString(input[keys[i]])}`; - } - return `{${keys.join(",")}}`; -} -/** - * Object hash - * @param object - * @returns - */ -function objectHash(object) { - return hashCode(toSortedString(object)).toString(16); -} -/** - * convert input to hex - * @param input - * @param length - */ -function toHex(input, length) { - let result = ""; - if (input instanceof ArrayBuffer || ArrayBuffer.isView(input)) { - for (const byte of Array.from(new Uint8Array(input))) { - result += byte.toString(16).padStart(2, "0"); - } - } - else { - for (const char of String(input)) { - result += char.charCodeAt(0).toString(16).padStart(2, "0"); + for (; i < values.length; i++) { + if (values[i].typ == exports.EnumToken.ImportantTokenType && values[i - 1]?.typ === exports.EnumToken.WhitespaceTokenType) { + values.splice(i - 1, 1); } - } - return result; -} -/** - * generate a hash - * @param input - * @param length - * @param algo - */ -async function hash(input, length = 6, algo) { - let result; - if (algo != null) { - switch (algo) { - case "hex": - return toHex(input).slice(0, length); - case "base64url": - case "base64": - result = btoa(input); - if (algo == "base64url") { - result = result.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); - } - return result.slice(0, length); - case "sha256": - case "sha384": - case "sha512": - case "sha1": - return toHex(await crypto.subtle.digest(algo.replace("sha", "SHA-"), new TextEncoder().encode(input))).slice(0, length); - default: - throw new Error(`Unsupported hash algorithm: ${algo}`); + else if (tokensfuncSet.has(values[i].typ) && + "chi" in values[i] && + values[i].typ != exports.EnumToken.WildCardFunctionTokenType && + values[i + 1]?.typ == exports.EnumToken.WhitespaceTokenType) { + values.splice(i + 1, 1); } } - return hashId(input, length); + return values; } + /** - * generate a hash - * @param input - * @param length - * @param algo + * feature walk mode + * + * @private */ -function syncHash(input, length = 6, algo) { - let result; - if (algo != null) { - switch (algo) { - case "hex": - return toHex(input).slice(0, length); - case "base64url": - case "base64": - result = btoa(input); - if (algo == "base64url") { - result = result.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); - } - return result.slice(0, length); - default: - throw new Error(`Unsupported hash algorithm: ${algo}`); - } - } - return hashId(input, length); -} +exports.FeatureWalkMode = void 0; +(function (FeatureWalkMode) { + /** + * pre process + */ + FeatureWalkMode[FeatureWalkMode["Pre"] = 1] = "Pre"; + /** + * post process + */ + FeatureWalkMode[FeatureWalkMode["Post"] = 2] = "Post"; +})(exports.FeatureWalkMode || (exports.FeatureWalkMode = {})); -const config = getConfig(); /** - * Represents a list of property declarations, compute shorthand and remove duplicate declarations. + * render ast token + * @param token + * @param options + * @param cache + * @param reducer + * @param errors + * @private */ -class PropertyList { - options = { removeDuplicateDeclarations: true, computeShorthand: true }; - declarations; - constructor(options = {}) { - this.options = options; - this.declarations = new Map(); +function printValue$1(token, arena, options, cache = Object.create(null), reducer, errors) { + const typ = arena.getType(token); + // console.error(EnumToken[typ]); + if (typ === exports.EnumToken.WhenElseFunctionTokenType && arena.equalsIgnoreCase(token, "val", "supports")) { + options = { ...options, minifyValues: false, minify: false, convertColor: false }; + reducer = null; } - add(declarations) { - let name; - let syntaxRules = null; - let result; - for (const declaration of declarations) { - name = declaration.typ != exports.EnumToken.DeclarationNodeType ? null : declaration.nam; - if (declaration[STATE] & - (exports.EnumAstNodeStatus.Invalid | exports.EnumAstNodeStatus.Unknown | exports.EnumAstNodeStatus.ValidationFailed) || - declaration.typ != exports.EnumToken.DeclarationNodeType || - equalsIgnoreCase("composes", name) || - (typeof this.options.removeDuplicateDeclarations === "string" && - this.options.removeDuplicateDeclarations === name) || - (Array.isArray(this.options.removeDuplicateDeclarations) - ? this.options.removeDuplicateDeclarations.includes(declaration.nam) - : !this.options.removeDuplicateDeclarations)) { - const key = objectHash(declaration); - if (this.declarations.has(key)) { - this.declarations.delete(key); + options.minifyValues ??= options.minify; + if (reducer == null) { + reducer = function (acc, curr) { + if (arena.getType(curr) == exports.EnumToken.CommentTokenType && options.removeComments) { + if (!options.preserveLicense || !arena.startsWith(curr, "val", "/*!")) { + return acc; } - this.declarations.set(key, declaration); - continue; + return acc + arena.getValue(curr); } - if (!this.options.computeShorthand) { - this.declarations.set(declaration.nam, declaration); - continue; + return acc + printValue$1(curr, arena, options, cache, reducer, errors); + }; + } + switch (typ) { + case exports.EnumToken.UrlFunctionTokenDefType: + case exports.EnumToken.FunctionTokenDefType: + case exports.EnumToken.MathFunctionTokenDefType: + case exports.EnumToken.ImageFunctionTokenDefType: + case exports.EnumToken.ColorFunctionTokenDefType: + case exports.EnumToken.TimingFunctionTokenDefType: + case exports.EnumToken.WhenElseFunctionTokenDefType: + case exports.EnumToken.SupportsFunctionTokenDefType: + case exports.EnumToken.TimelineFunctionTokenDefType: + case exports.EnumToken.GridTemplateFuncTokenDefType: + case exports.EnumToken.TransformFunctionTokenDefType: + case exports.EnumToken.ContainerFunctionTokenDefType: + case exports.EnumToken.PseudoClassFunctionTokenDefType: + case exports.EnumToken.GeneralEnclosedFunctionTokenDefType: + case exports.EnumToken.CustomFunctionTokenDefType: + case exports.EnumToken.WildCardFunctionTokenDefType: + return arena.getValue(token) + "("; + case exports.EnumToken.ListTokenType: + return arena + .children(token) + .reduce((acc, curr) => acc + printValue$1(curr, arena, options, cache), ""); + case exports.EnumToken.BinaryExpressionTokenType: + if (exports.EnumToken.Mul === token.op || + exports.EnumToken.Div === token.op) { + let result = ""; + if (token.l.typ == exports.EnumToken.BinaryExpressionTokenType && + [exports.EnumToken.Add, exports.EnumToken.Sub].includes(token.l.op)) { + result = "(" + printValue$1(token.l, arena, options, cache) + ")"; + } + else { + result = printValue$1(token.l, options, cache); + } + result += token.op == exports.EnumToken.Mul ? "*" : "/"; + if (token.r.typ == exports.EnumToken.BinaryExpressionTokenType && + [exports.EnumToken.Add, exports.EnumToken.Sub].includes(token.r.op)) { + result += "(" + printValue$1(token.r, options, cache) + ")"; + } + else { + result += printValue$1(token.r, options, cache); + } + return result; } - if (declaration[STATE] & exports.EnumAstNodeStatus.Unvalidated) { - syntaxRules = getParsedSyntax(ValidationSyntaxGroupEnum.Declarations, declaration.nam.toLowerCase()); - if (syntaxRules != null) { - result = matchAllSyntaxes(syntaxRules, createValidationContext(declaration.val), this.options); - declaration[STATE] |= - (result.success ? exports.EnumAstNodeStatus.Validated : exports.EnumAstNodeStatus.ValidationFailed) & - ~exports.EnumAstNodeStatus.Unvalidated; + return (printValue$1(token.l, options, cache) + + (token.op == exports.EnumToken.Add ? " + " : " - ") + + printValue$1(token.r, options, cache)); + case exports.EnumToken.FractionTokenType: + const fraction = printValue$1(token.l) + "/" + printValue$1(token.r); + if (+token.r.val != 0) { + const value = minifyNumber(+token.l.val / +token.r.val); + if (value.length <= fraction.length) { + return value; } } - // do not compute shorthand for invalid declarations - if (!(declaration[STATE] & exports.EnumAstNodeStatus.Validated)) { - this.declarations.set(objectHash(declaration), declaration); - continue; + return fraction; + case exports.EnumToken.Add: + return " + "; + case exports.EnumToken.Sub: + return " - "; + case exports.EnumToken.UniversalSelectorTokenType: + case exports.EnumToken.Star: + case exports.EnumToken.Mul: + return "*"; + case exports.EnumToken.Div: + return "/"; + case exports.EnumToken.WrappedValuesTokenType: + return ("{" + + token.chi.reduce((acc, curr) => acc + printValue$1(curr, options, cache), "") + + "}"); + case exports.EnumToken.ColorTokenType: + if (token.kin == exports.ColorType.LIGHT_DARK || + ("chi" in token && options.convertColor === false && token.chi.length == 2)) { + return (token.val + + "(" + + token.chi.reduce((acc, curr) => { + if (curr.typ === exports.EnumToken.IdenTokenType) { + parseColor(curr); + } + return acc + printValue$1(curr, options, cache); + }, "") + + ")"); } - let propertyName = declaration.nam; - let shortHandType; - let shorthand = null; - if (propertyName in config.properties) { - // @ts-ignore - if ("map" in config.properties[propertyName]) { - shortHandType = "map"; - // @ts-ignore - shorthand = config.properties[propertyName].map; - } - else { - shortHandType = "set"; - // @ts-ignore - shorthand = config.properties[propertyName].shorthand; + if (options.convertColor !== false) { + const value = convertColor(token, typeof options.convertColor === "boolean" + ? exports.ColorType.HEX + : (exports.ColorType[exports.ColorType[options.convertColor ?? "HEX"] + ?.toUpperCase?.() + .replaceAll?.("-", "_")] ?? exports.ColorType.HEX)); + if (value != null) { + token = value; + } + if (token.kin == exports.ColorType.HEX || token.kin == exports.ColorType.LIT) { + return reduceHexValue(token.val); } } - else if (propertyName in config.map) { - shortHandType = "map"; - // @ts-ignore - shorthand = config.map[propertyName].shorthand; + if (token.kin === exports.ColorType.SYS || + token.kin === exports.ColorType.DPSYS || + token.kin === exports.ColorType.NON_STD) { + return token.val; } - else if (propertyName in config.property) { - shortHandType = "single"; - // @ts-ignore - shorthand = config.property[propertyName]; + if (token.kin == exports.ColorType.HEX || token.kin == exports.ColorType.LIT) { + return token.val; } - if (this.declarations.has(shorthand)) { - let existing = this.declarations.get(shorthand); - if (existing instanceof PropertySet || existing instanceof PropertyMap) { - existing = existing.declarations.get(declaration.nam); - } - if (existing?.[STATE] & exports.EnumAstNodeStatus.ImportantDeclaration && - declaration[STATE] & ~exports.EnumAstNodeStatus.ImportantDeclaration) { - continue; - } + if (Array.isArray(token.chi)) { + const fnName = token.val; + const isLegacy = ["rgb", "rgba", "hsl", "hsla"].includes(token.val); + const hasAlpha = [ + "rgb", + "rgba", + "hsl", + "hsla", + "hwb", + "oklab", + "oklch", + "lab", + "lch", + ].includes(fnName); + const useAlpha = (hasAlpha && token.chi.length == (isLegacy ? 7 : 4)) || + ("color" == token.val && token.chi.length == 5); + return ((hasAlpha && token.val.endsWith("a") ? fnName.slice(0, -1) : fnName) + + "(" + + token + .chi.reduce((acc, curr, index, array) => { + if (/[,/]\s*$/.test(acc)) { + if (curr.typ == exports.EnumToken.WhitespaceTokenType) { + return acc.trimEnd(); + } + return acc.trimStart() + printValue$1(curr, options, cache); + } + if (isLegacy && curr.typ == exports.EnumToken.CommaTokenType) { + return acc.trimEnd() + " "; + } + if (curr.typ == exports.EnumToken.WhitespaceTokenType) { + return acc.trimEnd() + " "; + } + if (curr.typ == exports.EnumToken.CommaTokenType || + (curr.typ == exports.EnumToken.LiteralTokenType && curr.val == "/")) { + return acc.trimEnd() + (curr.typ == exports.EnumToken.CommaTokenType ? "," : "/"); + } + return (acc.trimEnd() + + (useAlpha && index == array.length - 1 ? "/" : " ") + + printValue$1(curr, options, cache)); + }, "") + .trimStart() + + ")"); } - // @ts-ignore - if (shortHandType == "map") { - let owner = this; - const mapName = - // @ts-ignore - config.map[propertyName]?.map ?? config.map[shorthand]?.properties?.[propertyName]?.map; - if (typeof mapName === "string") { - if (!this.declarations.has(mapName)) { - // @ts-ignore - this.declarations.set(mapName, new PropertyMap(config.map[mapName])); - } - owner = this.declarations.get(mapName); - } - else if (!this.declarations.has(shorthand)) { - // @ts-ignore - this.declarations.set(shorthand, new PropertyMap(config.map[shorthand])); - } - if (owner == this) { - // @ts-ignore - owner.declarations.get(shorthand).add(declaration); + case exports.EnumToken.UrlFunctionTokenType: + if (options.minifyValues && token.typ === exports.EnumToken.UrlFunctionTokenType) { + if (token.chi[0]?.typ === exports.EnumToken.BadUrlTokenType) { + return "url()"; } - else { - if (!owner.declarations.has(shorthand)) { - // @ts-ignore - owner.declarations.set(shorthand, new PropertyMap(config.map[shorthand])); + for (const child of token.chi) { + if (child.typ === exports.EnumToken.StringTokenType) { + if (child.val.slice(1, 5) !== "data:" && + urlTokenMatcher.test(child.val)) { + Object.assign(child, { + typ: exports.EnumToken.UrlTokenTokenType, + val: child.val.slice(1, -1), + }); + } + break; } - // @ts-ignore - owner.declarations.get(shorthand).add(declaration); - } - } - // @ts-ignore - else if (shortHandType == "set") { - // @ts-ignore - if (!this.declarations.has(shorthand)) { - // @ts-ignore - this.declarations.set( - // @ts-ignore - shorthand, - // @ts-ignore - new PropertySet(config.properties[shorthand])); - } - // @ts-ignore - this.declarations.get(shorthand).add(declaration); - } - else { - if (shorthand != null) { - this.mapValues(declaration, shorthand); } - this.declarations.set(propertyName, declaration); } - } - return this; - } - mapValues(declaration, mapping) { - let name; - let values = declaration.val; - if (mapping.pattern != null) { - // match pattern - for (const patterns of mapping.pattern) { - const set = new Set(values.filter((v) => v.typ !== exports.EnumToken.CommentTokenType && v.typ !== exports.EnumToken.WhitespaceTokenType)); - const val = []; - for (const pattern of patterns) { - switch (pattern) { - case "": - for (const v of set) { - if (v.typ === exports.EnumToken.LengthTokenType || - (v.typ === exports.EnumToken.NumberTokenType && v.val === 0)) { - val.push(v); - set.delete(v); + case exports.EnumToken.ParensTokenType: + case exports.EnumToken.FunctionTokenType: + case exports.EnumToken.MathFunctionTokenType: + case exports.EnumToken.ImageFunctionTokenType: + if (options.minifyValues && token.typ === exports.EnumToken.ImageFunctionTokenType) { + const slice = token.chi.slice(); + switch (token.val) { + case "linear-gradient": + case "repeating-linear-gradient": + { + let i; + let k; + let j; + let key; + for (i = 0; i < slice.length; i++) { + if (slice[i].typ == exports.EnumToken.ColorTokenType || + slice[i].typ === exports.EnumToken.CommaTokenType) { + break; + } + if (slice[i].typ == exports.EnumToken.IdenTokenType && + equalsIgnoreCase(slice[i].val, "to")) { + k = i + 1; + while (k < slice.length && + (slice[k].typ === exports.EnumToken.WhitespaceTokenType || + slice[k].typ === exports.EnumToken.CommentTokenType)) { + k++; + } + if (k < slice.length && slice[k].typ === exports.EnumToken.IdenTokenType) { + // convert keyword to angle + key = slice[k].val.toLowerCase(); + switch (key) { + case "right": + slice.splice(i, k - i + 1, { + typ: exports.EnumToken.AngleTokenType, + val: 90, + }); + break; + case "left": + slice.splice(i, k - i + 1, { + typ: exports.EnumToken.AngleTokenType, + val: 270, + }); + break; + case "top": + case "bottom": + j = k + 1; + while (j < slice.length && + (slice[j].typ === exports.EnumToken.WhitespaceTokenType || + slice[j].typ === exports.EnumToken.CommentTokenType)) { + j++; + } + if (j < slice.length && slice[j].typ !== exports.EnumToken.CommaTokenType) { + i = j - 1; + break; + } + // https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/gradient/linear-gradient#:~:text=If%20unspecified%2C%20it%20defaults%20to%20to%20bottom + if (key === "bottom") { + // to bottom -> 180deg is the default angle and will be removed + while (k < slice.length && + slice[k].typ != exports.EnumToken.CommaTokenType) { + k++; + } + slice.splice(i, k - i + 1); + i--; + continue; + } + slice.splice(i, k - i + 1, { + typ: exports.EnumToken.AngleTokenType, + val: key === "top" ? 0 : 180, + }); + i--; + break; + } + } + } + else if (slice[i].typ == exports.EnumToken.AngleTokenType) { + if ((180 - toDegrees(slice[i]).val) % 360 === 0) { + k = i + 1; + // while (k < slice.length && slice[k].typ !== EnumToken.CommaTokenType) { + // k++; + // } + slice.splice(i, k - i + 1); + } + } + } + if (slice[i]?.typ === exports.EnumToken.CommaTokenType) { + i++; + // while ( + // i < slice.length && + // (slice[i].typ === EnumToken.WhitespaceTokenType || + // slice[i].typ === EnumToken.CommentTokenType) + // ) { + // i++; + // } + } + if (slice[i]?.typ === exports.EnumToken.ColorTokenType) { + for (const token of reduceColorStops(slice.splice(i, slice.length - i))) { + slice.push(token); + } + } + } + break; + case "radial-gradient": + case "repeating-radial-gradient": + { + let i = 0; + // while ( + // i < slice.length && + // (slice[i].typ === EnumToken.WhitespaceTokenType || + // slice[i].typ === EnumToken.CommentTokenType) + // ) { + // i++; + // } + const positions = []; + const form = []; + const size = []; + const colorSpaceDef = []; + if (slice[i]?.typ === exports.EnumToken.IdenTokenType) { + if (equalsIgnoreCase(slice[i].val, "farthest-corner") || + equalsIgnoreCase(slice[i].val, "ellipse")) { + i++; + } + else if (equalsIgnoreCase(slice[i].val, "closest-side") || + equalsIgnoreCase(slice[i].val, "closest-corner") || + equalsIgnoreCase(slice[i].val, "farthest-side")) { + size.push(slice[i++]); + } + else if (equalsIgnoreCase(slice[i].val, "circle")) { + form.push(slice[i++]); + } + while (slice[i]?.typ === exports.EnumToken.WhitespaceTokenType || + slice[i]?.typ === exports.EnumToken.CommentTokenType) { + i++; + } + } + if (slice[i]?.typ === exports.EnumToken.IdenTokenType) { + if (equalsIgnoreCase(slice[i].val, "farthest-corner") || + equalsIgnoreCase(slice[i].val, "ellipse")) { + i++; + } + else if (equalsIgnoreCase(slice[i].val, "closest-side") || + equalsIgnoreCase(slice[i].val, "closest-corner") || + equalsIgnoreCase(slice[i].val, "farthest-side")) { + size.push(slice[i++]); + } + else if (equalsIgnoreCase(slice[i].val, "circle")) { + form.push(slice[i++]); + } + while (slice[i]?.typ === exports.EnumToken.WhitespaceTokenType || + slice[i]?.typ === exports.EnumToken.CommentTokenType) { + i++; + } + } + if (slice[i]?.typ === exports.EnumToken.IdenTokenType && + equalsIgnoreCase(slice[i].val, "at")) { + i++; + } + while (slice[i]?.typ === exports.EnumToken.WhitespaceTokenType || + slice[i]?.typ === exports.EnumToken.CommentTokenType) { + i++; + } + if (slice[i]?.typ === exports.EnumToken.PercentageTokenType || + (slice[i]?.typ === exports.EnumToken.NumberTokenType && 0 === slice[i].val) || + (slice[i]?.typ === exports.EnumToken.IdenTokenType && + (equalsIgnoreCase(slice[i].val, "center") || + equalsIgnoreCase(slice[i].val, "top") || + equalsIgnoreCase(slice[i].val, "bottom") || + equalsIgnoreCase(slice[i].val, "left") || + equalsIgnoreCase(slice[i].val, "right")))) { + do { + if (slice[i]?.typ === exports.EnumToken.IdenTokenType && + "in" === slice[i].val) { + break; + } + positions.push(slice[i]); + i++; + } while (slice[i]?.typ !== exports.EnumToken.CommaTokenType); + } + while (slice[i]?.typ !== exports.EnumToken.CommaTokenType) { + colorSpaceDef.push(slice[i++]); + } + if (slice[i]?.typ === exports.EnumToken.CommaTokenType) { + i++; + } + if (positions.length > 0) { + let k; + let position1 = ""; + let position2 = ""; + for (k = 0; k < positions.length; k++) { + if (positions[k].typ === exports.EnumToken.IdenTokenType) { + if (position1.length === 0) { + position1 = positions[k].val; + } + else { + position2 = positions[k].val; + } + } + else if (positions[k].typ === exports.EnumToken.PercentageTokenType || + positions[k].typ === exports.EnumToken.NumberTokenType) { + if (position1.length === 0) { + position1 = positions[k].val + "%"; + } + else { + position2 = positions[k].val + "%"; + } + } + if (position2.length > 0) { + break; + } + } + if (position1.length > 0) { + let key = `${position1} ${position2}`.trim(); + if (key === "50% 50%" || + key === "center center" || + key === "50%" || + key === "center") { + positions.length = 0; + } + else if (key === "0% 0%" || + key === "0 0" || + key === "top left" || + key === "left top") { + positions.splice(0, positions.length, { + typ: exports.EnumToken.PercentageTokenType, + val: 0, + }); + } + else if (key === "50% 0%" || + key === "50% 0" || + key === "center top" || + key === "top center") { + positions.splice(0, positions.length, { + typ: exports.EnumToken.IdenTokenType, + val: "top", + }); + } + else if (key === "top right" || key === "right top") { + positions.splice(0, positions.length, { + typ: exports.EnumToken.PercentageTokenType, + val: 100, + }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.PercentageTokenType, val: 0 }); + } + else if (key === "0 50%" || + key === "0% 50%" || + key === "left center" || + key === "center left") { + positions.splice(0, positions.length, { + typ: exports.EnumToken.IdenTokenType, + val: "left", + }); + } + else if (key === "100% 50%" || key === "right center" || key === "center right") { + positions.splice(0, positions.length, { + typ: exports.EnumToken.IdenTokenType, + val: "right", + }); + } + else if (key === "0 100%" || + key === "0% 100%" || + key === "left bottom" || + key === "bottom left") { + positions.splice(0, positions.length, { + typ: exports.EnumToken.IdenTokenType, + val: "bottom", + }); + } + else if (key === "50% 100%" || + key === "center bottom" || + key === "bottom center") { + positions.splice(0, positions.length, { + typ: exports.EnumToken.IdenTokenType, + val: "bottom", + }); + } + else if (key === "100% 100%" || + key === "right bottom" || + key === "bottom right") { + positions.splice(0, positions.length, { + typ: exports.EnumToken.PercentageTokenType, + val: 100, + }); + } + else if (position1 === position2) { + positions.splice(0, positions.length, positions[k]); + } + } + } + const result = []; + if (form.length > 0) { + for (const token of form) { + result.push(token); + } + } + if (size.length > 0) { + if (result.length > 0) { + result.push({ typ: exports.EnumToken.WhitespaceTokenType }); + } + for (const token of size) { + result.push(token); + } + } + if (positions.length > 0) { + if (result.length > 0) { + result.push({ typ: exports.EnumToken.WhitespaceTokenType }); + } + result.push({ typ: exports.EnumToken.IdenTokenType, val: "at" }, { typ: exports.EnumToken.WhitespaceTokenType }); + for (const token of positions) { + result.push(token); + } + } + if (colorSpaceDef.length > 0) { + if (result.length > 0) { + result.push({ typ: exports.EnumToken.WhitespaceTokenType }); + } + for (const token of colorSpaceDef) { + result.push(token); + } + } + if (result.length > 0) { + result.push({ typ: exports.EnumToken.CommaTokenType }); + } + for (const token of reduceColorStops(slice.slice(i))) { + result.push(token); + } + slice.length = 0; + for (const token of result) { + slice.push(token); + } + } + break; + case "conic-gradient": + case "repeating-conic-gradient": + { + let i = 0; + const angles = []; + const positions = []; + const colorSpaceDef = []; + // while ( + // i < slice.length && + // (slice[i].typ === EnumToken.WhitespaceTokenType || + // slice[i].typ === EnumToken.CommentTokenType) + // ) { + // i++; + // } + if (slice[i]?.typ === exports.EnumToken.IdenTokenType && + equalsIgnoreCase(slice[i].val, "from")) { + angles.push(slice[i++]); + while (i < slice.length && + (slice[i].typ === exports.EnumToken.WhitespaceTokenType || + slice[i].typ === exports.EnumToken.CommentTokenType)) { + angles.push(slice[i++]); + } + if ((slice[i]?.typ === exports.EnumToken.NumberTokenType || + slice[i]?.typ === exports.EnumToken.AngleTokenType) && + 0 === toDegrees(slice[i]).val) { + angles.length = 0; + i++; + } + else if (slice[i]?.typ !== exports.EnumToken.CommaTokenType && + slice[i].typ != exports.EnumToken.IdenTokenType) { + angles.push(slice[i++]); + } + while (i < slice.length && + (slice[i].typ === exports.EnumToken.WhitespaceTokenType || + slice[i].typ === exports.EnumToken.CommentTokenType)) { + i++; + } + } + if (slice[i]?.typ === exports.EnumToken.IdenTokenType && + equalsIgnoreCase(slice[i].val, "at")) { + i++; + while (i < slice.length && + (slice[i].typ === exports.EnumToken.WhitespaceTokenType || + slice[i].typ === exports.EnumToken.CommentTokenType)) { + i++; + } + let position1 = ""; + let position2 = ""; + if (slice[i]?.typ === exports.EnumToken.IdenTokenType && + !equalsIgnoreCase("in", slice[i].val)) { + position1 = slice[i].val; + positions.push(slice[i++]); + } + else if (slice[i]?.typ === exports.EnumToken.PercentageTokenType || + slice[i]?.typ === exports.EnumToken.NumberTokenType) { + position1 = slice[i].val + "%"; + positions.push(slice[i++]); + } + while (i < slice.length && + (slice[i].typ === exports.EnumToken.WhitespaceTokenType || + slice[i].typ === exports.EnumToken.CommentTokenType)) { + positions.push(slice[i++]); + } + if (slice[i]?.typ === exports.EnumToken.IdenTokenType && + !equalsIgnoreCase("in", slice[i].val)) { + position2 = slice[i].val; + positions.push(slice[i++]); + } + else if (slice[i]?.typ === exports.EnumToken.PercentageTokenType || + slice[i]?.typ === exports.EnumToken.NumberTokenType) { + position2 = slice[i].val + "%"; + positions.push(slice[i++]); + } + while (i < slice.length && + (slice[i].typ === exports.EnumToken.WhitespaceTokenType || + slice[i].typ === exports.EnumToken.CommentTokenType)) { + i++; + } + if (position1.length > 0) { + reducegradientBackgroundPosition(positions, `${position1} ${position2}`.trim()); } } - default: { - for (const v of set) { - if (v.typ === exports.EnumToken.IdenTokenType) { - const value = v.val.toLowerCase(); - if (value === pattern || new RegExp(`(^|\|)${pattern}(\||$)`).test(value)) { - val.push(v); - set.delete(v); + while (i < slice.length && + (slice[i].typ === exports.EnumToken.WhitespaceTokenType || + slice[i].typ === exports.EnumToken.CommentTokenType)) { + i++; + } + if (slice[i]?.typ === exports.EnumToken.IdenTokenType && + equalsIgnoreCase("in", slice[i].val)) { + while (i < slice.length && slice[i].typ !== exports.EnumToken.CommaTokenType) { + colorSpaceDef.push(slice[i++]); + } + } + if (slice[i]?.typ === exports.EnumToken.CommaTokenType) { + i++; + } + const result = []; + if (positions.length > 0) { + if (positions.length > 0) { + if (angles.length > 0) { + angles.push({ typ: exports.EnumToken.WhitespaceTokenType }); + } + angles.push({ typ: exports.EnumToken.IdenTokenType, val: "at" }, { typ: exports.EnumToken.WhitespaceTokenType }); + for (const position of positions) { + angles.push(position); } } } + if (angles.length > 0) { + for (const angle of angles) { + result.push(angle); + } + result.push({ typ: exports.EnumToken.CommaTokenType }); + } + if (colorSpaceDef.length > 0) { + if (colorSpaceDef.length > 0) { + if (result.length > 0) { + result.push({ typ: exports.EnumToken.WhitespaceTokenType }); + } + for (const token of colorSpaceDef) { + result.push(token); + } + } + result.push({ typ: exports.EnumToken.CommaTokenType }); + } + for (const token of reduceConicColorStops(slice.slice(i))) { + result.push(token); + } + slice.length = 0; + for (let j = 0; j < result.length; j++) { + slice.push(result[j]); + } } - } - } - if (set.size === 0) { - values = val.reduce((acc, curr) => { - if (acc.length > 0) { - acc.push({ typ: exports.EnumToken.WhitespaceTokenType }); - } - acc.push(curr); - return acc; - }, []); - break; - } - } - } - for (const val of declaration.val) { - if (val.typ === exports.EnumToken.IdenTokenType) { - name = val.val.toLowerCase(); - if (mapping.mapping[name] != null) { - // @ts-expect-error - Object.assign(val, mapping.mapping[name], { typ: exports.EnumToken[mapping.mapping[name].typ] }); + break; } + return token.val + "(" + slice.reduce(reducer, "") + ")"; } - } - if (values != declaration.val) { - declaration.val.length = 0; - for (const v of values) { - declaration.val.push(v); - } - } - } - [Symbol.iterator]() { - let iterator = this.declarations.values(); - const iterators = []; - // @ts-ignore - return { - next() { - let value = iterator.next(); - while ((value.done && iterators.length > 0) || - value.value instanceof PropertySet || - value.value instanceof PropertyMap) { - if (value.value instanceof PropertySet || value.value instanceof PropertyMap) { - iterators.unshift(iterator); - // @ts-ignore - iterator = value.value[Symbol.iterator](); - value = iterator.next(); - } - if (value.done && iterators.length > 0) { - iterator = iterators.shift(); - value = iterator.next(); - } - } - return value; - }, - }; - } -} - -class ComputeShorthandFeature { - accept = new Set([ - exports.EnumToken.RuleNodeType, - exports.EnumToken.AtRuleNodeType, - exports.EnumToken.KeyframesRuleNodeType, - ]); - get ordering() { - return 5; - } - get processMode() { - return exports.FeatureWalkMode.Post; - } - static register(options) { - if (options.computeShorthand) { - // @ts-ignore - options.features.push(new ComputeShorthandFeature(options)); - } - } - run(ast, options) { - if (!("chi" in ast || ast.chi?.length == 0)) { - return null; - } - // @ts-ignore - const j = ast.chi.length; - let k = 0; - let l; - const rules = []; - const declarations = []; - // @ts-ignore - for (; k < j; k++) { - l = k; - // capture comments with the next token - while (l + 1 < j) { + case exports.EnumToken.TimingFunctionTokenType: + case exports.EnumToken.PseudoClassFuncTokenType: + case exports.EnumToken.WhenElseFunctionTokenType: + case exports.EnumToken.TimelineFunctionTokenType: + case exports.EnumToken.GridTemplateFuncTokenType: + case exports.EnumToken.SupportsFunctionTokenType: + case exports.EnumToken.ContainerFunctionTokenType: + case exports.EnumToken.TransformFunctionTokenType: + case exports.EnumToken.GeneralEnclosedFunctionTokenType: + case exports.EnumToken.CustomFunctionTokenType: + case exports.EnumToken.WildCardFunctionTokenType: + if (token.typ == exports.EnumToken.MathFunctionTokenType && + token.chi.length == 1 && + ![exports.EnumToken.BinaryExpressionTokenType, exports.EnumToken.FractionTokenType, exports.EnumToken.IdenTokenType].includes(token.chi[0].typ) && // @ts-ignore - const node = ast.chi[l]; - if (node.typ == exports.EnumToken.CommentNodeType) { - l++; - continue; - } - break; - } - // @ts-ignore - const node = ast.chi[l]; - if (node.typ == exports.EnumToken.DeclarationNodeType) { - for (let m = k; m <= l; m++) { - declarations.push(ast.chi[m]); - } - } - else { - for (let m = k; m <= l; m++) { - rules.push(ast.chi[m]); - } - } - k = l; - } - if (declarations.length > 0) { - ast.chi.length = 0; - // @ts-expect-error - ast.chi.push(...new PropertyList(options).add(declarations), ...rules); - } - return ast; - } -} - -const trimTokenSpace = new Set([ - exports.EnumToken.CommaTokenType, - exports.EnumToken.DelimTokenType, - exports.EnumToken.GtTokenType, - exports.EnumToken.GteTokenType, - exports.EnumToken.LtTokenType, - exports.EnumToken.LteTokenType, - exports.EnumToken.ChildCombinatorTokenType, -]); -const trimSpaceAfter = new Set([ - ...tokensfuncSet.values(), - exports.EnumToken.ChildCombinatorTokenType, - exports.EnumToken.SupportsQueryConditionTokenType, - exports.EnumToken.SupportsQueryUnaryConditionTokenType, -]); -/** - * Replace token in its parent node - * @param parent - * @param node - * @param replacement - * @throws TypeError replacement is null - * @throws ReferenceError node not found in parent - */ -function replaceNodeOrValue(parent, node, replacement) { - if (replacement == null || (Array.isArray(replacement) && replacement.length === 0)) { - throw new TypeError(`replacement is null`); - } - for (const newNode of Array.isArray(replacement) ? replacement : [replacement]) { - if (newNode[PARENT] != node[PARENT]) { - newNode[PARENT] = node[PARENT]; - } - } - if (parent.typ == exports.EnumToken.BinaryExpressionTokenType) { - if (parent.l == node) { - parent.l = replacement; - } - else if (parent.r == node) { - parent.r = replacement; - } - else { - throw new ReferenceError("Node not found"); - } - } - else { - const target = "val" in parent && Array.isArray(parent.val) - ? parent.val - : (parent.chi ?? parent); - if (Array.isArray(target)) { - // @ts-ignore - const index = target.indexOf(node); - if (index == -1) { - throw new ReferenceError("Node not found"); - } - target.splice(index, 1, ...(Array.isArray(replacement) ? replacement : [replacement])); - } - else if ("l" in target && target.l == node) { - target.l = replacement; - } - else if ("r" in target && target.r == node) { - target.r = replacement; - } - else { - throw new ReferenceError("Node not found"); - } - } - node[PARENT] = null; - return true; -} -function trimWhiteSpaceTokens(tokens) { - let i = 0; - if (tokens[0]?.typ === exports.EnumToken.WhitespaceTokenType) { - tokens.shift(); - } - if (tokens[tokens.length - 1]?.typ === exports.EnumToken.WhitespaceTokenType) { - tokens.pop(); - } - if (tokens.length === 1) { - if (tokens[0].typ === exports.EnumToken.WhitespaceTokenType) { - tokens.length = 0; - } - return tokens; - } - for (; i < tokens.length; i++) { - if (trimSpaceAfter.has(tokens[i].typ) && tokens[i + 1]?.typ === exports.EnumToken.WhitespaceTokenType) { - tokens.splice(i + 1, 1); - } - else if (trimTokenSpace.has(tokens[i].typ)) { - if (tokens[i + 1]?.typ === exports.EnumToken.WhitespaceTokenType) { - tokens.splice(i + 1, 1); - } - if (i > 0 && tokens[i - 1].typ === exports.EnumToken.Whitespace) { - tokens.splice(--i, 1); - } - } - } - return tokens; -} - -class ComputeCalcExpressionFeature { - accept = new Set([exports.EnumToken.RuleNodeType, exports.EnumToken.AtRuleNodeType]); - get ordering() { - return 3; - } - get processMode() { - return exports.FeatureWalkMode.Post; - } - static register(options) { - if (options.computeCalcExpression) { - // @ts-ignore - options.features.push(new ComputeCalcExpressionFeature()); - } - } - run(ast) { - // @ts-ignore - if (ast.chi == null) { - return null; - } - for (const node of ast.chi) { - if (node.typ != exports.EnumToken.DeclarationNodeType) { - continue; + token.chi[0].val + ?.typ != exports.EnumToken.FractionTokenType) { + return (token.val + + "(" + + token.chi.reduce((acc, curr) => acc + + printValue$1(curr, token.typ == exports.EnumToken.FunctionTokenType ? { minify: false } : options, cache, reducer), "") + + ")"); } - const set = new Set(); - for (const { value, parent } of walkValues(node.val, node)) { - if (parent?.typ == exports.EnumToken.BinaryExpressionTokenType) { - continue; - } - if (value.typ == exports.EnumToken.BinaryExpressionTokenType) { - const result = evaluate([value]); - try { - // @ts-ignore - replaceNodeOrValue(parent, value, result); + return (arena.getValue(token) ?? "") + "(" + arena.children(token).reduce(reducer, "") + ")"; + case exports.EnumToken.BlockStartTokenType: + return "{"; + case exports.EnumToken.BlockEndTokenType: + return "}"; + case exports.EnumToken.StartParensTokenType: + return "("; + case exports.EnumToken.DelimTokenType: + case exports.EnumToken.EqualMatchTokenType: + return "="; + case exports.EnumToken.IncludeMatchTokenType: + return "~="; + case exports.EnumToken.DashMatchTokenType: + return "|="; + case exports.EnumToken.StartMatchTokenType: + return "^="; + case exports.EnumToken.EndMatchTokenType: + return "$="; + case exports.EnumToken.ContainMatchTokenType: + return "*="; + case exports.EnumToken.LtTokenType: + return "<"; + case exports.EnumToken.LteTokenType: + return "<="; + case exports.EnumToken.Tilda: + case exports.EnumToken.SubsequentSiblingCombinatorTokenType: + return "~"; + case exports.EnumToken.Plus: + case exports.EnumToken.NextSiblingCombinatorTokenType: + return "+"; + case exports.EnumToken.GtTokenType: + case exports.EnumToken.ChildCombinatorTokenType: + return ">"; + case exports.EnumToken.GteTokenType: + return ">="; + case exports.EnumToken.ColumnCombinatorTokenType: + return "||"; + case exports.EnumToken.EndParensTokenType: + return ")"; + case exports.EnumToken.AttrStartTokenType: + return "["; + case exports.EnumToken.AttrEndTokenType: + return "]"; + case exports.EnumToken.DescendantCombinatorTokenType: + case exports.EnumToken.WhitespaceTokenType: + return " "; + case exports.EnumToken.ColonTokenType: + return ":"; + case exports.EnumToken.DoubleColonTokenType: + return "::"; + case exports.EnumToken.SemiColonTokenType: + return ";"; + case exports.EnumToken.CommaTokenType: + return ","; + case exports.EnumToken.ImportantTokenType: + return "!important"; + case exports.EnumToken.Pipe: + return "|"; + case exports.EnumToken.AttrTokenType: + case exports.EnumToken.IdenListTokenType: + return "[" + arena.children(token).reduce(reducer, "") + "]"; + case exports.EnumToken.TimeTokenType: + case exports.EnumToken.AngleTokenType: + case exports.EnumToken.LengthTokenType: + case exports.EnumToken.DimensionTokenType: + case exports.EnumToken.FrequencyTokenType: + case exports.EnumToken.ResolutionTokenType: + let val = arena.hasFlags(token, exports.EnumAstNodeStatus.HasFractionValue) ? + printValue$1(arena.getNumericValue(token), arena, options, cache) + : minifyNumber(arena.getNumericValue(token)); + let unit = arena.getUnit(token); + if (typ == exports.EnumToken.AngleTokenType && !val.includes("/")) { + const angle = getAngle(token, arena); + let v; + let value = val + unit; + for (const u of ["deg", "turn", "rad", "grad"]) { + if (unit == u) { + continue; } - catch (e) { } - continue; - } - if (value != null && tokensfuncSet.has(value.typ)) { - if (!set.has(value)) { - set.add(value); - if (parent != null) { - const shouldEvaluate = value.typ === exports.EnumToken.MathFunctionTokenType || - (value.typ == exports.EnumToken.FunctionTokenType && - mathFuncs.includes(value.val)); - if (shouldEvaluate) { - // @ts-ignore - const cp = value.val != "calc" - ? [value] - : value.typ == exports.EnumToken.DeclarationNodeType - ? value.val - : value.chi; - const values = evaluate(cp); - // fix a + -b into a - b - for (const { value, parent: p } of walkValues(values)) { - if (value.typ === exports.EnumToken.BinaryExpressionTokenType) { - if (value.op === exports.EnumToken.Add && - Math.sign(value.r.val) == -1) { - value.op = exports.EnumToken.Sub; - // @ts-expect-error - value.r.val *= - -1; - } - } - } - // @ts-ignore - const children = parent.typ == exports.EnumToken.DeclarationNodeType - ? parent.val - : // @ts-ignore - parent.chi; - if (values.length == 1 && values[0].typ != exports.EnumToken.BinaryExpressionTokenType) { - for (let i = 0; i < children.length; i++) { - if (children[i] == value) { - children.splice(i, 1, !(parent.typ === exports.EnumToken.MathFunctionTokenType || - (parent.typ == exports.EnumToken.FunctionTokenType && - parent.val == "calc")) && - typeof values[0].val != "number" && - !(values[0].typ == exports.EnumToken.MathFunctionTokenType || - (values[0].typ == exports.EnumToken.FunctionTokenType && - mathFuncs.includes(values[0].val))) - ? { - typ: exports.EnumToken.MathFunctionTokenType, - val: "calc", - chi: values, - [LOCSRCID]: value[LOCSRCID], - [LOCSTA]: value[LOCSTA], - [LOCEND]: value[LOCEND], - } - : values[0]); - break; - } - } - } - else { - for (let i = 0; i < children.length; i++) { - if (children[i] == value) { - children.splice(i, 1, { - typ: exports.EnumToken.MathFunctionTokenType, - val: "calc", - chi: values, - [LOCSRCID]: value[LOCSRCID], - [LOCSTA]: value[LOCSTA], - [LOCEND]: value[LOCEND], - }); - break; - } - } - } + switch (u) { + case "deg": + v = minifyNumber(toPrecisionAngle(angle * 360, anglePrecision, false).toFixed(anglePrecision)); + if (v.length + 3 < value.length) { + val = v; + unit = u; + value = v + u; } - } + break; + case "turn": + v = minifyNumber(toPrecisionAngle(angle, anglePrecision, false).toFixed(anglePrecision)); + if (v.length + 4 < value.length) { + val = v; + unit = u; + value = v + u; + } + break; + case "rad": + v = minifyNumber(toPrecisionAngle(angle * (2 * Math.PI), anglePrecision, false).toFixed(anglePrecision)); + if (v.length + 3 < value.length) { + val = v; + unit = u; + value = v + u; + } + break; + case "grad": + v = minifyNumber(toPrecisionAngle(angle * 400, anglePrecision, false).toFixed(anglePrecision)); + if (v.length + 4 < value.length) { + val = v; + unit = u; + value = v + u; + } + break; } } } - } - return null; - } -} - -const identityMatrix = new Float64Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); -function identity() { - return identityMatrix.slice(); -} -function normalize$1(point) { - const [x, y, z] = point; - const norm = Math.sqrt(point[0] * point[0] + point[1] * point[1] + point[2] * point[2]); - return norm === 0 ? [0, 0, 0] : [x / norm, y / norm, z / norm]; -} -function dot(point1, point2) { - if (point1.length === 4 && point2.length === 4) { - return point1[0] * point2[0] + point1[1] * point2[1] + point1[2] * point2[2] + point1[3] * point2[3]; - } - return point1[0] * point2[0] + point1[1] * point2[1] + point1[2] * point2[2]; -} -function multiply(matrixA, matrixB) { - const result = new Float64Array(16); - result[0] = matrixA[0] * matrixB[0] + matrixA[4] * matrixB[1] + matrixA[8] * matrixB[2] + matrixA[12] * matrixB[3]; - result[1] = matrixA[1] * matrixB[0] + matrixA[5] * matrixB[1] + matrixA[9] * matrixB[2] + matrixA[13] * matrixB[3]; - result[2] = matrixA[2] * matrixB[0] + matrixA[6] * matrixB[1] + matrixA[10] * matrixB[2] + matrixA[14] * matrixB[3]; - result[3] = matrixA[3] * matrixB[0] + matrixA[7] * matrixB[1] + matrixA[11] * matrixB[2] + matrixA[15] * matrixB[3]; - result[4] = matrixA[0] * matrixB[4] + matrixA[4] * matrixB[5] + matrixA[8] * matrixB[6] + matrixA[12] * matrixB[7]; - result[5] = matrixA[1] * matrixB[4] + matrixA[5] * matrixB[5] + matrixA[9] * matrixB[6] + matrixA[13] * matrixB[7]; - result[6] = matrixA[2] * matrixB[4] + matrixA[6] * matrixB[5] + matrixA[10] * matrixB[6] + matrixA[14] * matrixB[7]; - result[7] = matrixA[3] * matrixB[4] + matrixA[7] * matrixB[5] + matrixA[11] * matrixB[6] + matrixA[15] * matrixB[7]; - result[8] = - matrixA[0] * matrixB[8] + matrixA[4] * matrixB[9] + matrixA[8] * matrixB[10] + matrixA[12] * matrixB[11]; - result[9] = - matrixA[1] * matrixB[8] + matrixA[5] * matrixB[9] + matrixA[9] * matrixB[10] + matrixA[13] * matrixB[11]; - result[10] = - matrixA[2] * matrixB[8] + matrixA[6] * matrixB[9] + matrixA[10] * matrixB[10] + matrixA[14] * matrixB[11]; - result[11] = - matrixA[3] * matrixB[8] + matrixA[7] * matrixB[9] + matrixA[11] * matrixB[10] + matrixA[15] * matrixB[11]; - result[12] = - matrixA[0] * matrixB[12] + matrixA[4] * matrixB[13] + matrixA[8] * matrixB[14] + matrixA[12] * matrixB[15]; - result[13] = - matrixA[1] * matrixB[12] + matrixA[5] * matrixB[13] + matrixA[9] * matrixB[14] + matrixA[13] * matrixB[15]; - result[14] = - matrixA[2] * matrixB[12] + matrixA[6] * matrixB[13] + matrixA[10] * matrixB[14] + matrixA[14] * matrixB[15]; - result[15] = - matrixA[3] * matrixB[12] + matrixA[7] * matrixB[13] + matrixA[11] * matrixB[14] + matrixA[15] * matrixB[15]; - return result; -} -function inverse(matrix) { - // Create augmented matrix [matrix | identity] - let augmented = [ - matrix[0], - matrix[1], - matrix[2], - matrix[3], - 1, - 0, - 0, - 0, - matrix[4], - matrix[5], - matrix[6], - matrix[7], - 0, - 1, - 0, - 0, - matrix[8], - matrix[9], - matrix[10], - matrix[11], - 0, - 0, - 1, - 0, - matrix[12], - matrix[13], - matrix[14], - matrix[15], - 0, - 0, - 0, - 1, - ]; - // Gaussian elimination with partial pivoting - for (let col = 0; col < 4; col++) { - // Find pivot row with maximum absolute value - let maxRow = col; - let maxVal = Math.abs(augmented[col * 4 + col]); - for (let row = col + 1; row < 4; row++) { - let val = Math.abs(augmented[row * 4 + col]); - if (val > maxVal) { - maxVal = val; - maxRow = row; + if (val === "0") { + if (typ == exports.EnumToken.TimeTokenType) { + return "0s"; + } + if (typ == exports.EnumToken.FrequencyTokenType) { + return "0Hz"; + } + // @ts-ignore + if (typ == exports.EnumToken.ResolutionTokenType) { + return "0x"; + } + return "0"; } - } - // Check for singularity - if (maxVal < epsilon) { - return null; - } - // Swap rows if necessary - if (maxRow !== col) { - [augmented[col], augmented[maxRow]] = [augmented[maxRow], augmented[col]]; - } - // Scale pivot row to make pivot element 1 - let pivot = augmented[col * 4 + col]; - for (let j = 0; j < 8; j++) { - augmented[col * 4 + j] /= pivot; - } - // Eliminate column in other rows - for (let row = 0; row < 4; row++) { - if (row !== col) { - let factor = augmented[row * 4 + col]; - for (let j = 0; j < 8; j++) { - augmented[row * 4 + j] -= factor * augmented[col * 4 + j]; + if (typ == exports.EnumToken.TimeTokenType) { + if (unit == "ms") { + // @ts-ignore + const v = minifyNumber(val / 1000); + if (v.length + 1 <= val.length) { + return v + "s"; + } + return val + "ms"; } + return val + "s"; } + if (typ == exports.EnumToken.ResolutionTokenType && unit == "dppx") { + unit = "x"; + } + return val.includes("/") ? val.replace("/", unit + "/") : minifyNumber(toPrecisionValue(val)) + unit; + case exports.EnumToken.FlexTokenType: + case exports.EnumToken.PercentageTokenType: + const uni = typ == exports.EnumToken.PercentageTokenType ? "%" : "fr"; + const perc = arena.hasFlags(token, exports.EnumAstNodeStatus.HasFractionValue) + ? printValue$1(arena.getNumericValue(token), arena, options, cache) + : minifyNumber(arena.getNumericValue(token)); + return options.minifyValues && perc == "0" + ? "0" + : perc.includes("/") + ? perc.replace("/", uni + "/") + : perc + uni; + case exports.EnumToken.NumberTokenType: + return arena.hasFlags(token, exports.EnumAstNodeStatus.HasFractionValue) + ? printValue$1(arena.getNumericValue(token), arena, options, cache) + : minifyNumber(arena.getNumericValue(token)); + case exports.EnumToken.InfinityTokenType: + return "1/0"; + case exports.EnumToken.NegativeInfinityTokenType: + return "-1/0"; + case exports.EnumToken.NaNTokenType: + return "NaN"; + case exports.EnumToken.AtRuleTokenType: + return "@" + arena.getName(token); + case exports.EnumToken.CommentTokenType: + case exports.EnumToken.CDOCOMMNodeType: + if (options.removeComments && (!options.preserveLicense || !arena.startsWith(token, "val", "/*!"))) { + return ""; + } + case exports.EnumToken.PseudoClassTokenType: + case exports.EnumToken.PseudoElementTokenType: + // https://www.w3.org/TR/selectors-4/#single-colon-pseudos + if (typ == exports.EnumToken.PseudoElementTokenType) { + let val = arena.getValue(token); + let slice = val.slice(1); + if (pseudoElements.includes(slice)) { + return slice; + } + } + case exports.EnumToken.UrlTokenTokenType: + case exports.EnumToken.HashTokenType: + case exports.EnumToken.IdenTokenType: + case exports.EnumToken.StringTokenType: + case exports.EnumToken.LiteralTokenType: + case exports.EnumToken.DashedIdenTokenType: + case exports.EnumToken.PseudoPageTokenType: + case exports.EnumToken.ClassSelectorTokenType: + return arena.getValue(token); + case exports.EnumToken.NestingSelectorTokenType: + return "&"; + case exports.EnumToken.InvalidAttrTokenType: + return ("[" + + arena.children(token).reduce((acc, curr) => acc + printValue$1(curr, arena, options, cache), "")); + case exports.EnumToken.InvalidClassSelectorTokenType: + return arena.getValue(token); + case exports.EnumToken.SupportsQueryUnaryConditionTokenType: + case exports.EnumToken.WhenElseUnaryConditionTokenType: + return (printValue$1(arena.getLeft(token), arena, options, cache, reducer, errors) + + " " + + printValue$1(arena.getRight(token), arena, options, cache, reducer, errors)); + case exports.EnumToken.SupportsQueryConditionTokenType: + case exports.EnumToken.WhenElseQueryConditionTokenType: + return (printValue$1(arena.getLeft(token), arena, options, cache, reducer, errors) + + " " + + printValue$1(arena.getOp(token), arena, options, cache, reducer, errors) + + " " + + printValue$1(arena.getRight(token), arena, options, cache, reducer, errors)); + case exports.EnumToken.IfConditionTokenType: + return token.l.length == 0 + ? "" + : token.l.reduce((acc, curr) => acc + printValue$1(curr, options, cache, reducer, errors), "") + + ":" + + (options.indent ?? "") + + token.r.reduce((acc, curr) => acc + printValue$1(curr, options, cache, reducer, errors), ""); + case exports.EnumToken.IfElseConditionTokenType: + return (printValue$1(token.l, options, cache, reducer, errors) + + (options.indent ?? "") + + printValue$1(token.r, options, cache, reducer, errors)); + case exports.EnumToken.DeclarationNodeType: + return (arena.getName(token) + + ":" + + (options.minifyValues ? filterValues(arena.children(token), arena) : arena.children(token)).reduce((acc, curr) => acc + printValue$1(curr, arena, options, cache, reducer, errors), "")); + case exports.EnumToken.MediaQueryUnaryFeatureTokenType: + return (printValue$1(arena.getLeft(token), arena, options, cache, reducer, errors) + + " " + + printValue$1(arena.getRight(token), arena, options, cache, reducer, errors)); + case exports.EnumToken.MediaQueryConditionTokenType: { + const indent = token.op.typ == exports.EnumToken.LtTokenType || + token.op.typ == exports.EnumToken.GtTokenType || + token.op.typ == exports.EnumToken.ColonTokenType || + token.op.typ == exports.EnumToken.DelimTokenType || + token.op.typ == exports.EnumToken.LteTokenType || + token.op.typ == exports.EnumToken.GteTokenType + ? "" + : " "; + return (token.l.reduce((acc, curr) => acc + printValue$1(curr, options, cache, reducer, errors), "") + + indent + + printValue$1(token.op, options, cache, reducer, errors) + + indent + + token.r.reduce((acc, curr) => acc + printValue$1(curr, options, cache, reducer, errors), "")); } + case exports.EnumToken.MediaRangeQueryTokenType: + return (token.l.reduce((acc, curr) => acc + printValue$1(curr, options, cache, reducer, errors), "") + + printValue$1(token.op1, options, cache, reducer, errors) + + token.val.reduce((acc, curr) => acc + printValue$1(curr, options, cache, reducer, errors), "") + + printValue$1(token.op2, options, cache, reducer, errors) + + token.r.reduce((acc, curr) => acc + printValue$1(curr, options, cache, reducer, errors), "")); + case exports.EnumToken.MediaFeatureTokenType: + return arena.getValue(token); + case exports.EnumToken.NotTokenType: + return "not"; + case exports.EnumToken.OnlyTokenType: + return "only"; + case exports.EnumToken.AndTokenType: + return "and"; + case exports.EnumToken.OrTokenType: + return "or"; + case exports.EnumToken.InvalidMediaQueryTokenType: + case exports.EnumToken.InvalidCommentTokenType: + case exports.EnumToken.BadCommentTokenType: + case exports.EnumToken.BadCdoTokenType: + case exports.EnumToken.BadStringTokenType: + case exports.EnumToken.BadUrlTokenType: + case exports.EnumToken.EOFTokenType: + return ""; + default: + console.debug({ token }); + throw new Error(`Unsupported token type for ${exports.EnumToken[typ]}`); } - // Extract the inverse from the right side of the augmented matrix - return augmented.slice(0, 16); -} -function round(number) { - const rounded = Math.round(number); - return Math.abs(rounded - number) <= epsilon ? rounded : +number.toPrecision(6); -} -// translate3d(25.9808px, 0, 15px ) rotateY(60deg) skewX(49.9999deg) scale(1, 1.2) -// translate → rotate → skew → scale -function decompose(original) { - const matrix = original.slice(); - // Normalize last row - if (matrix[15] === 0) { - return null; - } - for (let i = 0; i < 16; i++) - matrix[i] /= matrix[15]; - // Perspective extraction - const perspective = [0, 0, 0, 1]; - if (matrix[3] !== 0 || matrix[7] !== 0 || matrix[11] !== 0) { - const rightHandSide = [matrix[3], matrix[7], matrix[11], matrix[15]]; - const perspectiveMatrix = matrix.slice(); - perspectiveMatrix[3] = 0; - perspectiveMatrix[7] = 0; - perspectiveMatrix[11] = 0; - perspectiveMatrix[15] = 1; - // @ts-ignore - const inverted = inverse(original.slice()); - if (inverted === null) { - return null; - } - const transposedInverse = transposeMatrix4(inverted); - perspective[0] = dot(rightHandSide, transposedInverse.slice(0, 4)); - perspective[1] = dot(rightHandSide, transposedInverse.slice(4, 8)); - perspective[2] = dot(rightHandSide, transposedInverse.slice(8, 12)); - perspective[3] = dot(rightHandSide, transposedInverse.slice(12, 16)); - // Clear perspective from matrix - matrix[3] = 0; - matrix[7] = 0; - matrix[11] = 0; - matrix[15] = 1; - } - // Translation - const translate = [matrix[12], matrix[13], matrix[14]]; - matrix[12] = matrix[13] = matrix[14] = 0; - // Build the 3x3 matrix - const row0 = [matrix[0], matrix[1], matrix[2]]; - const row1 = [matrix[4], matrix[5], matrix[6]]; - const row2 = [matrix[8], matrix[9], matrix[10]]; - const cross = [ - row1[1] * row2[2] - row1[2] * row2[1], - row1[2] * row2[0] - row1[0] * row2[2], - row1[0] * row2[1] - row1[1] * row2[0], - ]; - // Compute scale - const scaleX = Math.hypot(row0[0], row0[1], row0[2]); - const row0Norm = normalize$1(row0); - const skewXY = dot(row0Norm, row1); - const row1Proj = [row1[0] - skewXY * row0Norm[0], row1[1] - skewXY * row0Norm[1], row1[2] - skewXY * row0Norm[2]]; - const scaleY = Math.hypot(row1Proj[0], row1Proj[1], row1Proj[2]); - const row1Norm = normalize$1(row1Proj); - const skewXZ = dot(row0Norm, row2); - const skewYZ = dot(row1Norm, row2); - const row2Proj = [ - row2[0] - skewXZ * row0Norm[0] - skewYZ * row1Norm[0], - row2[1] - skewXZ * row0Norm[1] - skewYZ * row1Norm[1], - row2[2] - skewXZ * row0Norm[2] - skewYZ * row1Norm[2], - ]; - const row2Norm = normalize$1(row2Proj); - const determinant = row0[0] * cross[0] + row0[1] * cross[1] + row0[2] * cross[2]; - const scaleZ = Math.hypot(row2Proj[0], row2Proj[1], row2Proj[2]) * (determinant < 0 ? -1 : 1); - // Build rotation matrix from orthonormalized vectors - const r00 = row0Norm[0], r01 = row1Norm[0], r02 = row2Norm[0]; - const r10 = row0Norm[1], r11 = row1Norm[1], r12 = row2Norm[1]; - const r20 = row0Norm[2], r21 = row1Norm[2], r22 = row2Norm[2]; - // Convert to quaternion - const trace = r00 + r11 + r22; - let qw, qx, qy, qz; - if (trace > 0) { - const s = 0.5 / Math.sqrt(trace + 1.0); - qw = 0.25 / s; - qx = (r21 - r12) * s; - qy = (r02 - r20) * s; - qz = (r10 - r01) * s; - } - else if (r00 > r11 && r00 > r22) { - const s = 2.0 * Math.sqrt(1.0 + r00 - r11 - r22); - qw = (r21 - r12) / s; - qx = 0.25 * s; - qy = (r01 + r10) / s; - qz = (r02 + r20) / s; - } - else if (r11 > r22) { - const s = 2.0 * Math.sqrt(1.0 + r11 - r00 - r22); - qw = (r02 - r20) / s; - qx = (r01 + r10) / s; - qy = 0.25 * s; - qz = (r12 + r21) / s; - } - else { - const s = 2.0 * Math.sqrt(1.0 + r22 - r00 - r11); - qw = (r10 - r01) / s; - qx = (r02 + r20) / s; - qy = (r12 + r21) / s; - qz = 0.25 * s; - } - [qx, qy, qz] = toZero([qx, qy, qz]); - let q = [Math.abs(qx), Math.abs(qy), Math.abs(qz)].reduce((acc, curr) => { - if (acc == 0 || (curr > 0 && curr < acc)) { - acc = curr; - } - return acc; - }, 0); - if (q > 0) { - qx /= q; - qy /= q; - qz /= q; - } - const rotate = [ - qx, - qy, - qz, - Object.is(qw, 0) ? 0 : (2 * Math.acos(qw) * 180) / Math.PI, - ]; - const scale = [scaleX, scaleY, scaleZ]; - const skew = [skewXY, skewXZ, skewYZ]; - return { - translate, - scale, - rotate, - skew, - perspective, - }; -} -function transposeMatrix4(m) { - return [m[0], m[4], m[8], m[12], m[1], m[5], m[9], m[13], m[2], m[6], m[10], m[14], m[3], m[7], m[11], m[15]]; -} -function toZero(v) { - for (let i = 0; i < v.length; i++) { - if (Math.abs(v[i]) <= epsilon) { - v[i] = 0; - } - else { - v[i] = +v[i].toPrecision(6); - } - } - return v; -} -// https://drafts.csswg.org/css-transforms-1/#2d-matrix -function is2DMatrix(matrix) { - // m13,m14, m23, m24, m31, m32, m34, m43 are all 0 - return (matrix[2] === 0 && - matrix[3] === 0 && - matrix[6] === 0 && - matrix[7] === 0 && - matrix[8] === 0 && - matrix[9] === 0 && - matrix[11] === 0 && - matrix[14] === 0 && - matrix[10] === 1 && - matrix[15] === 1); -} - -function translateX(x, from) { - const matrix = identity(); - matrix[3 * 4] = x; - return multiply(from, matrix); -} -function translateY(y, from) { - const matrix = identity(); - matrix[3 * 4 + 1] = y; - return multiply(from, matrix); -} -function translateZ(z, from) { - const matrix = identity(); - matrix[3 * 4 + 2] = z; - return multiply(from, matrix); -} -function translate(translate, from) { - const matrix = identity(); - matrix[3 * 4] = translate[0]; - matrix[3 * 4 + 1] = translate[1] ?? 0; - return multiply(from, matrix); -} -function translate3d(translate, from) { - const matrix = identity(); - matrix[3 * 4] = translate[0]; - matrix[3 * 4 + 1] = translate[1]; - matrix[3 * 4 + 2] = translate[2]; - return multiply(from, matrix); -} - -/** - * angle in radian - * @param angle - * @param x - * @param y - * @param z - * @param from - */ -function rotate3D(angle, x, y, z, from) { - const matrix = identity(); - const sc = Math.sin(angle / 2) * Math.cos(angle / 2); - const sq = Math.sin(angle / 2) * Math.sin(angle / 2); - const norm = Math.sqrt(x * x + y * y + z * z); - const unit = norm === 0 ? 0 : 1 / norm; - x *= unit; - y *= unit; - z *= unit; - matrix[0] = 1 - 2 * (y * y + z * z) * sq; - matrix[1] = 2 * (x * y * sq + z * sc); - matrix[2] = 2 * (x * z * sq - y * sc); - matrix[4] = 2 * (x * y * sq - z * sc); - matrix[4 + 1] = 1 - 2 * (x * x + z * z) * sq; - matrix[4 + 2] = 2 * (y * z * sq + x * sc); - matrix[2 * 4] = 2 * (x * z * sq + y * sc); - matrix[2 * 4 + 1] = 2 * (y * z * sq - x * sc); - matrix[2 * 4 + 2] = 1 - 2 * (x * x + y * y) * sq; - return multiply(from, matrix); -} -function rotate(angle, from) { - const matrix = identity(); - matrix[0] = Math.cos(angle); - matrix[1] = Math.sin(angle); - matrix[4] = -Math.sin(angle); - matrix[4 + 1] = Math.cos(angle); - return multiply(from, matrix); -} - -function scaleX(x, from) { - const matrix = identity(); - matrix[0] = x; - return multiply(from, matrix); -} -function scaleY(y, from) { - const matrix = identity(); - matrix[4 + 1] = y; - return multiply(from, matrix); -} -function scaleZ(z, from) { - const matrix = identity(); - matrix[2 * 4 + 2] = z; - return multiply(from, matrix); -} -function scale(x, y, from) { - const matrix = identity(); - matrix[0] = x; - matrix[4 + 1] = y; - return multiply(from, matrix); -} -function scale3d(x, y, z, from) { - const matrix = identity(); - matrix[0] = x; - matrix[4 + 1] = y; - matrix[2 * 4 + 2] = z; - return multiply(from, matrix); -} - -function parseMatrix(mat) { - if (mat.typ == exports.EnumToken.IdenTokenType) { - return mat.val == "none" ? identity() : null; - } - const children = mat.chi.filter((t) => t.typ == exports.EnumToken.NumberTokenType || t.typ == exports.EnumToken.IdenTokenType); - const values = []; - for (const child of children) { - if (child.typ != exports.EnumToken.NumberTokenType) { - return null; - } - // @ts-ignore - values.push(getNumber(child)); - } - // @ts-ignore - return matrix(values); + // errors?.push({ action: "ignore", message: `render: unexpected token ${JSON.stringify(token, null, 1)}` }); + // return ""; } -// use column-major order -function matrix(values) { - const matrix = identity(); - if (values.length === 6) { - matrix[0] = values[0]; - matrix[1] = values[1]; - matrix[4] = values[2]; - matrix[4 + 1] = values[3]; - matrix[3 * 4] = values[4]; - matrix[3 * 4 + 1] = values[5]; - } - else if (values.length === 16) { - matrix[0] = values[0]; - matrix[1] = values[1]; - matrix[2] = values[2]; - matrix[3] = values[3]; - matrix[4] = values[4]; - matrix[4 + 1] = values[5]; - matrix[4 + 2] = values[6]; - matrix[4 + 3] = values[7]; - matrix[2 * 4] = values[8]; - matrix[2 * 4 + 1] = values[9]; - matrix[2 * 4 + 2] = values[10]; - matrix[2 * 4 + 3] = values[11]; - matrix[3 * 4] = values[12]; - matrix[3 * 4 + 1] = values[13]; - matrix[3 * 4 + 2] = values[14]; - matrix[3 * 4 + 3] = values[15]; - } - else { - return null; +/** + * Remove whitespace tokens that are not needed + * @param values + * + * @internal + */ +function filterValues(values, arena) { + let i = 0; + let typ; + for (; i < values.length; i++) { + typ = arena.getType(values[i]); + if (typ == exports.EnumToken.ImportantTokenType && arena.getType(values[i - 1]) === exports.EnumToken.WhitespaceTokenType) { + values.splice(i - 1, 1); + } + else if (tokensfuncSet.has(typ) && + arena.hasFlags(values[i], exports.EnumAstNodeStatus.HasChildren) && + typ != exports.EnumToken.WildCardFunctionTokenType && + arena.getType(values[i + 1]) == exports.EnumToken.WhitespaceTokenType) { + values.splice(i + 1, 1); + } } - return matrix; + return values; } -function serialize(matrix) { - matrix = matrix.slice(); - // @ts-ignore - if (eq(matrix, identity())) { - return { - typ: exports.EnumToken.IdenTokenType, - val: "none", - }; - } - if (is2DMatrix(matrix)) { - // https://drafts.csswg.org/css-transforms-2/#two-dimensional-subset - return { - typ: exports.EnumToken.TransformFunctionTokenType, - val: "matrix", - chi: [matrix[0], matrix[1], matrix[4], matrix[4 + 1], matrix[3 * 4], matrix[3 * 4 + 1]].reduce((acc, t) => { - if (acc.length > 0) { - acc.push({ typ: exports.EnumToken.CommaTokenType }); + +const config$2 = getSyntaxConfig(); +function replacePseudo(tokens) { + return tokens.map((raw) => raw.map((r) => { + if (r.includes("(")) { + const index = r.indexOf("("); + const name = r.slice(0, index) + "()"; + if (name in pseudoAliasMap) { + return pseudoAliasMap[name] + r.slice(index); + } + return r; + } + return r in pseudoAliasMap && pseudoAliasMap[r] in config$2[ValidationSyntaxGroupEnum.Selectors] + ? pseudoAliasMap[r] + : r; + })); +} +function replaceAstNodes(tokens, root) { + let result = false; + for (const { value, parent } of walkValues(tokens, root)) { + if (value.typ == exports.EnumToken.MediaQueryConditionTokenType) { + const token = value.l.find((t) => t.typ == exports.EnumToken.IdenTokenType); + if (token != null) { + if (token.val in pseudoAliasMap) { + token.val = pseudoAliasMap[token.val]; + if ((equalsIgnoreCase(token.val, "min-resolution") || + equalsIgnoreCase(token.val, "max-resolution")) && + value.r?.[0]?.typ == exports.EnumToken.NumberTokenType) { + Object.assign(value.r?.[0], { + typ: exports.EnumToken.ResolutionTokenType, + unit: "x", + }); + result = true; + } + let isMin = token.val.startsWith("min-"); + let isMax = token.val.startsWith("max-"); + if (isMin || isMax) { + token.val = token.val.slice(4); + value.op.typ = isMax + ? exports.EnumToken.LteTokenType + : exports.EnumToken.GteTokenType; + } } - acc.push({ - typ: exports.EnumToken.NumberTokenType, - val: t, - }); - return acc; - }, []), - }; + } + } + else if (value.typ == exports.EnumToken.IdenTokenType || + value.typ == exports.EnumToken.PseudoClassFuncTokenType || + value.typ == exports.EnumToken.PseudoClassTokenType || + value.typ == exports.EnumToken.PseudoElementTokenType) { + let key = value.val + + (value.typ == exports.EnumToken.PseudoClassFuncTokenType ? "()" : ""); + if (key in pseudoAliasMap) { + const isPseudClass = pseudoAliasMap[key].startsWith("::"); + value.val = pseudoAliasMap[key]; + // if ( + // value.typ == EnumToken.IdenTokenType && + // ["min-resolution", "max-resolution"].includes((value as IdentToken).val) && + // parent?.typ == EnumToken.MediaQueryConditionTokenType && + // (parent as MediaQueryConditionToken).r?.[0]?.typ == EnumToken.NumberTokenType + // ) { + // Object.assign((parent as MediaQueryConditionToken).r?.[0], { + // typ: EnumToken.ResolutionTokenType, + // unit: "x", + // }); + // } + // else + if (isPseudClass && value.typ == exports.EnumToken.PseudoElementTokenType) { + // @ts-ignore + value.typ = exports.EnumToken.PseudoClassTokenType; + } + result = true; + } + } } - return { - typ: exports.EnumToken.TransformFunctionTokenType, - val: "matrix3d", - chi: matrix.reduce((acc, curr) => { + // remove duplicate tokens + if (tokens.find((t) => t.typ == exports.EnumToken.CommaTokenType) != null) { + const set = new Set(); + const split = splitTokenList(tokens, [exports.EnumToken.CommaTokenType]); + tokens.length = 0; + for (const token of split.reduce((acc, curr) => { + const str = curr.reduce((acc, curr) => acc + renderValue(curr), ""); + if (set.has(str)) { + return acc; + } + set.add(str); if (acc.length > 0) { - acc.push({ typ: exports.EnumToken.CommaTokenType }); + acc.push({ + typ: exports.EnumToken.CommaTokenType, + }); } - acc.push({ - typ: exports.EnumToken.NumberTokenType, - val: curr, - }); - return acc; - }, []), - }; + return acc.concat(curr); + }, [])) { + tokens.push(token); + } + } + return result; } - -// translate → rotate → skew → scale -function minify$1(matrix) { - const decomposed = decompose(matrix); - if (decomposed == null) { - return null; +class ComputePrefixFeature { + get ordering() { + return 0; } - const transforms = new Set(["translate", "scale", "skew", "perspective", "rotate"]); - const scales = new Set(["x", "y", "z"]); - const skew = new Set(["x", "y"]); - let result = []; - // check identity - if (round(decomposed.translate[0]) == 0 && - round(decomposed.translate[1]) == 0 && - round(decomposed.translate[2]) == 0) { - transforms.delete("translate"); + get processMode() { + return exports.FeatureWalkMode.Pre; } - if (round(decomposed.scale[0]) == 1 && round(decomposed.scale[1]) == 1 && round(decomposed.scale[2]) == 1) { - transforms.delete("scale"); + static register(options) { + if (options.removePrefix) { + // @ts-ignore + options.features.push(new ComputePrefixFeature(options)); + } } - if (round(decomposed.skew[0]) == 0 && round(decomposed.skew[1]) == 0) { - transforms.delete("skew"); + run(node) { + if (node[STATE] & (exports.EnumAstNodeStatus.Disallowed | exports.EnumAstNodeStatus.Invalid | exports.EnumAstNodeStatus.Malformed)) { + return null; + } + if (node.typ == exports.EnumToken.RuleNodeType) { + node.sel = replacePseudo(splitRule(node.sel)).reduce((acc, curr, index) => acc + (index > 0 ? "," : "") + curr.join(""), ""); + if (node[TOKENS] != null) { + replaceAstNodes(node[TOKENS]); + } + } + else if (node.typ == exports.EnumToken.DeclarationNodeType) { + if (node.nam.charAt(0) == "-") { + const match = node.nam.match(/^-([^-]+)-(.+)$/); + if (match != null) { + let nam = match[2]; + if (!(nam in config$2.declarations)) { + if (node.nam in pseudoAliasMap) { + nam = pseudoAliasMap[node.nam]; + } + } + if (nam in config$2.declarations) { + node.nam = nam; + } + } + } + let hasPrefix = false; + for (const { value } of walkValues(node.val)) { + if ((value.typ == exports.EnumToken.IdenTokenType || + (value.typ != exports.EnumToken.ParensTokenType && funcLike.includes(value.typ))) && + value.val.match(/^-([^-]+)-(.+)$/) != null) { + if (value.val.endsWith("-gradient")) { + if (regMatchLinearGradient.test(value.val)) { + this.webkitLinearToLinearGradient(value); + } + else if (regMatchRadialGradient.test(value.val)) { + this.webkitRadialToRadialGradient(value); + } + else if (equalsIgnoreCase(value.val, "-webkit-gradient")) { + this.webkitGradientToGradient(value); + } + // not supported yet + break; + } + hasPrefix = true; + break; + } + } + if (hasPrefix) { + const nodes = structuredClone(node.val); + for (const { value } of walkValues(nodes)) { + if (value.typ == exports.EnumToken.IdenTokenType || funcLike.includes(value.typ)) { + const match = value.val.match(/^-([^-]+)-(.+)$/); + if (match != null) { + value.val = match[2]; + } + } + } + node.val = nodes; + } + } + else if (node.typ == exports.EnumToken.AtRuleNodeType || node.typ == exports.EnumToken.KeyframesAtRuleNodeType) { + if (node.nam.startsWith("-")) { + const match = node.nam.match(/^-([^-]+)-(.+)$/); + if (match != null && "@" + match[2] in config$2.atRules) { + node.nam = match[2]; + } + } + if (node.typ == exports.EnumToken.AtRuleNodeType && node.val !== "") { + if (replaceAstNodes(node[TOKENS])) { + node.val = node[TOKENS].reduce((acc, curr) => acc + printValue$1(curr), ""); + } + } + } + return node; } - if (round(decomposed.perspective[2]) == 0) { - transforms.delete("perspective"); + /** + * Convert -webkit-linear-gradient to linear-gradient syntax + * @param tokens + * @returns + */ + webkitLinearToLinearGradient(token) { + let i; + let k; + let to; + let val; + let key; + const tokens = token.chi; + // conversion rules + // translation: + // - left -> to right + // - right -> to left + // - top -> to bottom + // - top right -> to bottom left + // - top left -> to bottom right + // - bottom -> to top + // - bottom right -> to top left + // - bottom left -> to top right + // https://drafts.csswg.org/css-images-3/#linear-gradients + // final angle: + // - to top -> 0deg + // - to right -> 90deg + // - to bottom -> 180deg + // - to left -> 270deg + for (i = 0; i < tokens.length; i++) { + if (tokens[i].typ == exports.EnumToken.AngleTokenType || + (tokens[i].typ == exports.EnumToken.NumberTokenType && tokens[i].val == 0)) { + tokens[i].val = + (90 - + // @ts-expect-error + (tokens[i].typ == exports.EnumToken.NumberTokenType + ? tokens[i] + : toDegrees(tokens[i]).val)) % + 360; + } + else if (tokens[i].typ == exports.EnumToken.IdenTokenType) { + key = tokens[i].val.toLowerCase(); + switch (key) { + case "right": + case "left": + tokens.splice(i, 1, { typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { + typ: exports.EnumToken.IdenTokenType, + val: key == "right" ? "left" : "right", + }); + i += 2; + break; + case "top": + case "bottom": + k = i + 1; + to = key == "top" ? "bottom" : "top"; + while (k < tokens.length && + (tokens[k].typ === exports.EnumToken.WhitespaceTokenType || + tokens[k].typ === exports.EnumToken.CommentTokenType)) { + k++; + } + if (tokens[k]?.typ === exports.EnumToken.IdenTokenType) { + val = ""; + if (equalsIgnoreCase(tokens[k].val, "left")) { + val = "right"; + } + if (equalsIgnoreCase(tokens[k].val, "right")) { + val = "left"; + } + if (val !== "") { + tokens.splice(i, k - i + 1, { typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: to }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val }); + i += 4; + break; + } + } + tokens.splice(i, 1, { typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: to }); + i += 2; + break; + } + } + } + token.val = equalsIgnoreCase(token.val, "-webkit-repeating-linear-gradient") + ? "repeating-linear-gradient" + : "linear-gradient"; } - if (round(decomposed.rotate[3]) == 0) { - transforms.delete("rotate"); + /** + * Convert -webkit-gradient(linear) to linear-gradient or linear-gradient syntax + * @param token + * @returns + */ + webkitGradientToGradient(token) { + let i = 0; + let k; + let key = ""; + let commaCount; + let type = ""; + let tokens = token.chi.slice(); + // linear or radial + if (equalsIgnoreCase(tokens[i].val, "linear")) { + type = "linear-gradient"; + i++; + } + tokens.splice(0, i + 1); + commaCount = 0; + for (i = 0; i < tokens.length; i++) { + if (tokens[i].typ === exports.EnumToken.CommaTokenType) { + commaCount++; + if (commaCount > 1) { + break; + } + } + if (tokens[i].typ === exports.EnumToken.IdenTokenType) { + key += (key.length > 0 ? " " : "") + tokens[i].val; + } + } + // mapping keywords + // left top → left bottom to bottom + // left bottom → left top to top + // left top → right top to right + // right top → left top to left + // left top → right bottom to bottom right + // right top → left bottom to bottom left + // left bottom → right top to top right + // right bottom → left top to top left + const replacements = []; + if (key === "left top left bottom") { + replacements.push({ typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "bottom" }); + } + else if (key === "left bottom left top") { + replacements.push({ typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "top" }); + } + else if (key === "left top right top") { + replacements.push({ typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "right" }); + } + else if (key === "right top left top") { + replacements.push({ typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "left" }); + } + else if (key === "left top right bottom") { + replacements.push({ typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "bottom" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "right" }); + } + else if (key === "right top left bottom") { + replacements.push({ typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "bottom" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "left" }); + } + else if (key === "left bottom right top") { + replacements.push({ typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "top" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "right" }); + } + else if (key === "right bottom left top") { + replacements.push({ typ: exports.EnumToken.IdenTokenType, val: "to" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "top" }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "left" }); + } + tokens.splice(0, i, ...replacements); + let checkStop = true; + let checkStopIndex = replacements.length + 1; + let colorStop = []; + for (i = replacements.length + 1; i < tokens.length; i++) { + if (tokens[i].typ === exports.EnumToken.CommaTokenType && checkStop) { + checkStopIndex = i; + continue; + } + if (tokens[i].typ === exports.EnumToken.FunctionTokenType) { + if (equalsIgnoreCase(tokens[i].val, "to")) { + colorStop.push(tokens[checkStopIndex]); + for (const token of tokens[i].chi) { + colorStop.push(token); + } + tokens.splice(checkStopIndex, i - checkStopIndex + 1); + i = checkStopIndex; + checkStop = false; + } + else if (equalsIgnoreCase(tokens[i].val, "from")) { + k = tokens[i].chi.length + 1; + tokens.splice(i, 1, ...tokens[i].chi, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.PercentageTokenType, val: 0 }); + i += k; + } + else if (equalsIgnoreCase(tokens[i].val, "color-stop")) { + k = tokens[i].chi.length - 1; + tokens.splice(i, 1, + // @ts-expect-error + ...tokens[i].chi.reverse().map((t) => t.typ === exports.EnumToken.CommaTokenType + ? { typ: exports.EnumToken.WhitespaceTokenType } + : t.typ === exports.EnumToken.NumberTokenType + ? { + typ: exports.EnumToken.PercentageTokenType, + // @ts-expect-error + val: t.val * 100, + } + : t)); + i += k; + } + } + } + if (colorStop.length > 0) { + for (const t of colorStop) { + tokens.push(t); + } + } + if (type !== "") { + token.val = type; + token.chi.length = 0; + for (const t of tokens) { + token.chi.push(t); + } + } } - if (transforms.has("translate")) { - let coordinates = new Set(["x", "y", "z"]); - for (let i = 0; i < 3; i++) { - if (round(decomposed.translate[i]) == 0) { - coordinates.delete(i == 0 ? "x" : i == 1 ? "y" : "z"); + /** + * Convert -webkit-radial-gradient to radial-gradient syntax + * @param tokens + * @returns + */ + webkitRadialToRadialGradient(token) { + let i = 0; + const tokens = token.chi; + while (i < tokens.length && + (tokens[i].typ === exports.EnumToken.WhitespaceTokenType || tokens[i].typ === exports.EnumToken.CommentTokenType)) { + i++; + } + const positions = []; + const form = []; + const size = []; + const colorStops = []; + if (tokens[i]?.typ === exports.EnumToken.PercentageTokenType || + (tokens[i]?.typ === exports.EnumToken.NumberTokenType && 0 === tokens[i].val) || + (tokens[i]?.typ === exports.EnumToken.IdenTokenType && + (equalsIgnoreCase(tokens[i].val, "center") || + equalsIgnoreCase(tokens[i].val, "top") || + equalsIgnoreCase(tokens[i].val, "bottom") || + equalsIgnoreCase(tokens[i].val, "left") || + equalsIgnoreCase(tokens[i].val, "right")))) { + do { + positions.push(tokens[i]); + i++; + } while (tokens[i]?.typ !== exports.EnumToken.CommaTokenType); + if (tokens[i]?.typ === exports.EnumToken.CommaTokenType) { + i++; } } - if (coordinates.size == 1) { - if (coordinates.has("x")) { - result.push({ - typ: exports.EnumToken.TransformFunctionTokenType, - val: "translate", - chi: [{ typ: exports.EnumToken.LengthTokenType, val: round(decomposed.translate[0]), unit: "px" }], - }); + if (tokens[i]?.typ === exports.EnumToken.IdenTokenType) { + if (equalsIgnoreCase(tokens[i].val, "closest-side") || + equalsIgnoreCase(tokens[i].val, "closest-corner") || + equalsIgnoreCase(tokens[i].val, "farthest-side") || + equalsIgnoreCase(tokens[i].val, "farthest-corner")) { + size.push(tokens[i++]); } - else { - let axis = coordinates.has("y") ? "y" : "z"; - let index = axis == "y" ? 1 : 2; - result.push({ - typ: exports.EnumToken.TransformFunctionTokenType, - val: "translate" + axis.toUpperCase(), - chi: [{ typ: exports.EnumToken.LengthTokenType, val: round(decomposed.translate[index]), unit: "px" }], - }); + else if (equalsIgnoreCase(tokens[i].val, "circle") || + equalsIgnoreCase(tokens[i].val, "ellipse")) { + form.push(tokens[i++]); + } + while (tokens[i]?.typ === exports.EnumToken.WhitespaceTokenType || tokens[i]?.typ === exports.EnumToken.CommentTokenType) { + i++; + } + if (tokens[i]?.typ === exports.EnumToken.CommaTokenType) { + i++; } } - else if (coordinates.has("z")) { - result.push({ - typ: exports.EnumToken.TransformFunctionTokenType, - val: "translate3d", - chi: [ - decomposed.translate[0] == 0 - ? { - typ: exports.EnumToken.NumberTokenType, - val: 0, - } - : { typ: exports.EnumToken.LengthTokenType, val: round(decomposed.translate[0]), unit: "px" }, - { typ: exports.EnumToken.CommaTokenType }, - decomposed.translate[1] == 0 - ? { - typ: exports.EnumToken.NumberTokenType, - val: 0, - } - : { typ: exports.EnumToken.LengthTokenType, val: round(decomposed.translate[1]), unit: "px" }, - { typ: exports.EnumToken.CommaTokenType }, - { typ: exports.EnumToken.LengthTokenType, val: round(decomposed.translate[2]), unit: "px" }, - ], - }); - } - else { - result.push({ - typ: exports.EnumToken.TransformFunctionTokenType, - val: "translate", - chi: [ - { typ: exports.EnumToken.LengthTokenType, val: round(decomposed.translate[0]), unit: "px" }, - { typ: exports.EnumToken.CommaTokenType }, - { typ: exports.EnumToken.LengthTokenType, val: round(decomposed.translate[1]), unit: "px" }, - ], - }); - } - } - if (transforms.has("rotate")) { - const [x, y, z, angle] = decomposed.rotate; - if (y == 0 && z == 0) { - result.push({ - typ: exports.EnumToken.TransformFunctionTokenType, - val: "rotateX", - chi: [ - { - typ: exports.EnumToken.AngleTokenType, - val: round(angle), - unit: "deg", - }, - ], - }); + if (tokens[i]?.typ === exports.EnumToken.IdenTokenType) { + if (equalsIgnoreCase(tokens[i].val, "closest-side") || + equalsIgnoreCase(tokens[i].val, "closest-corner") || + equalsIgnoreCase(tokens[i].val, "farthest-side") || + equalsIgnoreCase(tokens[i].val, "farthest-corner")) { + size.push(tokens[i++]); + } + else if (equalsIgnoreCase(tokens[i].val, "circle") || + equalsIgnoreCase(tokens[i].val, "ellipse")) { + form.push(tokens[i++]); + } + while (tokens[i]?.typ === exports.EnumToken.WhitespaceTokenType || tokens[i]?.typ === exports.EnumToken.CommentTokenType) { + i++; + } + if (tokens[i]?.typ === exports.EnumToken.CommaTokenType) { + i++; + } } - else if (x == 0 && z == 0) { - result.push({ - typ: exports.EnumToken.TransformFunctionTokenType, - val: "rotateY", - chi: [ - { - typ: exports.EnumToken.AngleTokenType, - val: round(angle), - unit: "deg", - }, - ], - }); + for (let m = i; m < tokens.length; m++) { + colorStops.push(tokens[m]); } - else if (x == 0 && y == 0) { - result.push({ - typ: exports.EnumToken.TransformFunctionTokenType, - val: "rotate", - chi: [ - { - typ: exports.EnumToken.AngleTokenType, - val: round(angle), - unit: "deg", - }, - ], - }); + tokens.length = 0; + if (form.length > 0 || size.length > 0) { + if (form.length === 0) { + form.push({ typ: exports.EnumToken.IdenTokenType, val: "ellipse" }); + } + if (size.length > 0) { + form.push({ typ: exports.EnumToken.WhitespaceTokenType }); + for (const token of size) { + form.push(token); + } + } + if (positions.length > 0) { + form.push({ typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.IdenTokenType, val: "at" }, { typ: exports.EnumToken.WhitespaceTokenType }); + for (const position of positions) { + form.push(position); + } + } + for (const token of form) { + tokens.push(token); + } + tokens.push({ typ: exports.EnumToken.CommaTokenType }); } - else { - result.push({ - typ: exports.EnumToken.TransformFunctionTokenType, - val: "rotate3d", - chi: [ - { - typ: exports.EnumToken.NumberTokenType, - val: round(x), - }, - { typ: exports.EnumToken.CommaTokenType }, - { - typ: exports.EnumToken.NumberTokenType, - val: round(y), - }, - { typ: exports.EnumToken.CommaTokenType }, - { - typ: exports.EnumToken.NumberTokenType, - val: round(z), - }, - { typ: exports.EnumToken.CommaTokenType }, - { - typ: exports.EnumToken.AngleTokenType, - val: round(angle), - unit: "deg", - }, - ], - }); + token.val = equalsIgnoreCase(token.val, "-webkit-repeating-radial-gradient") + ? "repeating-radial-gradient" + : "radial-gradient"; + for (const colorStop of colorStops) { + tokens.push(colorStop); } + return tokens; } - if (transforms.has("skew")) { - if (round(decomposed.skew[1]) == 0) { - skew.delete("y"); - } - for (let i = 0; i < 2; i++) { - decomposed.skew[i] = round((Math.atan(decomposed.skew[i]) * 180) / Math.PI); +} + +function inlineExpression(token) { + const result = []; + if (token.typ == exports.EnumToken.BinaryExpressionTokenType) { + const chi = inlineExpression(token.l); + chi.push({ typ: token.op }); + for (const child of inlineExpression(token.r)) { + chi.push(child); } - if (skew.size == 1) { - result.push({ - typ: exports.EnumToken.TransformFunctionTokenType, - val: "skew" + (skew.has("x") ? "" : "Y"), - chi: [{ typ: exports.EnumToken.AngleTokenType, val: round(decomposed.skew[0]), unit: "deg" }], - }); + result.push({ + typ: exports.EnumToken.ParensTokenType, + chi, + }); + } + else { + result.push(token); + } + return result; +} +function replace(node, variableScope) { + for (const { value, parent: parentValue } of walkValues(node.val)) { + if (value.typ == exports.EnumToken.BinaryExpressionTokenType && parentValue != null && "chi" in parentValue) { + // @ts-ignore + parentValue.chi.splice(parentValue.chi.indexOf(value), 1, ...inlineExpression(value)); } - else { - result.push({ - typ: exports.EnumToken.TransformFunctionTokenType, - val: "skew", - chi: [ - { typ: exports.EnumToken.AngleTokenType, val: round(decomposed.skew[0]), unit: "deg" }, - { typ: exports.EnumToken.CommaTokenType }, - { typ: exports.EnumToken.AngleTokenType, val: round(decomposed.skew[1]), unit: "deg" }, - ], - }); + } + for (const { value, parent: parentValue } of walkValues(node.val)) { + if (value.typ == exports.EnumToken.MathFunctionTokenDefType || + (value.typ == exports.EnumToken.WildCardFunctionTokenType && value.val == "var")) { + if (value.chi.length == 1 && + value.chi[0].typ == exports.EnumToken.DashedIdenTokenType) { + const info = (variableScope.get(value.chi[0].val)); + if (info?.replaceable) { + if (parentValue != null) { + let i = 0; + for (; i < parentValue.chi.length; i++) { + if (parentValue.chi[i] == value) { + parentValue.chi.splice(i, 1, ...info.node.val); + break; + } + } + } + else { + node.val = info.node.val.slice(); + } + } + } } } - if (transforms.has("scale")) { - const [sx, sy, sz] = toZero(decomposed.scale); - if (sz == 1) { - scales.delete("z"); +} +class InlineCssVariablesFeature { + accept = new Set([exports.EnumToken.RuleNodeType, exports.EnumToken.AtRuleNodeType]); + get ordering() { + return 2; + } + get processMode() { + return exports.FeatureWalkMode.Post; + } + static register(options) { + if (options.inlineCssVariables) { + // @ts-ignore + options.features.push(new InlineCssVariablesFeature()); } - if (sy == 1) { - scales.delete("y"); + } + run(ast, options = {}, parent, context) { + if (!("chi" in ast)) { + return null; } - if (sx == 1) { - scales.delete("x"); + if (!("variableScope" in context)) { + context.variableScope = new Map(); } - if (scales.size == 1) { - let prefix = scales.has("x") ? "X" : scales.has("y") ? "Y" : "Z"; - result.push({ - typ: exports.EnumToken.TransformFunctionTokenType, - val: "scale" + prefix, - chi: [{ typ: exports.EnumToken.NumberTokenType, val: round(prefix == "Z" ? sz : prefix == "Y" ? sy : sx) }], - }); + // [':root', 'html'] + const isRoot = parent.typ == exports.EnumToken.StyleSheetNodeType && + ast.typ == exports.EnumToken.RuleNodeType && + (ast[RAW] ?? splitRule(ast.sel)).some((segment) => segment.some((s) => s == ":root" || s == "html")); + const variableScope = context.variableScope; + // @ts-ignore + for (const node of ast.chi) { + if (node.typ !== exports.EnumToken.DeclarationNodeType) { + continue; + } + // css variable + if (node.nam.startsWith("--")) { + if (!variableScope.has(node.val)) { + const info = { + globalScope: isRoot, + // @ts-ignore + parent: new Set(), + declarationCount: 1, + replaceable: isRoot, + node: node, + values: structuredClone(node.val), + }; + info.parent.add(ast); + variableScope.set(node.nam, info); + let recursive = false; + for (const { value } of walkValues(node.val)) { + if (value?.typ == exports.EnumToken.MathFunctionTokenDefType || + (value?.typ == exports.EnumToken.WildCardFunctionTokenType && + (mathFuncs.includes(value.val) || + value.val === "var"))) { + recursive = true; + break; + } + } + if (recursive) { + replace(node, variableScope); + } + } + } + else { + replace(node, variableScope); + } } - else if (!scales.has("z")) { - result.push({ - typ: exports.EnumToken.TransformFunctionTokenType, - val: "scale", - chi: [ - { typ: exports.EnumToken.NumberTokenType, val: round(sx) }, - { typ: exports.EnumToken.CommaTokenType }, - { typ: exports.EnumToken.NumberTokenType, val: round(sy) }, - ], - }); + return null; + } + cleanup(ast, options = {}, context) { + const variableScope = context.variableScope; + if (variableScope == null) { + return; } - else { - result.push({ - typ: exports.EnumToken.TransformFunctionTokenType, - val: "scale3d", - chi: [ - { typ: exports.EnumToken.NumberTokenType, val: round(sx) }, - { typ: exports.EnumToken.CommaTokenType }, - { typ: exports.EnumToken.NumberTokenType, val: round(sy) }, - { typ: exports.EnumToken.CommaTokenType }, - { typ: exports.EnumToken.NumberTokenType, val: round(sz) }, - ], - }); + for (const info of variableScope.values()) { + if (info.replaceable) { + let i; + // drop declarations from :root{} + for (const parent of info.parent) { + i = parent.chi?.length ?? 0; + while (i--) { + if (parent.chi[i] == info.node) { + // @ts-ignore + parent.chi.splice(i, 1, { + typ: exports.EnumToken.CommentTokenType, + val: `/* ${info.node.nam}: ${info.values.reduce((acc, curr) => acc + printNode(curr, { convertColor: false }), "")} */`, + }); + break; + } + } + } + } } } - // identity - return result.length == 0 || (result.length == 1 && eqMatrix(identity(), result)) - ? [ - { - typ: exports.EnumToken.IdenTokenType, - val: "none", - }, - ] - : result; } -function eqMatrix(a, b) { - let mat = identity(); - let tmp = identity(); - const data = (Array.isArray(a) || ArrayBuffer.isView(a) ? a : parseMatrix(a)); - for (const transform of b) { - tmp = computeMatrix([transform], identity()); - if (tmp == null) { - return false; - } - mat = multiply(mat, tmp); - } - if (mat == null) { - return false; - } - for (let i = 0; i < 4; i++) { - for (let j = 0; j < 4; j++) { - if (Math.abs(mat[i * 4 + j] - data[i * 4 + j]) > epsilon) { - return false; + +function dedup(values) { + for (const value of values) { + let i = value.length; + while (i-- > 1) { + const t = value[i]; + const k = value[i == 1 ? 0 : i % 2]; + if (t.val == k.val && t.val == 0) { + if ((t.typ == exports.EnumToken.NumberTokenType && isLength(k)) || + (k.typ == exports.EnumToken.NumberTokenType && isLength(t)) || + isLength(k) || + isLength(t)) { + value.splice(i, 1); + continue; + } + } + if (eq(t, k)) { + value.splice(i, 1); + continue; } + break; } } - return true; + return values; } -function minifyTransformFunctions(transform) { - const name = transform.val; - if ("skewX" == name) { - transform.val = "skew"; - return transform; - } - if (!["translate", "translate3d", "scale", "scale3d"].includes(name)) { - return transform; +class PropertySet { + config; + declarations; + constructor(config) { + this.config = config; + this.declarations = new Map(); } - const values = []; - for (const token of transform.chi) { - if (token.typ == exports.EnumToken.CommentTokenType || - token.typ == exports.EnumToken.WhitespaceTokenType || - token.typ == exports.EnumToken.CommaTokenType) { - continue; - } - if (![ - exports.EnumToken.NumberTokenType, - exports.EnumToken.LengthTokenType, - exports.EnumToken.AngleTokenType, - exports.EnumToken.PercentageTokenType, - ].includes(token.typ)) { - return transform; + add(declaration) { + if (declaration.nam == this.config.shorthand) { + this.declarations = new Map(); } - if (token.typ == exports.EnumToken.PercentageTokenType && - typeof token.val == "number" && - name.startsWith("scale")) { - Object.assign(token, { - typ: exports.EnumToken.NumberTokenType, - val: token.val / 100, - }); + else { + // expand shorthand + if (this.declarations.has(this.config.shorthand)) { + let isValid = true; + let current = -1; + const tokens = []; + // @ts-ignore + for (let token of this.declarations.get(this.config.shorthand).val) { + if ( + // @ts-expect-error + this.config.types.some((t) => token.typ == exports.EnumToken[t]) || + (token.typ == exports.EnumToken.NumberTokenType && + token.val == 0 && + (this.config.types.includes("Length") || + this.config.types.includes("Angle") || + this.config.types.includes("Dimension")))) { + if (tokens.length == 0) { + tokens.push([]); + current++; + } + tokens[current].push(token); + continue; + } + if (token.typ != exports.EnumToken.WhitespaceTokenType && token.typ != exports.EnumToken.CommentTokenType) { + if (token.typ == exports.EnumToken.IdenTokenType && + this.config.keywords.includes(token.val)) { + if (tokens.length == 0) { + tokens.push([]); + current++; + } + tokens[current].push(token); + } + // @ts-ignore + if (token.typ == exports.EnumToken.LiteralTokenType && token.val == this.config.separator?.val) { + tokens.push([]); + current++; + continue; + } + isValid = false; + break; + } + } + if (isValid && tokens.length > 0) { + this.declarations.delete(this.config.shorthand); + for (const values of tokens) { + this.config.properties.forEach((property, index) => { + if (!this.declarations.has(property)) { + this.declarations.set(property, { + typ: exports.EnumToken.DeclarationNodeType, + nam: property, + val: [], + }); + } + while (index > 0 && index >= values.length) { + if (index > 1) { + index %= 2; + } + else { + index = 0; + break; + } + } + // @ts-ignore + const val = this.declarations.get(property).val; + if (val.length > 0) { + val.push({ typ: exports.EnumToken.WhitespaceTokenType }); + } + val.push({ ...values[index] }); + }); + } + } + this.declarations.set(declaration.nam, declaration); + return this; + } } - values.push(token); - } - if (values.length == 0) { - return transform; - } - if ((name == "translate" || name == "scale") && values.length > 2) { - return transform; + this.declarations.set(declaration.nam, declaration); + return this; } - const ignoredValue = name.startsWith("scale") ? 1 : 0; - const t = new Set(["x", "y", "z"]); - for (let i = 0; i < 3; i++) { - const axis = i == 0 ? "x" : i == 1 ? "y" : "z"; - if (values.length <= i || values[i].val == ignoredValue) { - t.delete(axis); + isShortHand() { + if (this.declarations.has(this.config.shorthand)) { + return this.declarations.size == 1; } + return this.config.properties.length == this.declarations.size; } - if (name == "translate3d" || name == "translate") { - if (t.size == 0) { - return { - typ: exports.EnumToken.TransformFunctionTokenType, - val: "translate", - chi: [{ typ: exports.EnumToken.NumberTokenType, val: 0 }], - }; - } - if (t.size == 1) { - return { - typ: exports.EnumToken.TransformFunctionTokenType, - val: "translate" + (t.has("x") ? "" : t.has("y") ? "Y" : "Z"), - chi: [values[t.has("x") ? 0 : t.has("y") ? 1 : 2]], - }; - } - if (t.size == 2) { - if (t.has("z")) { - return transform; + [Symbol.iterator]() { + let iterator; + const declarations = this.declarations; + if (declarations.size < this.config.properties.length) { + const values = [...declarations.values()]; + if (this.isShortHand()) { + const val = values[0].val.reduce((acc, curr) => { + if (![exports.EnumToken.WhitespaceTokenType, exports.EnumToken.CommentTokenType].includes(curr.typ)) { + acc.push(curr); + } + return acc; + }, []); + values[0].val = val.reduce((acc, curr) => { + if (acc.length > 0) { + acc.push({ typ: exports.EnumToken.WhitespaceTokenType }); + } + acc.push(curr); + return acc; + }, []); } - return { - typ: exports.EnumToken.TransformFunctionTokenType, - val: "translate", - chi: [values[0], { typ: exports.EnumToken.CommaTokenType }, values[1]], - }; - } - } - if (name == "scale3d" || name == "scale") { - if (t.size == 0) { - return { - typ: exports.EnumToken.TransformFunctionTokenType, - val: "scale", - chi: [{ typ: exports.EnumToken.NumberTokenType, val: 1 }], - }; - } - if (t.size == 1) { - return { - typ: exports.EnumToken.TransformFunctionTokenType, - val: "scale" + (t.has("x") ? "X" : t.has("y") ? "Y" : "Z"), - chi: [values[t.has("x") ? 0 : t.has("y") ? 1 : 2]], - }; + return values[Symbol.iterator](); } - if (t.size == 2) { - if (t.has("z")) { - return transform; + else { + const values = []; + this.config.properties.forEach((property) => { + let index = 0; + // @ts-ignore + for (const token of this.declarations.get(property)?.val ?? []) { + if (token == null) { + return this.declarations.values()[Symbol.iterator](); + } + if (token.typ == exports.EnumToken.WhitespaceTokenType) { + continue; + } + if (values.length == index) { + values.push([]); + } + values[index].push(token); + index++; + } + }); + if (this.config.valueSeparator == null) { + dedup(values); } - return { - typ: exports.EnumToken.TransformFunctionTokenType, - val: "scale", - chi: [values[0], { typ: exports.EnumToken.CommaTokenType }, values[1]], - }; + iterator = [ + { + typ: exports.EnumToken.DeclarationNodeType, + nam: this.config.shorthand, + val: values.reduce((acc, curr) => { + if (curr.length > 1) { + const k = curr.length * 2 - 1; + let i = 1; + while (i < k) { + curr.splice(i, 0, { typ: exports.EnumToken.WhitespaceTokenType }); + i += 2; + } + } + if (acc.length > 0) { + // @ts-ignore + acc.push({ ...this.config.separator, typ: exports.EnumToken.LiteralTokenType }); + } + for (const token of curr) { + acc.push(token); + } + return acc; + }, []), + }, + ][Symbol.iterator](); } + return iterator; } - return transform; } -function skewX(x, from) { - const matrix = identity(); - matrix[4] = Math.tan(x); - return multiply(from, matrix); -} -function skewY(y, from) { - const matrix = identity(); - matrix[1] = Math.tan(y); - return multiply(from, matrix); -} -// convert angle to radian -function skew(values, from) { - const matrix = identity(); - matrix[4] = Math.tan(values[0]); - if (values.length > 1) { - matrix[1] = Math.tan(values[1]); - } - return multiply(from, matrix); -} +var properties = { + gap: { + shorthand: "gap", + properties: [ + "row-gap", + "column-gap" + ], + types: [ + "Length", + "Perc" + ], + multiple: false, + separator: null, + keywords: [ + "normal" + ] + }, + "row-gap": { + shorthand: "gap" + }, + "column-gap": { + shorthand: "gap" + }, + inset: { + shorthand: "inset", + properties: [ + "top", + "right", + "bottom", + "left" + ], + types: [ + "Length", + "Perc" + ], + multiple: false, + separator: null, + keywords: [ + "auto" + ] + }, + top: { + shorthand: "inset" + }, + right: { + shorthand: "inset" + }, + bottom: { + shorthand: "inset" + }, + left: { + shorthand: "inset" + }, + margin: { + shorthand: "margin", + properties: [ + "margin-top", + "margin-right", + "margin-bottom", + "margin-left" + ], + types: [ + "Length", + "Perc" + ], + multiple: false, + separator: null, + keywords: [ + "auto" + ] + }, + "margin-top": { + shorthand: "margin" + }, + "margin-right": { + shorthand: "margin" + }, + "margin-bottom": { + shorthand: "margin" + }, + "margin-left": { + shorthand: "margin" + }, + padding: { + shorthand: "padding", + properties: [ + "padding-top", + "padding-right", + "padding-bottom", + "padding-left" + ], + types: [ + "Length", + "Perc" + ], + keywords: [ + ] + }, + "padding-top": { + shorthand: "padding" + }, + "padding-right": { + shorthand: "padding" + }, + "padding-bottom": { + shorthand: "padding" + }, + "padding-left": { + shorthand: "padding" + }, + "scroll-margin": { + shorthand: "scroll-margin", + properties: [ + "scroll-margin-top", + "scroll-margin-right", + "scroll-margin-bottom", + "scroll-margin-left" + ], + types: [ + "Length", + "Perc" + ], + "default": [ + "0" + ], + keywords: [ + "auto" + ] + }, + "scroll-margin-top": { + shorthand: "scroll-margin" + }, + "scroll-margin-right": { + shorthand: "scroll-margin" + }, + "scroll-margin-bottom": { + shorthand: "scroll-margin" + }, + "scroll-margin-left": { + shorthand: "scroll-margin" + }, + "scroll-padding": { + shorthand: "scroll-padding", + properties: [ + "scroll-padding-top", + "scroll-padding-right", + "scroll-padding-bottom", + "scroll-padding-left" + ], + types: [ + "Length", + "Perc" + ], + keywords: [ + "auto" + ] + }, + "scroll-padding-top": { + shorthand: "scroll-padding" + }, + "scroll-padding-right": { + shorthand: "scroll-padding" + }, + "scroll-padding-bottom": { + shorthand: "scroll-padding" + }, + "scroll-padding-left": { + shorthand: "scroll-padding" + }, + "border-radius": { + shorthand: "border-radius", + properties: [ + "border-top-left-radius", + "border-top-right-radius", + "border-bottom-right-radius", + "border-bottom-left-radius" + ], + types: [ + "Length", + "Perc" + ], + multiple: true, + separator: { + typ: "Literal", + val: "/" + }, + keywords: [ + ] + }, + "border-top-left-radius": { + shorthand: "border-radius" + }, + "border-top-right-radius": { + shorthand: "border-radius" + }, + "border-bottom-right-radius": { + shorthand: "border-radius" + }, + "border-bottom-left-radius": { + shorthand: "border-radius" + }, + "border-width": { + shorthand: "border-width", + map: "border", + properties: [ + "border-top-width", + "border-right-width", + "border-bottom-width", + "border-left-width" + ], + types: [ + "Length", + "Perc" + ], + "default": [ + "medium" + ], + keywords: [ + "thin", + "medium", + "thick" + ] + }, + "border-top-width": { + map: "border", + shorthand: "border-width" + }, + "border-right-width": { + map: "border", + shorthand: "border-width" + }, + "border-bottom-width": { + map: "border", + shorthand: "border-width" + }, + "border-left-width": { + map: "border", + shorthand: "border-width" + }, + "border-style": { + shorthand: "border-style", + map: "border", + properties: [ + "border-top-style", + "border-right-style", + "border-bottom-style", + "border-left-style" + ], + types: [ + ], + "default": [ + "none" + ], + keywords: [ + "none", + "hidden", + "dotted", + "dashed", + "solid", + "double", + "groove", + "ridge", + "inset", + "outset" + ] + }, + "border-top-style": { + map: "border", + shorthand: "border-style" + }, + "border-right-style": { + map: "border", + shorthand: "border-style" + }, + "border-bottom-style": { + map: "border", + shorthand: "border-style" + }, + "border-left-style": { + map: "border", + shorthand: "border-style" + }, + "border-color": { + shorthand: "border-color", + map: "border", + properties: [ + "border-top-color", + "border-right-color", + "border-bottom-color", + "border-left-color" + ], + types: [ + "Color" + ], + "default": [ + "currentcolor" + ], + keywords: [ + ] + }, + "border-top-color": { + map: "border", + shorthand: "border-color" + }, + "border-right-color": { + map: "border", + shorthand: "border-color" + }, + "border-bottom-color": { + map: "border", + shorthand: "border-color" + }, + "border-left-color": { + map: "border", + shorthand: "border-color" + } +}; +var map = { + "flex-flow": { + shorthand: "flex-flow", + pattern: "flex-direction flex-wrap", + keywords: [ + ], + "default": [ + "row", + "nowrap" + ], + properties: { + "flex-direction": { + keywords: [ + "row", + "row-reverse", + "column", + "column-reverse" + ], + "default": [ + "row" + ], + types: [ + ] + }, + "flex-wrap": { + keywords: [ + "wrap", + "nowrap", + "wrap-reverse" + ], + "default": [ + "nowrap" + ], + types: [ + ] + } + } + }, + "flex-direction": { + shorthand: "flex-flow" + }, + "flex-wrap": { + shorthand: "flex-flow" + }, + "scroll-timeline": { + shorthand: "scroll-timeline", + pattern: "scroll-timeline-name scroll-timeline-axis", + keywords: [ + "none", + "block", + "inline", + "x", + "y" + ], + "default": [ + "none", + "block" + ], + properties: { + "scroll-timeline-name": { + keywords: [ + "none" + ], + "default": [ + "none" + ], + types: [ + "DashedIden" + ] + }, + "scroll-timeline-axis": { + keywords: [ + "block", + "inline", + "x", + "y" + ], + "default": [ + "block" + ], + types: [ + ] + } + } + }, + "scroll-timeline-name": { + shorthand: "scroll-timeline" + }, + "scroll-timeline-axis": { + shorthand: "scroll-timeline" + }, + container: { + shorthand: "container", + pattern: "container-name container-type", + keywords: [ + ], + "default": [ + ], + properties: { + "container-name": { + required: true, + multiple: true, + keywords: [ + "none" + ], + "default": [ + "none" + ], + types: [ + "Iden", + "DashedIden" + ] + }, + "container-type": { + previous: "container-name", + prefix: { + typ: "Literal", + val: "/" + }, + keywords: [ + "size", + "inline-size", + "normal" + ], + "default": [ + "normal" + ], + types: [ + ] + } + } + }, + "container-name": { + shorthand: "container" + }, + "container-type": { + shorthand: "container" + }, + flex: { + shorthand: "flex", + pattern: "flex-grow flex-shrink flex-basis", + keywords: [ + "auto", + "none", + "initial" + ], + "default": [ + ], + mapping: { + "0 1 auto": "initial", + "0 0 auto": "none", + "1 1 auto": "auto" + }, + properties: { + "flex-grow": { + required: true, + keywords: [ + ], + "default": [ + ], + types: [ + "Number" + ] + }, + "flex-shrink": { + keywords: [ + ], + "default": [ + ], + types: [ + "Number" + ] + }, + "flex-basis": { + keywords: [ + "max-content", + "min-content", + "fit-content", + "fit-content", + "content", + "auto" + ], + "default": [ + ], + types: [ + "Length", + "Perc" + ] + } + } + }, + "flex-grow": { + shorthand: "flex" + }, + "flex-shrink": { + shorthand: "flex" + }, + "flex-basis": { + shorthand: "flex" + }, + columns: { + shorthand: "columns", + pattern: "column-count column-width", + keywords: [ + "auto" + ], + "default": [ + "auto", + "auto auto" + ], + properties: { + "column-count": { + keywords: [ + "auto" + ], + "default": [ + "auto" + ], + types: [ + "Number" + ] + }, + "column-width": { + keywords: [ + "auto" + ], + "default": [ + "auto" + ], + types: [ + "Length" + ] + } + } + }, + "column-count": { + shorthand: "columns" + }, + "column-width": { + shorthand: "columns" + }, + transition: { + shorthand: "transition", + multiple: true, + separator: { + typ: "Comma" + }, + pattern: "transition-property transition-duration transition-timing-function transition-delay transition-behavior", + keywords: [ + "none", + "all" + ], + "default": [ + "0s", + "0ms", + "all", + "ease", + "none", + "normal" + ], + mapping: { + "cubic-bezier(.25,.1,.25,1)": "ease", + "cubic-bezier(0,0,1,1)": "linear", + "cubic-bezier(.42,0,1,1)": "ease-in", + "cubic-bezier(0,0,.58,1)": "ease-out", + "cubic-bezier(.42,0,.58,.42)": "ease-in-out" + }, + properties: { + "transition-property": { + keywords: [ + "none", + "all" + ], + "default": [ + "all" + ], + types: [ + "Iden" + ] + }, + "transition-duration": { + keywords: [ + ], + "default": [ + "0s", + "0ms", + "normal" + ], + types: [ + "Time" + ] + }, + "transition-timing-function": { + keywords: [ + "ease", + "ease-in", + "ease-out", + "ease-in-out", + "linear", + "step-start", + "step-end" + ], + "default": [ + "ease" + ], + types: [ + "TimingFunction" + ], + mapping: { + "cubic-bezier(.25,.1,.25,1)": "ease", + "cubic-bezier(0,0,1,1)": "linear", + "cubic-bezier(.42,0,1,1)": "ease-in", + "cubic-bezier(0,0,.58,1)": "ease-out", + "cubic-bezier(.42,0,.58,.42)": "ease-in-out" + } + }, + "transition-delay": { + keywords: [ + ], + "default": [ + "0s" + ], + types: [ + "Time" + ] + }, + "transition-behavior": { + keywords: [ + "normal", + "allow-discrete" + ], + "default": [ + "normal" + ], + types: [ + ] + } + } + }, + "transition-property": { + shorthand: "transition" + }, + "transition-duration": { + shorthand: "transition" + }, + "transition-timing-function": { + shorthand: "transition" + }, + "transition-delay": { + shorthand: "transition" + }, + "transition-behavior": { + shorthand: "transition" + }, + animation: { + shorthand: "animation", + separator: { + typ: "Comma" + }, + pattern: "animation-name animation-duration animation-timing-function animation-delay animation-iteration-count animation-direction animation-fill-mode animation-play-state animation-timeline", + "default": [ + "1", + "0s", + "0ms", + "none", + "ease", + "normal", + "running", + "auto" + ], + properties: { + "animation-name": { + keywords: [ + "none" + ], + "default": [ + "none" + ], + types: [ + "Iden" + ] + }, + "animation-duration": { + keywords: [ + "auto" + ], + "default": [ + "0s", + "0ms", + "auto" + ], + types: [ + "Time" + ], + mapping: { + auto: "0s" + } + }, + "animation-timing-function": { + keywords: [ + "ease", + "ease-in", + "ease-out", + "ease-in-out", + "linear", + "step-start", + "step-end" + ], + "default": [ + "ease" + ], + types: [ + "TimingFunction" + ], + mapping: { + "cubic-bezier(.25,.1,.25,1)": "ease", + "cubic-bezier(0,0,1,1)": "linear", + "cubic-bezier(.42,0,1,1)": "ease-in", + "cubic-bezier(0,0,.58,1)": "ease-out", + "cubic-bezier(.42,0,.58,.42)": "ease-in-out" + } + }, + "animation-delay": { + keywords: [ + ], + "default": [ + "0s", + "0ms" + ], + types: [ + "Time" + ] + }, + "animation-iteration-count": { + keywords: [ + "infinite" + ], + "default": [ + "1" + ], + types: [ + "Number" + ] + }, + "animation-direction": { + keywords: [ + "normal", + "reverse", + "alternate", + "alternate-reverse" + ], + "default": [ + "normal" + ], + types: [ + ] + }, + "animation-fill-mode": { + keywords: [ + "none", + "forwards", + "backwards", + "both" + ], + "default": [ + "none" + ], + types: [ + ] + }, + "animation-play-state": { + keywords: [ + "running", + "paused" + ], + "default": [ + "running" + ], + types: [ + ] + }, + "animation-timeline": { + keywords: [ + "none", + "auto" + ], + "default": [ + "auto" + ], + types: [ + "DashedIden", + "TimelineFunction" + ] + } + } + }, + "animation-name": { + shorthand: "animation" + }, + "animation-duration": { + shorthand: "animation" + }, + "animation-timing-function": { + shorthand: "animation" + }, + "animation-delay": { + shorthand: "animation" + }, + "animation-iteration-count": { + shorthand: "animation" + }, + "animation-direction": { + shorthand: "animation" + }, + "animation-fill-mode": { + shorthand: "animation" + }, + "animation-play-state": { + shorthand: "animation" + }, + "animation-timeline": { + shorthand: "animation" + }, + "text-emphasis": { + shorthand: "text-emphasis", + pattern: "text-emphasis-color text-emphasis-style", + "default": [ + "none", + "currentcolor" + ], + properties: { + "text-emphasis-style": { + keywords: [ + "none", + "filled", + "open", + "dot", + "circle", + "double-circle", + "triangle", + "sesame" + ], + "default": [ + "none" + ], + types: [ + "String" + ] + }, + "text-emphasis-color": { + "default": [ + "currentcolor" + ], + types: [ + "Color" + ] + } + } + }, + "text-emphasis-style": { + shorthand: "text-emphasis" + }, + "text-emphasis-color": { + shorthand: "text-emphasis" + }, + border: { + shorthand: "border", + pattern: "border-color border-style border-width", + keywords: [ + "none" + ], + "default": [ + "0", + "none" + ], + properties: { + "border-color": { + types: [ + "Color" + ], + "default": [ + "currentcolor" + ], + keywords: [ + ] + }, + "border-style": { + types: [ + ], + "default": [ + "none" + ], + keywords: [ + "none", + "hidden", + "dotted", + "dashed", + "solid", + "double", + "groove", + "ridge", + "inset", + "outset" + ] + }, + "border-width": { + types: [ + "Length", + "Perc" + ], + "default": [ + "medium" + ], + keywords: [ + "thin", + "medium", + "thick" + ] + } + } + }, + "border-color": { + shorthand: "border" + }, + "border-style": { + shorthand: "border" + }, + "border-width": { + shorthand: "border" + }, + "list-style": { + shorthand: "list-style", + pattern: "list-style-type list-style-position list-style-image", + keywords: [ + "none", + "outside" + ], + "default": [ + "none", + "outside" + ], + properties: { + "list-style-position": { + types: [ + ], + "default": [ + "outside" + ], + keywords: [ + "inside", + "outside" + ] + }, + "list-style-image": { + "default": [ + "none" + ], + keywords: [ + "node" + ], + types: [ + "UrlFunc", + "ImageFunc" + ] + }, + "list-style-type": { + types: [ + "String", + "Iden", + "Symbols" + ], + "default": [ + "disc" + ], + keywords: [ + "disc", + "circle", + "square", + "decimal", + "decimal-leading-zero", + "lower-roman", + "upper-roman", + "lower-greek", + "lower-latin", + "upper-latin", + "none" + ] + } + } + }, + "list-style-position": { + shorthand: "list-style" + }, + "list-style-image": { + shorthand: "list-style" + }, + "list-style-type": { + shorthand: "list-style" + }, + overflow: { + shorthand: "overflow", + pattern: "overflow-x overflow-y", + keywords: [ + "auto", + "visible", + "hidden", + "clip", + "scroll" + ], + "default": [ + ], + mapping: { + "visible visible": "visible", + "auto auto": "auto", + "hidden hidden": "hidden", + "scroll scroll": "scroll" + }, + properties: { + "overflow-x": { + "default": [ + ], + types: [ + ], + keywords: [ + "auto", + "visible", + "hidden", + "clip", + "scroll" + ] + }, + "overflow-y": { + "default": [ + ], + types: [ + ], + keywords: [ + "auto", + "visible", + "hidden", + "clip", + "scroll" + ] + } + } + }, + "overflow-x": { + shorthand: "overflow" + }, + "overflow-y": { + shorthand: "overflow" + }, + outline: { + shorthand: "outline", + pattern: "outline-color outline-style outline-width", + keywords: [ + "none" + ], + "default": [ + "0", + "none", + "currentcolor" + ], + properties: { + "outline-color": { + types: [ + "Color" + ], + "default": [ + "currentcolor" + ], + keywords: [ + "currentcolor" + ] + }, + "outline-style": { + types: [ + ], + "default": [ + "none" + ], + keywords: [ + "auto", + "none", + "dotted", + "dashed", + "solid", + "double", + "groove", + "ridge", + "inset", + "outset" + ] + }, + "outline-width": { + types: [ + "Length", + "Perc" + ], + "default": [ + "medium" + ], + keywords: [ + "thin", + "medium", + "thick" + ] + } + } + }, + "outline-color": { + shorthand: "outline" + }, + "outline-style": { + shorthand: "outline" + }, + "outline-width": { + shorthand: "outline" + }, + font: { + shorthand: "font", + pattern: "font-weight font-style font-size line-height font-stretch font-variant font-family", + keywords: [ + "caption", + "icon", + "menu", + "message-box", + "small-caption", + "status-bar", + "-moz-window, ", + "-moz-document, ", + "-moz-desktop, ", + "-moz-info, ", + "-moz-dialog", + "-moz-button", + "-moz-pull-down-menu", + "-moz-list", + "-moz-field" + ], + "default": [ + ], + properties: { + "font-weight": { + types: [ + "Number" + ], + "default": [ + "400", + "normal" + ], + keywords: [ + "normal", + "bold", + "lighter", + "bolder" + ], + constraints: { + value: { + min: "1", + max: "1000" + } + }, + mapping: { + thin: "100", + hairline: "100", + "extra light": "200", + "ultra light": "200", + light: "300", + normal: "400", + regular: "400", + medium: "500", + "semi bold": "600", + "demi bold": "600", + bold: "700", + "extra bold": "800", + "ultra bold": "800", + black: "900", + heavy: "900", + "extra black": "950", + "ultra black": "950" + } + }, + "font-style": { + types: [ + "Angle" + ], + "default": [ + "normal" + ], + keywords: [ + "normal", + "italic", + "oblique" + ] + }, + "font-size": { + types: [ + "Length", + "Perc" + ], + "default": [ + ], + keywords: [ + "xx-small", + "x-small", + "small", + "medium", + "large", + "x-large", + "xx-large", + "xxx-large", + "larger", + "smaller" + ], + required: true + }, + "line-height": { + types: [ + "Length", + "Perc", + "Number" + ], + "default": [ + "normal" + ], + keywords: [ + "normal" + ], + previous: "font-size", + prefix: { + typ: "Literal", + val: "/" + } + }, + "font-stretch": { + types: [ + "Perc" + ], + "default": [ + "normal" + ], + keywords: [ + "ultra-condensed", + "extra-condensed", + "condensed", + "semi-condensed", + "normal", + "semi-expanded", + "expanded", + "extra-expanded", + "ultra-expanded" + ], + mapping: { + "ultra-condensed": "50%", + "extra-condensed": "62.5%", + condensed: "75%", + "semi-condensed": "87.5%", + normal: "100%", + "semi-expanded": "112.5%", + expanded: "125%", + "extra-expanded": "150%", + "ultra-expanded": "200%" + } + }, + "font-variant": { + types: [ + ], + "default": [ + "normal" + ], + keywords: [ + "normal", + "none", + "common-ligatures", + "no-common-ligatures", + "discretionary-ligatures", + "no-discretionary-ligatures", + "historical-ligatures", + "no-historical-ligatures", + "contextual", + "no-contextual", + "historical-forms", + "small-caps", + "all-small-caps", + "petite-caps", + "all-petite-caps", + "unicase", + "titling-caps", + "ordinal", + "slashed-zero", + "lining-nums", + "oldstyle-nums", + "proportional-nums", + "tabular-nums", + "diagonal-fractions", + "stacked-fractions", + "ordinal", + "slashed-zero", + "ruby", + "jis78", + "jis83", + "jis90", + "jis04", + "simplified", + "traditional", + "full-width", + "proportional-width", + "ruby", + "sub", + "super", + "text", + "emoji", + "unicode" + ] + }, + "font-family": { + types: [ + "String", + "Iden" + ], + "default": [ + ], + keywords: [ + "serif", + "sans-serif", + "monospace", + "cursive", + "fantasy", + "system-ui", + "ui-serif", + "ui-sans-serif", + "ui-monospace", + "ui-rounded", + "math", + "emoji", + "fangsong" + ], + required: true, + multiple: true, + separator: { + typ: "Comma" + } + } + } + }, + "font-weight": { + shorthand: "font" + }, + "font-style": { + shorthand: "font" + }, + "font-size": { + shorthand: "font" + }, + "line-height": { + shorthand: "font" + }, + "font-stretch": { + shorthand: "font" + }, + "font-variant": { + shorthand: "font" + }, + "font-family": { + shorthand: "font" + }, + background: { + shorthand: "background", + pattern: "background-attachment background-origin background-clip background-color background-image background-repeat background-position background-size", + keywords: [ + "none" + ], + "default": [ + "0 0", + "none", + "auto", + "repeat", + "transparent", + "#0000", + "scroll", + "padding-box", + "border-box" + ], + multiple: true, + set: { + "background-origin": [ + "background-clip" + ] + }, + separator: { + typ: "Comma" + }, + properties: { + "background-repeat": { + types: [ + ], + "default": [ + "repeat" + ], + multiple: true, + keywords: [ + "repeat-x", + "repeat-y", + "repeat", + "space", + "round", + "no-repeat" + ], + mapping: { + "repeat no-repeat": "repeat-x", + "no-repeat repeat": "repeat-y", + "repeat repeat": "repeat", + "space space": "space", + "round round": "round", + "no-repeat no-repeat": "no-repeat" + } + }, + "background-color": { + types: [ + "Color" + ], + "default": [ + "#0000", + "transparent" + ], + multiple: true, + keywords: [ + ] + }, + "background-image": { + types: [ + "UrlFunc", + "ImageFunc" + ], + "default": [ + "none" + ], + keywords: [ + "none" + ] + }, + "background-attachment": { + types: [ + ], + "default": [ + "scroll" + ], + multiple: true, + keywords: [ + "scroll", + "fixed", + "local" + ] + }, + "background-clip": { + types: [ + ], + "default": [ + "border-box" + ], + multiple: true, + keywords: [ + "border-box", + "padding-box", + "content-box", + "text" + ] + }, + "background-origin": { + types: [ + ], + "default": [ + "padding-box" + ], + multiple: true, + keywords: [ + "border-box", + "padding-box", + "content-box" + ] + }, + "background-position": { + multiple: true, + types: [ + "Perc", + "Length" + ], + "default": [ + "0 0", + "top left", + "left top" + ], + keywords: [ + "top", + "left", + "center", + "bottom", + "right" + ], + mapping: { + left: "0", + top: "0", + center: "50%", + "center center": "50%", + "50% 50%": "50%", + bottom: "100%", + right: "100%" + }, + constraints: { + mapping: { + max: 2 + } + } + }, + "background-size": { + multiple: true, + previous: "background-position", + prefix: { + typ: "Literal", + val: "/" + }, + types: [ + "Perc", + "Length" + ], + "default": [ + "auto", + "auto auto" + ], + keywords: [ + "auto", + "cover", + "contain" + ], + mapping: { + "auto auto": "auto" + } + } + } + }, + "background-repeat": { + shorthand: "background" + }, + "background-color": { + shorthand: "background" + }, + "background-image": { + shorthand: "background" + }, + "background-attachment": { + shorthand: "background" + }, + "background-clip": { + shorthand: "background" + }, + "background-origin": { + shorthand: "background" + }, + "background-position": { + shorthand: "background" + }, + "background-size": { + shorthand: "background" + }, + "grid-area": { + shorthand: "grid-area", + pattern: "grid-row-start grid-column-start grid-row-end grid-column-end", + keywords: [ + "auto" + ], + "default": [ + "auto" + ], + multiple: false, + expandShorthands: true, + separator: { + typ: "Literal", + val: "/" + }, + mapping: { + "auto/auto": "auto", + "auto/auto/auto": "auto", + "auto/auto/auto/auto": "auto" + }, + expandedProperties: { + "grid-row": [ + "grid-row-start", + "grid-row-end" + ], + "grid-column": [ + "grid-column-start", + "grid-column-end" + ] + }, + properties: { + "grid-row-start": { + map: "grid-area", + shorthand: "grid-row", + types: [ + "Iden", + "Number" + ], + "default": [ + "auto" + ], + keywords: [ + "auto" + ] + }, + "grid-column-start": { + map: "grid-area", + shorthand: "grid-column", + types: [ + "Iden", + "Number" + ], + prefix: { + typ: "Literal", + val: "/" + }, + "default": [ + "auto" + ], + keywords: [ + "auto" + ] + }, + "grid-row-end": { + map: "grid-area", + shorthand: "grid-row", + types: [ + "Iden", + "Number" + ], + prefix: { + typ: "Literal", + val: "/" + }, + "default": [ + "auto" + ], + keywords: [ + "auto" + ] + }, + "grid-column-end": { + map: "grid-area", + shorthand: "grid-column", + types: [ + "Iden", + "Number" + ], + prefix: { + typ: "Literal", + val: "/" + }, + "default": [ + "auto" + ], + keywords: [ + "auto" + ] + } + } + }, + "grid-row-start": { + shorthand: "grid-row" + }, + "grid-column-start": { + shorthand: "grid-column" + }, + "grid-row-end": { + shorthand: "grid-row" + }, + "grid-column-end": { + shorthand: "grid-column" + }, + "grid-row": { + shorthand: "grid-row", + map: "grid-area", + pattern: "grid-row-start grid-row-end", + types: [ + "Iden", + "Number" + ], + keywords: [ + "auto" + ], + "default": [ + "auto" + ], + multiple: false, + mapping: { + "auto auto": "auto", + "auto/auto": "auto" + }, + properties: { + "grid-row-start": { + map: "grid-area", + types: [ + "Iden", + "Number" + ], + "default": [ + "auto" + ], + multiple: true, + keywords: [ + "auto" + ] + }, + "grid-row-end": { + map: "grid-area", + types: [ + "Iden", + "Number" + ], + "default": [ + "auto" + ], + prefix: { + typ: "Literal", + val: "/" + }, + multiple: true, + keywords: [ + "auto" + ] + } + } + }, + "grid-column": { + shorthand: "grid-column", + map: "grid-area", + pattern: "grid-column-start grid-column-end", + types: [ + "Iden", + "Number" + ], + keywords: [ + "auto" + ], + "default": [ + "auto" + ], + multiple: false, + mapping: { + "auto/auto": "auto" + }, + properties: { + "grid-column-start": { + map: "grid-area", + types: [ + "Iden", + "Number" + ], + "default": [ + "auto" + ], + multiple: true, + keywords: [ + "auto" + ] + }, + "grid-column-end": { + map: "grid-area", + types: [ + "Iden", + "Number" + ], + "default": [ + "auto" + ], + multiple: true, + prefix: { + typ: "Literal", + val: "/" + }, + keywords: [ + "auto" + ] + } + } + }, + "grid-template": { + shorthand: "grid-template", + pattern: "grid-template-areas grid-template-rows grid-template-columns", + keywords: [ + "none" + ], + "default": [ + "none" + ], + multiple: false, + mapping: { + "none none": "none", + "none/none": "none", + "none none/none": "none" + }, + properties: { + "grid-template-areas": { + types: [ + "String" + ], + "default": [ + "none" + ], + multiple: true, + keywords: [ + "none", + "auto" + ], + mapping: { + } + }, + "grid-template-rows": { + types: [ + "Perc", + "Iden", + "Length", + "Flex" + ], + "default": [ + "none" + ], + multiple: true, + keywords: [ + "none" + ] + }, + "grid-template-columns": { + types: [ + "Perc", + "Iden", + "Length", + "Flex" + ], + "default": [ + "none" + ], + prefix: { + typ: "Literal", + val: "/" + }, + multiple: true, + keywords: [ + "none" + ] + } + } + }, + "grid-template-areas": { + shorthand: "grid-template" + }, + "grid-template-rows": { + shorthand: "grid-template" + }, + "grid-template-columns": { + shorthand: "grid-template" + } +}; +var property = { + "transform-origin": { + pattern: [ + [ + "left|center|right", + "top|center|bottom", + "" + ], + [ + "left|center|right", + "top|center|bottom" + ], + [ + "center|left|right|center|top|bottom|" + ] + ], + mapping: { + center: { + typ: "Perc", + val: 50 + }, + left: { + typ: "Perc", + val: 0 + }, + right: { + typ: "Perc", + val: 100 + }, + top: { + typ: "Perc", + val: 0 + }, + bottom: { + typ: "Perc", + val: 100 + } + } + } +}; +var config$1 = { + properties: properties, + map: map, + property: property +}; -function perspective(x, from) { - const matrix = identity(); - // @ts-ignore - matrix[2 * 4 + 3] = typeof x == "object" && x.val == "none" ? 0 : x == 0 ? Number.NEGATIVE_INFINITY : -1 / x; - return multiply(from, matrix); -} +Object.freeze(config$1); +// @ts-expect-error +const getConfig = () => config$1; -function compute(transformLists) { - transformLists = transformLists.slice(); - stripCommaToken(transformLists); - let matrix = identity(); - let mat; - let transforms; - const cumulative = []; - // all transform function arguments must be 0 or scale(1) - for (const transform of transformLists) { - switch (transform.val) { - case "rotate": - case "rotateX": - case "rotateY": - case "rotateZ": - case "rotate3d": - for (const child of transform.chi) { - if (child.typ == exports.EnumToken.WhitespaceTokenType || child.typ == exports.EnumToken.CommaTokenType) { - continue; - } - if ((child.typ != exports.EnumToken.AngleTokenType && - child.typ != exports.EnumToken.NumberTokenType && - child.typ != exports.EnumToken.PercentageTokenType) || - // angle >= 360deg, do not transform - Math.abs(getAngle(child)) >= 1) { - return null; - } - } - // break; - } +function matchType(val, properties) { + if ((val.typ === exports.EnumToken.IdenTokenType && properties.keywords.includes(val.val)) || + properties.types.some( + // @ts-expect-error + (t) => + // @ts-expect-error + (val.typ === exports.EnumToken.IdenTokenType && exports.EnumToken[t] === exports.EnumToken.ColorTokenType && isColor(val)) || + // @ts-expect-error + exports.EnumToken[t] === val.typ)) { + return true; } - for (const transformList of splitTransformList(transformLists)) { - mat = computeMatrix(transformList, identity()); - if (mat == null) { - return null; - } - matrix = multiply(matrix, mat); - transforms = minify$1(mat) ?? transformList; - for (let i = 0; i < transforms.length; i++) { - cumulative.push(transforms[i]); - } + if (val.typ === exports.EnumToken.NumberTokenType && val.val === 0) { + // @ts-ignore + return properties.types.some((type) => { + // @ts-ignore + const typ = exports.EnumToken[type]; + return typ == exports.EnumToken.LengthTokenType || typ == exports.EnumToken.AngleTokenType; + }); } - const serialized = serialize(matrix); - if (cumulative.length > 0) { - for (let i = 0; i < cumulative.length; i++) { - if (cumulative[i].typ == exports.EnumToken.IdenTokenType && cumulative[i].val == "none") { - cumulative.splice(i--, 1); - } - } - if (cumulative.length == 0) { - cumulative.push({ - typ: exports.EnumToken.IdenTokenType, - val: "none", - }); - } + if (tokensfuncSet.has(val.typ) && val.typ === exports.EnumToken.MathFunctionTokenType) { + return val.chi.every((t) => [ + exports.EnumToken.Add, + exports.EnumToken.Mul, + exports.EnumToken.Div, + exports.EnumToken.Sub, + exports.EnumToken.LiteralTokenType, + exports.EnumToken.CommaTokenType, + exports.EnumToken.WhitespaceTokenType, + exports.EnumToken.DimensionTokenType, + exports.EnumToken.NumberTokenType, + exports.EnumToken.LengthTokenType, + exports.EnumToken.AngleTokenType, + exports.EnumToken.PercentageTokenType, + exports.EnumToken.ResolutionTokenType, + exports.EnumToken.TimeTokenType, + exports.EnumToken.BinaryExpressionTokenType, + ].includes(t.typ) || matchType(t, properties)); } - const result = { - matrix: serialize(toZero(matrix)), - cumulative, - minified: minify$1(matrix) ?? [serialized], - }; - return result; + return false; } -function computeMatrix(transformList, matrixVar) { - let values = []; - let val; - let i = 0; - for (; i < transformList.length; i++) { - if (transformList[i].typ != exports.EnumToken.TransformFunctionTokenType || - !transformFunctions.includes(transformList[i].val)) { - return null; + +const propertiesConfig = getConfig(); +class PropertyMap { + declarations; + config; + requiredCount; + pattern; + constructor(config) { + const values = Object.values(config.properties); + this.requiredCount = + values.reduce((acc, curr) => (curr.required ? ++acc : acc), 0) || + values.length; + this.config = config; + this.declarations = new Map(); + this.pattern = config.pattern.split(/\s/); + } + add(declaration) { + if (declaration.nam == this.config.shorthand) { + this.declarations.clear(); + this.declarations.set(declaration.nam, declaration); + this.matchTypes(declaration); } - switch (transformList[i].val) { - case "translate": - case "translateX": - case "translateY": - case "translateZ": - case "translate3d": - { - values.length = 0; - const children = stripCommaToken(transformList[i].chi.slice()); - const valCount = transformList[i].val == "translate3d" - ? 3 - : transformList[i].val == "translate" - ? 2 - : 1; - for (let j = 0; j < children.length; j++) { - if (children[j].typ == exports.EnumToken.WhitespaceTokenType) { - continue; - } - val = length2Px(children[j]); - if (val == null) { - return null; - } - values.push(val); - } - if (values.length == 0 || values.length > valCount) { - return null; - } - if (transformList[i].val == "translateX") { - matrixVar = translateX(values[0], matrixVar); - } - else if (transformList[i].val == "translateY") { - matrixVar = translateY(values[0], matrixVar); - } - else if (transformList[i].val == "translateZ") { - matrixVar = translateZ(values[0], matrixVar); - } - else if (transformList[i].val == "translate") { - matrixVar = translate(values, matrixVar); - } - else { - // @ts-ignore - matrixVar = translate3d(values, matrixVar); + else { + const separator = this.config.separator != null + ? { + ...this.config.separator, + // @ts-ignore + typ: exports.EnumToken[this.config.separator.typ], + } + : null; + // expand shorthand + if (this.declarations.has(this.config.shorthand)) { + const tokens = {}; + const values = []; + const val = [[]]; + for (const curr of this.declarations.get(this.config.shorthand).val) { + // @ts-ignore + // @ts-ignore + if (separator != null && separator.typ == curr.typ && separator.val == curr.val) { + val.push([]); + continue; } + // @ts-ignore + val.at(-1).push(curr); } - break; - case "rotate": - case "rotateX": - case "rotateY": - case "rotateZ": - case "rotate3d": - { - let x = 0; - let y = 0; - let z = 0; - let angle; - let values = []; - let valuesCount = transformList[i].val == "rotate3d" ? 4 : 1; - for (const child of stripCommaToken(transformList[i].chi.slice())) { - values.push(child); - if (transformList[i].val == "rotateX") { - x = 1; + for (let current = 0; current < val.length; current++) { + values.push(...this.pattern.reduce((acc, property) => { + // let current: number = 0; + const props = this.config.properties[property]; + for (let i = 0; i < acc.length; i++) { + if (acc[i].typ == exports.EnumToken.CommentTokenType || + acc[i].typ == exports.EnumToken.WhitespaceTokenType) { + acc.splice(i, 1); + i--; + continue; + } + if ( + // @ts-ignore + acc[i][PROPERTYNAME] == property || + matchType(acc[i], props)) { + if ("prefix" in props && props.previous != null && !(props.previous in tokens)) { + return acc; + } + if (!(property in tokens)) { + tokens[property] = [[acc[i]]]; + } + else { + if (current == tokens[property].length) { + tokens[property].push([acc[i]]); + } + else { + tokens[property][current].push({ typ: exports.EnumToken.WhitespaceTokenType }, acc[i]); + } + } + acc.splice(i, 1); + i--; + // @ts-ignore + if ("prefix" in props && acc[i]?.typ == exports.EnumToken[props.prefix.typ]) { + if ( + // @ts-ignore + acc[i].typ == exports.EnumToken[props.prefix.typ] && + // @ts-ignore + acc[i].val == this.config.properties[property].prefix.val) { + acc.splice(i, 1); + i--; + } + } + if (props.multiple) { + continue; + } + return acc; + } + else { + if (property in tokens && tokens[property].length > current) { + return acc; + } + } } - else if (transformList[i].val == "rotateY") { - y = 1; + if (property in tokens && tokens[property].length > current) { + return acc; } - else if (transformList[i].val == "rotate" || - transformList[i].val == "rotateZ") { - z = 1; + // default + if (props.default.length > 0) { + const defaults = parseString(props.default[0]); + if (!(property in tokens)) { + tokens[property] = [[...defaults]]; + } + else { + if (current == tokens[property].length) { + tokens[property].push([]); + for (let i = 0; i < defaults.length; i++) { + tokens[property][current].push(defaults[i]); + } + } + else { + tokens[property][current].push({ + typ: exports.EnumToken.WhitespaceTokenType, + }); + for (let i = 0; i < defaults.length; i++) { + tokens[property][current].push(defaults[i]); + } + } + } } - } - if (values.length != valuesCount) { - return null; - } - if (transformList[i].val == "rotate3d") { - x = getNumber(values[0]); - y = getNumber(values[1]); - z = getNumber(values[2]); - } - angle = getAngle(values.at(-1)); - if ([x, y, z, angle].some((t) => typeof t != "number" || Number.isNaN(+t))) { - return null; - } - if (transformList[i].val == "rotate" || - transformList[i].val == "rotateZ") { - matrixVar = rotate(angle * 2 * Math.PI, matrixVar); - } - else { - matrixVar = rotate3D(angle * 2 * Math.PI, x, y, z, matrixVar); + return acc; + }, val[current])); + } + if (values.length == 0) { + this.declarations = Object.entries(tokens).reduce((acc, curr) => { + acc.set(curr[0], { + typ: exports.EnumToken.DeclarationNodeType, + nam: curr[0], + val: curr[1].reduce((acc, curr) => { + if (acc.length > 0) { + acc.push({ ...separator }); + } + for (let i = 0; i < curr.length; i++) { + acc.push(curr[i]); + } + return acc; + }, []), + }); + return acc; + }, new Map()); + } + } + // @ts-ignore + const config = propertiesConfig.properties[declaration.nam]; + let property = declaration.nam; + if (config != null) { + property = config.shorthand; + let value = this.declarations.get(property); + if (!(value instanceof PropertySet)) { + // @ts-ignore + this.declarations.set(property, new PropertySet(propertiesConfig.properties[config.shorthand])); + // Token[] + if (value != null) { + // @ts-ignore + this.declarations.get(property).add(value); } } + this.declarations.get(property).add(declaration); + } + else { + this.declarations.set(declaration.nam, declaration); + } + } + return this; + } + [Symbol.iterator]() { + const propertiesMapping = { ...this.config.properties }; + const patterns = this.config.pattern.split(" "); + let hasMapping = false; + let iterable; + let requiredCount = 0; + let property; + let isShorthand = true; + let declarations = this.declarations; + for (const key of declarations.keys()) { + if (declarations.has(key) && declarations.get(key) instanceof PropertyMap) { + hasMapping = true; break; - case "scale": - case "scaleX": - case "scaleY": - case "scaleZ": - case "scale3d": - { - values.length = 0; - let child; - const children = stripCommaToken(transformList[i].chi.slice()); - for (let k = 0; k < children.length; k++) { - child = children[k]; - if (child.typ != exports.EnumToken.NumberTokenType && child.typ != exports.EnumToken.PercentageTokenType) { - return null; - } - values.push(getNumber(child)); - } - if (transformList[i].val == "scale3d") { - if (values.length != 3) { - return null; + } + } + if (hasMapping) { + const mapped = {}; + let key; + for (const value of declarations.values()) { + key = value.nam; + if (value instanceof PropertyMap) { + for (const [k, v] of value.declarations) { + if (k == value.config.shorthand) { + for (const [key, val] of Object.entries(value.config.properties)) { + mapped[key] = cloneNode(v); + for (let i = 0; i < v.val.length; i++) { + // @ts-ignore + if (key == v.val[i][PROPERTYNAME]) { + if (mapped[key].val.length > 0) { + mapped[key].val.push({ + typ: exports.EnumToken.WhitespaceTokenType, + }); + } + mapped[key].val.push(v.val[i]); + } + } + if (mapped[key].val.length == 0) { + mapped[key].val.push(...parseString(value.config.properties[key].default[0])); + } + } } - matrixVar = scale3d(values[0], values[1], values[2], matrixVar); - break; - } - if (transformList[i].val == "scale") { - if (values.length != 1 && values.length != 2) { - return null; + else { + mapped[k] = v; } - matrixVar = scale(values[0], values[1] ?? values[0], matrixVar); - break; - } - if (values.length != 1) { - return null; - } - else if (transformList[i].val == "scaleX") { - matrixVar = scaleX(values[0], matrixVar); - } - else if (transformList[i].val == "scaleY") { - matrixVar = scaleY(values[0], matrixVar); - } - else if (transformList[i].val == "scaleZ") { - matrixVar = scaleZ(values[0], matrixVar); } } - break; - case "skew": - case "skewX": - case "skewY": - { - values.length = 0; - let child; - let value; - for (let k = 0; k < transformList[i].chi.length; k++) { - child = transformList[i].chi[k]; - if (child.typ == exports.EnumToken.CommentTokenType || - child.typ == exports.EnumToken.CommaTokenType || - child.typ == exports.EnumToken.WhitespaceTokenType) { - continue; - } - value = getAngle(child); - values.push(value * 2 * Math.PI); - } - if (values.length == 0 || - values.length > (transformList[i].val == "skew" ? 2 : 1)) { - return null; + else { + mapped[key] = value; + } + } + // console.error(mapped); + if (patterns.length === Object.keys(mapped).length) { + declarations = new Map(); + for (const key of patterns) { + if (mapped[key] != null) { + declarations.set(key, mapped[key]); } - if (transformList[i].val == "skew") { - matrixVar = skew(values, matrixVar); + } + } + } + for (property of Object.keys(propertiesMapping)) { + if (propertiesMapping[property].required) { + if (!declarations.has(property)) { + isShorthand = false; + break; + } + else { + const val = declarations.get(property); + if (val instanceof PropertySet && !val.isShortHand()) { + isShorthand = false; + break; } else { - matrixVar = - transformList[i].val == "skewX" - ? skewX(values[0], matrixVar) - : skewY(values[0], matrixVar); + requiredCount++; } } - break; - case "perspective": - { - const values = []; - let child; - let value; - for (let k = 0; k < transformList[i].chi.length; k++) { - child = transformList[i].chi[k]; - if (child.typ == exports.EnumToken.CommentTokenType || child.typ == exports.EnumToken.WhitespaceTokenType) { - continue; + } + } + if (requiredCount === 0) { + requiredCount = declarations.size; + } + if (!isShorthand || requiredCount < this.requiredCount) { + if (isShorthand && declarations.has(this.config.shorthand)) { + const cache = new Map(); + const removeDefaults = (declaration) => { + let i; + let t; + let map = new Map(); + let value = []; + let values = []; + // @ts-ignore + let typ = (exports.EnumToken[this.config.separator?.typ] ?? exports.EnumToken.CommaTokenType); + // @ts-ignore + const sep = this.config.separator == null + ? null + : { + ...this.config.separator, + typ: exports.EnumToken[this.config.separator.typ], + }; + // @ts-ignore + const separator = this.config.separator + ? printValue$1({ + ...this.config.separator, + typ: exports.EnumToken[this.config.separator.typ], + }) + : ","; + this.matchTypes(declaration); + values.push(value); + for (i = 0; i < declaration.val.length; i++) { + t = declaration.val[i]; + if (!cache.has(t)) { + cache.set(t, printValue$1(t, { minify: true })); } - if (child.typ == exports.EnumToken.IdenTokenType && child.val == "none") { - values.push(child); + if (t.typ == typ && separator == cache.get(t)) { + this.removeDefaults(map, value); + value = []; + values.push(value); + map.clear(); continue; } - value = length2Px(child); - if (value == null) { - return null; + value.push(t); + // @ts-ignore + if (t[PROPERTYNAME] != null) { + // @ts-ignore + if (!map.has(t[PROPERTYNAME])) { + // @ts-ignore + map.set(t[PROPERTYNAME], { t: [t], value: [cache.get(t)] }); + } + else { + // @ts-ignore + const v = map.get(t[PROPERTYNAME]); + v.t.push(t); + v.value.push(cache.get(t)); + } } - values.push(value); - } - if (values.length != 1) { - return null; } - matrixVar = perspective(values[0], matrixVar); - } - break; - case "matrix3d": - case "matrix": - { - const values = []; - let value; - for (const token of transformList[i].chi) { - if ([ - exports.EnumToken.WhitespaceTokenType, - exports.EnumToken.CommentTokenType, - exports.EnumToken.CommaTokenType, - ].includes(token.typ)) { - continue; + this.removeDefaults(map, value); + declaration.val = values.reduce((acc, curr) => { + if (sep != null && acc.length > 0) { + acc.push({ ...sep }); } - value = getNumber(token); - if (value == null) { - return null; + for (const cr of curr) { + if (cr.typ == exports.EnumToken.WhitespaceTokenType && acc.at(-1)?.typ == cr.typ) { + continue; + } + acc.push(cr); } - values.push(value); + return acc; + }, []); + while (declaration.val.at(-1)?.typ == exports.EnumToken.WhitespaceTokenType) { + declaration.val.pop(); } - if (transformList[i].val == "matrix") { - if (values.length != 6) { - return null; + while (declaration.val.at(0)?.typ == exports.EnumToken.WhitespaceTokenType) { + declaration.val.shift(); + } + return declaration; + }; + const values = [...declarations.values()].reduce((acc, curr) => { + if (curr instanceof PropertySet || curr instanceof PropertyMap) { + for (const declaration of curr) { + acc.push(declaration); } } - else if (values.length != 16) { - return null; + else { + acc.push(curr); } - matrixVar = multiply(matrixVar, matrix(values)); - } - break; - default: - return null; - } - } - return matrixVar; -} -function splitTransformList(transformList) { - let pattern = null; - const tokens = []; - for (let i = 0; i < transformList.length; i++) { - if (transformList[i].typ == exports.EnumToken.CommentTokenType || - transformList[i].typ == exports.EnumToken.WhitespaceTokenType) { - continue; - } - if (pattern == null || - (transformList[i].typ == exports.EnumToken.TransformFunctionTokenType && - !transformList[i].val.startsWith(pattern))) { - if (transformList[i].typ == exports.EnumToken.TransformFunctionTokenType) { - if (transformList[i].val.startsWith("scale")) { - pattern = "scale"; - } - else if (transformList[i].val.startsWith("rotate")) { - pattern = "rotate"; - } - else if (transformList[i].val.startsWith("translate")) { - pattern = "translate"; - } - else { - pattern = null; - } - tokens.push([transformList[i]]); - continue; - } - } - if (pattern != null && - transformList[i].typ == exports.EnumToken.TransformFunctionTokenType && - transformList[i].val.startsWith(pattern)) { - tokens[tokens.length - 1].push(transformList[i]); - continue; - } - tokens.push([transformList[i]]); - } - return tokens; -} - -class TransformCssFeature { - accept = new Set([ - exports.EnumToken.RuleNodeType, - exports.EnumToken.AtRuleNodeType, - exports.EnumToken.KeyframesRuleNodeType, - ]); - get ordering() { - return 4; - } - get processMode() { - return exports.FeatureWalkMode.Post; - } - static register(options) { - // @ts-ignore - if (options.computeTransform) { - // @ts-ignore - options.features.push(new TransformCssFeature()); - } - } - run(ast) { - if (ast.chi == null) { - return null; - } - let i = 0; - let node; - // @ts-ignore - for (; i < ast.chi.length; i++) { - // @ts-ignore - node = ast.chi[i]; - if (node[STATE] & (exports.EnumAstNodeStatus.Invalid | exports.EnumAstNodeStatus.ValidationFailed)) { - continue; - } - if (node.typ != exports.EnumToken.DeclarationNodeType || - !node.nam.match(/^(-[a-z]+-)?transform$/)) { - continue; - } - const children = []; - for (const child of node.val) { - children.push(child.typ == exports.EnumToken.TransformFunctionTokenType - ? minifyTransformFunctions(child) - : child); - } - let { matrix, cumulative, minified } = compute(children) ?? { - matrix: null, - cumulative: null, - minified: null, - }; - if (matrix == null || cumulative == null || minified == null) { - node.val = children.map((t) => t.typ == exports.EnumToken.TransformFunctionTokenType ? minifyTransformFunctions(t) : t); - continue; - } - let r = [filterValues(children)]; - if (eqMatrix(matrix, cumulative)) { - r.push(cumulative); - } - if (eqMatrix(matrix, minified)) { - r.push(minified); - } - const l = renderValue(matrix).length; - node.val = r.reduce((acc, curr) => { - if (curr.reduce((acc, t) => acc + renderValue(t), "").length < l) { - return curr; - } - return acc; - }, [matrix]); - } - return null; - } -} - -/** - * Search the ast subtree and return the first match - * - * ```ts - * // find the first ast declaration node which name is 'aspect-ratio' -import { find, EnumToken, transform } from "@tbela99/css-parser"; -import type { AstNode } from "@tbela99/css-parser"; - - * const css = ` - -button { - aspect-ratio: 1; - width: if(media(any-pointer: fine): 30px; else: 44px); -} - `; - - // find declaration which contain a '30px' - const nodeMatcher = (node: AstNode) => - return node.typ == EnumToken.DeclarationNodeType && (node as AstDeclaration).nam == 'aspect-ratio'; - - const result = await transform(css); - const node = find(result.ast, nodeMatcher); - - console.log({node}); - - ``` - * - * @param ast - * @param matcher - * @returns - */ -function find(ast, matcher) { - for (const { node, parent } of walk(ast)) { - if (matcher(node, parent)) { - return node; - } - } - return null; -} -/** - * - * @param ast - * @param matcher - * @returns - * - * Search the ast sub-tree by checking each node's value token and return the first match - * - ```ts - // find the first ast node which contains the length token '30px' -import { findByValue, EnumToken, transform } from "@tbela99/css-parser"; -import type { AstNode } from "@tbela99/css-parser"; - - const css = ` - -button { - aspect-ratio: 1; - width: if(media(any-pointer: fine): 30px; else: 44px); -} - `; - - // find declaration which contain the length token '30px' - const nodeMatcher = (value: Token) => - return value.typ == EnumToken.LengthTokenType && (value as LengthToken).val == 30 && (value as LengthToken).unit == 'px' ; - - const result = await transform(css); - const { node, value } = findByValue(result.ast, nodeMatcher) ?? {}; - - console.log({node, value}); - - ``` - */ -function findByValue(ast, matcher) { - let source; - for (const { node } of walk(ast)) { - source = null; - if (node.typ === exports.EnumToken.DeclarationNodeType) { - source = node.val; - } - else if (Array.isArray(node[TOKENS])) { - source = node[TOKENS]; - } - if (source == null) { - continue; - } - for (const { value, parent, root: rootNode, parents } of walkValues(source, node)) { - if (matcher(value, node)) { - // @ts-ignore - return { node, value: { node: value, parent, root: rootNode, parents } }; - } - } - } - return null; -} -/** - * Search the ast sub-tree and return all matches - * - * ```ts - * // find the first ast declaration node which name is 'aspect-ratio' -* import { findAll, EnumToken, transform } from "@tbela99/css-parser"; -* import type { AstNode } from "@tbela99/css-parser"; - -const css = ` - -button { - aspect-ratio: 1; - width: if(media(any-pointer: fine): 30px; else: 44px); -} -button-small { - aspect-ratio: 1; - width: if(media(any-pointer: fine): 20px; else: 30px); -} - `; - - const nodeMatcher = (node: AstNode) => - return node.typ == EnumToken.DeclarationNodeType && (node as AstDeclaration).nam == 'aspect-ratio'; - - const result = await transform(css); - const nodes = findAll(result.ast, nodeMatcher); - - console.log({nodes}); - - ``` - * - * @param ast - * @param matcher - * @returns - */ -function findAll(ast, matcher) { - const result = []; - for (const { node, parent } of walk(ast)) { - if (matcher(node, parent)) { - result.push(node); - } - } - return result; -} -/** - * Search the ast sub-tree and return the last match. - * - * ```ts - * // find the first ast declaration node which name is 'aspect-ratio' -import { findLast, EnumToken, transform } from "@tbela99/css-parser"; -import type { AstNode } from "@tbela99/css-parser"; - - * const css = ` - -button { - aspect-ratio: 1; - width: if(media(any-pointer: fine): 30px; else: 44px); -} - `; - - const nodeMatcher = (node: AstNode) => - return node.typ == EnumToken.DeclarationNodeType && (node as AstDeclaration).nam == 'aspect-ratio'; - - const result = await transform(css); - const node = findLast(result.ast, nodeMatcher); - - console.log({node}); - - ``` - * - * @param ast - * @param matcher - * @returns - */ -function findLast(ast, matcher) { - for (const { node, parent } of walk(ast, null, true)) { - if (matcher(node, parent)) { - return node; - } - } - return null; -} - -const nodeMatcher = (value) => value.typ === exports.EnumToken.IfConditionTokenType || - (value.typ === exports.EnumToken.WildCardFunctionTokenType && equalsIgnoreCase("if", value.val)); -function substituteIfElseNode(declaration, node, wrapper, parentWrapper, cache) { - const result = []; - let nodeMap = new Map(); - let clonedDeclaration; - let targetParentWrapper = parentWrapper; - let targetWrapper = wrapper; - if (node.typ === exports.EnumToken.IfElseConditionTokenType) { - // - clonedDeclaration = cloneNode(declaration, true, nodeMap); - // replace else: ... with the actual value - if (node.r.typ === exports.EnumToken.IfConditionTokenType) { - const target = node.r.l.find((t) => t.typ != exports.EnumToken.CommentTokenType && t.typ != exports.EnumToken.WhitespaceTokenType); - // - if (target.typ === exports.EnumToken.IdenTokenType && equalsIgnoreCase("else", target.val)) { - replaceNodeOrValue(nodeMap.get(targetParentWrapper), nodeMap.get(targetWrapper), node.r.r.at(-1)?.typ === - exports.EnumToken.SemiColonTokenType - ? trimArray(node.r.r.slice(0, -1)) - : node.r.r); - // @ts-expect-error - if (targetParentWrapper.typ != exports.EnumToken.DeclarationNodeType) { - let index = targetParentWrapper.chi.indexOf(targetWrapper); - if (index != -1) { - let i; - let k; - let siblingWrapper; - let left = node.l.l.find((t) => t.typ != exports.EnumToken.CommentTokenType && t.typ != exports.EnumToken.WhitespaceTokenType); - for (i = index + 1; i < targetParentWrapper.chi.length; i++) { - if (targetParentWrapper.chi[i].typ === targetWrapper.typ) { - siblingWrapper = targetParentWrapper.chi[i]; - for (k = 0; k < siblingWrapper.chi.length; k++) { - if (siblingWrapper.chi[k].typ === exports.EnumToken.IfElseConditionTokenType) { - let leftSide = siblingWrapper.chi[k].l.l.find((t) => t.typ != exports.EnumToken.CommentTokenType && - t.typ != exports.EnumToken.WhitespaceTokenType); - if (eq(left, leftSide)) { - replaceNodeOrValue(nodeMap.get(targetParentWrapper), nodeMap.get(targetParentWrapper.chi[i]), siblingWrapper.chi[k] - .r.r.at(-1)?.typ === exports.EnumToken.SemiColonTokenType - ? trimArray(siblingWrapper.chi[k] - .r.r.slice(0, -1)) - : siblingWrapper.chi[k] - .r.r); - // @ts-ignore - cache.add(siblingWrapper.chi[k].l); - } - } - } - } + return acc; + }, []); + let isImportant = false; + let dec; + const filtered = []; + for (const declaration of values) { + dec = removeDefaults(declaration); + for (const t of dec.val) { + if (t.typ == exports.EnumToken.ImportantTokenType) { + isImportant = true; + } + if (filtered.length == 0 && + t.typ != exports.EnumToken.WhitespaceTokenType && + t.typ != exports.EnumToken.ImportantTokenType) { + filtered.push(dec); } } } + if (filtered.length == 0 && this.config.default.length > 0) { + filtered.push({ + typ: exports.EnumToken.DeclarationNodeType, + nam: this.config.shorthand, + val: parseString(this.config.default[0]), + }); + if (isImportant) { + filtered[0].val.push({ + typ: exports.EnumToken.ImportantTokenType, + }); + } + } + return (filtered.length > 0 ? filtered : values)[Symbol.iterator](); } - } - result.push(clonedDeclaration); - nodeMap.clear(); - clonedDeclaration = cloneNode(declaration, true, nodeMap); - replaceNodeOrValue(nodeMap.get(targetParentWrapper), nodeMap.get(targetWrapper), node.l); - result.push(clonedDeclaration); - } - else if (node.typ === exports.EnumToken.IfConditionTokenType) { - const left = node.l.find((t) => t.typ != exports.EnumToken.CommentTokenType && t.typ != exports.EnumToken.WhitespaceTokenType); - if (left == null) { - return result; - } - if (left.typ === exports.EnumToken.IdenTokenType && equalsIgnoreCase("else", left.val)) { - clonedDeclaration = cloneNode(declaration, true, nodeMap); - replaceNodeOrValue(nodeMap.get(parentWrapper), - // @ts-expect-error - nodeMap.get(targetWrapper.typ === exports.EnumToken.DeclarationNodeType ? node : targetWrapper), node.r.at(-1)?.typ === exports.EnumToken.SemiColonTokenType ? trimArray(node.r.slice(0, -1)) : node.r); - result.push(clonedDeclaration); - } - else if (left?.typ === exports.EnumToken.WhenElseFunctionTokenType) { - const atRule = Object.assign(cloneNode(declaration), { - typ: exports.EnumToken.AtRuleNodeType, - nam: left.val, - chi: [], - }); - if (declaration[PARENT] != null) { - atRule[LOCSRCID] = declaration[PARENT][LOCSRCID]; - atRule[LOCSTA] = declaration[PARENT][LOCSTA]; - atRule[LOCEND] = declaration[PARENT][LOCEND]; - } - atRule[TOKENS] = [{ typ: exports.EnumToken.ParensTokenType, chi: left.chi.slice() }]; - const minify = atRule.nam !== "supports"; - const options = { - minify, - convertColor: minify, - }; - atRule.val = atRule[TOKENS].reduce((acc, curr) => acc + renderValue(curr, options), ""); - clonedDeclaration = cloneNode(declaration, true, nodeMap); - replaceNodeOrValue(nodeMap.get(targetWrapper), nodeMap.get(node), node.r.at(-1)?.typ === exports.EnumToken.SemiColonTokenType ? trimArray(node.r.slice(0, -1)) : node.r); - clonedDeclaration[PARENT] = atRule; - atRule.chi.push(clonedDeclaration); - result.push(atRule); - processNode(clonedDeclaration, cache); - } - else if (left.typ === exports.EnumToken.ContainerFunctionTokenType) { - const atRule = Object.assign(cloneNode(declaration), { - typ: exports.EnumToken.AtRuleNodeType, - nam: "container", - chi: [], - }); - atRule[TOKENS] = [left]; - atRule.val = atRule[TOKENS].reduce((acc, curr) => acc + renderValue(curr), ""); - if (declaration[PARENT] != null) { - atRule[LOCSRCID] = declaration[PARENT][LOCSRCID]; - atRule[LOCSTA] = declaration[PARENT][LOCSTA]; - atRule[LOCEND] = declaration[PARENT][LOCEND]; - } - clonedDeclaration = cloneNode(declaration, true, nodeMap); - replaceNodeOrValue(nodeMap.get(targetWrapper.typ === exports.EnumToken.WildCardFunctionTokenType ? targetParentWrapper : targetWrapper), nodeMap.get(targetWrapper.typ === exports.EnumToken.WildCardFunctionTokenType ? targetWrapper : node), node.r.at(-1)?.typ === exports.EnumToken.SemiColonTokenType - ? trimArray(node.r.slice(0, -1)) - : node.r); - clonedDeclaration[PARENT] = atRule; - atRule.chi.push(clonedDeclaration); - result.push(atRule); - processNode(clonedDeclaration, cache); - } - } - return result; -} -function processNode(declarationNode, cache) { - let i; - let k = -1; - let astNode; - const result = []; - const stack = [declarationNode]; - while (++k < stack.length) { - astNode = stack[k]; - const { node: declaration, value: node } = findByValue(astNode, nodeMatcher) ?? {}; - if (declaration == null || node == null) { - result.push(astNode); - continue; - } - // @ts-expect-error - const parents = [...node.parents?.()]; - const parentWrapper = node.parent ?? parents.find((node) => !nodeMatcher(node)); - if (node.node.typ === exports.EnumToken.WildCardFunctionTokenType) { - for (i = 0; i < node.node.chi.length; i++) { - stack.push( - // @ts-expect-error - ...substituteIfElseNode( - // @ts-expect-error - declaration, node.node.chi[i], node.node, parentWrapper, cache)); + for (const declaration of declarations.values()) { + if (declaration instanceof PropertySet || declaration instanceof PropertyMap) { + continue; + } + const config = declaration.nam == this.config.shorthand + ? this.config + : (propertiesMapping[declaration.nam] ?? this.config); + if (!("mapping" in config)) { + continue; + } + // @ts-ignore + for (const [key, val] of Object.entries(config.mapping)) { + const keys = parseString(key); + if (keys.length != declaration.val.length) { + continue; + } + if (eq(declaration.val, keys)) { + declaration.val = parseString(val); + break; + } + } } + // @ts-ignore + iterable = declarations.values(); } else { - stack.push( - // @ts-expect-error - ...substituteIfElseNode( - // @ts-expect-error - declaration, node.node, parentWrapper, parents[parents.indexOf(parentWrapper) + 1] ?? declaration, cache)); - } - } - if (result.length > 0) { - // @ts-expect-error - replaceNodeOrValue(declarationNode[PARENT], declarationNode, result); - } - // else remove node? - // @ts-expect-error - return result; -} -class ExpandIfFeature { - accept = new Set([exports.EnumToken.DeclarationNodeType]); - get ordering() { - return 1; - } - get processMode() { - return exports.FeatureWalkMode.Pre; - } - static register(options) { - if (options.expandIfSyntax) { + let count = 0; + let match; + const separator = this.config.separator != null + ? { + ...this.config.separator, + // @ts-ignore + typ: exports.EnumToken[this.config.separator.typ], + } + : null; + const tokens = {}; // @ts-ignore - options.features.push(new ExpandIfFeature()); - } - } - run(declaration) { - return processNode(declaration, new Set()); - } -} - -var allFeatures = /*#__PURE__*/Object.freeze({ - __proto__: null, - ComputeCalcExpressionFeature: ComputeCalcExpressionFeature, - ComputePrefixFeature: ComputePrefixFeature, - ComputeShorthandFeature: ComputeShorthandFeature, - ExpandIfFeature: ExpandIfFeature, - InlineCssVariablesFeature: InlineCssVariablesFeature, - TransformCssFeature: TransformCssFeature -}); - -const notEndingWith = ["(", "["].concat(combinators); -const rules = [ - exports.EnumToken.AtRuleNodeType, - exports.EnumToken.RuleNodeType, - exports.EnumToken.AtRuleTokenType, - exports.EnumToken.KeyframesRuleNodeType, -]; -// @ts-ignore -const features = Object.values(allFeatures).sort((a, b) => a.ordering - b.ordering); -/** - * Apply minification rules to the ast tree - * @param ast - * @param options - * @param recursive - * @param errors - * @param nestingContent - * - * @param context - * @private - */ -function minify(ast, options = {}, recursive = false, errors, nestingContent, context = {}) { - let preprocess = false; - let postprocess = false; - let parents; - let replacement; - let { sourcemap, module, ...options2 } = options; - if (!(options2.features != null)) { - options2 = { - removeDuplicateDeclarations: true, - computeShorthand: true, - computeCalcExpression: true, - removePrefix: false, - features: [], - ...options2, - }; - for (const feature of features) { - feature.register(options2); - } - options2.features.sort((a, b) => a.ordering - b.ordering); - } - for (const feature of options2.features) { - if (feature.processMode & exports.FeatureWalkMode.Pre) { - preprocess = true; - } - if (feature.processMode & exports.FeatureWalkMode.Post) { - postprocess = true; - } - } - if (preprocess) { - parents = new Set([ast]); - for (const parent of parents) { - if (parent.typ == exports.EnumToken.CommentTokenType || parent.typ == exports.EnumToken.CDOCOMMTokenType) { - continue; + Object.entries(propertiesMapping).reduce((acc, curr) => { + if (!declarations.has(curr[0])) { + if (curr[1].required) { + acc.push(curr[0]); + } + return acc; + } + let current = 0; + const props = propertiesMapping[curr[0]]; + const properties = (declarations.get(curr[0])); + for (const declaration of properties instanceof PropertySet || properties instanceof PropertyMap + ? [...properties].flat() + : [properties]) { + // @ts-ignore + for (const val of declaration.val) { + // @ts-ignore + if (separator != null && separator.typ == val.typ && separator.val == val.val) { + current++; + if (tokens[curr[0]].length == current) { + tokens[curr[0]].push([]); + } + continue; + } + if (val.typ == exports.EnumToken.WhitespaceTokenType || val.typ == exports.EnumToken.CommentTokenType) { + continue; + } + // @ts-ignore + if (props.multiple && + props.separator != null && + // @ts-ignore + exports.EnumToken[props.separator.typ] === val.typ && + // @ts-ignore + (val.typ === exports.EnumToken.CommaTokenType || props.separator.val == val.val)) { + continue; + } + // @ts-ignore + match = val.typ == exports.EnumToken.CommentTokenType || matchType(val, curr[1]); + if (isShorthand) { + isShorthand = match; + } + // @ts-ignore + if (val[PROPERTYNAME] == property || match) { + if (!(curr[0] in tokens)) { + tokens[curr[0]] = [[]]; + } + // is default value + tokens[curr[0]][current].push(val); + } + else { + acc.push(curr[0]); + break; + } + } + } + if (count === 0) { + count = current; + } + return acc; + }, []); + // grid-template + if (this.config.shorthand == "grid-template" && patterns.length == Object.keys(tokens).length) { + const k = tokens[patterns[0]][0].length; + const l = tokens[patterns[1]][0].length; + const j = k < l ? k : l; + const result = []; + let i; + for (i = 0; i < j; i++) { + if (i < k) { + result.push(tokens[patterns[0]][0][i]); + } + if (i < l) { + result.push(tokens[patterns[1]][0][i]); + } + } + if (j < k) { + result.push(...tokens[patterns[0]][0].slice(j)); + } + if (j < l) { + result.push(...tokens[patterns[1]][0].slice(j)); + } + const separator = this.config.properties[patterns[2]]?.prefix; + result.push( + // @ts-ignore + { ...separator, typ: exports.EnumToken[separator.typ] }, ...tokens[patterns[2]][0].reduce((acc, curr) => { + if (acc.length > 0) { + acc.push({ typ: exports.EnumToken.WhitespaceTokenType }); + } + acc.push(curr); + return acc; + }, [])); + const declaration = cloneNode(declarations.get(patterns[0])); + declaration.nam = this.config.shorthand; + declaration.val = result; + return [declaration][Symbol.iterator](); } - replacement = parent; - for (const feature of options2.features) { - if ((feature.processMode & exports.FeatureWalkMode.Pre) === 0 || - (feature.accept != null && !feature.accept.has(parent.typ))) { - continue; + count++; + if (!isShorthand || + Object.entries(propertiesMapping).some((entry) => { + // missing required property + return entry[1].required && !(entry[0] in tokens); + }) || + // @ts-ignore + !Object.values(tokens).every((v) => + // @ts-expect-error + v.filter((t) => t.typ != exports.EnumToken.CommentTokenType).length === count)) { + // @ts-ignore + iterable = declarations.values(); + } + else if (hasMapping) { + if (patterns.length == Object.keys(tokens).length) { + const declaration = cloneNode(declarations.get(patterns[0])); + declaration.nam = this.config.shorthand; + for (let i = 0; i < patterns.length; i++) { + if (declaration.val.length > 0) { + declaration.val.push( + // @ts-ignore + this.config.separator != null + ? { + ...this.config.separator, + typ: exports.EnumToken[this.config.separator.typ], + } + : { typ: exports.EnumToken.WhitespaceTokenType }); + } + declaration.val.push(...tokens[patterns[i]].reduce((acc, curr) => { + if (acc.length > 0) { + acc.push({ typ: exports.EnumToken.WhitespaceTokenType }); + } + acc.push(...curr.reduce((acc, curr) => { + if (acc.length > 0) { + acc.push({ typ: exports.EnumToken.WhitespaceTokenType }); + } + acc.push(curr); + return acc; + }, [])); + return acc; + }, [])); + } + return [declaration][Symbol.iterator](); } - if (rules.includes(replacement.typ) && !Array.isArray(replacement[TOKENS])) { - replacement[TOKENS] = parseString(replacement.typ == exports.EnumToken.RuleNodeType || replacement.typ === exports.EnumToken.KeyframesRuleNodeType - ? replacement.sel - : // @ts-ignore - replacement.nam); + iterable = declarations.values(); + } + else { + let values = Object.entries(tokens) + .reduce((acc, curr) => { + const props = propertiesMapping[curr[0]]; + for (let i = 0; i < curr[1].length; i++) { + if (acc.length == i) { + acc.push([]); + } + // if (acc[acc.length - 1].length > 0) { + // acc[acc.length - 1].push( + // // @ts-ignore + // this.config.separator != null ? + // { + // ...this.config.separator, + // typ: EnumToken[this.config.separator.typ as keyof typeof EnumToken], + // } : { typ: EnumToken.WhitespaceTokenType }, + // ) + // } + let values = curr[1][i]; + // .reduce( + // (acc, curr) => { + // // if (acc.length > 0) { + // // acc.push({ typ: EnumToken.WhitespaceTokenType }); + // // } + // acc.push(curr); + // return acc; + // }, + // [], + // ); + if (props.default.includes(curr[1][i] + .reduce((acc, curr) => acc + printNode(curr) + " ", "") + .trimEnd())) { + if (!propertiesMapping[curr[0]].required) { + continue; + } + } + // remove default values + let doFilterDefault = true; + if (curr[0] in propertiesConfig.properties) { + for (let v of values) { + if (![ + exports.EnumToken.WhitespaceTokenType, + exports.EnumToken.CommentTokenType, + exports.EnumToken.IdenTokenType, + ].includes(v.typ) || + (v.typ == exports.EnumToken.IdenTokenType && + !propertiesMapping[curr[0]].default.includes(v.val))) { + doFilterDefault = false; + break; + } + } + } + // remove default values + const filtered = values.filter((val) => { + if (val.typ == exports.EnumToken.WhitespaceTokenType || + val.typ == exports.EnumToken.CommentTokenType) { + return false; + } + return (!doFilterDefault || + !(val.typ == exports.EnumToken.IdenTokenType && + props.default.includes(val.val))); + }); + if (filtered.length > 0 || + !(this.requiredCount == requiredCount && propertiesMapping[curr[0]].required)) { + values = filtered; + } + if (values.length > 0) { + if ("mapping" in props) { + if (!("constraints" in props) || + // @ts-ignore + !("max" in props.constraints) || + values.length <= props.constraints.mapping.max) { + let i = values.length; + while (i--) { + if (values[i].typ == exports.EnumToken.IdenTokenType && + // @ts-expect-error + values[i].val in props.mapping) { + // @ts-ignore + values.splice(i, 1, ...parseString(props.mapping[values[i].val])); + } + } + } + } + if ("prefix" in props) { + // @ts-ignore + acc[i].push({ ...props.prefix, typ: exports.EnumToken[props.prefix.typ] }); + } + else if (acc[i].length > 0) { + // @ts-ignore + acc[i].push({ typ: exports.EnumToken.WhitespaceTokenType }); + } + for (const v of values.reduce((acc, curr) => { + if (acc.length > 0) { + // @ts-ignore + acc.push({ + ...((props.separator && { + ...props.separator, + // @ts-ignore + typ: exports.EnumToken[props.separator.typ], + }) ?? { typ: exports.EnumToken.WhitespaceTokenType }), + }); + } + // @ts-ignore + acc.push(curr); + return acc; + }, [])) { + acc[i].push(v); + } + } + } + return acc; + }, []) + .reduce((acc, curr) => { + if (acc.length > 0) { + acc.push({ ...separator }); + } + if (curr.length == 0 && this.config.default.length > 0) { + curr.push(...parseString(this.config.default[0]).reduce((acc, curr) => { + if (acc.length > 0) { + acc.push({ typ: exports.EnumToken.WhitespaceTokenType }); + } + acc.push(curr); + return acc; + }, [])); + } + for (const c of curr) { + acc.push(c); + } + return acc; + }, []); + if (this.config.mapping != null) { + const val = values.reduce((acc, curr) => acc + + printNode(curr, { + removeComments: true, + minify: true, + }), ""); + if (val in this.config.mapping) { + values.length = 0; + values.push({ + typ: ['"', "'"].includes(val.charAt(0)) + ? exports.EnumToken.StringTokenType + : exports.EnumToken.IdenTokenType, + // @ts-ignore + val: this.config.mapping[val], + }); + } } - const result = feature.run(replacement, options2, // @ts-ignore - parent[PARENT] ?? ast, context, exports.FeatureWalkMode.Pre); - if (result != null) { - replacement = result; + if (values.length == 1 && + // @ts-ignore + typeof values[0].val == "string" && + this.config.default.includes(values[0].val.toLowerCase()) && + this.config.default[0] != values[0].val.toLowerCase()) { + // @ts-ignore/ + values = parseString(this.config.default[0]); } + iterable = [ + { + typ: exports.EnumToken.DeclarationNodeType, + nam: this.config.shorthand, + val: values, + }, + ][Symbol.iterator](); } - if (replacement != null && - (!Array.isArray(replacement) || replacement.length > 0) && - replacement != parent && - parent[PARENT] != null) { - // @ts-ignore - replaceNodeOrValue(parent[PARENT], parent, replacement); - } + } + const iterators = []; + // @ts-ignore + return { // @ts-ignore - if (replacement.chi != null) { + next() { + let v = iterable.next(); // @ts-ignore - for (const node of replacement.chi) { - node[PARENT] = replacement; - parents.add(node); + while (v.done || v.value instanceof PropertySet) { + if (v.value instanceof PropertySet) { + // @ts-ignore + iterators.push(iterable); + iterable = v.value[Symbol.iterator](); + v = iterable.next(); + } + if (v.done) { + if (iterators.length > 0) { + // @ts-ignore + iterable = iterators.pop(); + v = iterable.next(); + } + if (v.done && iterators.length == 0) { + break; + } + } } - } - } - for (const feature of options2.features) { - if (feature.processMode & exports.FeatureWalkMode.Pre && "cleanup" in feature) { - // @ts-ignore - feature.cleanup(ast, options2, context, exports.FeatureWalkMode.Pre); - } - } + return v; + }, + }; } - doMinify(ast, options2, recursive, errors, nestingContent, context); - parents = new Set([ast]); - for (const parent of parents) { - if (parent.typ == exports.EnumToken.CommentTokenType || parent.typ == exports.EnumToken.CDOCOMMTokenType) { - continue; + matchTypes(declaration) { + const patterns = this.pattern.slice(); + const values = []; + let i; + let j; + const map = new Map(); + for (i = 0; i < declaration.val.length; i++) { + values.push(declaration.val[i]); } - replacement = parent; - if (postprocess) { - for (const feature of options2.features) { - if ((feature.processMode & exports.FeatureWalkMode.Post) === 0 || - (feature.accept != null && !feature.accept.has(parent.typ))) { - continue; + for (i = 0; i < patterns.length; i++) { + for (j = 0; j < values.length; j++) { + if (!map.has(patterns[i])) { + // @ts-ignore + map.set(patterns[i], this.config.properties?.[patterns[i]]?.constraints?.mapping?.max ?? 1); } - const result = feature.run(replacement, options2, - // @ts-ignore - parent[PARENT] ?? ast, context, exports.FeatureWalkMode.Post); - if (result != null) { - replacement = result; + let count = map.get(patterns[i]); + if (count > 0 && matchType(values[j], this.config.properties[patterns[i]])) { + // @ts-expect-error + values[j][PROPERTYNAME] = patterns[i]; + map.set(patterns[i], --count); + values.splice(j--, 1); } } } - if (replacement != null && - (!Array.isArray(replacement) || replacement.length > 0) && - replacement != parent && - parent[PARENT] != null) { - // @ts-ignore - replaceNodeOrValue(parent[PARENT], parent, replacement); - } - // @ts-ignore - if (replacement.chi != null) { - // @ts-ignore - for (const node of replacement.chi) { - node[PARENT] = replacement; - parents.add(node); - } - } - } - if (postprocess) { - for (const feature of options2.features) { - if (feature.processMode & exports.FeatureWalkMode.Post && "cleanup" in feature) { - // @ts-ignore - feature.cleanup(ast, options2, context, exports.FeatureWalkMode.Post); + if (this.config.set != null) { + for (const [key, val] of Object.entries(this.config.set)) { + if (map.has(key)) { + for (const v of val) { + // missing + if (map.get(v) == 1) { + let i = declaration.val.length; + while (i--) { + // @ts-expect-error + if (declaration.val[i][PROPERTYNAME] == key) { + const val = { ...declaration.val[i] }; + // @ts-expect-error + val[PROPERTYNAME] = v; + declaration.val.splice(i, 0, val, { typ: exports.EnumToken.WhitespaceTokenType }); + } + } + } + } + } } } } - return ast; -} -function transformAtRuleMediaPrelude(values) { - let hasUpdates = false; - for (let { value, parent, parents } of walkValues(values)) { - if (value.typ === exports.EnumToken.MediaQueryConditionTokenType) { - if (value.op.typ == exports.EnumToken.AndTokenType && - // @ts-ignore - value.l.typ === exports.EnumToken.IdenTokenType && - // @ts-ignore - value.l.val.toLowerCase() === "all") { - if (parent === null) { - // @ts-ignore - values[values.indexOf(value)] = value.l; - } - else { - // @ts-ignore - replaceNodeOrValue(parent, value, value.l); - // @ts-ignore - value = value.l; - } - hasUpdates = true; + removeDefaults(map, value) { + for (const [key, val] of map) { + const config = this.config.properties[key]; + if (config == null) { + continue; } - } - // range operator - if (parent != null && - parent.typ === exports.EnumToken.MediaQueryConditionTokenType && - parent.op.typ == exports.EnumToken.AndTokenType && - // @ts-ignore - parent.l.typ == exports.EnumToken.ParensTokenType) { - let token = parent.r.find((t) => t.typ !== exports.EnumToken.WhitespaceTokenType && t.typ !== exports.EnumToken.CommentTokenType); - if (token?.typ === exports.EnumToken.ParensTokenType) { - // @ts-ignore - const node1 = parent.l.chi.find((t) => t.typ !== exports.EnumToken.WhitespaceTokenType && t.typ !== exports.EnumToken.CommentTokenType); - const node2 = token.chi.find((t) => t.typ !== exports.EnumToken.WhitespaceTokenType && t.typ !== exports.EnumToken.CommentTokenType); - if (node1?.typ === exports.EnumToken.MediaQueryConditionTokenType && - node2?.typ === exports.EnumToken.MediaQueryConditionTokenType && - node1.op.typ == exports.EnumToken.ColonTokenType && - node2.op.typ == exports.EnumToken.ColonTokenType && - // @ts-ignore - node1.l.typ == exports.EnumToken.IdenTokenType && - // @ts-ignore - node2.l.typ == exports.EnumToken.IdenTokenType && - // @ts-ignore - node1.l.val.startsWith("min-") && - // @ts-ignore - node2.l.val.startsWith("max-") && - // @ts-ignore - node1.l.val.slice(4) == - // @ts-ignore - node2.l.val.slice(4)) { - const val1 = node1.r.find((t) => t.typ !== exports.EnumToken.WhitespaceTokenType && t.typ !== exports.EnumToken.CommentTokenType); - const val2 = node2.r.find((t) => t.typ !== exports.EnumToken.WhitespaceTokenType && t.typ !== exports.EnumToken.CommentTokenType); - const replacement = { - typ: exports.EnumToken.ParensTokenType, - chi: [ - // @ts-ignore - { - typ: exports.EnumToken.MediaRangeQueryTokenType, - op: { - typ: exports.EnumToken.IdenTokenType, + const v = val.value.join(" "); + if (config.default.includes(v) || (value.length == 1 && this.config.default.includes(v))) { + for (const token of value) { + if (val.t.includes(token)) { + let index = value.indexOf(token); + value.splice(index, 1); + if (config.prefix != null) { + while (index-- > 0) { + if (value[index].typ == exports.EnumToken.WhitespaceTokenType) { + continue; + } + if ( + // @ts-expect-error + value[index].typ == exports.EnumToken[config.prefix.typ] && // @ts-ignore - val: node1.l.val.slice(4), - }, - l: val1, - r: val2, - [LOCSRCID]: value[LOCSRCID], - [LOCSTA]: value[LOCSTA], - [LOCEND]: value[LOCEND], - }, - ], - }; - // @ts-expect-error - const p = parents?.[parents?.indexOf?.(parent) + 1]; - if (p != null) { - // @ts-ignore - replaceNodeOrValue(p, parent, replacement); - } - else { - // @ts-ignore - values.splice(values.indexOf(parent), 1, replacement); + value[index].val == config.prefix.val) { + value.splice(index, 1); + break; + } + } + } } - hasUpdates = true; - value = replacement; } } } } - return { hasUpdates, values: trimArray(values) }; } + +// Alphabet: a-z, A-Z, 0-9, _, - +const LOWER = "abcdefghijklmnopqrstuvwxyz"; +const DIGITS = "0123456789"; +const FULL_ALPHABET = (LOWER + DIGITS).split(""); // 64 chars +const FIRST_ALPHABET = LOWER.split(""); // 54 chars (no digits) /** - * Minify at-rule media - * - remove redundant tokens - * - generate range queries - * - * @private - * @param tokens + * supported hash algorithms */ -function minifyAtRuleMedia(tokens) { - let hasUpdates = false; - const sections = tokens - .reduce((acc, t) => { - if (t.typ === exports.EnumToken.CommaTokenType) { - acc.push([]); - } - else { - acc[acc.length - 1].push(t); - } - return acc; - }, [[]]) - .reduce((acc, values) => { - if (acc.has("all")) { - return acc; - } - const result = transformAtRuleMediaPrelude(values); - if (result.values.length === 0) { - return acc; +const hashAlgorithms = ["hex", "base64", "base64url", "sha1", "sha256", "sha384", "sha512"]; +// simple deterministic hash → number +function hashCode(str) { + let hash = 0; + let l = str.length; + let i = 0; + while (i < l) { + hash = (hash * 31 + str.charCodeAt(i++)) >>> 0; + } + return hash; +} +/** + * generate a hash id + * @param input + * @param length + */ +function hashId(input, length = 6) { + let n = hashCode(input); + const chars = []; + // First character: must not be a digit + chars.push(FIRST_ALPHABET[n % FIRST_ALPHABET.length]); + // Remaining characters + for (let i = 1; i < length; i++) { + n = (n + chars.length * i) % FULL_ALPHABET.length; + chars.push(FULL_ALPHABET[n]); + } + return chars.join(""); +} +/** + * Object to string + * @param input + * @returns + */ +function toSortedString(input) { + if (input == null) { + return "null"; + } + if (typeof input !== "object") { + return String(input); + } + if (Array.isArray(input)) { + const result = new Array(input.length); + for (const value of input) { + result.push(toSortedString(value)); } - if (result.hasUpdates) { - hasUpdates = true; + return JSON.stringify(result); + } + const keys = Object.keys(input).sort(); + for (let i = 0; i < keys.length; i++) { + keys[i] = `${keys[i]}:${toSortedString(input[keys[i]])}`; + } + return `{${keys.join(",")}}`; +} +/** + * Object hash + * @param object + * @returns + */ +function objectHash(object) { + return hashCode(toSortedString(object)).toString(16); +} +/** + * convert input to hex + * @param input + * @param length + */ +function toHex(input, length) { + let result = ""; + if (input instanceof ArrayBuffer || ArrayBuffer.isView(input)) { + for (const byte of Array.from(new Uint8Array(input))) { + result += byte.toString(16).padStart(2, "0"); } - acc.set(values.reduce((acc, t) => acc + renderValue(t), ""), result.values); - return acc; - }, new Map()); - if (sections.has("all")) { - tokens.length = 0; } - else if (hasUpdates) { - tokens.length = 0; - tokens.push(...[...sections.values()].reduce((acc, t) => { - if (acc.length > 0) { - acc.push({ - typ: exports.EnumToken.CommaTokenType, - }); - } - for (const token of t) { - acc.push(token); - } - return acc; - }, [])); + else { + for (const char of String(input)) { + result += char.charCodeAt(0).toString(16).padStart(2, "0"); + } } - // return ast; - return tokens; + return result; } /** - * Reduce selectors - * @param acc - * @param curr - * - * @private + * generate a hash + * @param input + * @param length + * @param algo */ -function reduce(acc, curr) { - // trim :is() - if (curr[0] == "&") { - if (curr[1] == " " && !isIdent(curr[2]) && !isFunction(curr[2])) { - const all = new Array(Math.max(curr.length - 2, 0)); - let write = 0; - for (let read = 2; read < curr.length; read++) { - all[write++] = curr[read]; - } - curr.length = 0; - for (let index = 0; index < write; index++) { - curr[index] = all[index]; - } +async function hash(input, length = 6, algo) { + let result; + if (algo != null) { + switch (algo) { + case "hex": + return toHex(input).slice(0, length); + case "base64url": + case "base64": + result = btoa(input); + if (algo == "base64url") { + result = result.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); + } + return result.slice(0, length); + case "sha256": + case "sha384": + case "sha512": + case "sha1": + return toHex(await crypto.subtle.digest(algo.replace("sha", "SHA-"), new TextEncoder().encode(input))).slice(0, length); + default: + throw new Error(`Unsupported hash algorithm: ${algo}`); } } - acc.push(curr.join("")); - return acc; + return hashId(input, length); } /** - * Apply minification rules to the ast tree - * @param ast - * @param options - * @param recursive - * @param errors - * @param nestingContent - * @param context - * - * @private + * generate a hash + * @param input + * @param length + * @param algo */ -function doMinify(ast, options = {}, recursive = false, errors, nestingContent, context = {}) { - if (!("nodes" in context)) { - context.nodes = new Set(); +function syncHash(input, length = 6, algo) { + let result; + if (algo != null) { + switch (algo) { + case "hex": + return toHex(input).slice(0, length); + case "base64url": + case "base64": + result = btoa(input); + if (algo == "base64url") { + result = result.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); + } + return result.slice(0, length); + default: + throw new Error(`Unsupported hash algorithm: ${algo}`); + } } - if (context.nodes.has(ast)) { - return ast; + return hashId(input, length); +} + +const config = getConfig(); +/** + * Represents a list of property declarations, compute shorthand and remove duplicate declarations. + */ +class PropertyList { + options = { removeDuplicateDeclarations: true, computeShorthand: true }; + declarations; + constructor(options = {}) { + this.options = options; + this.declarations = new Map(); } - context.nodes.add(ast); - // @ts-ignore - if ("chi" in ast && ast.chi.length > 0) { - const reducer = reduce.bind(ast); - if (!nestingContent) { - nestingContent = options.nestingRules && ast.typ == exports.EnumToken.RuleNodeType; - } - let i = 0; - let previous = null; - let node = null; - let nodeIndex = -1; - for (; i < ast.chi.length; i++) { - if (ast.chi[i].typ === exports.EnumToken.CommentNodeType) { + add(declarations) { + let name; + let syntaxRules = null; + let result; + for (const declaration of declarations) { + name = declaration.typ != exports.EnumToken.DeclarationNodeType ? null : declaration.nam; + if (declaration[STATE] & + (exports.EnumAstNodeStatus.Invalid | exports.EnumAstNodeStatus.Unknown | exports.EnumAstNodeStatus.ValidationFailed) || + declaration.typ != exports.EnumToken.DeclarationNodeType || + equalsIgnoreCase("composes", name) || + (typeof this.options.removeDuplicateDeclarations === "string" && + this.options.removeDuplicateDeclarations === name) || + (Array.isArray(this.options.removeDuplicateDeclarations) + ? this.options.removeDuplicateDeclarations.includes(declaration.nam) + : !this.options.removeDuplicateDeclarations)) { + const key = objectHash(declaration); + if (this.declarations.has(key)) { + this.declarations.delete(key); + } + this.declarations.set(key, declaration); continue; } - while (previous?.typ === exports.EnumToken.CommentNodeType) { - // @ts-ignore - previous = ast.chi[--nodeIndex]; + if (!this.options.computeShorthand) { + this.declarations.set(declaration.nam, declaration); + continue; + } + if (declaration[STATE] & exports.EnumAstNodeStatus.Unvalidated) { + syntaxRules = getParsedSyntax(ValidationSyntaxGroupEnum.Declarations, declaration.nam.toLowerCase()); + if (syntaxRules != null) { + result = matchAllSyntaxes(syntaxRules, createValidationContext(declaration.val), this.options); + declaration[STATE] |= + (result.success ? exports.EnumAstNodeStatus.Validated : exports.EnumAstNodeStatus.ValidationFailed) & + ~exports.EnumAstNodeStatus.Unvalidated; + } } - node = ast.chi[i]; - if (node.typ === exports.EnumToken.AtRuleNodeType && node.nam === "font-face") { + // do not compute shorthand for invalid declarations + if (!(declaration[STATE] & exports.EnumAstNodeStatus.Validated)) { + this.declarations.set(objectHash(declaration), declaration); continue; } - if (node.typ === exports.EnumToken.KeyframesAtRuleNodeType) { - if (previous?.typ === exports.EnumToken.KeyframesAtRuleNodeType && - node.nam === previous.nam && - node.val === previous.val) { - ast.chi?.splice(nodeIndex--, 1); - previous = ast?.chi?.[nodeIndex] ?? null; - i = nodeIndex; - continue; - } - } - else if (node.typ === exports.EnumToken.KeyframesRuleNodeType) { - if (previous?.typ === exports.EnumToken.KeyframesRuleNodeType && - node.sel === previous.sel) { - // do not merge keyframes - // https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@keyframes#resolving_duplicates - for (const child of node.chi) { - previous.chi.push(child); - } + let propertyName = declaration.nam; + let shortHandType; + let shorthand = null; + if (propertyName in config.properties) { + // @ts-ignore + if ("map" in config.properties[propertyName]) { + shortHandType = "map"; // @ts-ignore - ast.chi.splice(i, 1); - previous = ast?.chi?.[nodeIndex] ?? null; - i = nodeIndex; - continue; + shorthand = config.properties[propertyName].map; } - let k; - for (k = 0; k < node.chi.length; k++) { - if (node.chi[k].typ == exports.EnumToken.DeclarationNodeType) { - let l = node.chi[k].val.length; - while (l--) { - if (node.chi[k].val[l].typ == - exports.EnumToken.ImportantTokenType) { - node.chi.splice(k--, 1); - break; - } - if ([exports.EnumToken.WhitespaceTokenType, exports.EnumToken.CommentTokenType].includes(node.chi[k].val[l].typ)) { - continue; - } - break; - } - } + else { + shortHandType = "set"; + // @ts-ignore + shorthand = config.properties[propertyName].shorthand; } } - else if (node.typ == exports.EnumToken.AtRuleNodeType) { - if (node.nam == "media") { - if (Array.isArray(node[TOKENS])) { - const slice = node[TOKENS].slice(); - minifyAtRuleMedia(slice); - if (slice.length !== node[TOKENS].length) { - node[TOKENS].length = 0; - for (const token of slice) { - node[TOKENS].push(token); - } - node.val = slice.reduce((acc, curr, index, arr) => acc + - (curr.typ === exports.EnumToken.CommentTokenType || - (curr.typ === exports.EnumToken.WhitespaceTokenType && - arr[index + 1]?.typ === exports.EnumToken.CommentTokenType && - (index + 3 < arr.length || - arr[index + 2]?.typ === exports.EnumToken.WhitespaceTokenType)) - ? "" - : renderValue(curr)), ""); - } - } - if (["all", "", null].includes(node.val)) { - ast.chi?.splice(i--, 1, ...node.chi); - continue; - } - } - else if (node.nam === "import" && Array.isArray(node[TOKENS])) { - let l = 0; - let token; - for (; l < node[TOKENS].length; l++) { - token = node[TOKENS][l]; - if (token.typ === exports.EnumToken.ParensTokenType || - token.typ === exports.EnumToken.MediaQueryConditionTokenType || - (token.typ === exports.EnumToken.IdenTokenType && "layer" !== token.val)) { - break; - } - } - if (l < node[TOKENS].length) { - const slice = node[TOKENS]?.slice(l); - node[TOKENS].splice(l, slice.length, ...minifyAtRuleMedia(slice)); - node.val = trimArray(node[TOKENS]).reduce((acc, curr, index, arr) => acc + - (curr.typ === exports.EnumToken.CommentTokenType || - (curr.typ === exports.EnumToken.WhitespaceTokenType && - arr[index + 1]?.typ === exports.EnumToken.CommentTokenType && - (index + 3 < arr.length || arr[index + 2].typ === exports.EnumToken.WhitespaceTokenType)) - ? "" - : renderValue(curr)), ""); - } - } - else if (ast.typ === node.typ && - ast.nam === node.nam && - ast.val === node.val) { - // @ts-ignore - replaceNodeOrValue(ast, node, node.chi); - i--; - continue; + else if (propertyName in config.map) { + shortHandType = "map"; + // @ts-ignore + shorthand = config.map[propertyName].shorthand; + } + else if (propertyName in config.property) { + shortHandType = "single"; + // @ts-ignore + shorthand = config.property[propertyName]; + } + if (this.declarations.has(shorthand)) { + let existing = this.declarations.get(shorthand); + if (existing instanceof PropertySet || existing instanceof PropertyMap) { + existing = existing.declarations.get(declaration.nam); } - if (previous?.typ == exports.EnumToken.AtRuleNodeType && - node.nam != "font-face" && - previous.nam === node.nam && - previous.val === node.val) { - if ("chi" in node) { - for (const child of node.chi) { - previous.chi.push(child); - } - if (!hasDeclaration(previous)) { - context.nodes.delete(previous); - doMinify(previous, options, recursive, errors, nestingContent, context); - } - } - ast?.chi?.splice(i--, 1); + if (existing?.[STATE] & exports.EnumAstNodeStatus.ImportantDeclaration && + declaration[STATE] & ~exports.EnumAstNodeStatus.ImportantDeclaration) { continue; } - // if (!hasDeclaration(node as AstAtRule)) { - // doMinify(node, options, recursive, errors, nestingContent, context); - // } - if ("chi" in node) { - doMinify(node, options, recursive, errors, nestingContent, context); - } - previous = node; - nodeIndex = i; - continue; } // @ts-ignore - else if (node.typ === exports.EnumToken.RuleNodeType) { - reduceRuleSelector(node); - let wrapper = null; - let match; - if (options.nestingRules) { - if (previous?.typ == exports.EnumToken.RuleNodeType) { - reduceRuleSelector(previous); + if (shortHandType == "map") { + let owner = this; + const mapName = + // @ts-ignore + config.map[propertyName]?.map ?? config.map[shorthand]?.properties?.[propertyName]?.map; + if (typeof mapName === "string") { + if (!this.declarations.has(mapName)) { // @ts-ignore - match = matchSelectors(previous[RAW], node[RAW]); - if (match != null) { - wrapper = wrapNodes(previous, node, match, ast, reducer, i, nodeIndex); - nodeIndex = i - 1; - previous = ast.chi[nodeIndex]; - } - } - if (wrapper != null) { - while (i < ast.chi.length) { - const nextNode = ast.chi[i]; - if (nextNode.typ != exports.EnumToken.RuleNodeType) { - break; - } - reduceRuleSelector(nextNode); - match = matchSelectors(wrapper[RAW], nextNode[RAW]); - if (match == null) { - break; - } - wrapper = wrapNodes(wrapper, nextNode, match, ast, reducer, i, nodeIndex); - } - nodeIndex = --i; - previous = ast.chi[nodeIndex]; - doMinify(wrapper, options, recursive, errors, nestingContent, context); - continue; + this.declarations.set(mapName, new PropertyMap(config.map[mapName])); } + owner = this.declarations.get(mapName); + } + else if (!this.declarations.has(shorthand)) { // @ts-ignore - else if (node[OPTIMIZED] != null && - // @ts-ignore - node[OPTIMIZED].match && - // @ts-ignore - node[OPTIMIZED].selector.length > 1) { - // @ts-ignore - wrapper = { - ...node, - chi: [], - sel: node[OPTIMIZED].optimized[0], - [RAW]: [[node[OPTIMIZED].optimized[0]]], - }; - // @ts-ignore - node.sel = node[OPTIMIZED].selector.reduce(reducer, []).join(","); - // @ts-ignore - node[RAW] = node[OPTIMIZED].selector.slice(); - node[TOKENS] = null; - // @ts-ignore - wrapper.chi.push(node); - // @ts-ignore - ast.chi.splice(i, 1, wrapper); - node = wrapper; - } - else if (node[OPTIMIZED]?.reducible) { - if (node[OPTIMIZED].optimized.length === 1) { - const sel1 = node[OPTIMIZED].optimized[0] + - ":is(" + - node[OPTIMIZED].selector.reduce(reducer, []).join(",") + - ")"; - const sel2 = node[OPTIMIZED].selector.reduce((acc, curr) => - // @ts-ignore - (acc.length > 0 ? acc + "," : "") + node[OPTIMIZED].optimized[0] + curr.join(""), ""); - node.sel = sel1.length < sel2.length ? sel1 : sel2; - node[TOKENS] = null; - } - else if (node[OPTIMIZED].optimized.length === 0) { - const testIdent = /^[a-zA-Z]/; - node.sel = node[OPTIMIZED].selector.reduce((acc, curr) => (acc.length > 0 ? acc + "," : "") + - (nestingContent && testIdent.test(curr[0]) ? "& " : "") + - curr.join(""), ""); - node[TOKENS] = null; - } - } + this.declarations.set(shorthand, new PropertyMap(config.map[shorthand])); } - // @ts-ignore - else if (node[OPTIMIZED]?.match) { - let wrap = true; + if (owner == this) { // @ts-ignore - const selector = node[OPTIMIZED].selector.reduce((acc, curr) => { - if (curr[0] == "&" && curr.length > 1) { - if (curr[1] == " ") { - curr.splice(0, 2); - } - else { - curr.splice(0, 1); - } - } - else if (combinators.includes(curr[0])) { - curr.unshift("&"); - wrap = false; - } - acc.push(curr); - return acc; - }, []); - if (!wrap) { - wrap = selector.some((s) => s[0] != "&"); - } - let rule = null; - const optimized = node[OPTIMIZED].optimized.slice(); - if (optimized.length > 1) { - const check = optimized.at(-2); - if (!combinators.includes(check)) { - const last = optimized[optimized.length - 1]; - const all = new Array(Math.max(optimized.length - 1, 0)); - let write = 0; - for (let read = 0; read < optimized.length - 1; read++) { - all[write++] = optimized[read]; - } - optimized.length = 0; - for (let index = 0; index < write; index++) { - optimized[index] = all[index]; - } - wrap = false; - rule = - optimized.join("") + - `:is(${selector - .map((s) => { - const next = s[0] == "&" ? new Array(s.length) : new Array(s.length + 1); - let write = 0; - if (s[0] == "&") { - next[write++] = last; - for (let read = 1; read < s.length; read++) { - next[write++] = s[read]; - } - } - else { - next[write++] = last; - for (let read = 0; read < s.length; read++) { - next[write++] = s[read]; - } - } - const rebuilt = new Array(write); - for (let index = 0; index < write; index++) { - rebuilt[index] = next[index]; - } - return rebuilt.join(""); - }) - .join(",")})`; - } - } - if (rule == null) { - rule = selector - .map((s) => { - if (s[0] == "&") { - const replacement = node[OPTIMIZED].optimized; - const all = new Array(Math.max(s.length - 1 + replacement.length, 0)); - let write = 0; - for (let read = 0; read < replacement.length; read++) { - all[write++] = replacement[read]; - } - for (let read = 1; read < s.length; read++) { - all[write++] = s[read]; - } - const rebuilt = new Array(write); - for (let index = 0; index < write; index++) { - rebuilt[index] = all[index]; - } - return rebuilt.join(""); - } - return s.join(""); - }) - .join(","); - } - let sel = wrap ? node[OPTIMIZED].optimized.join("") + `:is(${rule})` : rule; - if (sel.length < node.sel.length) { - node.sel = sel; - node[TOKENS] = null; - } + owner.declarations.get(shorthand).add(declaration); } - else if (node[OPTIMIZED]?.reducible) { - if (node[OPTIMIZED].optimized.length === 1) { - const sel1 = node[OPTIMIZED].optimized[0] + - ":is(" + - node[OPTIMIZED].selector.reduce(reducer, []).join(",") + - ")"; - const sel2 = node[OPTIMIZED].selector.reduce((acc, curr) => + else { + if (!owner.declarations.has(shorthand)) { // @ts-ignore - (acc.length > 0 ? acc + "," : "") + node[OPTIMIZED].optimized[0] + curr.join(""), ""); - node.sel = sel1.length < sel2.length ? sel1 : sel2; - node[TOKENS] = null; - } - else if (node[OPTIMIZED].optimized.length === 0) { - const testIdent = /^[a-zA-Z]/; - node.sel = node[OPTIMIZED].selector.reduce((acc, curr) => (acc.length > 0 ? acc + "," : "") + - (nestingContent && testIdent.test(curr[0]) ? "& " : "") + - curr.join(""), ""); - node[TOKENS] = null; + owner.declarations.set(shorthand, new PropertyMap(config.map[shorthand])); } // @ts-ignore + owner.declarations.get(shorthand).add(declaration); } - else if (node[OPTIMIZED]?.optimized.length > 0) { + } + // @ts-ignore + else if (shortHandType == "set") { + // @ts-ignore + if (!this.declarations.has(shorthand)) { // @ts-ignore - const sel = node[OPTIMIZED].optimized.join(""); - if (sel.length < node.sel.length) { - node.sel = sel; - // @ts-ignore - node[RAW] = [node[OPTIMIZED].optimized.slice()]; - node[TOKENS] = null; - } + this.declarations.set( + // @ts-ignore + shorthand, + // @ts-ignore + new PropertySet(config.properties[shorthand])); } - doMinify(node, options, recursive, errors, nestingContent, context); + // @ts-ignore + this.declarations.get(shorthand).add(declaration); } - if (previous != null) { - if ("chi" in previous && "chi" in node) { - if (previous.typ === node.typ) { - let shouldMerge = true; - let k = previous.chi.length; - while (k-- > 0) { - if (previous.chi[k].typ === exports.EnumToken.CommentNodeType || - previous.chi[k].typ === exports.EnumToken.InvalidRuleNodeType || - previous.chi[k].typ === exports.EnumToken.InvalidRuleNodeType) { - continue; - } - shouldMerge = previous.chi[k].typ === exports.EnumToken.DeclarationNodeType; - break; - } - if (shouldMerge) { - if (((node.typ === exports.EnumToken.RuleNodeType || - node.typ === exports.EnumToken.KeyframesRuleNodeType) && - node.sel === previous.sel) || - // @ts-ignore - (node.typ == exports.EnumToken.AtRuleNodeType && - node.nam !== "font-face" && - // @ts-ignore - node.nam === previous.nam)) { - const array = []; - for (let i = 0; i < previous.chi.length; i++) { - array.push(previous.chi[i]); - } - for (let i = 0; i < node.chi.length; i++) { - array.push(node.chi[i]); + else { + if (shorthand != null) { + this.mapValues(declaration, shorthand); + } + this.declarations.set(propertyName, declaration); + } + } + return this; + } + mapValues(declaration, mapping) { + let name; + let values = declaration.val; + if (mapping.pattern != null) { + // match pattern + for (const patterns of mapping.pattern) { + const set = new Set(values.filter((v) => v.typ !== exports.EnumToken.CommentTokenType && v.typ !== exports.EnumToken.WhitespaceTokenType)); + const val = []; + for (const pattern of patterns) { + switch (pattern) { + case "": + for (const v of set) { + if (v.typ === exports.EnumToken.LengthTokenType || + (v.typ === exports.EnumToken.NumberTokenType && v.val === 0)) { + val.push(v); + set.delete(v); } - // @ts-ignore - node.chi = array; - doMinify(node, options, recursive, errors, nestingContent, context); - ast.chi.splice(nodeIndex, 1); - previous = ast.chi[--i]; - nodeIndex = i; - continue; } - else if (node.typ == previous?.typ && - [exports.EnumToken.KeyframesRuleNodeType, exports.EnumToken.RuleNodeType].includes(node.typ)) { - const intersect = diff$1(previous, node, options); - if (intersect != null) { - if (intersect.node1.chi.length == 0) { - ast.chi.splice(i--, 1); - } - else { - ast.chi.splice(i--, 1, intersect.node1); - } - if (intersect.node2.chi.length == 0) { - if (intersect.result != null) { - ast.chi.splice(nodeIndex, 1, intersect.result); - } - else { - ast.chi.splice(nodeIndex, 1); - } - i--; - if (nodeIndex == i) { - nodeIndex = i; - } - } - else { - if (intersect.result != null) { - ast.chi.splice(nodeIndex, 1, intersect.result, intersect.node2); - } - else { - ast.chi.splice(nodeIndex, 1, intersect.node2); - } - i = (nodeIndex ?? 0) + 1; - } - if (node != ast.chi[i]) { - node = ast.chi[i]; + default: { + for (const v of set) { + if (v.typ === exports.EnumToken.IdenTokenType) { + const value = v.val.toLowerCase(); + if (value === pattern || new RegExp(`(^|\|)${pattern}(\||$)`).test(value)) { + val.push(v); + set.delete(v); } - previous = intersect.result; - nodeIndex = i; } } } } - if (recursive && previous != null && previous != node) { - if (!hasDeclaration(previous)) { - doMinify(previous, options, recursive, errors, nestingContent, context); - } + } + if (set.size === 0) { + values = val.reduce((acc, curr) => { + if (acc.length > 0) { + acc.push({ typ: exports.EnumToken.WhitespaceTokenType }); + } + acc.push(curr); + return acc; + }, []); + break; + } + } + } + for (const val of declaration.val) { + if (val.typ === exports.EnumToken.IdenTokenType) { + name = val.val.toLowerCase(); + if (mapping.mapping[name] != null) { + // @ts-expect-error + Object.assign(val, mapping.mapping[name], { typ: exports.EnumToken[mapping.mapping[name].typ] }); + } + } + } + if (values != declaration.val) { + declaration.val.length = 0; + for (const v of values) { + declaration.val.push(v); + } + } + } + [Symbol.iterator]() { + let iterator = this.declarations.values(); + const iterators = []; + // @ts-ignore + return { + next() { + let value = iterator.next(); + while ((value.done && iterators.length > 0) || + value.value instanceof PropertySet || + value.value instanceof PropertyMap) { + if (value.value instanceof PropertySet || value.value instanceof PropertyMap) { + iterators.unshift(iterator); + // @ts-ignore + iterator = value.value[Symbol.iterator](); + value = iterator.next(); + } + if (value.done && iterators.length > 0) { + iterator = iterators.shift(); + value = iterator.next(); } } - } - if (!nestingContent && - previous != null && - previous.typ == exports.EnumToken.RuleNodeType && - previous.sel.includes("&")) { - fixSelector(previous); - } - previous = node; - nodeIndex = i; + return value; + }, + }; + } +} + +class ComputeShorthandFeature { + accept = new Set([ + exports.EnumToken.RuleNodeType, + exports.EnumToken.AtRuleNodeType, + exports.EnumToken.KeyframesRuleNodeType, + ]); + get ordering() { + return 5; + } + get processMode() { + return exports.FeatureWalkMode.Post; + } + static register(options) { + if (options.computeShorthand) { + // @ts-ignore + options.features.push(new ComputeShorthandFeature(options)); + } + } + run(ast, options) { + if (!("chi" in ast || ast.chi?.length == 0)) { + return null; } // @ts-ignore - if (recursive && node != null && node.chi != null) { - if (node.typ == exports.EnumToken.KeyframesAtRuleNodeType || - !node.chi.some((n) => n.typ == exports.EnumToken.DeclarationNodeType)) { - if (!(node.typ == exports.EnumToken.AtRuleNodeType && node.nam != "font-face")) { - doMinify(node, options, recursive, errors, nestingContent, context); + const j = ast.chi.length; + let k = 0; + let l; + const rules = []; + const declarations = []; + // @ts-ignore + for (; k < j; k++) { + l = k; + // capture comments with the next token + while (l + 1 < j) { + // @ts-ignore + const node = ast.chi[l]; + if (node.typ == exports.EnumToken.CommentNodeType) { + l++; + continue; + } + break; + } + // @ts-ignore + const node = ast.chi[l]; + if (node.typ == exports.EnumToken.DeclarationNodeType) { + for (let m = k; m <= l; m++) { + declarations.push(ast.chi[m]); + } + } + else { + for (let m = k; m <= l; m++) { + rules.push(ast.chi[m]); } } + k = l; } - if (!nestingContent && - node != null && - node.typ == exports.EnumToken.RuleNodeType && - node.sel.includes("&")) { - fixSelector(node); + if (declarations.length > 0) { + ast.chi.length = 0; + // @ts-expect-error + ast.chi.push(...new PropertyList(options).add(declarations), ...rules); } + return ast; } - return ast; } + +const trimTokenSpace = new Set([ + exports.EnumToken.CommaTokenType, + exports.EnumToken.DelimTokenType, + exports.EnumToken.GtTokenType, + exports.EnumToken.GteTokenType, + exports.EnumToken.LtTokenType, + exports.EnumToken.LteTokenType, + exports.EnumToken.ChildCombinatorTokenType, +]); +const trimSpaceAfter = new Set([ + ...tokensfuncSet.values(), + exports.EnumToken.ChildCombinatorTokenType, + exports.EnumToken.SupportsQueryConditionTokenType, + exports.EnumToken.SupportsQueryUnaryConditionTokenType, +]); /** - * Check if a rule has a declaration + * Replace token in its parent node + * @param parent * @param node - * - * @private + * @param replacement + * @throws TypeError replacement is null + * @throws ReferenceError node not found in parent */ -function hasDeclaration(node) { - // @ts-ignore - for (let i = 0; i < node.chi?.length; i++) { - // @ts-ignore - if (node.chi[i].typ == exports.EnumToken.CommentNodeType) { - continue; +function replaceNodeOrValue(parent, node, replacement) { + if (replacement == null || (Array.isArray(replacement) && replacement.length === 0)) { + throw new TypeError(`replacement is null`); + } + for (const newNode of Array.isArray(replacement) ? replacement : [replacement]) { + if (newNode[PARENT] != node[PARENT]) { + newNode[PARENT] = node[PARENT]; } - // @ts-ignore - return node.chi[i].typ == exports.EnumToken.DeclarationNodeType; } - return true; -} -/** - * Optimize selector - * @param selector - * - * @private - */ -function optimizeSelector(selector) { - const map = new Set(); - selector = selector - .reduce((acc, curr) => { - // @ts-ignore - if (curr.length > 0 && curr.at(-1).startsWith(":is(")) { + if (parent.typ == exports.EnumToken.BinaryExpressionTokenType) { + if (parent.l == node) { + parent.l = replacement; + } + else if (parent.r == node) { + parent.r = replacement; + } + else { + throw new ReferenceError("Node not found"); + } + } + else { + const target = "val" in parent && Array.isArray(parent.val) + ? parent.val + : (parent.chi ?? parent); + if (Array.isArray(target)) { // @ts-ignore - const rules = splitRule(curr.at(-1).slice(4, -1)).map((x) => { - if (x[0] == "&" && x.length > 1) { - return x.slice(x[1] == " " ? 2 : 1); - } - return x; - }); - const part = curr.slice(0, -1); - for (const rule of rules) { - acc.push(part.concat(rule)); + const index = target.indexOf(node); + if (index == -1) { + throw new ReferenceError("Node not found"); } - return acc; + target.splice(index, 1, ...(Array.isArray(replacement) ? replacement : [replacement])); } - acc.push(curr); - return acc; - }, []) - .filter((x) => { - const str = x.join(""); - if (map.has(str)) { - return false; + else if ("l" in target && target.l == node) { + target.l = replacement; } - map.add(str); - return true; - }); - const optimized = []; - const k = selector.reduce((acc, curr) => acc == 0 ? curr.length : curr.length == 0 ? acc : Math.min(acc, curr.length), 0); - let i = 0; - let j; - let match; - for (; i < k; i++) { - const item = selector[0][i]; - match = true; - for (j = 1; j < selector.length; j++) { - if (item != selector[j][i]) { - match = false; - break; - } + else if ("r" in target && target.r == node) { + target.r = replacement; } - if (!match) { - break; + else { + throw new ReferenceError("Node not found"); } - optimized.push(item); } - while (optimized.length > 0) { - const last = optimized.at(-1); - if (last == " " || combinators.includes(last)) { - const all = new Array(Math.max(optimized.length - 1, 0)); - let write = 0; - for (let read = 0; read < optimized.length - 1; read++) { - all[write++] = optimized[read]; - } - optimized.length = 0; - for (let index = 0; index < write; index++) { - optimized[index] = all[index]; - } - continue; - } - break; + node[PARENT] = null; + return true; +} +function trimWhiteSpaceTokens(tokens) { + let i = 0; + if (tokens[0]?.typ === exports.EnumToken.WhitespaceTokenType) { + tokens.shift(); } - for (let i1 = 0; i1 < selector.length; i1++) { - const all = new Array(Math.max(selector[i1].length - optimized.length, 0)); - let write = 0; - for (let read = optimized.length; read < selector[i1].length; read++) { - all[write++] = selector[i1][read]; - } - selector[i1].length = 0; - for (let index = 0; index < write; index++) { - selector[i1][index] = all[index]; + if (tokens[tokens.length - 1]?.typ === exports.EnumToken.WhitespaceTokenType) { + tokens.pop(); + } + if (tokens.length === 1) { + if (tokens[0].typ === exports.EnumToken.WhitespaceTokenType) { + tokens.length = 0; } + return tokens; } - let reducible = optimized.length == 1; - if (optimized[0] == "&") { - if (optimized[1] == " ") { - const all = new Array(Math.max(optimized.length - 2, 0)); - let write = 0; - for (let read = 2; read < optimized.length; read++) { - all[write++] = optimized[read]; + for (; i < tokens.length; i++) { + if (trimSpaceAfter.has(tokens[i].typ) && tokens[i + 1]?.typ === exports.EnumToken.WhitespaceTokenType) { + tokens.splice(i + 1, 1); + } + else if (trimTokenSpace.has(tokens[i].typ)) { + if (tokens[i + 1]?.typ === exports.EnumToken.WhitespaceTokenType) { + tokens.splice(i + 1, 1); } - optimized.length = 0; - for (let index = 0; index < write; index++) { - optimized[index] = all[index]; + if (i > 0 && tokens[i - 1].typ === exports.EnumToken.Whitespace) { + tokens.splice(--i, 1); } } } - if (optimized.length == 0 || optimized[0].charAt(0) == "&" || selector.length == 1) { - return { - match: false, - optimized, - selector: selector.map((selector) => selector[0] == "&" && selector[1] == " " ? selector.slice(2) : selector), - reducible: selector.length > 1 && selector.every((selector) => !combinators.includes(selector[0])), - }; - } - return { - match: true, - optimized, - selector: selector.reduce((acc, curr) => { - let hasCompound = true; - if (hasCompound && curr.length > 0) { - hasCompound = !["&"].concat(combinators).includes(curr[0].charAt(0)); - } - // @ts-ignore - if (hasCompound && curr[0] == " ") { - hasCompound = false; - curr.unshift("&"); - } - if (curr.length == 0) { - curr.push("&"); - hasCompound = false; - } - if (reducible) { - const chr = curr[0].charAt(0); - // @ts-ignore - reducible = chr == "." || chr == ":" || isIdentStart(chr.charCodeAt(0)); - } - acc.push(hasCompound ? ["&"].concat(curr) : curr); - return acc; - }, []), - reducible: selector.every((selector) => ![">", "+", "~", "&"].includes(selector[0])), - }; + return tokens; } -/** - * Split selector string - * @param buffer - * - * @internal - */ -function splitRule(buffer) { - const result = [[]]; - let str = ""; - for (let i = 0; i < buffer.length; i++) { - let chr = buffer.charAt(i); - if (isWhiteSpace(chr.charCodeAt(0))) { - if (str !== "") { - // @ts-ignore - result.at(-1).push(str); - str = ""; - } + +class ComputeCalcExpressionFeature { + accept = new Set([exports.EnumToken.RuleNodeType, exports.EnumToken.AtRuleNodeType]); + get ordering() { + return 3; + } + get processMode() { + return exports.FeatureWalkMode.Post; + } + static register(options) { + if (options.computeCalcExpression) { // @ts-ignore - if (result.at(-1).length > 0) { - // @ts-ignore - result.at(-1).push(" "); - } - // i = k; - continue; + options.features.push(new ComputeCalcExpressionFeature()); } - if (chr == ",") { - if (str !== "") { - result.at(-1).push(str); - str = ""; + } + run(ast) { + // @ts-ignore + if (ast.chi == null) { + return null; + } + for (const node of ast.chi) { + if (node.typ != exports.EnumToken.DeclarationNodeType) { + continue; + } + const set = new Set(); + for (const { value, parent } of walkValues(node.val, node)) { + if (parent?.typ == exports.EnumToken.BinaryExpressionTokenType) { + continue; + } + if (value.typ == exports.EnumToken.BinaryExpressionTokenType) { + const result = evaluate([value]); + try { + // @ts-ignore + replaceNodeOrValue(parent, value, result); + } + catch (e) { } + continue; + } + if (value != null && tokensfuncSet.has(value.typ)) { + if (!set.has(value)) { + set.add(value); + if (parent != null) { + const shouldEvaluate = value.typ === exports.EnumToken.MathFunctionTokenType || + (value.typ == exports.EnumToken.FunctionTokenType && + mathFuncs.includes(value.val)); + if (shouldEvaluate) { + // @ts-ignore + const cp = value.val != "calc" + ? [value] + : value.typ == exports.EnumToken.DeclarationNodeType + ? value.val + : value.chi; + const values = evaluate(cp); + // fix a + -b into a - b + for (const { value, parent: p } of walkValues(values)) { + if (value.typ === exports.EnumToken.BinaryExpressionTokenType) { + if (value.op === exports.EnumToken.Add && + Math.sign(value.r.val) == -1) { + value.op = exports.EnumToken.Sub; + // @ts-expect-error + value.r.val *= + -1; + } + } + } + // @ts-ignore + const children = parent.typ == exports.EnumToken.DeclarationNodeType + ? parent.val + : // @ts-ignore + parent.chi; + if (values.length == 1 && values[0].typ != exports.EnumToken.BinaryExpressionTokenType) { + for (let i = 0; i < children.length; i++) { + if (children[i] == value) { + children.splice(i, 1, !(parent.typ === exports.EnumToken.MathFunctionTokenType || + (parent.typ == exports.EnumToken.FunctionTokenType && + parent.val == "calc")) && + typeof values[0].val != "number" && + !(values[0].typ == exports.EnumToken.MathFunctionTokenType || + (values[0].typ == exports.EnumToken.FunctionTokenType && + mathFuncs.includes(values[0].val))) + ? { + typ: exports.EnumToken.MathFunctionTokenType, + val: "calc", + chi: values, + [LOCSRCID]: value[LOCSRCID], + [LOCSTA]: value[LOCSTA], + [LOCEND]: value[LOCEND], + } + : values[0]); + break; + } + } + } + else { + for (let i = 0; i < children.length; i++) { + if (children[i] == value) { + children.splice(i, 1, { + typ: exports.EnumToken.MathFunctionTokenType, + val: "calc", + chi: values, + [LOCSRCID]: value[LOCSRCID], + [LOCSTA]: value[LOCSTA], + [LOCEND]: value[LOCEND], + }); + break; + } + } + } + } + } + } + } } - result.push([]); - continue; } - if (chr == ".") { - if (str !== "") { - result.at(-1).push(str); - str = ""; + return null; + } +} + +const identityMatrix = new Float64Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); +function identity() { + return identityMatrix.slice(); +} +function normalize(point) { + const [x, y, z] = point; + const norm = Math.sqrt(point[0] * point[0] + point[1] * point[1] + point[2] * point[2]); + return norm === 0 ? [0, 0, 0] : [x / norm, y / norm, z / norm]; +} +function dot(point1, point2) { + if (point1.length === 4 && point2.length === 4) { + return point1[0] * point2[0] + point1[1] * point2[1] + point1[2] * point2[2] + point1[3] * point2[3]; + } + return point1[0] * point2[0] + point1[1] * point2[1] + point1[2] * point2[2]; +} +function multiply(matrixA, matrixB) { + const result = new Float64Array(16); + result[0] = matrixA[0] * matrixB[0] + matrixA[4] * matrixB[1] + matrixA[8] * matrixB[2] + matrixA[12] * matrixB[3]; + result[1] = matrixA[1] * matrixB[0] + matrixA[5] * matrixB[1] + matrixA[9] * matrixB[2] + matrixA[13] * matrixB[3]; + result[2] = matrixA[2] * matrixB[0] + matrixA[6] * matrixB[1] + matrixA[10] * matrixB[2] + matrixA[14] * matrixB[3]; + result[3] = matrixA[3] * matrixB[0] + matrixA[7] * matrixB[1] + matrixA[11] * matrixB[2] + matrixA[15] * matrixB[3]; + result[4] = matrixA[0] * matrixB[4] + matrixA[4] * matrixB[5] + matrixA[8] * matrixB[6] + matrixA[12] * matrixB[7]; + result[5] = matrixA[1] * matrixB[4] + matrixA[5] * matrixB[5] + matrixA[9] * matrixB[6] + matrixA[13] * matrixB[7]; + result[6] = matrixA[2] * matrixB[4] + matrixA[6] * matrixB[5] + matrixA[10] * matrixB[6] + matrixA[14] * matrixB[7]; + result[7] = matrixA[3] * matrixB[4] + matrixA[7] * matrixB[5] + matrixA[11] * matrixB[6] + matrixA[15] * matrixB[7]; + result[8] = + matrixA[0] * matrixB[8] + matrixA[4] * matrixB[9] + matrixA[8] * matrixB[10] + matrixA[12] * matrixB[11]; + result[9] = + matrixA[1] * matrixB[8] + matrixA[5] * matrixB[9] + matrixA[9] * matrixB[10] + matrixA[13] * matrixB[11]; + result[10] = + matrixA[2] * matrixB[8] + matrixA[6] * matrixB[9] + matrixA[10] * matrixB[10] + matrixA[14] * matrixB[11]; + result[11] = + matrixA[3] * matrixB[8] + matrixA[7] * matrixB[9] + matrixA[11] * matrixB[10] + matrixA[15] * matrixB[11]; + result[12] = + matrixA[0] * matrixB[12] + matrixA[4] * matrixB[13] + matrixA[8] * matrixB[14] + matrixA[12] * matrixB[15]; + result[13] = + matrixA[1] * matrixB[12] + matrixA[5] * matrixB[13] + matrixA[9] * matrixB[14] + matrixA[13] * matrixB[15]; + result[14] = + matrixA[2] * matrixB[12] + matrixA[6] * matrixB[13] + matrixA[10] * matrixB[14] + matrixA[14] * matrixB[15]; + result[15] = + matrixA[3] * matrixB[12] + matrixA[7] * matrixB[13] + matrixA[11] * matrixB[14] + matrixA[15] * matrixB[15]; + return result; +} +function inverse(matrix) { + // Create augmented matrix [matrix | identity] + let augmented = [ + matrix[0], + matrix[1], + matrix[2], + matrix[3], + 1, + 0, + 0, + 0, + matrix[4], + matrix[5], + matrix[6], + matrix[7], + 0, + 1, + 0, + 0, + matrix[8], + matrix[9], + matrix[10], + matrix[11], + 0, + 0, + 1, + 0, + matrix[12], + matrix[13], + matrix[14], + matrix[15], + 0, + 0, + 0, + 1, + ]; + // Gaussian elimination with partial pivoting + for (let col = 0; col < 4; col++) { + // Find pivot row with maximum absolute value + let maxRow = col; + let maxVal = Math.abs(augmented[col * 4 + col]); + for (let row = col + 1; row < 4; row++) { + let val = Math.abs(augmented[row * 4 + col]); + if (val > maxVal) { + maxVal = val; + maxRow = row; } - str += chr; - continue; } - if (combinators.includes(chr)) { - if (str !== "") { - result.at(-1).push(str); - str = ""; - } - if (chr == "|" && buffer.charAt(i + 1) == "|") { - chr += buffer.charAt(++i); - } - result.at(-1).push(chr); - continue; + // Check for singularity + if (maxVal < epsilon) { + return null; } - if (chr == ":") { - if (str !== "") { - result.at(-1).push(str); - str = ""; - } - if (buffer.charAt(i + 1) == ":") { - chr += buffer.charAt(++i); - } - str += chr; - continue; + // Swap rows if necessary + if (maxRow !== col) { + [augmented[col], augmented[maxRow]] = [augmented[maxRow], augmented[col]]; } - str += chr; - if (chr == "\\") { - str += buffer.charAt(++i); - continue; + // Scale pivot row to make pivot element 1 + let pivot = augmented[col * 4 + col]; + for (let j = 0; j < 8; j++) { + augmented[col * 4 + j] /= pivot; } - if (chr == "(" || chr == "[") { - const open = chr; - const close = chr == "(" ? ")" : "]"; - let inParens = 1; - let k = i; - while (++k < buffer.length) { - chr = buffer.charAt(k); - if (chr == "\\") { - str += buffer.slice(k, k + 2); - k++; - continue; - } - str += chr; - if (chr == open) { - inParens++; - } - else if (chr == close) { - inParens--; - } - if (inParens == 0) { - break; + // Eliminate column in other rows + for (let row = 0; row < 4; row++) { + if (row !== col) { + let factor = augmented[row * 4 + col]; + for (let j = 0; j < 8; j++) { + augmented[row * 4 + j] -= factor * augmented[col * 4 + j]; } } - i = k; } } - if (str !== "") { - result.at(-1).push(str); - } - return result; + // Extract the inverse from the right side of the augmented matrix + return augmented.slice(0, 16); } -/** - * Reduce selector - * @param acc - * @param curr - * - * @private - */ -function reduceSelector(acc, curr) { - let hasCompoundSelector = true; - // @ts-ignore - curr = curr.slice(this.match[0].length); - while (curr.length > 0) { - if (curr[0] == " ") { - hasCompoundSelector = false; - curr.unshift("&"); - continue; +function round(number) { + const rounded = Math.round(number); + return Math.abs(rounded - number) <= epsilon ? rounded : +number.toPrecision(6); +} +// translate3d(25.9808px, 0, 15px ) rotateY(60deg) skewX(49.9999deg) scale(1, 1.2) +// translate → rotate → skew → scale +function decompose(original) { + const matrix = original.slice(); + // Normalize last row + if (matrix[15] === 0) { + return null; + } + for (let i = 0; i < 16; i++) + matrix[i] /= matrix[15]; + // Perspective extraction + const perspective = [0, 0, 0, 1]; + if (matrix[3] !== 0 || matrix[7] !== 0 || matrix[11] !== 0) { + const rightHandSide = [matrix[3], matrix[7], matrix[11], matrix[15]]; + const perspectiveMatrix = matrix.slice(); + perspectiveMatrix[3] = 0; + perspectiveMatrix[7] = 0; + perspectiveMatrix[11] = 0; + perspectiveMatrix[15] = 1; + // @ts-ignore + const inverted = inverse(original.slice()); + if (inverted === null) { + return null; } - break; + const transposedInverse = transposeMatrix4(inverted); + perspective[0] = dot(rightHandSide, transposedInverse.slice(0, 4)); + perspective[1] = dot(rightHandSide, transposedInverse.slice(4, 8)); + perspective[2] = dot(rightHandSide, transposedInverse.slice(8, 12)); + perspective[3] = dot(rightHandSide, transposedInverse.slice(12, 16)); + // Clear perspective from matrix + matrix[3] = 0; + matrix[7] = 0; + matrix[11] = 0; + matrix[15] = 1; } - if (hasCompoundSelector && curr.length > 0) { - hasCompoundSelector = !["&"].concat(combinators).includes(curr[0].charAt(0)); + // Translation + const translate = [matrix[12], matrix[13], matrix[14]]; + matrix[12] = matrix[13] = matrix[14] = 0; + // Build the 3x3 matrix + const row0 = [matrix[0], matrix[1], matrix[2]]; + const row1 = [matrix[4], matrix[5], matrix[6]]; + const row2 = [matrix[8], matrix[9], matrix[10]]; + const cross = [ + row1[1] * row2[2] - row1[2] * row2[1], + row1[2] * row2[0] - row1[0] * row2[2], + row1[0] * row2[1] - row1[1] * row2[0], + ]; + // Compute scale + const scaleX = Math.hypot(row0[0], row0[1], row0[2]); + const row0Norm = normalize(row0); + const skewXY = dot(row0Norm, row1); + const row1Proj = [row1[0] - skewXY * row0Norm[0], row1[1] - skewXY * row0Norm[1], row1[2] - skewXY * row0Norm[2]]; + const scaleY = Math.hypot(row1Proj[0], row1Proj[1], row1Proj[2]); + const row1Norm = normalize(row1Proj); + const skewXZ = dot(row0Norm, row2); + const skewYZ = dot(row1Norm, row2); + const row2Proj = [ + row2[0] - skewXZ * row0Norm[0] - skewYZ * row1Norm[0], + row2[1] - skewXZ * row0Norm[1] - skewYZ * row1Norm[1], + row2[2] - skewXZ * row0Norm[2] - skewYZ * row1Norm[2], + ]; + const row2Norm = normalize(row2Proj); + const determinant = row0[0] * cross[0] + row0[1] * cross[1] + row0[2] * cross[2]; + const scaleZ = Math.hypot(row2Proj[0], row2Proj[1], row2Proj[2]) * (determinant < 0 ? -1 : 1); + // Build rotation matrix from orthonormalized vectors + const r00 = row0Norm[0], r01 = row1Norm[0], r02 = row2Norm[0]; + const r10 = row0Norm[1], r11 = row1Norm[1], r12 = row2Norm[1]; + const r20 = row0Norm[2], r21 = row1Norm[2], r22 = row2Norm[2]; + // Convert to quaternion + const trace = r00 + r11 + r22; + let qw, qx, qy, qz; + if (trace > 0) { + const s = 0.5 / Math.sqrt(trace + 1.0); + qw = 0.25 / s; + qx = (r21 - r12) * s; + qy = (r02 - r20) * s; + qz = (r10 - r01) * s; } - if (curr[0] == ":is(") { - let canReduce = true; - const isCompound = curr.reduce((acc, token, index) => { - if (index == 0) { - canReduce = curr[1] == "&"; - } - else if (token == ")") ; - else if (token == ",") { - if (!canReduce) { - canReduce = curr[index + 1] == "&"; - } - acc.push([]); - } - else - acc.at(-1)?.push(token); - return acc; - }, [[]]); - if (canReduce) { - curr = isCompound.reduce((acc, curr) => { - if (acc.length > 0) { - acc.push(","); - } - for (const c of curr) { - acc.push(c); - } - return acc; - }, []); - } + else if (r00 > r11 && r00 > r22) { + const s = 2.0 * Math.sqrt(1.0 + r00 - r11 - r22); + qw = (r21 - r12) / s; + qx = 0.25 * s; + qy = (r01 + r10) / s; + qz = (r02 + r20) / s; } - acc.push( - // @ts-ignore - this.match.length == 0 - ? ["&"] - : hasCompoundSelector && curr[0] != "&" && (curr.length == 0 || !combinators.includes(curr[0].charAt(0))) - ? ["&"].concat(curr) - : curr); - return acc; -} -/** - * Match selectors - * @param selector1 - * @param selector2 - * - * @private - */ -function matchSelectors(selector1, selector2) { - let match = [[]]; - const j = Math.min(selector1.reduce((acc, curr) => Math.min(acc, curr.length), selector1.length > 0 ? selector1[0].length : 0), selector2.reduce((acc, curr) => Math.min(acc, curr.length), selector2.length > 0 ? selector2[0].length : 0)); - let i = 0; - let k; - let l; - let token; - let matching = true; - let matchFunction = 0; - let inAttr = 0; - const regEx = /^:is\(([:.][^\s,]+)\)$/; - for (const _1 of selector1) { - if (_1[0] !== "&") { - continue; - } - for (let i = 1; i < _1.length; i++) { - const token = _1[i]; - if (token.startsWith(":is(")) { - const match = regEx.exec(token); - if (match != null) { - _1[i] = match[1]; - } - } - } + else if (r11 > r22) { + const s = 2.0 * Math.sqrt(1.0 + r11 - r00 - r22); + qw = (r02 - r20) / s; + qx = (r01 + r10) / s; + qy = 0.25 * s; + qz = (r12 + r21) / s; } - for (const _1 of selector2) { - if (_1[0] !== "&") { - continue; - } - for (let i = 1; i < _1.length; i++) { - const token = _1[i]; - if (token.startsWith(":is(")) { - const match = regEx.exec(token); - if (match != null) { - _1[i] = match[1]; - } - } - } + else { + const s = 2.0 * Math.sqrt(1.0 + r22 - r00 - r11); + qw = (r10 - r01) / s; + qx = (r02 + r20) / s; + qy = (r12 + r21) / s; + qz = 0.25 * s; } - for (; i < j; i++) { - k = 0; - token = selector1[0][i]; - for (; k < selector1.length; k++) { - if (selector1[k][i] != token) { - matching = false; - break; - } - } - if (matching) { - l = 0; - for (; l < selector2.length; l++) { - if (selector2[l][i] != token) { - matching = false; - break; - } - } + [qx, qy, qz] = toZero([qx, qy, qz]); + let q = [Math.abs(qx), Math.abs(qy), Math.abs(qz)].reduce((acc, curr) => { + if (acc == 0 || (curr > 0 && curr < acc)) { + acc = curr; } - if (!matching) { - break; + return acc; + }, 0); + if (q > 0) { + qx /= q; + qy /= q; + qz /= q; + } + const rotate = [ + qx, + qy, + qz, + Object.is(qw, 0) ? 0 : (2 * Math.acos(qw) * 180) / Math.PI, + ]; + const scale = [scaleX, scaleY, scaleZ]; + const skew = [skewXY, skewXZ, skewYZ]; + return { + translate, + scale, + rotate, + skew, + perspective, + }; +} +function transposeMatrix4(m) { + return [m[0], m[4], m[8], m[12], m[1], m[5], m[9], m[13], m[2], m[6], m[10], m[14], m[3], m[7], m[11], m[15]]; +} +function toZero(v) { + for (let i = 0; i < v.length; i++) { + if (Math.abs(v[i]) <= epsilon) { + v[i] = 0; } - if (token.endsWith("(")) { - matchFunction++; + else { + v[i] = +v[i].toPrecision(6); } - match.at(-1).push(token); } - // invalid function - if (matchFunction != 0 || inAttr != 0) { - return null; + return v; +} +// https://drafts.csswg.org/css-transforms-1/#2d-matrix +function is2DMatrix(matrix) { + // m13,m14, m23, m24, m31, m32, m34, m43 are all 0 + return (matrix[2] === 0 && + matrix[3] === 0 && + matrix[6] === 0 && + matrix[7] === 0 && + matrix[8] === 0 && + matrix[9] === 0 && + matrix[11] === 0 && + matrix[14] === 0 && + matrix[10] === 1 && + matrix[15] === 1); +} + +function translateX(x, from) { + const matrix = identity(); + matrix[3 * 4] = x; + return multiply(from, matrix); +} +function translateY(y, from) { + const matrix = identity(); + matrix[3 * 4 + 1] = y; + return multiply(from, matrix); +} +function translateZ(z, from) { + const matrix = identity(); + matrix[3 * 4 + 2] = z; + return multiply(from, matrix); +} +function translate(translate, from) { + const matrix = identity(); + matrix[3 * 4] = translate[0]; + matrix[3 * 4 + 1] = translate[1] ?? 0; + return multiply(from, matrix); +} +function translate3d(translate, from) { + const matrix = identity(); + matrix[3 * 4] = translate[0]; + matrix[3 * 4 + 1] = translate[1]; + matrix[3 * 4 + 2] = translate[2]; + return multiply(from, matrix); +} + +/** + * angle in radian + * @param angle + * @param x + * @param y + * @param z + * @param from + */ +function rotate3D(angle, x, y, z, from) { + const matrix = identity(); + const sc = Math.sin(angle / 2) * Math.cos(angle / 2); + const sq = Math.sin(angle / 2) * Math.sin(angle / 2); + const norm = Math.sqrt(x * x + y * y + z * z); + const unit = norm === 0 ? 0 : 1 / norm; + x *= unit; + y *= unit; + z *= unit; + matrix[0] = 1 - 2 * (y * y + z * z) * sq; + matrix[1] = 2 * (x * y * sq + z * sc); + matrix[2] = 2 * (x * z * sq - y * sc); + matrix[4] = 2 * (x * y * sq - z * sc); + matrix[4 + 1] = 1 - 2 * (x * x + z * z) * sq; + matrix[4 + 2] = 2 * (y * z * sq + x * sc); + matrix[2 * 4] = 2 * (x * z * sq + y * sc); + matrix[2 * 4 + 1] = 2 * (y * z * sq - x * sc); + matrix[2 * 4 + 2] = 1 - 2 * (x * x + y * y) * sq; + return multiply(from, matrix); +} +function rotate(angle, from) { + const matrix = identity(); + matrix[0] = Math.cos(angle); + matrix[1] = Math.sin(angle); + matrix[4] = -Math.sin(angle); + matrix[4 + 1] = Math.cos(angle); + return multiply(from, matrix); +} + +function scaleX(x, from) { + const matrix = identity(); + matrix[0] = x; + return multiply(from, matrix); +} +function scaleY(y, from) { + const matrix = identity(); + matrix[4 + 1] = y; + return multiply(from, matrix); +} +function scaleZ(z, from) { + const matrix = identity(); + matrix[2 * 4 + 2] = z; + return multiply(from, matrix); +} +function scale(x, y, from) { + const matrix = identity(); + matrix[0] = x; + matrix[4 + 1] = y; + return multiply(from, matrix); +} +function scale3d(x, y, z, from) { + const matrix = identity(); + matrix[0] = x; + matrix[4 + 1] = y; + matrix[2 * 4 + 2] = z; + return multiply(from, matrix); +} + +function parseMatrix(mat) { + if (mat.typ == exports.EnumToken.IdenTokenType) { + return mat.val == "none" ? identity() : null; } - for (const part of match) { - while (part.length > 0) { - const token = part.at(-1); - if (token == " " || combinators.includes(token) || notEndingWith.includes(token.at(-1))) { - part.pop(); - continue; - } - break; + const children = mat.chi.filter((t) => t.typ == exports.EnumToken.NumberTokenType || t.typ == exports.EnumToken.IdenTokenType); + const values = []; + for (const child of children) { + if (child.typ != exports.EnumToken.NumberTokenType) { + return null; } + // @ts-ignore + values.push(getNumber(child)); } - if (match.every((t) => t.length == 0)) { - return null; + // @ts-ignore + return matrix(values); +} +// use column-major order +function matrix(values) { + const matrix = identity(); + if (values.length === 6) { + matrix[0] = values[0]; + matrix[1] = values[1]; + matrix[4] = values[2]; + matrix[4 + 1] = values[3]; + matrix[3 * 4] = values[4]; + matrix[3 * 4 + 1] = values[5]; + } + else if (values.length === 16) { + matrix[0] = values[0]; + matrix[1] = values[1]; + matrix[2] = values[2]; + matrix[3] = values[3]; + matrix[4] = values[4]; + matrix[4 + 1] = values[5]; + matrix[4 + 2] = values[6]; + matrix[4 + 3] = values[7]; + matrix[2 * 4] = values[8]; + matrix[2 * 4 + 1] = values[9]; + matrix[2 * 4 + 2] = values[10]; + matrix[2 * 4 + 3] = values[11]; + matrix[3 * 4] = values[12]; + matrix[3 * 4 + 1] = values[13]; + matrix[3 * 4 + 2] = values[14]; + matrix[3 * 4 + 3] = values[15]; } - if (eq([["&"]], match)) { + else { return null; } - const reducer = reduceSelector.bind({ match }); - // @ts-ignore - selector1 = selector1.reduce(reducer, []); + return matrix; +} +function serialize(matrix) { + matrix = matrix.slice(); // @ts-ignore - selector2 = selector2.reduce(reducer, []); - return selector1 == null || selector2 == null - ? null - : { - eq: eq(selector1, selector2), - match, - selector1, - selector2, + if (eq(matrix, identity())) { + return { + typ: exports.EnumToken.IdenTokenType, + val: "none", }; -} -/** - * Fix selector - * @param node - * - * @private - */ -function fixSelector(node) { - if (node.sel.includes("&")) { - const attributes = parseString(node.sel); - for (const attr of walkValues(attributes)) { - if (attr.value.typ == exports.EnumToken.PseudoClassFuncTokenType && - attr.value.val == ":is") { - let i = attr.value.chi.length; - while (i--) { - if (attr.value.chi[i].typ == exports.EnumToken.NestingSelectorTokenType) { - attr.value.chi.splice(i, 1); - } + } + if (is2DMatrix(matrix)) { + // https://drafts.csswg.org/css-transforms-2/#two-dimensional-subset + return { + typ: exports.EnumToken.TransformFunctionTokenType, + val: "matrix", + chi: [matrix[0], matrix[1], matrix[4], matrix[4 + 1], matrix[3 * 4], matrix[3 * 4 + 1]].reduce((acc, t) => { + if (acc.length > 0) { + acc.push({ typ: exports.EnumToken.CommaTokenType }); } - } - } - node.sel = attributes.reduce((acc, curr) => acc + renderValue(curr), ""); - node[TOKENS] = null; + acc.push({ + typ: exports.EnumToken.NumberTokenType, + val: t, + }); + return acc; + }, []), + }; } -} -/** - * Wrap nodes - * @param previous - * @param node - * @param match - * @param ast - * @param reducer - * @param i - * @param nodeIndex - * - * @private - */ -function wrapNodes(previous, node, match, ast, reducer, i, nodeIndex) { - // @ts-ignore - let pSel = match.selector1.reduce(reducer, []).join(","); - // @ts-ignore - let nSel = match.selector2.reduce(reducer, []).join(","); - const wrapper = { - ...previous, - chi: [], - // @ts-ignore - sel: match.match.reduce(reducer, []).join(","), - [RAW]: match.match.map((t) => t.slice()), - }; - if (pSel == "&" || pSel === "") { - for (const child of previous.chi) { - wrapper.chi.push(child); - } - if (nSel == "&" || nSel === "") { - for (const child of node.chi) { - wrapper.chi.push(child); + return { + typ: exports.EnumToken.TransformFunctionTokenType, + val: "matrix3d", + chi: matrix.reduce((acc, curr) => { + if (acc.length > 0) { + acc.push({ typ: exports.EnumToken.CommaTokenType }); } - } - else { - wrapper.chi.push(node); - } + acc.push({ + typ: exports.EnumToken.NumberTokenType, + val: curr, + }); + return acc; + }, []), + }; +} + +// translate → rotate → skew → scale +function minify$1(matrix) { + const decomposed = decompose(matrix); + if (decomposed == null) { + return null; } - else { - wrapper.chi.push(previous, node); + const transforms = new Set(["translate", "scale", "skew", "perspective", "rotate"]); + const scales = new Set(["x", "y", "z"]); + const skew = new Set(["x", "y"]); + let result = []; + // check identity + if (round(decomposed.translate[0]) == 0 && + round(decomposed.translate[1]) == 0 && + round(decomposed.translate[2]) == 0) { + transforms.delete("translate"); } - ast.chi.splice(i, 1, wrapper); - ast.chi.splice(nodeIndex, 1); - previous.sel = pSel; - previous[RAW] = match.selector1; - previous[TOKENS] = null; - node.sel = nSel; - node[RAW] = match.selector2; - node[TOKENS] = null; - reduceRuleSelector(wrapper); - wrapper[TOKENS] = null; - return wrapper; -} -/** - * Diff nodes - * @param n1 - * @param n2 - * @param options - * - * @private - */ -function diff$1(n1, n2, options = {}) { - if (!("cache" in options)) { - options.cache = new WeakMap(); + if (round(decomposed.scale[0]) == 1 && round(decomposed.scale[1]) == 1 && round(decomposed.scale[2]) == 1) { + transforms.delete("scale"); } - let node1 = n1; - let node2 = n2; - let exchanged = false; - if (node1.chi.length > node2.chi.length) { - const t = node1; - node1 = node2; - node2 = t; - exchanged = true; + if (round(decomposed.skew[0]) == 0 && round(decomposed.skew[1]) == 0) { + transforms.delete("skew"); } - let i = node1.chi.length; - let j = node2.chi.length; - const raw1 = node1[RAW]; - const raw2 = node2[RAW]; - if (raw1 != null && raw2 != null) { - const prefixes1 = new Set(); - const prefixes2 = new Set(); - for (const token1 of raw1) { - for (const t of token1) { - if (t.includes(":")) { - const matches = t.match(/::?-([a-z]+)-/); - if (matches == null) { - continue; - } - prefixes1.add(matches[1]); - if (prefixes1.size > 1) { - break; - } - } - } - if (prefixes1.size > 1) { - break; + if (round(decomposed.perspective[2]) == 0) { + transforms.delete("perspective"); + } + if (round(decomposed.rotate[3]) == 0) { + transforms.delete("rotate"); + } + if (transforms.has("translate")) { + let coordinates = new Set(["x", "y", "z"]); + for (let i = 0; i < 3; i++) { + if (round(decomposed.translate[i]) == 0) { + coordinates.delete(i == 0 ? "x" : i == 1 ? "y" : "z"); } } - for (const token2 of raw2) { - for (const t of token2) { - if (t.includes(":")) { - const matches = t.match(/::?-([a-z]+)-/); - if (matches == null) { - continue; - } - prefixes2.add(matches[1]); - if (prefixes2.size > 1) { - break; - } - } + if (coordinates.size == 1) { + if (coordinates.has("x")) { + result.push({ + typ: exports.EnumToken.TransformFunctionTokenType, + val: "translate", + chi: [{ typ: exports.EnumToken.LengthTokenType, val: round(decomposed.translate[0]), unit: "px" }], + }); } - if (prefixes2.size > 1) { - break; + else { + let axis = coordinates.has("y") ? "y" : "z"; + let index = axis == "y" ? 1 : 2; + result.push({ + typ: exports.EnumToken.TransformFunctionTokenType, + val: "translate" + axis.toUpperCase(), + chi: [{ typ: exports.EnumToken.LengthTokenType, val: round(decomposed.translate[index]), unit: "px" }], + }); } } - if (prefixes1.size != prefixes2.size) { - return null; + else if (coordinates.has("z")) { + result.push({ + typ: exports.EnumToken.TransformFunctionTokenType, + val: "translate3d", + chi: [ + decomposed.translate[0] == 0 + ? { + typ: exports.EnumToken.NumberTokenType, + val: 0, + } + : { typ: exports.EnumToken.LengthTokenType, val: round(decomposed.translate[0]), unit: "px" }, + { typ: exports.EnumToken.CommaTokenType }, + decomposed.translate[1] == 0 + ? { + typ: exports.EnumToken.NumberTokenType, + val: 0, + } + : { typ: exports.EnumToken.LengthTokenType, val: round(decomposed.translate[1]), unit: "px" }, + { typ: exports.EnumToken.CommaTokenType }, + { typ: exports.EnumToken.LengthTokenType, val: round(decomposed.translate[2]), unit: "px" }, + ], + }); } - for (const prefix of prefixes1) { - if (!prefixes2.has(prefix)) { - return null; - } + else { + result.push({ + typ: exports.EnumToken.TransformFunctionTokenType, + val: "translate", + chi: [ + { typ: exports.EnumToken.LengthTokenType, val: round(decomposed.translate[0]), unit: "px" }, + { typ: exports.EnumToken.CommaTokenType }, + { typ: exports.EnumToken.LengthTokenType, val: round(decomposed.translate[1]), unit: "px" }, + ], + }); } } - const css1 = options.cache.get(node1); - const css2 = options.cache.get(node2); - node1 = { ...node1, chi: node1.chi.slice() }; - node2 = { ...node2, chi: node2.chi.slice() }; - if (css1 != null) { - options.cache.set(node1, css1); - } - if (css2 != null) { - options.cache.set(node2, css2); - } - if (raw1 != null) { - node1[RAW] = raw1; - } - if (raw2 != null) { - node2[RAW] = raw2; + if (transforms.has("rotate")) { + const [x, y, z, angle] = decomposed.rotate; + if (y == 0 && z == 0) { + result.push({ + typ: exports.EnumToken.TransformFunctionTokenType, + val: "rotateX", + chi: [ + { + typ: exports.EnumToken.AngleTokenType, + val: round(angle), + unit: "deg", + }, + ], + }); + } + else if (x == 0 && z == 0) { + result.push({ + typ: exports.EnumToken.TransformFunctionTokenType, + val: "rotateY", + chi: [ + { + typ: exports.EnumToken.AngleTokenType, + val: round(angle), + unit: "deg", + }, + ], + }); + } + else if (x == 0 && y == 0) { + result.push({ + typ: exports.EnumToken.TransformFunctionTokenType, + val: "rotate", + chi: [ + { + typ: exports.EnumToken.AngleTokenType, + val: round(angle), + unit: "deg", + }, + ], + }); + } + else { + result.push({ + typ: exports.EnumToken.TransformFunctionTokenType, + val: "rotate3d", + chi: [ + { + typ: exports.EnumToken.NumberTokenType, + val: round(x), + }, + { typ: exports.EnumToken.CommaTokenType }, + { + typ: exports.EnumToken.NumberTokenType, + val: round(y), + }, + { typ: exports.EnumToken.CommaTokenType }, + { + typ: exports.EnumToken.NumberTokenType, + val: round(z), + }, + { typ: exports.EnumToken.CommaTokenType }, + { + typ: exports.EnumToken.AngleTokenType, + val: round(angle), + unit: "deg", + }, + ], + }); + } } - const intersect = []; - while (i--) { - if (node1.chi[i].typ == exports.EnumToken.CommentNodeType) { - continue; + if (transforms.has("skew")) { + if (round(decomposed.skew[1]) == 0) { + skew.delete("y"); } - j = node2.chi.length; - while (j--) { - if (node2.chi[j].typ == exports.EnumToken.CommentNodeType) { - continue; - } - if (node1.chi[i].nam == node2.chi[j].nam) { - if (node1.chi[i].typ == node2.chi[j].typ && eq(node1.chi[i], node2.chi[j])) { - intersect.push(node1.chi[i]); - node1.chi.splice(i, 1); - node2.chi.splice(j, 1); - options.cache.delete(node1); - options.cache.delete(node2); - break; - } - } + for (let i = 0; i < 2; i++) { + decomposed.skew[i] = round((Math.atan(decomposed.skew[i]) * 180) / Math.PI); + } + if (skew.size == 1) { + result.push({ + typ: exports.EnumToken.TransformFunctionTokenType, + val: "skew" + (skew.has("x") ? "" : "Y"), + chi: [{ typ: exports.EnumToken.AngleTokenType, val: round(decomposed.skew[0]), unit: "deg" }], + }); + } + else { + result.push({ + typ: exports.EnumToken.TransformFunctionTokenType, + val: "skew", + chi: [ + { typ: exports.EnumToken.AngleTokenType, val: round(decomposed.skew[0]), unit: "deg" }, + { typ: exports.EnumToken.CommaTokenType }, + { typ: exports.EnumToken.AngleTokenType, val: round(decomposed.skew[1]), unit: "deg" }, + ], + }); } } - const result = intersect.length === 0 && (node1.chi.length > 0 || node2.chi.length > 0) - ? null - : { - ...node1, - // @ts-ignore - sel: [ - ...new Set(splitRule(node1.sel) - .concat(splitRule(node2.sel)) - .map((s) => s.join(""))), - ].join(","), - // @ts-ignore - chi: intersect.reverse(), - }; - let op = { level: 0, ...options }; - if (result == null || - [n1, n2].reduce((acc, curr) => { - let css = options.cache.get(curr); - if (css == null) { - let level = 0; - let parent = curr[PARENT]; - while (parent != null && parent.typ != exports.EnumToken.StyleSheetNodeType) { - level++; - parent = parent[PARENT]; - } - op.level = level; - css = doRender(curr, op).code; - options.cache.set(curr, css); - } - return curr.chi.length == 0 ? acc : acc + css.length; - }, 0) <= - [node1, node2, result].reduce((acc, curr) => { - let css = options.cache.get(curr); - if (css != null) { - return curr.chi.length == 0 ? acc : acc + css.length; - } - let level = 0; - let parent = curr[PARENT]; - while (parent != null && parent.typ != exports.EnumToken.StyleSheetNodeType) { - level++; - parent = parent[PARENT]; - } - op.level = level; - css = doRender(curr, op).code; - return curr.chi.length == 0 ? acc : acc + css.length; - }, 0)) { - if (node1.chi.length != 0 && node2.chi.length != 0) { - return null; + if (transforms.has("scale")) { + const [sx, sy, sz] = toZero(decomposed.scale); + if (sz == 1) { + scales.delete("z"); + } + if (sy == 1) { + scales.delete("y"); + } + if (sx == 1) { + scales.delete("x"); + } + if (scales.size == 1) { + let prefix = scales.has("x") ? "X" : scales.has("y") ? "Y" : "Z"; + result.push({ + typ: exports.EnumToken.TransformFunctionTokenType, + val: "scale" + prefix, + chi: [{ typ: exports.EnumToken.NumberTokenType, val: round(prefix == "Z" ? sz : prefix == "Y" ? sy : sx) }], + }); + } + else if (!scales.has("z")) { + result.push({ + typ: exports.EnumToken.TransformFunctionTokenType, + val: "scale", + chi: [ + { typ: exports.EnumToken.NumberTokenType, val: round(sx) }, + { typ: exports.EnumToken.CommaTokenType }, + { typ: exports.EnumToken.NumberTokenType, val: round(sy) }, + ], + }); + } + else { + result.push({ + typ: exports.EnumToken.TransformFunctionTokenType, + val: "scale3d", + chi: [ + { typ: exports.EnumToken.NumberTokenType, val: round(sx) }, + { typ: exports.EnumToken.CommaTokenType }, + { typ: exports.EnumToken.NumberTokenType, val: round(sy) }, + { typ: exports.EnumToken.CommaTokenType }, + { typ: exports.EnumToken.NumberTokenType, val: round(sz) }, + ], + }); } } - if (result != null) { - result[TOKENS] = null; - result[RAW] = null; - const optimized = optimizeSelector(splitRule(result.sel)); - if (optimized?.match) { - const rule = optimized.selector.reduce((acc, curr) => { - if (acc.length > 0) { - acc += ","; - } - if (curr.length > 2 && curr[0] === "&" && curr[1] === " ") { - return acc + curr.slice(2).join(""); - } - else if (curr.length > 1 && curr[0] === "&") { - return acc + curr.slice(1).join(""); - } - return acc + curr.join(""); - }, ""); - const match = optimized.optimized.join(""); - const sel = match + ":is(" + replaceCompound(rule, match) + ")"; - if (sel.length < result.sel.length) { - result.sel = sel; - result[TOKENS] = null; + // identity + return result.length == 0 || (result.length == 1 && eqMatrix(identity(), result)) + ? [ + { + typ: exports.EnumToken.IdenTokenType, + val: "none", + }, + ] + : result; +} +function eqMatrix(a, b) { + let mat = identity(); + let tmp = identity(); + const data = (Array.isArray(a) || ArrayBuffer.isView(a) ? a : parseMatrix(a)); + for (const transform of b) { + tmp = computeMatrix([transform], identity()); + if (tmp == null) { + return false; + } + mat = multiply(mat, tmp); + } + if (mat == null) { + return false; + } + for (let i = 0; i < 4; i++) { + for (let j = 0; j < 4; j++) { + if (Math.abs(mat[i * 4 + j] - data[i * 4 + j]) > epsilon) { + return false; } } } - return { result, node1: exchanged ? node2 : node1, node2: exchanged ? node1 : node2 }; + return true; } -/** - * Reduce rule selector - * @param node - * - * @private - */ -function reduceRuleSelector(node) { - if (node[RAW] == null) { - node[RAW] = splitRule(node.sel); +function minifyTransformFunctions(transform) { + const name = transform.val; + if ("skewX" == name) { + transform.val = "skew"; + return transform; } - let optimized = optimizeSelector(node[RAW].reduce((acc, curr) => { - acc.push(curr.slice()); - return acc; - }, [])); - if (optimized != null) { - node[OPTIMIZED] = optimized; + if (!["translate", "translate3d", "scale", "scale3d"].includes(name)) { + return transform; } - if (optimized != null && optimized.match && optimized.reducible && optimized.selector.length > 1) { - for (const selector of optimized.selector) { - if (selector.length > 1 && - selector[0] == "&" && - (combinators.includes(selector[1]) || !/^[a-zA-Z:]/.test(selector[1]))) { - const all = new Array(Math.max(selector.length - 1, 0)); - let write = 0; - for (let read = 1; read < selector.length; read++) { - all[write++] = selector[read]; - } - selector.length = 0; - for (let index = 0; index < write; index++) { - selector[index] = all[index]; - } + const values = []; + for (const token of transform.chi) { + if (token.typ == exports.EnumToken.CommentTokenType || + token.typ == exports.EnumToken.WhitespaceTokenType || + token.typ == exports.EnumToken.CommaTokenType) { + continue; + } + if (![ + exports.EnumToken.NumberTokenType, + exports.EnumToken.LengthTokenType, + exports.EnumToken.AngleTokenType, + exports.EnumToken.PercentageTokenType, + ].includes(token.typ)) { + return transform; + } + if (token.typ == exports.EnumToken.PercentageTokenType && + typeof token.val == "number" && + name.startsWith("scale")) { + Object.assign(token, { + typ: exports.EnumToken.NumberTokenType, + val: token.val / 100, + }); + } + values.push(token); + } + if (values.length == 0) { + return transform; + } + if ((name == "translate" || name == "scale") && values.length > 2) { + return transform; + } + const ignoredValue = name.startsWith("scale") ? 1 : 0; + const t = new Set(["x", "y", "z"]); + for (let i = 0; i < 3; i++) { + const axis = i == 0 ? "x" : i == 1 ? "y" : "z"; + if (values.length <= i || values[i].val == ignoredValue) { + t.delete(axis); + } + } + if (name == "translate3d" || name == "translate") { + if (t.size == 0) { + return { + typ: exports.EnumToken.TransformFunctionTokenType, + val: "translate", + chi: [{ typ: exports.EnumToken.NumberTokenType, val: 0 }], + }; + } + if (t.size == 1) { + return { + typ: exports.EnumToken.TransformFunctionTokenType, + val: "translate" + (t.has("x") ? "" : t.has("y") ? "Y" : "Z"), + chi: [values[t.has("x") ? 0 : t.has("y") ? 1 : 2]], + }; + } + if (t.size == 2) { + if (t.has("z")) { + return transform; } + return { + typ: exports.EnumToken.TransformFunctionTokenType, + val: "translate", + chi: [values[0], { typ: exports.EnumToken.CommaTokenType }, values[1]], + }; } - const unique = new Set(); - const reduced = optimized.selector.reduce((acc, curr) => { - const sig = curr.join(""); - if (!unique.has(sig)) { - if (acc.length > 0) { - acc.push(","); - } - unique.add(sig); - for (const c of curr) { - acc.push(c); - } + } + if (name == "scale3d" || name == "scale") { + if (t.size == 0) { + return { + typ: exports.EnumToken.TransformFunctionTokenType, + val: "scale", + chi: [{ typ: exports.EnumToken.NumberTokenType, val: 1 }], + }; + } + if (t.size == 1) { + return { + typ: exports.EnumToken.TransformFunctionTokenType, + val: "scale" + (t.has("x") ? "X" : t.has("y") ? "Y" : "Z"), + chi: [values[t.has("x") ? 0 : t.has("y") ? 1 : 2]], + }; + } + if (t.size == 2) { + if (t.has("z")) { + return transform; } - return acc; - }, []); - const raw = [ - [optimized.optimized[0], reduced.length === 1 ? reduced.join("") : ":is("].concat(reduced).concat(")"), - ]; - const sel = raw[0].join(""); - if (sel.length < node.sel.length) { - node.sel = sel; - node[RAW] = raw; - node[TOKENS] = null; + return { + typ: exports.EnumToken.TransformFunctionTokenType, + val: "scale", + chi: [values[0], { typ: exports.EnumToken.CommaTokenType }, values[1]], + }; } } + return transform; } -/** - * expand css nesting ast nodes - * @param ast - * - * @private - */ -function expand(ast) { - if (ast[STATE] & - (exports.EnumAstNodeStatus.Invalid | - exports.EnumAstNodeStatus.Disallowed | - exports.EnumAstNodeStatus.Unknown | - exports.EnumAstNodeStatus.Unparsed | - exports.EnumAstNodeStatus.Malformed)) { - return ast; +function skewX(x, from) { + const matrix = identity(); + matrix[4] = Math.tan(x); + return multiply(from, matrix); +} +function skewY(y, from) { + const matrix = identity(); + matrix[1] = Math.tan(y); + return multiply(from, matrix); +} +// convert angle to radian +function skew(values, from) { + const matrix = identity(); + matrix[4] = Math.tan(values[0]); + if (values.length > 1) { + matrix[1] = Math.tan(values[1]); } - const result = Object.assign(cloneNode(ast), { chi: [] }); - let children; - for (let i = 0; i < ast.chi.length; i++) { - let node = ast.chi[i]; - if (node.typ === exports.EnumToken.RuleNodeType) { - children = expandRule(node); - for (const child of children) { - child[PARENT] = result; - result.chi.push(child); - } - } - else if (node.typ == exports.EnumToken.AtRuleNodeType && "chi" in node) { - let hasRule = false; - let j = node.chi.length; - while (j--) { - // @ts-ignore - if (node.chi[j].typ == exports.EnumToken.RuleNodeType || node.chi[j].typ == exports.EnumToken.AtRuleNodeType) { - hasRule = true; - break; - } - } - if (hasRule) { - node = expand(node); - for (const child of node.chi) { - child[PARENT] = result; + return multiply(from, matrix); +} + +function perspective(x, from) { + const matrix = identity(); + // @ts-ignore + matrix[2 * 4 + 3] = typeof x == "object" && x.val == "none" ? 0 : x == 0 ? Number.NEGATIVE_INFINITY : -1 / x; + return multiply(from, matrix); +} + +function compute(transformLists) { + transformLists = transformLists.slice(); + stripCommaToken(transformLists); + let matrix = identity(); + let mat; + let transforms; + const cumulative = []; + // all transform function arguments must be 0 or scale(1) + for (const transform of transformLists) { + switch (transform.val) { + case "rotate": + case "rotateX": + case "rotateY": + case "rotateZ": + case "rotate3d": + for (const child of transform.chi) { + if (child.typ == exports.EnumToken.WhitespaceTokenType || child.typ == exports.EnumToken.CommaTokenType) { + continue; + } + if ((child.typ != exports.EnumToken.AngleTokenType && + child.typ != exports.EnumToken.NumberTokenType && + child.typ != exports.EnumToken.PercentageTokenType) || + // angle >= 360deg, do not transform + Math.abs(getAngle(child)) >= 1) { + return null; + } } - node[PARENT] = result; - // @ts-ignore - result.chi.push(node); - } - else { - node[PARENT] = result; - // @ts-ignore - result.chi.push(node); + // break; + } + } + for (const transformList of splitTransformList(transformLists)) { + mat = computeMatrix(transformList, identity()); + if (mat == null) { + return null; + } + matrix = multiply(matrix, mat); + transforms = minify$1(mat) ?? transformList; + for (let i = 0; i < transforms.length; i++) { + cumulative.push(transforms[i]); + } + } + const serialized = serialize(matrix); + if (cumulative.length > 0) { + for (let i = 0; i < cumulative.length; i++) { + if (cumulative[i].typ == exports.EnumToken.IdenTokenType && cumulative[i].val == "none") { + cumulative.splice(i--, 1); } } - else { - node[PARENT] = result; - // @ts-ignore - result.chi.push(node); + if (cumulative.length == 0) { + cumulative.push({ + typ: exports.EnumToken.IdenTokenType, + val: "none", + }); } } + const result = { + matrix: serialize(toZero(matrix)), + cumulative, + minified: minify$1(matrix) ?? [serialized], + }; return result; } -function expandRule(node) { - if (node[STATE] & - (exports.EnumAstNodeStatus.Invalid | - exports.EnumAstNodeStatus.Disallowed | - exports.EnumAstNodeStatus.Unknown | - exports.EnumAstNodeStatus.Unparsed | - exports.EnumAstNodeStatus.Malformed)) { - return [node]; - } - const ast = Object.assign(cloneNode(node), { chi: node.chi.slice() }); - const result = []; - if (ast.typ == exports.EnumToken.RuleNodeType) { - let i = 0; - for (; i < ast.chi.length; i++) { - if (ast.chi[i].typ == exports.EnumToken.RuleNodeType) { - const rule = ast.chi[i]; - if (!rule.sel.includes("&")) { - const selRule = splitRule(rule.sel); - const arSelf = splitRule(ast.sel) - .filter((r) => r.every((t) => t != ":before" && t != ":after" && !t.startsWith("::"))) - .reduce((acc, curr) => acc.concat([curr.join("")]), []) - .join(","); - if (arSelf.length == 0) { - ast.chi.splice(i--, 1); - continue; +function computeMatrix(transformList, matrixVar) { + let values = []; + let val; + let i = 0; + for (; i < transformList.length; i++) { + if (transformList[i].typ != exports.EnumToken.TransformFunctionTokenType || + !transformFunctions.includes(transformList[i].val)) { + return null; + } + switch (transformList[i].val) { + case "translate": + case "translateX": + case "translateY": + case "translateZ": + case "translate3d": + { + values.length = 0; + const children = stripCommaToken(transformList[i].chi.slice()); + const valCount = transformList[i].val == "translate3d" + ? 3 + : transformList[i].val == "translate" + ? 2 + : 1; + for (let j = 0; j < children.length; j++) { + if (children[j].typ == exports.EnumToken.WhitespaceTokenType) { + continue; + } + val = length2Px(children[j]); + if (val == null) { + return null; + } + values.push(val); } - for (let i1 = 0; i1 < selRule.length; i1++) { - const arr = selRule[i1]; - combinators.includes(arr[0].charAt(0)) ? arr.unshift(arSelf) : arr.unshift(arSelf, " "); + if (values.length == 0 || values.length > valCount) { + return null; } - rule.sel = selRule - .reduce((acc, curr) => { - acc.push(curr.join("")); - return acc; - }, []) - .join(","); - } - else { - let childSelectorCompound = []; - let withCompound = []; - let withoutCompound = []; - // pseudo elements cannot be used with '&' - // https://www.w3.org/TR/css-nesting-1/#example-7145ff1e - const rules = splitRule(ast.sel).filter((r) => r.every((t) => t != ":before" && t != ":after" && !t.startsWith("::"))); - const parentSelector = !node.sel.includes("&"); - if (rules.length == 0) { - ast.chi.splice(i--, 1); - continue; + if (transformList[i].val == "translateX") { + matrixVar = translateX(values[0], matrixVar); } - for (const sel of rule[RAW] ?? splitRule(rule.sel)) { - const s = sel.join(""); - if (s.includes("&") || parentSelector) { - if (s.indexOf("&", 1) == -1) { - if (s.at(0) == "&") { - if (s.at(1) == " ") { - childSelectorCompound.push(s.slice(2)); - } - else { - if (s == "&" || parentSelector) { - withCompound.push(s); - } - } - } - else { - withoutCompound.push(s); - } - } - else { - withCompound.push(s); - } - } + else if (transformList[i].val == "translateY") { + matrixVar = translateY(values[0], matrixVar); } - const selectors = []; - const selector = rules.length > 1 ? ":is(" + rules.map((a) => a.join("")).join(",") + ")" : rules[0].join(""); - if (childSelectorCompound.length > 0) { - if (childSelectorCompound.length == 1) { - selectors.push(replaceCompound("& " + childSelectorCompound[0].trim(), selector)); + else if (transformList[i].val == "translateZ") { + matrixVar = translateZ(values[0], matrixVar); + } + else if (transformList[i].val == "translate") { + matrixVar = translate(values, matrixVar); + } + else { + // @ts-ignore + matrixVar = translate3d(values, matrixVar); + } + } + break; + case "rotate": + case "rotateX": + case "rotateY": + case "rotateZ": + case "rotate3d": + { + let x = 0; + let y = 0; + let z = 0; + let angle; + let values = []; + let valuesCount = transformList[i].val == "rotate3d" ? 4 : 1; + for (const child of stripCommaToken(transformList[i].chi.slice())) { + values.push(child); + if (transformList[i].val == "rotateX") { + x = 1; } - else { - selectors.push(replaceCompound("& :is(" + - childSelectorCompound.reduce((acc, curr) => acc + (acc.length > 0 ? "," : "") + curr.trim(), "") + - ")", selector)); + else if (transformList[i].val == "rotateY") { + y = 1; } - } - if (withCompound.length > 0) { - if (withCompound.every((t) => t[0] == "&" && t.indexOf("&", 1) == -1)) { - // for (const w of withCompound) { - // for (let m = 0; m < w.length; m++) { - // // for (let n = 0; n < w[m].length; n++) { - // withoutCompound.push(w[m].slice(1)); - // // } - // } - // } - withoutCompound.push(...withCompound.map((t) => t.slice(1))); - withCompound.length = 0; + else if (transformList[i].val == "rotate" || + transformList[i].val == "rotateZ") { + z = 1; } } - if (withoutCompound.length > 0) { - if (withoutCompound.length == 1) { - const useIs = rules.length == 1 && - selector.match(/^[a-zA-Z.:]/) != null && - selector.includes(" ") && - withoutCompound.length == 1 && - withoutCompound[0].match(/^[a-zA-Z]+$/) != null; - const compound = useIs ? ":is(&)" : "&"; - selectors.push(replaceCompound(rules.length == 1 - ? useIs - ? withoutCompound[0] + ":is(&)" - : selector.match(/^[.:]/) && withoutCompound[0].match(/^[a-zA-Z]+$/) - ? withoutCompound[0] + compound - : compound + withoutCompound[0] - : withoutCompound[0].match(/^[a-zA-Z:]+$/) - ? withoutCompound[0].trim() + compound - : "&" + - (withoutCompound[0].match(/^\S+$/) - ? withoutCompound[0].trim() - : ":is(" + withoutCompound[0].trim() + ")"), selector)); + if (values.length != valuesCount) { + return null; + } + if (transformList[i].val == "rotate3d") { + x = getNumber(values[0]); + y = getNumber(values[1]); + z = getNumber(values[2]); + } + angle = getAngle(values.at(-1)); + if ([x, y, z, angle].some((t) => typeof t != "number" || Number.isNaN(+t))) { + return null; + } + if (transformList[i].val == "rotate" || + transformList[i].val == "rotateZ") { + matrixVar = rotate(angle * 2 * Math.PI, matrixVar); + } + else { + matrixVar = rotate3D(angle * 2 * Math.PI, x, y, z, matrixVar); + } + } + break; + case "scale": + case "scaleX": + case "scaleY": + case "scaleZ": + case "scale3d": + { + values.length = 0; + let child; + const children = stripCommaToken(transformList[i].chi.slice()); + for (let k = 0; k < children.length; k++) { + child = children[k]; + if (child.typ != exports.EnumToken.NumberTokenType && child.typ != exports.EnumToken.PercentageTokenType) { + return null; } - else { - selectors.push(replaceCompound("&:is(" + - withoutCompound.reduce((acc, curr) => acc + (acc.length > 0 ? "," : "") + curr.trim(), "") + - ")", selector)); + values.push(getNumber(child)); + } + if (transformList[i].val == "scale3d") { + if (values.length != 3) { + return null; } + matrixVar = scale3d(values[0], values[1], values[2], matrixVar); + break; } - if (withCompound.length > 0) { - if (withCompound.length == 1) { - selectors.push(replaceCompound(withCompound[0], selector)); + if (transformList[i].val == "scale") { + if (values.length != 1 && values.length != 2) { + return null; } + matrixVar = scale(values[0], values[1] ?? values[0], matrixVar); + break; + } + if (values.length != 1) { + return null; + } + else if (transformList[i].val == "scaleX") { + matrixVar = scaleX(values[0], matrixVar); + } + else if (transformList[i].val == "scaleY") { + matrixVar = scaleY(values[0], matrixVar); + } + else if (transformList[i].val == "scaleZ") { + matrixVar = scaleZ(values[0], matrixVar); } - rule.sel = selectors.reduce((acc, curr) => (curr.length == 0 ? acc : acc + (acc.length > 0 ? "," : "") + curr), ""); - } - ast.chi.splice(i--, 1); - for (const s of expandRule(rule)) { - result.push(s); } - } - else if (ast.chi[i].typ == exports.EnumToken.AtRuleNodeType) { - let astAtRule = ast.chi[i]; - const values = []; - if (astAtRule.nam === "scope") { - if (astAtRule.val.includes("&")) { - astAtRule.val = replaceCompound(astAtRule.val, ast.sel); + break; + case "skew": + case "skewX": + case "skewY": + { + values.length = 0; + let child; + let value; + for (let k = 0; k < transformList[i].chi.length; k++) { + child = transformList[i].chi[k]; + if (child.typ == exports.EnumToken.CommentTokenType || + child.typ == exports.EnumToken.CommaTokenType || + child.typ == exports.EnumToken.WhitespaceTokenType) { + continue; + } + value = getAngle(child); + values.push(value * 2 * Math.PI); } - const slice = astAtRule.chi - .slice() - .filter((t) => t.typ == exports.EnumToken.RuleNodeType && t.sel.includes("&")); - if (slice.length > 0) { - expandRule({ ...node, chi: astAtRule.chi.slice() }); + if (values.length == 0 || + values.length > (transformList[i].val == "skew" ? 2 : 1)) { + return null; + } + if (transformList[i].val == "skew") { + matrixVar = skew(values, matrixVar); + } + else { + matrixVar = + transformList[i].val == "skewX" + ? skewX(values[0], matrixVar) + : skewY(values[0], matrixVar); } } - else { - // @ts-ignore - const clone = { ...ast, chi: astAtRule.chi.slice() }; - // @ts-ignore - astAtRule.chi.length = 0; - for (const r of expandRule(clone)) { - if (r.typ == exports.EnumToken.AtRuleNodeType && "chi" in r) { - if (astAtRule.val !== "" && r.val !== "") { - if (astAtRule.nam === "media" && r.nam === "media") { - r.val = astAtRule.val + " and " + r.val; - } - else if (astAtRule.nam == "layer" && r.nam == "layer") { - r.val = astAtRule.val + "." + r.val; - } - } - // @ts-ignore - values.push(r); + break; + case "perspective": + { + const values = []; + let child; + let value; + for (let k = 0; k < transformList[i].chi.length; k++) { + child = transformList[i].chi[k]; + if (child.typ == exports.EnumToken.CommentTokenType || child.typ == exports.EnumToken.WhitespaceTokenType) { + continue; } - else if (r.typ == exports.EnumToken.RuleNodeType) { - for (const rule of expandRule(r)) { - // @ts-ignore - astAtRule.chi.push(rule); - } + if (child.typ == exports.EnumToken.IdenTokenType && child.val == "none") { + values.push(child); + continue; + } + value = length2Px(child); + if (value == null) { + return null; } + values.push(value); } + if (values.length != 1) { + return null; + } + matrixVar = perspective(values[0], matrixVar); } - if (astAtRule.chi.length > 0) { - result.push(astAtRule); - } - for (const r of values) { - result.push(r); - } - ast.chi.splice(i--, 1); - } - } - } - // @ts-ignore - return ast.chi.length > 0 ? [ast].concat(result) : result; -} -/** - * replace compound selector - * @param input - * @param replace - */ -function replaceCompound(input, replace) { - const tokens = parseString(input); - let replacement = null; - for (const t of walkValues(tokens)) { - if (t.value.typ == exports.EnumToken.NestingSelectorTokenType) { - if (tokens.length == 2) { - if (replacement == null) { - replacement = parseString(replace); + break; + case "matrix3d": + case "matrix": + { + const values = []; + let value; + for (const token of transformList[i].chi) { + if ([ + exports.EnumToken.WhitespaceTokenType, + exports.EnumToken.CommentTokenType, + exports.EnumToken.CommaTokenType, + ].includes(token.typ)) { + continue; + } + value = getNumber(token); + if (value == null) { + return null; + } + values.push(value); + } + if (transformList[i].val == "matrix") { + if (values.length != 6) { + return null; + } + } + else if (values.length != 16) { + return null; + } + matrixVar = multiply(matrixVar, matrix(values)); } - Object.assign(t.value, { - typ: exports.EnumToken.LiteralTokenType, - val: replaceCompoundLiteral(t.value.val, replace), - }); - continue; - } - const rule = splitRule(replace); - Object.assign(t.value, { - typ: exports.EnumToken.LiteralTokenType, - val: rule.length > 1 ? ":is(" + replace + ")" : replace, - }); - } - } - return tokens.reduce((acc, curr) => acc + renderValue(curr), ""); -} -function replaceCompoundLiteral(selector, replace) { - const tokens = [""]; - let i = 0; - for (; i < selector.length; i++) { - if (selector.charAt(i) == "&") { - tokens.push("&", ""); - } - } - return tokens - .sort((a, b) => { - if (a == "&") { - return 1; - } - return b == "&" ? -1 : 0; - }) - .reduce((acc, curr) => acc + (curr == "&" ? replace : curr), ""); -} - -/** - * - * @param value - * @returns - */ -function toBase64(value) { - let result = ''; - for (const c of new TextEncoder().encode(value)) { - result += String.fromCharCode(c); - } - return btoa(result); -} - -// from https://github.com/Rich-Harris/vlq/tree/master -// credit: Rich Harris -const integer_to_char = {}; -const char_to_integer = {}; -let i = 0; -for (const char of 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=') { - char_to_integer[char] = i; - integer_to_char[i++] = char; -} -/** - * @param {string} str - */ -function decode(str) { - /** @type {number[]} */ - let result = []; - let shift = 0; - let value = 0; - for (let i = 0; i < str.length; i += 1) { - let integer = char_to_integer[str[i]]; - // if (integer === undefined) { - // throw new Error('Invalid character (' + str[i] + ')'); - // } - const has_continuation_bit = integer & 32; - integer &= 31; - value += integer << shift; - if (has_continuation_bit) { - shift += 5; - } - else { - const should_negate = value & 1; - value >>>= 1; - if (should_negate) { - result.push(value === 0 ? -2147483648 : -value); - } - else { - result.push(value); - } - // reset - value = shift = 0; + break; + default: + return null; } } - return result; -} -/** - * - * @param value - * @returns - */ -function encode(value) { - // if (typeof value === 'number') { - // return encode_integer(value); - // } - let result = ''; - for (let i = 0; i < value.length; i += 1) { - result += encode_integer(value[i]); - } - return result; -} -/** - * - * @param num - * @returns - */ -function encode_integer(num) { - let result = ''; - if (num < 0) { - num = (-num << 1) | 1; - } - else { - num <<= 1; - } - do { - let clamped = num & 31; - num >>>= 5; - if (num > 0) { - clamped |= 32; - } - result += integer_to_char[clamped]; - } while (num > 0); - return result; + return matrixVar; } - -/** - * Generate and parse source map - * - * ```ts - * const sourcemap = new SourceMap(); - * - * // you can also pass an input sourcemap - * // const sourcemap = new SourceMap(sourcemapObjectOrJSONString); - * - * sourcemap.addSourceContent(0, '/css/styles.css', null); - * sourcemap.addSourceContent(1, '/css/typography.css', null); - * - * const maps = []; - * - * maps.add([ - 1, 1, 0, 1, 1]); - * maps.add([ - 2, 1, 1, 1, 1 - ]); - - sourcemap.add(maps); - - // convert to inline sourcemap - sourcemap.toUrl(); - - // convert to JSON - sourcemap.toJSON(); - - // find the original file, line and column - sourcemap.find(2, 1); - ``` - */ -class SourceMap { - /** - * - * @private - */ - keys = new Set(); - /** - * Last location - */ - lastLocation = null; - /** - * Version - * @private - */ - version = 3; - /** - * Sources map - * @private - */ - sourcesMap = []; - /** - * Sources content - * @private - */ - sourcesContent = []; - /** - * Sources - * @private - */ - sources = []; - /** - * Map - * @private - * - */ - map = new Map(); - /** - * Map - * @private - * - */ - reverseMap = new Map(); - /** - * Line - * @private - */ - line = -1; - /** - * - * @param sourcemaps input sourcemap - */ - constructor(sourcemaps) { - if (typeof sourcemaps === "string") { - if (sourcemaps.startsWith("data:")) { - let encoding = ""; - let offset = sourcemaps.indexOf(",") + 1; - if (offset == 0) { - offset = sourcemaps.lastIndexOf(";") + 1; - } - else { - encoding = sourcemaps.slice(sourcemaps.lastIndexOf(";") + 1, offset - 1); - } - if (encoding == "base64") { - sourcemaps = atob(sourcemaps.slice(offset)); - } - else { - sourcemaps = decodeURIComponent(sourcemaps.slice(offset)); - } - } - sourcemaps = JSON.parse(sourcemaps); +function splitTransformList(transformList) { + let pattern = null; + const tokens = []; + for (let i = 0; i < transformList.length; i++) { + if (transformList[i].typ == exports.EnumToken.CommentTokenType || + transformList[i].typ == exports.EnumToken.WhitespaceTokenType) { + continue; } - if (sourcemaps != null) { - this.sources = sourcemaps.sources?.slice() ?? []; - this.sourcesContent = sourcemaps.sourcesContent?.slice() ?? []; - const decodedMappings = sourcemaps.mappings - .split(";") - .map((mapping) => mapping.split(",").map((mapping) => decode(mapping))); - this.line = decodedMappings.length - 1; - for (let index = 0; index < decodedMappings.length; index++) { - if (decodedMappings[index].length == 0 || - (decodedMappings[index].length == 1 && decodedMappings[index][0].length == 0)) { - continue; + if (pattern == null || + (transformList[i].typ == exports.EnumToken.TransformFunctionTokenType && + !transformList[i].val.startsWith(pattern))) { + if (transformList[i].typ == exports.EnumToken.TransformFunctionTokenType) { + if (transformList[i].val.startsWith("scale")) { + pattern = "scale"; } - this.map.set(index, decodedMappings[index]); - } - this.computePositions(); - } - } - /** - * add source - * @param id source id - * @param fileName source file - * @param content source content - * @returns - */ - addSourceContent(id, fileName, content) { - if (this.sourcesMap.includes(id)) { - return; - } - this.sourcesMap[this.sourcesMap.length] = id; - this.sources[this.sources.length] = fileName || null; - this.sourcesContent[this.sourcesContent.length] = content || null; - } - /** - * Add multiple sourcemaps - * @param maps - * @throws - */ - add(maps) { - let srcIndex; - for (let [newLine, newColumn, srcId, ln, col] of maps) { - const key = `${srcId}:${ln}:${col}:${newLine}:${newColumn}`; - if (this.keys.has(key)) { - continue; - } - this.keys.add(key); - const line = newLine - 1; - let record; - if (line > this.line) { - this.line = line; - } - srcIndex = this.sourcesMap.indexOf(srcId); - if (srcIndex == -1) { - throw new Error(`Source file ${srcId} not added to sourcemap`); - } - if (!this.map.has(line)) { - record = [Math.max(0, newColumn - 1), srcIndex, ln - 1, col - 1]; - this.map.set(line, [record]); - } - else { - const arr = this.map.get(line); - record = [Math.max(0, newColumn - 1) - arr[0][0], srcIndex - arr[0][1], ln - 1, col - 1]; - arr.push(record); - } - if (this.lastLocation != null) { - record[2] -= this.lastLocation.ln - 1; - record[3] -= this.lastLocation.col - 1; - } - this.lastLocation ??= { ln, col }; - this.lastLocation.ln = ln; - this.lastLocation.col = col; - } - } - /** - * compute original positions - */ - computePositions() { - this.reverseMap.clear(); - let sourceFileIndex = 0; // second field - let sourceCodeLine = 0; // third field - let sourceCodeColumn = 0; // fourth field - // let nameIndex: number = 0; // fifth field - let generatedCodeColumn; - let result; - // mappings to original source - for (let [i, line] of this.map.entries()) { - if (line.length === 0 || (line.length === 1 && line[0].length === 0)) { - continue; - } - generatedCodeColumn = line[0][0]; // first field - reset each time - line = line - .map((segment, index, array) => { - if (segment.length === 0) { - return []; + else if (transformList[i].val.startsWith("rotate")) { + pattern = "rotate"; } - generatedCodeColumn = index == 0 ? segment[0] : segment[0] + array[0][0]; - result = [generatedCodeColumn]; - if (segment.length <= 1) { - return result; + else if (transformList[i].val.startsWith("translate")) { + pattern = "translate"; } - sourceFileIndex = index == 0 ? segment[1] : segment[1] + array[0][1]; - sourceCodeLine += segment[2]; - sourceCodeColumn += segment[3]; - result.push(sourceFileIndex, sourceCodeLine, sourceCodeColumn); - // nameIndex not needed - // if (segment.length === 5) { - // nameIndex += segment[4]; - // result.push(nameIndex); - // } - return result; - }) - .sort((a, b) => { - if (a[1] !== b[1]) { - return a[1] - b[1]; + else { + pattern = null; } - return a[0] - b[0]; - }); - if (line.length == 0 || (line.length == 1 && line[0].length == 0)) { + tokens.push([transformList[i]]); continue; } - this.reverseMap.set(i, line); } + if (pattern != null && + transformList[i].typ == exports.EnumToken.TransformFunctionTokenType && + transformList[i].val.startsWith(pattern)) { + tokens[tokens.length - 1].push(transformList[i]); + continue; + } + tokens.push([transformList[i]]); } - /** - * retrieve original sources, lines and columns - * @param line generated line - * @param column generated column - */ - find(line, column) { - if (this.reverseMap.size == 0) { - this.computePositions(); + return tokens; +} + +class TransformCssFeature { + accept = new Set([ + exports.EnumToken.RuleNodeType, + exports.EnumToken.AtRuleNodeType, + exports.EnumToken.KeyframesRuleNodeType, + ]); + get ordering() { + return 4; + } + get processMode() { + return exports.FeatureWalkMode.Post; + } + static register(options) { + // @ts-ignore + if (options.computeTransform) { + // @ts-ignore + options.features.push(new TransformCssFeature()); } - if (!this.reverseMap.has(--line)) { + } + run(ast) { + if (ast.chi == null) { return null; } - column--; - const result = []; - for (const record of this.reverseMap.get(line)) { - if (record.length == 0 || record[0] < column) { + let i = 0; + let node; + // @ts-ignore + for (; i < ast.chi.length; i++) { + // @ts-ignore + node = ast.chi[i]; + if (node[STATE] & (exports.EnumAstNodeStatus.Invalid | exports.EnumAstNodeStatus.ValidationFailed)) { continue; } - if (record[0] > column) { - break; + if (node.typ != exports.EnumToken.DeclarationNodeType || + !node.nam.match(/^(-[a-z]+-)?transform$/)) { + continue; } - result.push([ - this.sources?.[record[1]] ?? null, - record[2] + 1, - record[3] + 1, - this.sourcesContent?.[record[1]] ?? null, - ]); - } - return result.length == 0 ? null : result; - } - /** - * Convert to URL encoded string - */ - toUrl() { - // /*# sourceMappingURL = ${url} */ - return `data:application/json;charset=utf-8;base64,${toBase64(JSON.stringify(this.toJSON()))}`; - } - /** - * Convert to JSON object - */ - toJSON() { - const mappings = []; - let i = 0; - for (; i <= this.line; i++) { - if (!this.map.has(i)) { - mappings.push(""); + const children = []; + for (const child of node.val) { + children.push(child.typ == exports.EnumToken.TransformFunctionTokenType + ? minifyTransformFunctions(child) + : child); } - else { - mappings.push(this.map.get(i).reduce((acc, curr) => acc + (acc === "" ? "" : ",") + encode(curr), "")); + let { matrix, cumulative, minified } = compute(children) ?? { + matrix: null, + cumulative: null, + minified: null, + }; + if (matrix == null || cumulative == null || minified == null) { + node.val = children.map((t) => t.typ == exports.EnumToken.TransformFunctionTokenType ? minifyTransformFunctions(t) : t); + continue; + } + let r = [filterValues(children)]; + if (eqMatrix(matrix, cumulative)) { + r.push(cumulative); + } + if (eqMatrix(matrix, minified)) { + r.push(minified); } + const l = printValue$1(matrix).length; + node.val = r.reduce((acc, curr) => { + if (curr.reduce((acc, t) => acc + printValue$1(t), "").length < l) { + return curr; + } + return acc; + }, [matrix]); } - return { - version: this.version, - sources: this.sources.slice(), - sourcesContent: this.sourcesContent?.slice(), - mappings: mappings.join(";"), - }; + return null; } } /** - * Compute line and column of the offset + * Search the ast subtree and return the first match + * + * ```ts + * // find the first ast declaration node which name is 'aspect-ratio' +import { find, EnumToken, transform } from "@tbela99/css-parser"; +import type { AstNode } from "@tbela99/css-parser"; + + * const css = ` + +button { + aspect-ratio: 1; + width: if(media(any-pointer: fine): 30px; else: 44px); +} + `; + + // find declaration which contain a '30px' + const nodeMatcher = (node: AstNode) => + return node.typ == EnumToken.DeclarationNodeType && (node as AstDeclaration).nam == 'aspect-ratio'; + + const result = await transform(css); + const node = find(result.ast, nodeMatcher); + + console.log({node}); + + ``` + * + * @param ast + * @param matcher + * @returns */ -class LineMap { - /** - * line starts - */ - lineStarts; - /** - * Constructor - * @param lines - */ - constructor(lines = []) { - if (lines.length === 0) { - lines.push(0); - } - this.lineStarts = lines; - } - /** - * Compute line and column of the offset - * @param offset - * @returns - */ - getOffsets(offset) { - const line = this.search(offset); - const column = offset - this.lineStarts[line]; - // [line, column] - return [line + 1, line == 0 ? column + 1 : column]; - } - /** - * search the greatest index of the value less than or equal to offset - * @param offset - * @returns - */ - search(offset) { - // search lineStarts using binary search - let start = 0; - let end = this.lineStarts.length - 1; - let mid = 0; - let result = -1; - while (start <= end) { - mid = start + ((end - start) >>> 1); - if (this.lineStarts[mid] <= offset) { - result = mid; - start = mid + 1; - } - else if (this.lineStarts[mid] > offset) { - end = mid - 1; - } +function find(ast, matcher) { + for (const { node, parent } of walk(ast)) { + if (matcher(node, parent)) { + return node; } - return result; - } - /** - * get line starts - * @returns - */ - getLineStarts() { - return this.lineStarts; - } - /** - * add line start - */ - addLineStart(lineStart) { - this.lineStarts.push(lineStart); } + return null; } - /** - * match url - */ -const matchUrl = /^(https?:)?\/\//; -const windowsPathnameRegexp = /^\/?[a-zA-Z]:/; -/** - * return the directory name of a path - * @param path * - * @private + * @param ast + * @param matcher + * @returns + * + * Search the ast sub-tree by checking each node's value token and return the first match + * + ```ts + // find the first ast node which contains the length token '30px' +import { findByValue, EnumToken, transform } from "@tbela99/css-parser"; +import type { AstNode } from "@tbela99/css-parser"; + + const css = ` + +button { + aspect-ratio: 1; + width: if(media(any-pointer: fine): 30px; else: 44px); +} + `; + + // find declaration which contain the length token '30px' + const nodeMatcher = (value: Token) => + return value.typ == EnumToken.LengthTokenType && (value as LengthToken).val == 30 && (value as LengthToken).unit == 'px' ; + + const result = await transform(css); + const { node, value } = findByValue(result.ast, nodeMatcher) ?? {}; + + console.log({node, value}); + + ``` */ -function dirname(path) { - if (path === "") { - return ""; - } - if (path.startsWith("data:")) { - return path; - } - let i = 0; - let parts = [""]; - for (; i < path.length; i++) { - const chr = path.charAt(i); - if (chr == "/") { - parts.push(""); +function findByValue(ast, matcher) { + let source; + for (const { node } of walk(ast)) { + source = null; + if (node.typ === exports.EnumToken.DeclarationNodeType) { + source = node.val; } - else { - parts[parts.length - 1] += chr; + else if (Array.isArray(node[TOKENS])) { + source = node[TOKENS]; + } + if (source == null) { + continue; + } + for (const { value, parent, root: rootNode, parents } of walkValues(source, node)) { + if (matcher(value, node)) { + // @ts-ignore + return { node, value: { node: value, parent, root: rootNode, parents } }; + } } } - parts.pop(); - return parts.join("/"); + return null; } /** - * split path - * @param result - * @private + * Search the ast sub-tree and return all matches + * + * ```ts + * // find the first ast declaration node which name is 'aspect-ratio' +* import { findAll, EnumToken, transform } from "@tbela99/css-parser"; +* import type { AstNode } from "@tbela99/css-parser"; + +const css = ` + +button { + aspect-ratio: 1; + width: if(media(any-pointer: fine): 30px; else: 44px); +} +button-small { + aspect-ratio: 1; + width: if(media(any-pointer: fine): 20px; else: 30px); +} + `; + + const nodeMatcher = (node: AstNode) => + return node.typ == EnumToken.DeclarationNodeType && (node as AstDeclaration).nam == 'aspect-ratio'; + + const result = await transform(css); + const nodes = findAll(result.ast, nodeMatcher); + + console.log({nodes}); + + ``` + * + * @param ast + * @param matcher + * @returns */ -function splitPath(result) { - if (result.length == 0) { - return { parts: [], i: 0 }; - } - const parts = result == "/" ? [] : [""]; - let i = 0; - for (; i < result.length; i++) { - const chr = result.charAt(i); - if (chr == "/") { - parts.push(""); - } - // else if (chr == "?" || chr == "#") { - // break; - // } - else { - parts[parts.length - 1] += chr; +function findAll(ast, matcher) { + const result = []; + for (const { node, parent } of walk(ast)) { + if (matcher(node, parent)) { + result.push(node); } } - // let k: number = -1; - // while (++k < parts.length) { - // if (parts[k] == ".") { - // parts.splice(k--, 1); - // } else if (parts[k] == "..") { - // parts.splice(k - 1, 2); - // k -= 2; - // } - // } - return { parts, i }; + return result; } /** - * Nomalize path - * @param path - * @private + * Search the ast sub-tree and return the last match. + * + * ```ts + * // find the first ast declaration node which name is 'aspect-ratio' +import { findLast, EnumToken, transform } from "@tbela99/css-parser"; +import type { AstNode } from "@tbela99/css-parser"; + + * const css = ` + +button { + aspect-ratio: 1; + width: if(media(any-pointer: fine): 30px; else: 44px); +} + `; + + const nodeMatcher = (node: AstNode) => + return node.typ == EnumToken.DeclarationNodeType && (node as AstDeclaration).nam == 'aspect-ratio'; + + const result = await transform(css); + const node = findLast(result.ast, nodeMatcher); + + console.log({node}); + + ``` + * + * @param ast + * @param matcher + * @returns */ -const normalize = memoize(function (path) { - let parts = []; - let i = 0; - if (path.includes("\\")) { - path = path.replace(/(\\)/g, "/"); - } - if (windowsPathnameRegexp.test(path)) { - path = path.replace(windowsPathnameRegexp, ""); +function findLast(ast, matcher) { + for (const { node, parent } of walk(ast, null, true)) { + if (matcher(node, parent)) { + return node; + } } - for (; i < path.length; i++) { - const chr = path.charAt(i); - if (chr == "/") { - if (parts.length == 0 || parts[parts.length - 1] !== "") { - parts.push(""); + return null; +} + +const nodeMatcher = (value) => value.typ === exports.EnumToken.IfConditionTokenType || + (value.typ === exports.EnumToken.WildCardFunctionTokenType && equalsIgnoreCase("if", value.val)); +function substituteIfElseNode(declaration, node, wrapper, parentWrapper, cache) { + const result = []; + let nodeMap = new Map(); + let clonedDeclaration; + let targetParentWrapper = parentWrapper; + let targetWrapper = wrapper; + if (node.typ === exports.EnumToken.IfElseConditionTokenType) { + // + clonedDeclaration = cloneNode(declaration, true, nodeMap); + // replace else: ... with the actual value + if (node.r.typ === exports.EnumToken.IfConditionTokenType) { + const target = node.r.l.find((t) => t.typ != exports.EnumToken.CommentTokenType && t.typ != exports.EnumToken.WhitespaceTokenType); + // + if (target.typ === exports.EnumToken.IdenTokenType && equalsIgnoreCase("else", target.val)) { + replaceNodeOrValue(nodeMap.get(targetParentWrapper), nodeMap.get(targetWrapper), node.r.r.at(-1)?.typ === + exports.EnumToken.SemiColonTokenType + ? trimArray(node.r.r.slice(0, -1)) + : node.r.r); + // @ts-expect-error + if (targetParentWrapper.typ != exports.EnumToken.DeclarationNodeType) { + let index = targetParentWrapper.chi.indexOf(targetWrapper); + if (index != -1) { + let i; + let k; + let siblingWrapper; + let left = node.l.l.find((t) => t.typ != exports.EnumToken.CommentTokenType && t.typ != exports.EnumToken.WhitespaceTokenType); + for (i = index + 1; i < targetParentWrapper.chi.length; i++) { + if (targetParentWrapper.chi[i].typ === targetWrapper.typ) { + siblingWrapper = targetParentWrapper.chi[i]; + for (k = 0; k < siblingWrapper.chi.length; k++) { + if (siblingWrapper.chi[k].typ === exports.EnumToken.IfElseConditionTokenType) { + let leftSide = siblingWrapper.chi[k].l.l.find((t) => t.typ != exports.EnumToken.CommentTokenType && + t.typ != exports.EnumToken.WhitespaceTokenType); + if (eq(left, leftSide)) { + replaceNodeOrValue(nodeMap.get(targetParentWrapper), nodeMap.get(targetParentWrapper.chi[i]), siblingWrapper.chi[k] + .r.r.at(-1)?.typ === exports.EnumToken.SemiColonTokenType + ? trimArray(siblingWrapper.chi[k] + .r.r.slice(0, -1)) + : siblingWrapper.chi[k] + .r.r); + // @ts-ignore + cache.add(siblingWrapper.chi[k].l); + } + } + } + } + } + } + } } } - else if (chr == "?" || chr == "#") { - break; + result.push(clonedDeclaration); + nodeMap.clear(); + clonedDeclaration = cloneNode(declaration, true, nodeMap); + replaceNodeOrValue(nodeMap.get(targetParentWrapper), nodeMap.get(targetWrapper), node.l); + result.push(clonedDeclaration); + } + else if (node.typ === exports.EnumToken.IfConditionTokenType) { + const left = node.l.find((t) => t.typ != exports.EnumToken.CommentTokenType && t.typ != exports.EnumToken.WhitespaceTokenType); + if (left == null) { + return result; + } + if (left.typ === exports.EnumToken.IdenTokenType && equalsIgnoreCase("else", left.val)) { + clonedDeclaration = cloneNode(declaration, true, nodeMap); + replaceNodeOrValue(nodeMap.get(parentWrapper), + // @ts-expect-error + nodeMap.get(targetWrapper.typ === exports.EnumToken.DeclarationNodeType ? node : targetWrapper), node.r.at(-1)?.typ === exports.EnumToken.SemiColonTokenType ? trimArray(node.r.slice(0, -1)) : node.r); + result.push(clonedDeclaration); + } + else if (left?.typ === exports.EnumToken.WhenElseFunctionTokenType) { + const atRule = Object.assign(cloneNode(declaration), { + typ: exports.EnumToken.AtRuleNodeType, + nam: left.val, + chi: [], + }); + if (declaration[PARENT] != null) { + atRule[LOCSRCID] = declaration[PARENT][LOCSRCID]; + atRule[LOCSTA] = declaration[PARENT][LOCSTA]; + atRule[LOCEND] = declaration[PARENT][LOCEND]; + } + atRule[TOKENS] = [{ typ: exports.EnumToken.ParensTokenType, chi: left.chi.slice() }]; + const minify = atRule.nam !== "supports"; + const options = { + minify, + convertColor: minify, + }; + atRule.val = atRule[TOKENS].reduce((acc, curr) => acc + printValue(curr, options), ""); + clonedDeclaration = cloneNode(declaration, true, nodeMap); + replaceNodeOrValue(nodeMap.get(targetWrapper), nodeMap.get(node), node.r.at(-1)?.typ === exports.EnumToken.SemiColonTokenType ? trimArray(node.r.slice(0, -1)) : node.r); + clonedDeclaration[PARENT] = atRule; + atRule.chi.push(clonedDeclaration); + result.push(atRule); + processNode(clonedDeclaration, cache); } - else { - if (parts.length == 0) { - parts.push(""); + else if (left.typ === exports.EnumToken.ContainerFunctionTokenType) { + const atRule = Object.assign(cloneNode(declaration), { + typ: exports.EnumToken.AtRuleNodeType, + nam: "container", + chi: [], + }); + atRule[TOKENS] = [left]; + atRule.val = atRule[TOKENS].reduce((acc, curr) => acc + printValue(curr), ""); + if (declaration[PARENT] != null) { + atRule[LOCSRCID] = declaration[PARENT][LOCSRCID]; + atRule[LOCSTA] = declaration[PARENT][LOCSTA]; + atRule[LOCEND] = declaration[PARENT][LOCEND]; } - parts[parts.length - 1] += chr; + clonedDeclaration = cloneNode(declaration, true, nodeMap); + replaceNodeOrValue(nodeMap.get(targetWrapper.typ === exports.EnumToken.WildCardFunctionTokenType ? targetParentWrapper : targetWrapper), nodeMap.get(targetWrapper.typ === exports.EnumToken.WildCardFunctionTokenType ? targetWrapper : node), node.r.at(-1)?.typ === exports.EnumToken.SemiColonTokenType + ? trimArray(node.r.slice(0, -1)) + : node.r); + clonedDeclaration[PARENT] = atRule; + atRule.chi.push(clonedDeclaration); + result.push(atRule); + processNode(clonedDeclaration, cache); } } + return result; +} +function processNode(declarationNode, cache) { + let i; let k = -1; - while (++k < parts.length) { - // if (parts[k] == ".") { - // parts.splice(k--, 1); - // } else - if (k > 0 && parts[k] == "..") { - parts.splice(k - 1, 2); - k -= 2; - } - } - return (path.charAt(0) == "/" ? "/" : "") + parts.join("/"); -}); -/** - * diff path - * @param path1 - * @param path2 - * @private - */ -const diff = memoize(function (path1, path2) { - let { parts } = splitPath(path1); - const { parts: dirs } = splitPath(path2); - for (const p of dirs) { - if (parts[0] == p) { - parts.shift(); - } - else { - parts.unshift(".."); - } - } - return parts.join("/"); -}); -/** - * resolve path - * @param url url or path to resolve - * @param currentDirectory directory used to resolve the path - * @param cwd current working directory - * - * @private - */ -const resolve = memoize(function (url, currentDirectory, cwd) { - if (matchUrl.test(url)) { - return { - absolute: url, - relative: url, - }; - } - cwd ??= ""; - currentDirectory ??= ""; - url = normalize(url); - if (cwd !== "") { - cwd = normalize(cwd); - } - if (currentDirectory !== "") { - currentDirectory = normalize(currentDirectory); - } - let dir = cwd || currentDirectory; - if (windowsPathnameRegexp.test(dir)) { - dir = dir.replace(windowsPathnameRegexp, ""); - } - const absolute = dir == "" || url.startsWith("/") || url.startsWith(dir) || windowsPathnameRegexp.test(url) - ? resolvePath(url) - : resolvePath(dir, url); - return { - absolute, - relative: dir === "" ? absolute : diff(absolute, dir), - }; -}); -/** - * - * @param parts - * @returns - * @private - */ -function resolvePath(...parts) { - const path = parts.filter(Boolean).join("/"); - const isAbsolute = /^[\\/]/.test(path); - const segments = path.split(/[\\/]+/); - const resolved = []; - for (const segment of segments) { - if (!segment || segment === ".") { + let astNode; + const result = []; + const stack = [declarationNode]; + while (++k < stack.length) { + astNode = stack[k]; + const { node: declaration, value: node } = findByValue(astNode, nodeMatcher) ?? {}; + if (declaration == null || node == null) { + result.push(astNode); continue; } - if (segment === "..") { - if (resolved.length && resolved[resolved.length - 1] !== "..") { - resolved.pop(); - } - else if (!isAbsolute) { - resolved.push(".."); + // @ts-expect-error + const parents = [...node.parents?.()]; + const parentWrapper = node.parent ?? parents.find((node) => !nodeMatcher(node)); + if (node.node.typ === exports.EnumToken.WildCardFunctionTokenType) { + for (i = 0; i < node.node.chi.length; i++) { + stack.push( + // @ts-expect-error + ...substituteIfElseNode( + // @ts-expect-error + declaration, node.node.chi[i], node.node, parentWrapper, cache)); } } else { - resolved.push(segment); + stack.push( + // @ts-expect-error + ...substituteIfElseNode( + // @ts-expect-error + declaration, node.node, parentWrapper, parents[parents.indexOf(parentWrapper) + 1] ?? declaration, cache)); } } - let result = resolved.join("/"); - if (isAbsolute) { - result = "/" + result; + if (result.length > 0) { + // @ts-expect-error + replaceNodeOrValue(declarationNode[PARENT], declarationNode, result); } - return result || (isAbsolute ? "/" : "."); + // else remove node? + // @ts-expect-error + return result; } - -/** - * Source file ID - */ -let sourceId = 0; -/** - * Source file helper class - */ -class SourceFile { - /** - * Source file ID - */ - id; - /** - * Source file path - */ - file; - /** - * Line map - */ - lineStarts; - /** - * Source file content - */ - content; - inputSourceMap = null; - /** - * Constructor - * @param content - * @param lines - * @param file - */ - constructor(content, lines, file = null) { - this.id = sourceId++; - this.content = content; - this.file = file; - this.lineStarts = new LineMap(lines); - } - /** - * Update source content - * @param content - */ - append(content) { - this.content += content; - } - /** - * get file name - * @returns - */ - getFileName() { - return this.file; - } - /** - * get content - * @returns - */ - getContent() { - return this.content; - } - /** - * get text - * @param start - * @param length - * @returns - */ - getText(start, length) { - return this.content.slice(start, start + length); - } - /** - * Compute line and column of the offset - * @param offset - * @returns - */ - getOffsets(offset) { - return this.lineStarts.getOffsets(offset); - } - /** - * get source location - * @param offset - * @returns - */ - getSourceLocation(offset) { - return [this.file, ...this.getOffsets(offset)]; - } - /** - * get line starts - * @returns - */ - getLineStarts() { - return this.lineStarts.getLineStarts(); +class ExpandIfFeature { + accept = new Set([exports.EnumToken.DeclarationNodeType]); + get ordering() { + return 1; } - /** - * add line start - * @param lineStart - */ - addLineStart(lineStart) { - this.lineStarts.addLineStart(lineStart); + get processMode() { + return exports.FeatureWalkMode.Pre; } - /** - * set input source map - * @param inputSourceMap - */ - setInputSourceMap(inputSourceMap) { - this.inputSourceMap = inputSourceMap == null ? null : new SourceMap(inputSourceMap); + static register(options) { + if (options.expandIfSyntax) { + // @ts-ignore + options.features.push(new ExpandIfFeature()); + } } - /** - * return input source map - * @returns - */ - getInputSourceMap() { - return this.inputSourceMap; + run(declaration) { + return processNode(declaration, new Set()); } } +var allFeatures = /*#__PURE__*/Object.freeze({ + __proto__: null, + ComputeCalcExpressionFeature: ComputeCalcExpressionFeature, + ComputePrefixFeature: ComputePrefixFeature, + ComputeShorthandFeature: ComputeShorthandFeature, + ExpandIfFeature: ExpandIfFeature, + InlineCssVariablesFeature: InlineCssVariablesFeature, + TransformCssFeature: TransformCssFeature +}); + +const notEndingWith = ["(", "["].concat(combinators); +const rules = [ + exports.EnumToken.AtRuleNodeType, + exports.EnumToken.RuleNodeType, + exports.EnumToken.AtRuleTokenType, + exports.EnumToken.KeyframesRuleNodeType, +]; +// @ts-ignore +const features = Object.values(allFeatures).sort((a, b) => a.ordering - b.ordering); /** - * render ast - * @param data + * Apply minification rules to the ast tree + * @param ast * @param options - * @param mapping + * @param recursive + * @param errors + * @param nestingContent + * + * @param context * @private */ -function doRender(data, options = {}, mapping) { - const minify = options.minify ?? true; - const beautify = options.beautify ?? !minify; - options = { - ...(beautify - ? { - indent: " ", - newLine: "\n", - } - : { - indent: "", - newLine: "", - }), - ...(minify - ? { - removeEmpty: true, - removeComments: true, - minify: true, - } - : { - removeEmpty: false, - removeComments: false, - }), - minifyValues: true, - sourcemap: false, - convertColor: true, - expandNestingRules: false, - preserveLicense: false, - ...options, - }; - if (options.withParents) { - let parent; - while (data[PARENT] != null) { - // @ts-ignore - // parent = { ...data[PARENT], chi: [data] }; - parent = cloneNode(data[PARENT]); - // @ts-ignore - parent.chi = [data]; - // @ts-ignore - parent[PARENT] = data[PARENT][PARENT]; - // @ts-ignore - data = parent; +function minify(ast, options = {}, recursive = false, errors, nestingContent, context = {}) { + let preprocess = false; + let postprocess = false; + let parents; + let replacement; + let { sourcemap, module, ...options2 } = options; + if (!(options2.features != null)) { + options2 = { + removeDuplicateDeclarations: true, + computeShorthand: true, + computeCalcExpression: true, + removePrefix: false, + features: [], + ...options2, + }; + for (const feature of features) { + feature.register(options2); } + options2.features.sort((a, b) => a.ordering - b.ordering); } - const startTime = performance.now(); - const errors = []; - const sourcemap = options.sourcemap ? new SourceMap() : null; - const sourcemaps = options.sourcemap ? { sources: [], maps: [] } : null; - const cache = Object.create(null); - const sourceLocation = { - end: 0, - }; - const linesMap = options.sourcemap ? new LineMap() : null; - let code = ""; - if (mapping != null) { - if (mapping.importMapping != null) { - const absolutePath = options.resolve(options.output != null ? dirname(options.output) : dirname(options.src), options.cwd).absolute; - for (let [key, value] of Object.entries(mapping.importMapping)) { - key = options.resolve(options.resolve(key, options.cwd).absolute, absolutePath).relative; - if (!key.startsWith("/") && !key.startsWith(".")) { - key = "./" + key; - } - code += `:import("${key}")${options.indent}{${options.newLine}${Object.entries(value).reduce((acc, [k, v]) => acc + (acc.length > 0 ? options.newLine : "") + `${options.indent}${v}:${options.indent}${k};`, "")}${options.newLine}}${options.newLine}`; - } + for (const feature of options2.features) { + if (feature.processMode & exports.FeatureWalkMode.Pre) { + preprocess = true; } - code += `:export${options.indent}{${options.newLine}${Object.entries(mapping.mapping).reduce((acc, [k, v]) => acc + (acc.length > 0 ? options.newLine : "") + `${options.indent}${k}:${options.indent}${v};`, "")}${options.newLine}}${options.newLine}`; - if (sourcemap != null) { - move(sourceLocation, linesMap, code); + if (feature.processMode & exports.FeatureWalkMode.Post) { + postprocess = true; } } - if (options.output != null) { - // @ts-ignore - options.output = options.resolve(options.output, options.cwd).absolute; - } - const result = { - code: code + - renderAstNode(options.expandNestingRules && - [exports.EnumToken.StyleSheetNodeType, exports.EnumToken.AtRuleNodeType, exports.EnumToken.RuleNodeType].includes(data.typ) && - "chi" in data - ? expand(data) - : data, options, sourcemaps, sourceLocation, linesMap, errors, function reducer(acc, curr) { - if (curr.typ == exports.EnumToken.CommentTokenType && options.removeComments) { - if (!options.preserveLicense || !curr.val.startsWith("/*!")) { - return acc; - } - return acc + curr.val; + if (preprocess) { + parents = new Set([ast]); + for (const parent of parents) { + if (parent.typ == exports.EnumToken.CommentTokenType || parent.typ == exports.EnumToken.CDOCOMMTokenType) { + continue; + } + replacement = parent; + for (const feature of options2.features) { + if ((feature.processMode & exports.FeatureWalkMode.Pre) === 0 || + (feature.accept != null && !feature.accept.has(parent.typ))) { + continue; } - return acc + renderValue(curr, options, cache, reducer); - }, cache), - errors, - stats: { - total: `${(performance.now() - startTime).toFixed(2)}ms`, - }, - }; - if (sourcemap != null) { - let source; - for (const sourceId of sourcemaps.sources) { - source = options.sourcesMap.get(sourceId); - sourcemap.addSourceContent(source.id, source.getFileName(), source.getContent()); - } - sourcemap.add(sourcemaps.maps); - result.map = sourcemap; - if (options.sourcemap === "inline") { - result.code += `\n/*# sourceMappingURL=${result.map.toUrl()} */`; - } - } - return result; -} -/** - * Update source map - * @param node - * @param options - * @param cache - * @param sourcemaps - * @param sourceLocation - * @param linesMap - * @param str - * - * @internal - */ -function updateSourceMap(node, options, cache, sourcemaps, sourceLocation, linesMap, str) { - let offset = 0; - // eat leanding whitespace - while (offset < str.length && isWhiteSpace(str.charCodeAt(offset))) { - offset++; - } - if (offset > 0) { - move(sourceLocation, linesMap, str, 0, offset + 1); - } - if (node[LOCSTA] != null) { - const source = options.sourcesMap.get(node[LOCSRCID]); - const inputSourceMap = source.getInputSourceMap(); - const offsets = source.getOffsets(node[LOCSTA]); - const [newLine, newColumn] = linesMap.getOffsets(sourceLocation.end); - let records = null; - let srcId = node[LOCSRCID]; - let sourceFileName = source.getFileName() || null; - let sourceContent; // = (source.getContent() as string) || null; - if (inputSourceMap != null && (records = inputSourceMap.find(offsets[0], offsets[1])) != null) { - let newId = null; - for (const record of records) { - newId = null; + if (rules.includes(replacement.typ) && !Array.isArray(replacement[TOKENS])) { + replacement[TOKENS] = parseString(replacement.typ == exports.EnumToken.RuleNodeType || replacement.typ === exports.EnumToken.KeyframesRuleNodeType + ? replacement.sel + : // @ts-ignore + replacement.nam); + } + const result = feature.run(replacement, options2, // @ts-ignore - sourceFileName = record[0] || null; + parent[PARENT] ?? ast, context, exports.FeatureWalkMode.Pre); + if (result != null) { + replacement = result; + } + } + if (replacement != null && + (!Array.isArray(replacement) || replacement.length > 0) && + replacement != parent && + parent[PARENT] != null) { // @ts-ignore - offsets[0] = record[1]; + replaceNodeOrValue(parent[PARENT], parent, replacement); + } + // @ts-ignore + if (replacement.chi != null) { // @ts-ignore - offsets[1] = record[2]; - sourceContent = record[3] || null; - if (sourceFileName != null && options.output != null && !sourceFileName.startsWith("data:")) { - if (cache[sourceFileName] == null) { - const absolute = options.resolve(dirname(options.output), options.cwd) - .absolute; - const absoluteSourcePath = options.resolve(dirname(options.src || ""), options.cwd).absolute; - // resolution is relative to the source file - const absoluteSourceFileName = options.resolve(sourceFileName, absoluteSourcePath) - .absolute; - cache[sourceFileName] = options.resolve(absoluteSourceFileName, absolute).relative; - } - sourceFileName = cache[sourceFileName]; - } - for (const [id, file] of options.sourcesMap.entries()) { - if (file.getFileName() === sourceFileName) { - newId = id; - break; - } - if (sourceFileName == null && file.getContent() === sourceContent) { - newId = id; - break; - } - } - if (newId == null) { - const source = new SourceFile(sourceContent, [], sourceFileName); - options.sourcesMap.set(source.id, source); - newId = source.id; - } - srcId = newId; - if (!sourcemaps.sources.includes(srcId)) { - sourcemaps.sources.push(srcId); + for (const node of replacement.chi) { + node[PARENT] = replacement; + parents.add(node); } - sourcemaps.maps.push([newLine, newColumn, srcId, offsets[0], offsets[1]]); } } - else { - // if (sourceFileName != null && options.output != null && !sourceFileName.startsWith("data:")) { - // if (cache[sourceFileName] == null) { - // const absolute = options.resolve!(dirname(options.output as string), options.cwd as string) - // .absolute as string; - // const absoluteSourceFileName = options.resolve!(sourceFileName, options.cwd as string) - // .absolute as string; - // cache[sourceFileName] = options.resolve!(absoluteSourceFileName, absolute).relative as string; - // } - // sourceFileName = cache[sourceFileName] as string; - // } - if (!sourcemaps.sources.includes(srcId)) { - sourcemaps.sources.push(srcId); + for (const feature of options2.features) { + if (feature.processMode & exports.FeatureWalkMode.Pre && "cleanup" in feature) { + // @ts-ignore + feature.cleanup(ast, options2, context, exports.FeatureWalkMode.Pre); } - sourcemaps.maps.push([newLine, newColumn, srcId, offsets[0], offsets[1]]); } } - move(sourceLocation, linesMap, str, offset); -} -/** - * Update position - * @param sourceLocation - * @param linesMap - * @param str - * @param start - * @param end - */ -function move(sourceLocation, linesMap, str, start, end) { - let i = start ?? 0; - let j = end ?? str.length; - let codepoint; - let char; - for (; i < j; i++) { - char = str.charAt(i); - codepoint = char.charCodeAt(0); - sourceLocation.end += char.length; - if (codepoint == 0xa || // \n - codepoint == 0xb || // \v - codepoint == 0xc || // \f - codepoint == 0xd || // \r - codepoint == 0x2028 || // \u2028 - codepoint == 0x2029 // \u2029 - ) { - if (codepoint == 0xd && i + 1 < str.length && str.charCodeAt(i + 1) == 0xa) ; - else if (codepoint == 0xa && i > 0 && str.charCodeAt(i - 1) == 0xd) ; - else { - linesMap.addLineStart(sourceLocation.end); - } + doMinify(ast, options2, recursive, errors, nestingContent, context); + parents = new Set([ast]); + for (const parent of parents) { + if (parent.typ == exports.EnumToken.CommentTokenType || parent.typ == exports.EnumToken.CDOCOMMTokenType) { + continue; } - } -} -/** - * render ast node - * @param data - * @param options - * @param sourcemaps - * @param sourceLocation - * @param linesMap - * @param errors - * @param reducer - * @param cache - * @param level - * @param indents - * - * @internal - */ -function renderAstNode(data, options, sourcemaps, sourceLocation, linesMap, errors, reducer, cache, level = 0, indents = []) { - if (indents.length < level + 1) { - indents.push(options.indent.repeat(level)); - } - if (indents.length < level + 2) { - indents.push(options.indent.repeat(level + 1)); - } - // @ts-ignore - let children = ""; - let str = ""; - const indent = indents[level]; - const indentSub = indents[level + 1]; - switch (data.typ) { - case exports.EnumToken.DeclarationNodeType: - return `${data.nam}:${options.indent}${(options.minifyValues - ? filterValues(data.val) - : data.val).reduce(reducer, "")}`; - case exports.EnumToken.CommentNodeType: - case exports.EnumToken.CDOCOMMNodeType: - if (data.val.startsWith("/*# sourceMappingURL=")) { - // ignore sourcemap comment - return ""; - } - return !options.removeComments || (options.preserveLicense && data.val.startsWith("/*!")) - ? data.val - : ""; - case exports.EnumToken.StyleSheetNodeType: - for (const node of data.chi) { - str = renderAstNode(node, options, sourcemaps, sourceLocation, linesMap, errors, reducer, cache, level, indents); - if (str === "") { + replacement = parent; + if (postprocess) { + for (const feature of options2.features) { + if ((feature.processMode & exports.FeatureWalkMode.Post) === 0 || + (feature.accept != null && !feature.accept.has(parent.typ))) { continue; } - if (children.length > 0) { - str = options.newLine + str; - } - children += str; - if (sourcemaps != null && str !== "" && options.newLine) { - move(sourceLocation, linesMap, options.newLine); + const result = feature.run(replacement, options2, + // @ts-ignore + parent[PARENT] ?? ast, context, exports.FeatureWalkMode.Post); + if (result != null) { + replacement = result; } } - return children; - case exports.EnumToken.AtRuleNodeType: - case exports.EnumToken.RuleNodeType: - case exports.EnumToken.KeyframesRuleNodeType: - case exports.EnumToken.KeyframesAtRuleNodeType: - if ([exports.EnumToken.AtRuleNodeType, exports.EnumToken.KeyframesAtRuleNodeType].includes(data.typ) && !("chi" in data)) { - return `${indent}@${data.nam}${data.val === "" ? "" : options.indent || " "}${data.val};`; + } + if (replacement != null && + (!Array.isArray(replacement) || replacement.length > 0) && + replacement != parent && + parent[PARENT] != null) { + // @ts-ignore + replaceNodeOrValue(parent[PARENT], parent, replacement); + } + // @ts-ignore + if (replacement.chi != null) { + // @ts-ignore + for (const node of replacement.chi) { + node[PARENT] = replacement; + parents.add(node); } - const lineMapLength = linesMap ? linesMap.getLineStarts().length : 0; - const prelude = (indent.length > 0 ? options.newLine : "") + - indent + - ([exports.EnumToken.AtRuleNodeType, exports.EnumToken.KeyframesAtRuleNodeType].includes(data.typ) - ? `@${data.nam}${data.val === "" ? "" : options.indent || " "}${data.val}${options.indent}{` - : data.sel + `${options.indent}{`); - if (sourcemaps != null) { - updateSourceMap(data, options, cache, sourcemaps, sourceLocation, linesMap, prelude); + } + } + if (postprocess) { + for (const feature of options2.features) { + if (feature.processMode & exports.FeatureWalkMode.Post && "cleanup" in feature) { + // @ts-ignore + feature.cleanup(ast, options2, context, exports.FeatureWalkMode.Post); } - let node; - let recordDeclarationSourceMap = data.typ == exports.EnumToken.AtRuleNodeType; - for (let i = 0; i < data.chi.length; i++) { - node = data.chi[i]; - if (node.typ == exports.EnumToken.CommentNodeType) { - str = - options.removeComments && - (!options.preserveLicense || !node.val.startsWith("/*!")) - ? "" - : node.val; - } - else if (node.typ == exports.EnumToken.DeclarationNodeType) { - str = `${node.nam}:${options.indent}${(options.minifyValues - ? filterValues(node.val) - : node.val) - .reduce(reducer, "") - .trimEnd()};`; + } + } + return ast; +} +function transformAtRuleMediaPrelude(values) { + let hasUpdates = false; + for (let { value, parent, parents } of walkValues(values)) { + if (value.typ === exports.EnumToken.MediaQueryConditionTokenType) { + if (value.op.typ == exports.EnumToken.AndTokenType && + // @ts-ignore + value.l.typ === exports.EnumToken.IdenTokenType && + // @ts-ignore + value.l.val.toLowerCase() === "all") { + if (parent === null) { + // @ts-ignore + values[values.indexOf(value)] = value.l; } else { - str = renderAstNode(node, options, sourcemaps, sourceLocation, linesMap, errors, reducer, cache, level + 1, indents); - if (str === "") { - continue; - } - children += str; - str = ""; - continue; - } - if (str === "") { - continue; + // @ts-ignore + replaceNodeOrValue(parent, value, value.l); + // @ts-ignore + value = value.l; } - str = options.newLine + indentSub + str; - children += str; - if (sourcemaps != null && str !== "") { - if (node.typ == exports.EnumToken.DeclarationNodeType && recordDeclarationSourceMap) { - // if declaration is child of at-rule, then record it - // .rule { - // @media screen { - // color: red; - // } - // } + hasUpdates = true; + } + } + // range operator + if (parent != null && + parent.typ === exports.EnumToken.MediaQueryConditionTokenType && + parent.op.typ == exports.EnumToken.AndTokenType && + // @ts-ignore + parent.l.typ == exports.EnumToken.ParensTokenType) { + let token = parent.r.find((t) => t.typ !== exports.EnumToken.WhitespaceTokenType && t.typ !== exports.EnumToken.CommentTokenType); + if (token?.typ === exports.EnumToken.ParensTokenType) { + // @ts-ignore + const node1 = parent.l.chi.find((t) => t.typ !== exports.EnumToken.WhitespaceTokenType && t.typ !== exports.EnumToken.CommentTokenType); + const node2 = token.chi.find((t) => t.typ !== exports.EnumToken.WhitespaceTokenType && t.typ !== exports.EnumToken.CommentTokenType); + if (node1?.typ === exports.EnumToken.MediaQueryConditionTokenType && + node2?.typ === exports.EnumToken.MediaQueryConditionTokenType && + node1.op.typ == exports.EnumToken.ColonTokenType && + node2.op.typ == exports.EnumToken.ColonTokenType && + // @ts-ignore + node1.l.typ == exports.EnumToken.IdenTokenType && + // @ts-ignore + node2.l.typ == exports.EnumToken.IdenTokenType && + // @ts-ignore + node1.l.val.startsWith("min-") && + // @ts-ignore + node2.l.val.startsWith("max-") && + // @ts-ignore + node1.l.val.slice(4) == + // @ts-ignore + node2.l.val.slice(4)) { + const val1 = node1.r.find((t) => t.typ !== exports.EnumToken.WhitespaceTokenType && t.typ !== exports.EnumToken.CommentTokenType); + const val2 = node2.r.find((t) => t.typ !== exports.EnumToken.WhitespaceTokenType && t.typ !== exports.EnumToken.CommentTokenType); + const replacement = { + typ: exports.EnumToken.ParensTokenType, + chi: [ + // @ts-ignore + { + typ: exports.EnumToken.MediaRangeQueryTokenType, + op: { + typ: exports.EnumToken.IdenTokenType, + // @ts-ignore + val: node1.l.val.slice(4), + }, + l: val1, + r: val2, + [LOCSRCID]: value[LOCSRCID], + [LOCSTA]: value[LOCSTA], + [LOCEND]: value[LOCEND], + }, + ], + }; + // @ts-expect-error + const p = parents?.[parents?.indexOf?.(parent) + 1]; + if (p != null) { // @ts-ignore - updateSourceMap(node, options, cache, sourcemaps, sourceLocation, linesMap, str); + replaceNodeOrValue(p, parent, replacement); } else { - move(sourceLocation, linesMap, str); + // @ts-ignore + values.splice(values.indexOf(parent), 1, replacement); } + hasUpdates = true; + value = replacement; } } - if (children.endsWith(";")) { - children = children.slice(0, -1); - sourceLocation.end--; + } + } + return { hasUpdates, values: trimArray(values) }; +} +/** + * Minify at-rule media + * - remove redundant tokens + * - generate range queries + * + * @private + * @param tokens + */ +function minifyAtRuleMedia(tokens) { + let hasUpdates = false; + const sections = tokens + .reduce((acc, t) => { + if (t.typ === exports.EnumToken.CommaTokenType) { + acc.push([]); + } + else { + acc[acc.length - 1].push(t); + } + return acc; + }, [[]]) + .reduce((acc, values) => { + if (acc.has("all")) { + return acc; + } + const result = transformAtRuleMediaPrelude(values); + if (result.values.length === 0) { + return acc; + } + if (result.hasUpdates) { + hasUpdates = true; + } + acc.set(values.reduce((acc, t) => acc + printValue(t), ""), result.values); + return acc; + }, new Map()); + if (sections.has("all")) { + tokens.length = 0; + } + else if (hasUpdates) { + tokens.length = 0; + tokens.push(...[...sections.values()].reduce((acc, t) => { + if (acc.length > 0) { + acc.push({ + typ: exports.EnumToken.CommaTokenType, + }); } - if (options.removeEmpty && children === "") { - if (sourcemaps != null) { - sourceLocation.end -= prelude.length; - linesMap.getLineStarts().length = lineMapLength; - } - return ""; + for (const token of t) { + acc.push(token); } - const end = options.newLine + indent + `}`; - if (sourcemaps != null) { - move(sourceLocation, linesMap, end); + return acc; + }, [])); + } + // return ast; + return tokens; +} +/** + * Reduce selectors + * @param acc + * @param curr + * + * @private + */ +function reduce(acc, curr) { + // trim :is() + if (curr[0] == "&") { + if (curr[1] == " " && !isIdent(curr[2]) && !isFunction(curr[2])) { + const all = new Array(Math.max(curr.length - 2, 0)); + let write = 0; + for (let read = 2; read < curr.length; read++) { + all[write++] = curr[read]; } - return prelude + children + end; - default: - return ""; + curr.length = 0; + for (let index = 0; index < write; index++) { + curr[index] = all[index]; + } + } } + acc.push(curr.join("")); + return acc; } /** - * render ast token - * @param token + * Apply minification rules to the ast tree + * @param ast * @param options - * @param cache - * @param reducer + * @param recursive * @param errors + * @param nestingContent + * @param context + * * @private */ -function renderValue(token, options = {}, cache = Object.create(null), reducer, errors) { - if (token.typ === exports.EnumToken.WhenElseFunctionTokenType && - equalsIgnoreCase(token.val, "supports")) { - options = { ...options, minifyValues: false, minify: false, convertColor: false }; - reducer = null; +function doMinify(ast, options = {}, recursive = false, errors, nestingContent, context = {}) { + if (!("nodes" in context)) { + context.nodes = new Set(); } - options.minifyValues ??= options.minify; - if (reducer == null) { - reducer = function (acc, curr) { - if (curr.typ == exports.EnumToken.CommentTokenType && options.removeComments) { - if (!options.preserveLicense || !curr.val.startsWith("/*!")) { - return acc; - } - return acc + curr.val; - } - return acc + renderValue(curr, options, cache, reducer); - }; + if (context.nodes.has(ast)) { + return ast; } - switch (token.typ) { - case exports.EnumToken.UrlFunctionTokenDefType: - case exports.EnumToken.FunctionTokenDefType: - case exports.EnumToken.MathFunctionTokenDefType: - case exports.EnumToken.ImageFunctionTokenDefType: - case exports.EnumToken.ColorFunctionTokenDefType: - case exports.EnumToken.TimingFunctionTokenDefType: - case exports.EnumToken.WhenElseFunctionTokenDefType: - case exports.EnumToken.SupportsFunctionTokenDefType: - case exports.EnumToken.TimelineFunctionTokenDefType: - case exports.EnumToken.GridTemplateFuncTokenDefType: - case exports.EnumToken.TransformFunctionTokenDefType: - case exports.EnumToken.ContainerFunctionTokenDefType: - case exports.EnumToken.PseudoClassFunctionTokenDefType: - case exports.EnumToken.GeneralEnclosedFunctionTokenDefType: - case exports.EnumToken.CustomFunctionTokenDefType: - case exports.EnumToken.WildCardFunctionTokenDefType: - return token.val + "("; - case exports.EnumToken.ListToken: - return token.chi.reduce((acc, curr) => acc + renderValue(curr, options, cache), ""); - case exports.EnumToken.BinaryExpressionTokenType: - if (exports.EnumToken.Mul === token.op || - exports.EnumToken.Div === token.op) { - let result = ""; - if (token.l.typ == exports.EnumToken.BinaryExpressionTokenType && - [exports.EnumToken.Add, exports.EnumToken.Sub].includes(token.l.op)) { - result = "(" + renderValue(token.l, options, cache) + ")"; - } - else { - result = renderValue(token.l, options, cache); - } - result += token.op == exports.EnumToken.Mul ? "*" : "/"; - if (token.r.typ == exports.EnumToken.BinaryExpressionTokenType && - [exports.EnumToken.Add, exports.EnumToken.Sub].includes(token.r.op)) { - result += "(" + renderValue(token.r, options, cache) + ")"; - } - else { - result += renderValue(token.r, options, cache); - } - return result; + context.nodes.add(ast); + // @ts-ignore + if ("chi" in ast && ast.chi.length > 0) { + const reducer = reduce.bind(ast); + if (!nestingContent) { + nestingContent = options.nestingRules && ast.typ == exports.EnumToken.RuleNodeType; + } + let i = 0; + let previous = null; + let node = null; + let nodeIndex = -1; + for (; i < ast.chi.length; i++) { + if (ast.chi[i].typ === exports.EnumToken.CommentNodeType) { + continue; } - return (renderValue(token.l, options, cache) + - (token.op == exports.EnumToken.Add ? " + " : " - ") + - renderValue(token.r, options, cache)); - case exports.EnumToken.FractionTokenType: - const fraction = renderValue(token.l) + "/" + renderValue(token.r); - if (+token.r.val != 0) { - const value = minifyNumber(+token.l.val / +token.r.val); - if (value.length <= fraction.length) { - return value; - } + while (previous?.typ === exports.EnumToken.CommentNodeType) { + // @ts-ignore + previous = ast.chi[--nodeIndex]; } - return fraction; - case exports.EnumToken.Add: - return " + "; - case exports.EnumToken.Sub: - return " - "; - case exports.EnumToken.UniversalSelectorTokenType: - case exports.EnumToken.Star: - case exports.EnumToken.Mul: - return "*"; - case exports.EnumToken.Div: - return "/"; - case exports.EnumToken.WrappedValuesTokenType: - return ("{" + - token.chi.reduce((acc, curr) => acc + renderValue(curr, options, cache), "") + - "}"); - case exports.EnumToken.ColorTokenType: - if (token.kin == exports.ColorType.LIGHT_DARK || - ("chi" in token && options.convertColor === false && token.chi.length == 2)) { - return (token.val + - "(" + - token.chi.reduce((acc, curr) => { - if (curr.typ === exports.EnumToken.IdenTokenType) { - parseColor(curr); - } - return acc + renderValue(curr, options, cache); - }, "") + - ")"); + node = ast.chi[i]; + if (node.typ === exports.EnumToken.AtRuleNodeType && node.nam === "font-face") { + continue; } - if (options.convertColor !== false) { - const value = convertColor(token, typeof options.convertColor === "boolean" - ? exports.ColorType.HEX - : (exports.ColorType[exports.ColorType[options.convertColor ?? "HEX"] - ?.toUpperCase?.() - .replaceAll?.("-", "_")] ?? exports.ColorType.HEX)); - if (value != null) { - token = value; + if (node.typ === exports.EnumToken.KeyframesAtRuleNodeType) { + if (previous?.typ === exports.EnumToken.KeyframesAtRuleNodeType && + node.nam === previous.nam && + node.val === previous.val) { + ast.chi?.splice(nodeIndex--, 1); + previous = ast?.chi?.[nodeIndex] ?? null; + i = nodeIndex; + continue; } - if (token.kin == exports.ColorType.HEX || token.kin == exports.ColorType.LIT) { - return reduceHexValue(token.val); + } + else if (node.typ === exports.EnumToken.KeyframesRuleNodeType) { + if (previous?.typ === exports.EnumToken.KeyframesRuleNodeType && + node.sel === previous.sel) { + // do not merge keyframes + // https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@keyframes#resolving_duplicates + for (const child of node.chi) { + previous.chi.push(child); + } + // @ts-ignore + ast.chi.splice(i, 1); + previous = ast?.chi?.[nodeIndex] ?? null; + i = nodeIndex; + continue; + } + let k; + for (k = 0; k < node.chi.length; k++) { + if (node.chi[k].typ == exports.EnumToken.DeclarationNodeType) { + let l = node.chi[k].val.length; + while (l--) { + if (node.chi[k].val[l].typ == + exports.EnumToken.ImportantTokenType) { + node.chi.splice(k--, 1); + break; + } + if ([exports.EnumToken.WhitespaceTokenType, exports.EnumToken.CommentTokenType].includes(node.chi[k].val[l].typ)) { + continue; + } + break; + } + } } } - if (token.kin === exports.ColorType.SYS || - token.kin === exports.ColorType.DPSYS || - token.kin === exports.ColorType.NON_STD) { - return token.val; - } - if (token.kin == exports.ColorType.HEX || token.kin == exports.ColorType.LIT) { - return token.val; - } - if (Array.isArray(token.chi)) { - const fnName = token.val; - const isLegacy = ["rgb", "rgba", "hsl", "hsla"].includes(token.val); - const hasAlpha = [ - "rgb", - "rgba", - "hsl", - "hsla", - "hwb", - "oklab", - "oklch", - "lab", - "lch", - ].includes(fnName); - const useAlpha = (hasAlpha && token.chi.length == (isLegacy ? 7 : 4)) || - ("color" == token.val && token.chi.length == 5); - return ((hasAlpha && token.val.endsWith("a") ? fnName.slice(0, -1) : fnName) + - "(" + - token - .chi.reduce((acc, curr, index, array) => { - if (/[,/]\s*$/.test(acc)) { - if (curr.typ == exports.EnumToken.WhitespaceTokenType) { - return acc.trimEnd(); + else if (node.typ == exports.EnumToken.AtRuleNodeType) { + if (node.nam == "media") { + if (Array.isArray(node[TOKENS])) { + const slice = node[TOKENS].slice(); + minifyAtRuleMedia(slice); + if (slice.length !== node[TOKENS].length) { + node[TOKENS].length = 0; + for (const token of slice) { + node[TOKENS].push(token); } - return acc.trimStart() + renderValue(curr, options, cache); - } - if (isLegacy && curr.typ == exports.EnumToken.CommaTokenType) { - return acc.trimEnd() + " "; - } - if (curr.typ == exports.EnumToken.WhitespaceTokenType) { - return acc.trimEnd() + " "; + node.val = slice.reduce((acc, curr, index, arr) => acc + + (curr.typ === exports.EnumToken.CommentTokenType || + (curr.typ === exports.EnumToken.WhitespaceTokenType && + arr[index + 1]?.typ === exports.EnumToken.CommentTokenType && + (index + 3 < arr.length || + arr[index + 2]?.typ === exports.EnumToken.WhitespaceTokenType)) + ? "" + : printValue(curr)), ""); } - if (curr.typ == exports.EnumToken.CommaTokenType || - (curr.typ == exports.EnumToken.LiteralTokenType && curr.val == "/")) { - return acc.trimEnd() + (curr.typ == exports.EnumToken.CommaTokenType ? "," : "/"); + } + if (["all", "", null].includes(node.val)) { + ast.chi?.splice(i--, 1, ...node.chi); + continue; + } + } + else if (node.nam === "import" && Array.isArray(node[TOKENS])) { + let l = 0; + let token; + for (; l < node[TOKENS].length; l++) { + token = node[TOKENS][l]; + if (token.typ === exports.EnumToken.ParensTokenType || + token.typ === exports.EnumToken.MediaQueryConditionTokenType || + (token.typ === exports.EnumToken.IdenTokenType && "layer" !== token.val)) { + break; } - return (acc.trimEnd() + - (useAlpha && index == array.length - 1 ? "/" : " ") + - renderValue(curr, options, cache)); - }, "") - .trimStart() + - ")"); - } - case exports.EnumToken.UrlFunctionTokenType: - if (options.minifyValues && token.typ === exports.EnumToken.UrlFunctionTokenType) { - if (token.chi[0]?.typ === exports.EnumToken.BadUrlTokenType) { - return "url()"; + } + if (l < node[TOKENS].length) { + const slice = node[TOKENS]?.slice(l); + node[TOKENS].splice(l, slice.length, ...minifyAtRuleMedia(slice)); + node.val = trimArray(node[TOKENS]).reduce((acc, curr, index, arr) => acc + + (curr.typ === exports.EnumToken.CommentTokenType || + (curr.typ === exports.EnumToken.WhitespaceTokenType && + arr[index + 1]?.typ === exports.EnumToken.CommentTokenType && + (index + 3 < arr.length || arr[index + 2].typ === exports.EnumToken.WhitespaceTokenType)) + ? "" + : printValue(curr)), ""); + } } - for (const child of token.chi) { - if (child.typ === exports.EnumToken.StringTokenType) { - if (child.val.slice(1, 5) !== "data:" && - urlTokenMatcher.test(child.val)) { - Object.assign(child, { - typ: exports.EnumToken.UrlTokenTokenType, - val: child.val.slice(1, -1), - }); + else if (ast.typ === node.typ && + ast.nam === node.nam && + ast.val === node.val) { + // @ts-ignore + replaceNodeOrValue(ast, node, node.chi); + i--; + continue; + } + if (previous?.typ == exports.EnumToken.AtRuleNodeType && + node.nam != "font-face" && + previous.nam === node.nam && + previous.val === node.val) { + if ("chi" in node) { + for (const child of node.chi) { + previous.chi.push(child); + } + if (!hasDeclaration(previous)) { + context.nodes.delete(previous); + doMinify(previous, options, recursive, errors, nestingContent, context); } - break; } + ast?.chi?.splice(i--, 1); + continue; + } + // if (!hasDeclaration(node as AstAtRule)) { + // doMinify(node, options, recursive, errors, nestingContent, context); + // } + if ("chi" in node) { + doMinify(node, options, recursive, errors, nestingContent, context); } + previous = node; + nodeIndex = i; + continue; } - case exports.EnumToken.ParensTokenType: - case exports.EnumToken.FunctionTokenType: - case exports.EnumToken.MathFunctionTokenType: - case exports.EnumToken.ImageFunctionTokenType: - if (options.minifyValues && token.typ === exports.EnumToken.ImageFunctionTokenType) { - const slice = token.chi.slice(); - switch (token.val) { - case "linear-gradient": - case "repeating-linear-gradient": - { - let i; - let k; - let j; - let key; - for (i = 0; i < slice.length; i++) { - if (slice[i].typ == exports.EnumToken.ColorTokenType || - slice[i].typ === exports.EnumToken.CommaTokenType) { - break; - } - if (slice[i].typ == exports.EnumToken.IdenTokenType && - equalsIgnoreCase(slice[i].val, "to")) { - k = i + 1; - while (k < slice.length && - (slice[k].typ === exports.EnumToken.WhitespaceTokenType || - slice[k].typ === exports.EnumToken.CommentTokenType)) { - k++; - } - if (k < slice.length && slice[k].typ === exports.EnumToken.IdenTokenType) { - // convert keyword to angle - key = slice[k].val.toLowerCase(); - switch (key) { - case "right": - slice.splice(i, k - i + 1, { - typ: exports.EnumToken.AngleTokenType, - val: 90, - }); - break; - case "left": - slice.splice(i, k - i + 1, { - typ: exports.EnumToken.AngleTokenType, - val: 270, - }); - break; - case "top": - case "bottom": - j = k + 1; - while (j < slice.length && - (slice[j].typ === exports.EnumToken.WhitespaceTokenType || - slice[j].typ === exports.EnumToken.CommentTokenType)) { - j++; - } - if (j < slice.length && slice[j].typ !== exports.EnumToken.CommaTokenType) { - i = j - 1; - break; - } - // https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/gradient/linear-gradient#:~:text=If%20unspecified%2C%20it%20defaults%20to%20to%20bottom - if (key === "bottom") { - // to bottom -> 180deg is the default angle and will be removed - while (k < slice.length && - slice[k].typ != exports.EnumToken.CommaTokenType) { - k++; - } - slice.splice(i, k - i + 1); - i--; - continue; - } - slice.splice(i, k - i + 1, { - typ: exports.EnumToken.AngleTokenType, - val: key === "top" ? 0 : 180, - }); - i--; - break; - } - } - } - else if (slice[i].typ == exports.EnumToken.AngleTokenType) { - if ((180 - toDegrees(slice[i]).val) % 360 === 0) { - k = i + 1; - // while (k < slice.length && slice[k].typ !== EnumToken.CommaTokenType) { - // k++; - // } - slice.splice(i, k - i + 1); - } - } - } - if (slice[i]?.typ === exports.EnumToken.CommaTokenType) { - i++; - // while ( - // i < slice.length && - // (slice[i].typ === EnumToken.WhitespaceTokenType || - // slice[i].typ === EnumToken.CommentTokenType) - // ) { - // i++; - // } - } - if (slice[i]?.typ === exports.EnumToken.ColorTokenType) { - for (const token of reduceColorStops(slice.splice(i, slice.length - i))) { - slice.push(token); - } - } + // @ts-ignore + else if (node.typ === exports.EnumToken.RuleNodeType) { + reduceRuleSelector(node); + let wrapper = null; + let match; + if (options.nestingRules) { + if (previous?.typ == exports.EnumToken.RuleNodeType) { + reduceRuleSelector(previous); + // @ts-ignore + match = matchSelectors(previous[RAW], node[RAW]); + if (match != null) { + wrapper = wrapNodes(previous, node, match, ast, reducer, i, nodeIndex); + nodeIndex = i - 1; + previous = ast.chi[nodeIndex]; } - break; - case "radial-gradient": - case "repeating-radial-gradient": - { - let i = 0; - // while ( - // i < slice.length && - // (slice[i].typ === EnumToken.WhitespaceTokenType || - // slice[i].typ === EnumToken.CommentTokenType) - // ) { - // i++; - // } - const positions = []; - const form = []; - const size = []; - const colorSpaceDef = []; - if (slice[i]?.typ === exports.EnumToken.IdenTokenType) { - if (equalsIgnoreCase(slice[i].val, "farthest-corner") || - equalsIgnoreCase(slice[i].val, "ellipse")) { - i++; - } - else if (equalsIgnoreCase(slice[i].val, "closest-side") || - equalsIgnoreCase(slice[i].val, "closest-corner") || - equalsIgnoreCase(slice[i].val, "farthest-side")) { - size.push(slice[i++]); - } - else if (equalsIgnoreCase(slice[i].val, "circle")) { - form.push(slice[i++]); - } - while (slice[i]?.typ === exports.EnumToken.WhitespaceTokenType || - slice[i]?.typ === exports.EnumToken.CommentTokenType) { - i++; - } - } - if (slice[i]?.typ === exports.EnumToken.IdenTokenType) { - if (equalsIgnoreCase(slice[i].val, "farthest-corner") || - equalsIgnoreCase(slice[i].val, "ellipse")) { - i++; - } - else if (equalsIgnoreCase(slice[i].val, "closest-side") || - equalsIgnoreCase(slice[i].val, "closest-corner") || - equalsIgnoreCase(slice[i].val, "farthest-side")) { - size.push(slice[i++]); - } - else if (equalsIgnoreCase(slice[i].val, "circle")) { - form.push(slice[i++]); - } - while (slice[i]?.typ === exports.EnumToken.WhitespaceTokenType || - slice[i]?.typ === exports.EnumToken.CommentTokenType) { - i++; - } - } - if (slice[i]?.typ === exports.EnumToken.IdenTokenType && - equalsIgnoreCase(slice[i].val, "at")) { - i++; - } - while (slice[i]?.typ === exports.EnumToken.WhitespaceTokenType || - slice[i]?.typ === exports.EnumToken.CommentTokenType) { - i++; - } - if (slice[i]?.typ === exports.EnumToken.PercentageTokenType || - (slice[i]?.typ === exports.EnumToken.NumberTokenType && 0 === slice[i].val) || - (slice[i]?.typ === exports.EnumToken.IdenTokenType && - (equalsIgnoreCase(slice[i].val, "center") || - equalsIgnoreCase(slice[i].val, "top") || - equalsIgnoreCase(slice[i].val, "bottom") || - equalsIgnoreCase(slice[i].val, "left") || - equalsIgnoreCase(slice[i].val, "right")))) { - do { - if (slice[i]?.typ === exports.EnumToken.IdenTokenType && - "in" === slice[i].val) { - break; - } - positions.push(slice[i]); - i++; - } while (slice[i]?.typ !== exports.EnumToken.CommaTokenType); - } - while (slice[i]?.typ !== exports.EnumToken.CommaTokenType) { - colorSpaceDef.push(slice[i++]); - } - if (slice[i]?.typ === exports.EnumToken.CommaTokenType) { - i++; - } - if (positions.length > 0) { - let k; - let position1 = ""; - let position2 = ""; - for (k = 0; k < positions.length; k++) { - if (positions[k].typ === exports.EnumToken.IdenTokenType) { - if (position1.length === 0) { - position1 = positions[k].val; - } - else { - position2 = positions[k].val; - } - } - else if (positions[k].typ === exports.EnumToken.PercentageTokenType || - positions[k].typ === exports.EnumToken.NumberTokenType) { - if (position1.length === 0) { - position1 = positions[k].val + "%"; - } - else { - position2 = positions[k].val + "%"; - } - } - if (position2.length > 0) { - break; - } - } - if (position1.length > 0) { - let key = `${position1} ${position2}`.trim(); - if (key === "50% 50%" || - key === "center center" || - key === "50%" || - key === "center") { - positions.length = 0; - } - else if (key === "0% 0%" || - key === "0 0" || - key === "top left" || - key === "left top") { - positions.splice(0, positions.length, { - typ: exports.EnumToken.PercentageTokenType, - val: 0, - }); - } - else if (key === "50% 0%" || - key === "50% 0" || - key === "center top" || - key === "top center") { - positions.splice(0, positions.length, { - typ: exports.EnumToken.IdenTokenType, - val: "top", - }); - } - else if (key === "top right" || key === "right top") { - positions.splice(0, positions.length, { - typ: exports.EnumToken.PercentageTokenType, - val: 100, - }, { typ: exports.EnumToken.WhitespaceTokenType }, { typ: exports.EnumToken.PercentageTokenType, val: 0 }); - } - else if (key === "0 50%" || - key === "0% 50%" || - key === "left center" || - key === "center left") { - positions.splice(0, positions.length, { - typ: exports.EnumToken.IdenTokenType, - val: "left", - }); - } - else if (key === "100% 50%" || key === "right center" || key === "center right") { - positions.splice(0, positions.length, { - typ: exports.EnumToken.IdenTokenType, - val: "right", - }); - } - else if (key === "0 100%" || - key === "0% 100%" || - key === "left bottom" || - key === "bottom left") { - positions.splice(0, positions.length, { - typ: exports.EnumToken.IdenTokenType, - val: "bottom", - }); - } - else if (key === "50% 100%" || - key === "center bottom" || - key === "bottom center") { - positions.splice(0, positions.length, { - typ: exports.EnumToken.IdenTokenType, - val: "bottom", - }); - } - else if (key === "100% 100%" || - key === "right bottom" || - key === "bottom right") { - positions.splice(0, positions.length, { - typ: exports.EnumToken.PercentageTokenType, - val: 100, - }); - } - else if (position1 === position2) { - positions.splice(0, positions.length, positions[k]); - } - } - } - const result = []; - if (form.length > 0) { - for (const token of form) { - result.push(token); - } - } - if (size.length > 0) { - if (result.length > 0) { - result.push({ typ: exports.EnumToken.WhitespaceTokenType }); - } - for (const token of size) { - result.push(token); - } + } + if (wrapper != null) { + while (i < ast.chi.length) { + const nextNode = ast.chi[i]; + if (nextNode.typ != exports.EnumToken.RuleNodeType) { + break; } - if (positions.length > 0) { - if (result.length > 0) { - result.push({ typ: exports.EnumToken.WhitespaceTokenType }); - } - result.push({ typ: exports.EnumToken.IdenTokenType, val: "at" }, { typ: exports.EnumToken.WhitespaceTokenType }); - for (const token of positions) { - result.push(token); - } + reduceRuleSelector(nextNode); + match = matchSelectors(wrapper[RAW], nextNode[RAW]); + if (match == null) { + break; } - if (colorSpaceDef.length > 0) { - if (result.length > 0) { - result.push({ typ: exports.EnumToken.WhitespaceTokenType }); - } - for (const token of colorSpaceDef) { - result.push(token); - } + wrapper = wrapNodes(wrapper, nextNode, match, ast, reducer, i, nodeIndex); + } + nodeIndex = --i; + previous = ast.chi[nodeIndex]; + doMinify(wrapper, options, recursive, errors, nestingContent, context); + continue; + } + // @ts-ignore + else if (node[OPTIMIZED] != null && + // @ts-ignore + node[OPTIMIZED].match && + // @ts-ignore + node[OPTIMIZED].selector.length > 1) { + // @ts-ignore + wrapper = { + ...node, + chi: [], + sel: node[OPTIMIZED].optimized[0], + [RAW]: [[node[OPTIMIZED].optimized[0]]], + }; + // @ts-ignore + node.sel = node[OPTIMIZED].selector.reduce(reducer, []).join(","); + // @ts-ignore + node[RAW] = node[OPTIMIZED].selector.slice(); + node[TOKENS] = null; + // @ts-ignore + wrapper.chi.push(node); + // @ts-ignore + ast.chi.splice(i, 1, wrapper); + node = wrapper; + } + else if (node[OPTIMIZED]?.reducible) { + if (node[OPTIMIZED].optimized.length === 1) { + const sel1 = node[OPTIMIZED].optimized[0] + + ":is(" + + node[OPTIMIZED].selector.reduce(reducer, []).join(",") + + ")"; + const sel2 = node[OPTIMIZED].selector.reduce((acc, curr) => + // @ts-ignore + (acc.length > 0 ? acc + "," : "") + node[OPTIMIZED].optimized[0] + curr.join(""), ""); + node.sel = sel1.length < sel2.length ? sel1 : sel2; + node[TOKENS] = null; + } + else if (node[OPTIMIZED].optimized.length === 0) { + const testIdent = /^[a-zA-Z]/; + node.sel = node[OPTIMIZED].selector.reduce((acc, curr) => (acc.length > 0 ? acc + "," : "") + + (nestingContent && testIdent.test(curr[0]) ? "& " : "") + + curr.join(""), ""); + node[TOKENS] = null; + } + } + } + // @ts-ignore + else if (node[OPTIMIZED]?.match) { + let wrap = true; + // @ts-ignore + const selector = node[OPTIMIZED].selector.reduce((acc, curr) => { + if (curr[0] == "&" && curr.length > 1) { + if (curr[1] == " ") { + curr.splice(0, 2); } - if (result.length > 0) { - result.push({ typ: exports.EnumToken.CommaTokenType }); + else { + curr.splice(0, 1); } - for (const token of reduceColorStops(slice.slice(i))) { - result.push(token); + } + else if (combinators.includes(curr[0])) { + curr.unshift("&"); + wrap = false; + } + acc.push(curr); + return acc; + }, []); + if (!wrap) { + wrap = selector.some((s) => s[0] != "&"); + } + let rule = null; + const optimized = node[OPTIMIZED].optimized.slice(); + if (optimized.length > 1) { + const check = optimized.at(-2); + if (!combinators.includes(check)) { + const last = optimized[optimized.length - 1]; + const all = new Array(Math.max(optimized.length - 1, 0)); + let write = 0; + for (let read = 0; read < optimized.length - 1; read++) { + all[write++] = optimized[read]; } - slice.length = 0; - for (const token of result) { - slice.push(token); + optimized.length = 0; + for (let index = 0; index < write; index++) { + optimized[index] = all[index]; } + wrap = false; + rule = + optimized.join("") + + `:is(${selector + .map((s) => { + const next = s[0] == "&" ? new Array(s.length) : new Array(s.length + 1); + let write = 0; + if (s[0] == "&") { + next[write++] = last; + for (let read = 1; read < s.length; read++) { + next[write++] = s[read]; + } + } + else { + next[write++] = last; + for (let read = 0; read < s.length; read++) { + next[write++] = s[read]; + } + } + const rebuilt = new Array(write); + for (let index = 0; index < write; index++) { + rebuilt[index] = next[index]; + } + return rebuilt.join(""); + }) + .join(",")})`; } - break; - case "conic-gradient": - case "repeating-conic-gradient": - { - let i = 0; - const angles = []; - const positions = []; - const colorSpaceDef = []; - // while ( - // i < slice.length && - // (slice[i].typ === EnumToken.WhitespaceTokenType || - // slice[i].typ === EnumToken.CommentTokenType) - // ) { - // i++; - // } - if (slice[i]?.typ === exports.EnumToken.IdenTokenType && - equalsIgnoreCase(slice[i].val, "from")) { - angles.push(slice[i++]); - while (i < slice.length && - (slice[i].typ === exports.EnumToken.WhitespaceTokenType || - slice[i].typ === exports.EnumToken.CommentTokenType)) { - angles.push(slice[i++]); - } - if ((slice[i]?.typ === exports.EnumToken.NumberTokenType || - slice[i]?.typ === exports.EnumToken.AngleTokenType) && - 0 === toDegrees(slice[i]).val) { - angles.length = 0; - i++; - } - else if (slice[i]?.typ !== exports.EnumToken.CommaTokenType && - slice[i].typ != exports.EnumToken.IdenTokenType) { - angles.push(slice[i++]); - } - while (i < slice.length && - (slice[i].typ === exports.EnumToken.WhitespaceTokenType || - slice[i].typ === exports.EnumToken.CommentTokenType)) { - i++; - } - } - if (slice[i]?.typ === exports.EnumToken.IdenTokenType && - equalsIgnoreCase(slice[i].val, "at")) { - i++; - while (i < slice.length && - (slice[i].typ === exports.EnumToken.WhitespaceTokenType || - slice[i].typ === exports.EnumToken.CommentTokenType)) { - i++; - } - let position1 = ""; - let position2 = ""; - if (slice[i]?.typ === exports.EnumToken.IdenTokenType && - !equalsIgnoreCase("in", slice[i].val)) { - position1 = slice[i].val; - positions.push(slice[i++]); - } - else if (slice[i]?.typ === exports.EnumToken.PercentageTokenType || - slice[i]?.typ === exports.EnumToken.NumberTokenType) { - position1 = slice[i].val + "%"; - positions.push(slice[i++]); - } - while (i < slice.length && - (slice[i].typ === exports.EnumToken.WhitespaceTokenType || - slice[i].typ === exports.EnumToken.CommentTokenType)) { - positions.push(slice[i++]); - } - if (slice[i]?.typ === exports.EnumToken.IdenTokenType && - !equalsIgnoreCase("in", slice[i].val)) { - position2 = slice[i].val; - positions.push(slice[i++]); - } - else if (slice[i]?.typ === exports.EnumToken.PercentageTokenType || - slice[i]?.typ === exports.EnumToken.NumberTokenType) { - position2 = slice[i].val + "%"; - positions.push(slice[i++]); + } + if (rule == null) { + rule = selector + .map((s) => { + if (s[0] == "&") { + const replacement = node[OPTIMIZED].optimized; + const all = new Array(Math.max(s.length - 1 + replacement.length, 0)); + let write = 0; + for (let read = 0; read < replacement.length; read++) { + all[write++] = replacement[read]; } - while (i < slice.length && - (slice[i].typ === exports.EnumToken.WhitespaceTokenType || - slice[i].typ === exports.EnumToken.CommentTokenType)) { - i++; + for (let read = 1; read < s.length; read++) { + all[write++] = s[read]; } - if (position1.length > 0) { - reducegradientBackgroundPosition(positions, `${position1} ${position2}`.trim()); + const rebuilt = new Array(write); + for (let index = 0; index < write; index++) { + rebuilt[index] = all[index]; } + return rebuilt.join(""); } - while (i < slice.length && - (slice[i].typ === exports.EnumToken.WhitespaceTokenType || - slice[i].typ === exports.EnumToken.CommentTokenType)) { - i++; + return s.join(""); + }) + .join(","); + } + let sel = wrap ? node[OPTIMIZED].optimized.join("") + `:is(${rule})` : rule; + if (sel.length < node.sel.length) { + node.sel = sel; + node[TOKENS] = null; + } + } + else if (node[OPTIMIZED]?.reducible) { + if (node[OPTIMIZED].optimized.length === 1) { + const sel1 = node[OPTIMIZED].optimized[0] + + ":is(" + + node[OPTIMIZED].selector.reduce(reducer, []).join(",") + + ")"; + const sel2 = node[OPTIMIZED].selector.reduce((acc, curr) => + // @ts-ignore + (acc.length > 0 ? acc + "," : "") + node[OPTIMIZED].optimized[0] + curr.join(""), ""); + node.sel = sel1.length < sel2.length ? sel1 : sel2; + node[TOKENS] = null; + } + else if (node[OPTIMIZED].optimized.length === 0) { + const testIdent = /^[a-zA-Z]/; + node.sel = node[OPTIMIZED].selector.reduce((acc, curr) => (acc.length > 0 ? acc + "," : "") + + (nestingContent && testIdent.test(curr[0]) ? "& " : "") + + curr.join(""), ""); + node[TOKENS] = null; + } + // @ts-ignore + } + else if (node[OPTIMIZED]?.optimized.length > 0) { + // @ts-ignore + const sel = node[OPTIMIZED].optimized.join(""); + if (sel.length < node.sel.length) { + node.sel = sel; + // @ts-ignore + node[RAW] = [node[OPTIMIZED].optimized.slice()]; + node[TOKENS] = null; + } + } + doMinify(node, options, recursive, errors, nestingContent, context); + } + if (previous != null) { + if ("chi" in previous && "chi" in node) { + if (previous.typ === node.typ) { + let shouldMerge = true; + let k = previous.chi.length; + while (k-- > 0) { + if (previous.chi[k].typ === exports.EnumToken.CommentNodeType || + previous.chi[k].typ === exports.EnumToken.InvalidRuleNodeType || + previous.chi[k].typ === exports.EnumToken.InvalidRuleNodeType) { + continue; } - if (slice[i]?.typ === exports.EnumToken.IdenTokenType && - equalsIgnoreCase("in", slice[i].val)) { - while (i < slice.length && slice[i].typ !== exports.EnumToken.CommaTokenType) { - colorSpaceDef.push(slice[i++]); + shouldMerge = previous.chi[k].typ === exports.EnumToken.DeclarationNodeType; + break; + } + if (shouldMerge) { + if (((node.typ === exports.EnumToken.RuleNodeType || + node.typ === exports.EnumToken.KeyframesRuleNodeType) && + node.sel === previous.sel) || + // @ts-ignore + (node.typ == exports.EnumToken.AtRuleNodeType && + node.nam !== "font-face" && + // @ts-ignore + node.nam === previous.nam)) { + const array = []; + for (let i = 0; i < previous.chi.length; i++) { + array.push(previous.chi[i]); } + for (let i = 0; i < node.chi.length; i++) { + array.push(node.chi[i]); + } + // @ts-ignore + node.chi = array; + doMinify(node, options, recursive, errors, nestingContent, context); + ast.chi.splice(nodeIndex, 1); + previous = ast.chi[--i]; + nodeIndex = i; + continue; } - if (slice[i]?.typ === exports.EnumToken.CommaTokenType) { - i++; - } - const result = []; - if (positions.length > 0) { - if (positions.length > 0) { - if (angles.length > 0) { - angles.push({ typ: exports.EnumToken.WhitespaceTokenType }); + else if (node.typ == previous?.typ && + [exports.EnumToken.KeyframesRuleNodeType, exports.EnumToken.RuleNodeType].includes(node.typ)) { + const intersect = diff(previous, node, options); + if (intersect != null) { + if (intersect.node1.chi.length == 0) { + ast.chi.splice(i--, 1); } - angles.push({ typ: exports.EnumToken.IdenTokenType, val: "at" }, { typ: exports.EnumToken.WhitespaceTokenType }); - for (const position of positions) { - angles.push(position); + else { + ast.chi.splice(i--, 1, intersect.node1); } - } - } - if (angles.length > 0) { - for (const angle of angles) { - result.push(angle); - } - result.push({ typ: exports.EnumToken.CommaTokenType }); - } - if (colorSpaceDef.length > 0) { - if (colorSpaceDef.length > 0) { - if (result.length > 0) { - result.push({ typ: exports.EnumToken.WhitespaceTokenType }); + if (intersect.node2.chi.length == 0) { + if (intersect.result != null) { + ast.chi.splice(nodeIndex, 1, intersect.result); + } + else { + ast.chi.splice(nodeIndex, 1); + } + i--; + if (nodeIndex == i) { + nodeIndex = i; + } } - for (const token of colorSpaceDef) { - result.push(token); + else { + if (intersect.result != null) { + ast.chi.splice(nodeIndex, 1, intersect.result, intersect.node2); + } + else { + ast.chi.splice(nodeIndex, 1, intersect.node2); + } + i = (nodeIndex ?? 0) + 1; + } + if (node != ast.chi[i]) { + node = ast.chi[i]; } + previous = intersect.result; + nodeIndex = i; } - result.push({ typ: exports.EnumToken.CommaTokenType }); - } - for (const token of reduceConicColorStops(slice.slice(i))) { - result.push(token); - } - slice.length = 0; - for (let j = 0; j < result.length; j++) { - slice.push(result[j]); } } + } + if (recursive && previous != null && previous != node) { + if (!hasDeclaration(previous)) { + doMinify(previous, options, recursive, errors, nestingContent, context); + } + } + } + } + if (!nestingContent && + previous != null && + previous.typ == exports.EnumToken.RuleNodeType && + previous.sel.includes("&")) { + fixSelector(previous); + } + previous = node; + nodeIndex = i; + } + // @ts-ignore + if (recursive && node != null && node.chi != null) { + if (node.typ == exports.EnumToken.KeyframesAtRuleNodeType || + !node.chi.some((n) => n.typ == exports.EnumToken.DeclarationNodeType)) { + if (!(node.typ == exports.EnumToken.AtRuleNodeType && node.nam != "font-face")) { + doMinify(node, options, recursive, errors, nestingContent, context); + } + } + } + if (!nestingContent && + node != null && + node.typ == exports.EnumToken.RuleNodeType && + node.sel.includes("&")) { + fixSelector(node); + } + } + return ast; +} +/** + * Check if a rule has a declaration + * @param node + * + * @private + */ +function hasDeclaration(node) { + // @ts-ignore + for (let i = 0; i < node.chi?.length; i++) { + // @ts-ignore + if (node.chi[i].typ == exports.EnumToken.CommentNodeType) { + continue; + } + // @ts-ignore + return node.chi[i].typ == exports.EnumToken.DeclarationNodeType; + } + return true; +} +/** + * Optimize selector + * @param selector + * + * @private + */ +function optimizeSelector(selector) { + const map = new Set(); + selector = selector + .reduce((acc, curr) => { + // @ts-ignore + if (curr.length > 0 && curr.at(-1).startsWith(":is(")) { + // @ts-ignore + const rules = splitRule(curr.at(-1).slice(4, -1)).map((x) => { + if (x[0] == "&" && x.length > 1) { + return x.slice(x[1] == " " ? 2 : 1); + } + return x; + }); + const part = curr.slice(0, -1); + for (const rule of rules) { + acc.push(part.concat(rule)); + } + return acc; + } + acc.push(curr); + return acc; + }, []) + .filter((x) => { + const str = x.join(""); + if (map.has(str)) { + return false; + } + map.add(str); + return true; + }); + const optimized = []; + const k = selector.reduce((acc, curr) => acc == 0 ? curr.length : curr.length == 0 ? acc : Math.min(acc, curr.length), 0); + let i = 0; + let j; + let match; + for (; i < k; i++) { + const item = selector[0][i]; + match = true; + for (j = 1; j < selector.length; j++) { + if (item != selector[j][i]) { + match = false; + break; + } + } + if (!match) { + break; + } + optimized.push(item); + } + while (optimized.length > 0) { + const last = optimized.at(-1); + if (last == " " || combinators.includes(last)) { + const all = new Array(Math.max(optimized.length - 1, 0)); + let write = 0; + for (let read = 0; read < optimized.length - 1; read++) { + all[write++] = optimized[read]; + } + optimized.length = 0; + for (let index = 0; index < write; index++) { + optimized[index] = all[index]; + } + continue; + } + break; + } + for (let i1 = 0; i1 < selector.length; i1++) { + const all = new Array(Math.max(selector[i1].length - optimized.length, 0)); + let write = 0; + for (let read = optimized.length; read < selector[i1].length; read++) { + all[write++] = selector[i1][read]; + } + selector[i1].length = 0; + for (let index = 0; index < write; index++) { + selector[i1][index] = all[index]; + } + } + let reducible = optimized.length == 1; + if (optimized[0] == "&") { + if (optimized[1] == " ") { + const all = new Array(Math.max(optimized.length - 2, 0)); + let write = 0; + for (let read = 2; read < optimized.length; read++) { + all[write++] = optimized[read]; + } + optimized.length = 0; + for (let index = 0; index < write; index++) { + optimized[index] = all[index]; + } + } + } + if (optimized.length == 0 || optimized[0].charAt(0) == "&" || selector.length == 1) { + return { + match: false, + optimized, + selector: selector.map((selector) => selector[0] == "&" && selector[1] == " " ? selector.slice(2) : selector), + reducible: selector.length > 1 && selector.every((selector) => !combinators.includes(selector[0])), + }; + } + return { + match: true, + optimized, + selector: selector.reduce((acc, curr) => { + let hasCompound = true; + if (hasCompound && curr.length > 0) { + hasCompound = !["&"].concat(combinators).includes(curr[0].charAt(0)); + } + // @ts-ignore + if (hasCompound && curr[0] == " ") { + hasCompound = false; + curr.unshift("&"); + } + if (curr.length == 0) { + curr.push("&"); + hasCompound = false; + } + if (reducible) { + const chr = curr[0].charAt(0); + // @ts-ignore + reducible = chr == "." || chr == ":" || isIdentStart(chr.charCodeAt(0)); + } + acc.push(hasCompound ? ["&"].concat(curr) : curr); + return acc; + }, []), + reducible: selector.every((selector) => ![">", "+", "~", "&"].includes(selector[0])), + }; +} +/** + * Split selector string + * @param buffer + * + * @internal + */ +function splitRule(buffer) { + const result = [[]]; + let str = ""; + for (let i = 0; i < buffer.length; i++) { + let chr = buffer.charAt(i); + if (isWhiteSpace(chr.charCodeAt(0))) { + if (str !== "") { + // @ts-ignore + result.at(-1).push(str); + str = ""; + } + // @ts-ignore + if (result.at(-1).length > 0) { + // @ts-ignore + result.at(-1).push(" "); + } + // i = k; + continue; + } + if (chr == ",") { + if (str !== "") { + result.at(-1).push(str); + str = ""; + } + result.push([]); + continue; + } + if (chr == ".") { + if (str !== "") { + result.at(-1).push(str); + str = ""; + } + str += chr; + continue; + } + if (combinators.includes(chr)) { + if (str !== "") { + result.at(-1).push(str); + str = ""; + } + if (chr == "|" && buffer.charAt(i + 1) == "|") { + chr += buffer.charAt(++i); + } + result.at(-1).push(chr); + continue; + } + if (chr == ":") { + if (str !== "") { + result.at(-1).push(str); + str = ""; + } + if (buffer.charAt(i + 1) == ":") { + chr += buffer.charAt(++i); + } + str += chr; + continue; + } + str += chr; + if (chr == "\\") { + str += buffer.charAt(++i); + continue; + } + if (chr == "(" || chr == "[") { + const open = chr; + const close = chr == "(" ? ")" : "]"; + let inParens = 1; + let k = i; + while (++k < buffer.length) { + chr = buffer.charAt(k); + if (chr == "\\") { + str += buffer.slice(k, k + 2); + k++; + continue; + } + str += chr; + if (chr == open) { + inParens++; + } + else if (chr == close) { + inParens--; + } + if (inParens == 0) { + break; + } + } + i = k; + } + } + if (str !== "") { + result.at(-1).push(str); + } + return result; +} +/** + * Reduce selector + * @param acc + * @param curr + * + * @private + */ +function reduceSelector(acc, curr) { + let hasCompoundSelector = true; + // @ts-ignore + curr = curr.slice(this.match[0].length); + while (curr.length > 0) { + if (curr[0] == " ") { + hasCompoundSelector = false; + curr.unshift("&"); + continue; + } + break; + } + if (hasCompoundSelector && curr.length > 0) { + hasCompoundSelector = !["&"].concat(combinators).includes(curr[0].charAt(0)); + } + if (curr[0] == ":is(") { + let canReduce = true; + const isCompound = curr.reduce((acc, token, index) => { + if (index == 0) { + canReduce = curr[1] == "&"; + } + else if (token == ")") ; + else if (token == ",") { + if (!canReduce) { + canReduce = curr[index + 1] == "&"; + } + acc.push([]); + } + else + acc.at(-1)?.push(token); + return acc; + }, [[]]); + if (canReduce) { + curr = isCompound.reduce((acc, curr) => { + if (acc.length > 0) { + acc.push(","); + } + for (const c of curr) { + acc.push(c); + } + return acc; + }, []); + } + } + acc.push( + // @ts-ignore + this.match.length == 0 + ? ["&"] + : hasCompoundSelector && curr[0] != "&" && (curr.length == 0 || !combinators.includes(curr[0].charAt(0))) + ? ["&"].concat(curr) + : curr); + return acc; +} +/** + * Match selectors + * @param selector1 + * @param selector2 + * + * @private + */ +function matchSelectors(selector1, selector2) { + let match = [[]]; + const j = Math.min(selector1.reduce((acc, curr) => Math.min(acc, curr.length), selector1.length > 0 ? selector1[0].length : 0), selector2.reduce((acc, curr) => Math.min(acc, curr.length), selector2.length > 0 ? selector2[0].length : 0)); + let i = 0; + let k; + let l; + let token; + let matching = true; + let matchFunction = 0; + let inAttr = 0; + const regEx = /^:is\(([:.][^\s,]+)\)$/; + for (const _1 of selector1) { + if (_1[0] !== "&") { + continue; + } + for (let i = 1; i < _1.length; i++) { + const token = _1[i]; + if (token.startsWith(":is(")) { + const match = regEx.exec(token); + if (match != null) { + _1[i] = match[1]; + } + } + } + } + for (const _1 of selector2) { + if (_1[0] !== "&") { + continue; + } + for (let i = 1; i < _1.length; i++) { + const token = _1[i]; + if (token.startsWith(":is(")) { + const match = regEx.exec(token); + if (match != null) { + _1[i] = match[1]; + } + } + } + } + for (; i < j; i++) { + k = 0; + token = selector1[0][i]; + for (; k < selector1.length; k++) { + if (selector1[k][i] != token) { + matching = false; + break; + } + } + if (matching) { + l = 0; + for (; l < selector2.length; l++) { + if (selector2[l][i] != token) { + matching = false; + break; + } + } + } + if (!matching) { + break; + } + if (token.endsWith("(")) { + matchFunction++; + } + match.at(-1).push(token); + } + // invalid function + if (matchFunction != 0 || inAttr != 0) { + return null; + } + for (const part of match) { + while (part.length > 0) { + const token = part.at(-1); + if (token == " " || combinators.includes(token) || notEndingWith.includes(token.at(-1))) { + part.pop(); + continue; + } + break; + } + } + if (match.every((t) => t.length == 0)) { + return null; + } + if (eq([["&"]], match)) { + return null; + } + const reducer = reduceSelector.bind({ match }); + // @ts-ignore + selector1 = selector1.reduce(reducer, []); + // @ts-ignore + selector2 = selector2.reduce(reducer, []); + return selector1 == null || selector2 == null + ? null + : { + eq: eq(selector1, selector2), + match, + selector1, + selector2, + }; +} +/** + * Fix selector + * @param node + * + * @private + */ +function fixSelector(node) { + if (node.sel.includes("&")) { + const attributes = parseString(node.sel); + for (const attr of walkValues(attributes)) { + if (attr.value.typ == exports.EnumToken.PseudoClassFuncTokenType && + attr.value.val == ":is") { + let i = attr.value.chi.length; + while (i--) { + if (attr.value.chi[i].typ == exports.EnumToken.NestingSelectorTokenType) { + attr.value.chi.splice(i, 1); + } + } + } + } + node.sel = attributes.reduce((acc, curr) => acc + printValue(curr), ""); + node[TOKENS] = null; + } +} +/** + * Wrap nodes + * @param previous + * @param node + * @param match + * @param ast + * @param reducer + * @param i + * @param nodeIndex + * + * @private + */ +function wrapNodes(previous, node, match, ast, reducer, i, nodeIndex) { + // @ts-ignore + let pSel = match.selector1.reduce(reducer, []).join(","); + // @ts-ignore + let nSel = match.selector2.reduce(reducer, []).join(","); + const wrapper = { + ...previous, + chi: [], + // @ts-ignore + sel: match.match.reduce(reducer, []).join(","), + [RAW]: match.match.map((t) => t.slice()), + }; + if (pSel == "&" || pSel === "") { + for (const child of previous.chi) { + wrapper.chi.push(child); + } + if (nSel == "&" || nSel === "") { + for (const child of node.chi) { + wrapper.chi.push(child); + } + } + else { + wrapper.chi.push(node); + } + } + else { + wrapper.chi.push(previous, node); + } + ast.chi.splice(i, 1, wrapper); + ast.chi.splice(nodeIndex, 1); + previous.sel = pSel; + previous[RAW] = match.selector1; + previous[TOKENS] = null; + node.sel = nSel; + node[RAW] = match.selector2; + node[TOKENS] = null; + reduceRuleSelector(wrapper); + wrapper[TOKENS] = null; + return wrapper; +} +/** + * Diff nodes + * @param n1 + * @param n2 + * @param options + * + * @private + */ +function diff(n1, n2, options = {}) { + if (!("cache" in options)) { + options.cache = new WeakMap(); + } + let node1 = n1; + let node2 = n2; + let exchanged = false; + if (node1.chi.length > node2.chi.length) { + const t = node1; + node1 = node2; + node2 = t; + exchanged = true; + } + let i = node1.chi.length; + let j = node2.chi.length; + const raw1 = node1[RAW]; + const raw2 = node2[RAW]; + if (raw1 != null && raw2 != null) { + const prefixes1 = new Set(); + const prefixes2 = new Set(); + for (const token1 of raw1) { + for (const t of token1) { + if (t.includes(":")) { + const matches = t.match(/::?-([a-z]+)-/); + if (matches == null) { + continue; + } + prefixes1.add(matches[1]); + if (prefixes1.size > 1) { break; + } } - return token.val + "(" + slice.reduce(reducer, "") + ")"; } - case exports.EnumToken.TimingFunctionTokenType: - case exports.EnumToken.PseudoClassFuncTokenType: - case exports.EnumToken.WhenElseFunctionTokenType: - case exports.EnumToken.TimelineFunctionTokenType: - case exports.EnumToken.GridTemplateFuncTokenType: - case exports.EnumToken.SupportsFunctionTokenType: - case exports.EnumToken.ContainerFunctionTokenType: - case exports.EnumToken.TransformFunctionTokenType: - case exports.EnumToken.GeneralEnclosedFunctionTokenType: - case exports.EnumToken.CustomFunctionTokenType: - case exports.EnumToken.WildCardFunctionTokenType: - if (token.typ == exports.EnumToken.MathFunctionTokenType && - token.chi.length == 1 && - ![exports.EnumToken.BinaryExpressionTokenType, exports.EnumToken.FractionTokenType, exports.EnumToken.IdenTokenType].includes(token.chi[0].typ) && - // @ts-ignore - token.chi[0].val - ?.typ != exports.EnumToken.FractionTokenType) { - return (token.val + - "(" + - token.chi.reduce((acc, curr) => acc + - renderValue(curr, token.typ == exports.EnumToken.FunctionTokenType ? { minify: false } : options, cache, reducer), "") + - ")"); + if (prefixes1.size > 1) { + break; } - return (token.val ?? "") + "(" + token.chi.reduce(reducer, "") + ")"; - case exports.EnumToken.BlockStartTokenType: - return "{"; - case exports.EnumToken.BlockEndTokenType: - return "}"; - case exports.EnumToken.StartParensTokenType: - return "("; - case exports.EnumToken.DelimTokenType: - case exports.EnumToken.EqualMatchTokenType: - return "="; - case exports.EnumToken.IncludeMatchTokenType: - return "~="; - case exports.EnumToken.DashMatchTokenType: - return "|="; - case exports.EnumToken.StartMatchTokenType: - return "^="; - case exports.EnumToken.EndMatchTokenType: - return "$="; - case exports.EnumToken.ContainMatchTokenType: - return "*="; - case exports.EnumToken.LtTokenType: - return "<"; - case exports.EnumToken.LteTokenType: - return "<="; - case exports.EnumToken.Tilda: - case exports.EnumToken.SubsequentSiblingCombinatorTokenType: - return "~"; - case exports.EnumToken.Plus: - case exports.EnumToken.NextSiblingCombinatorTokenType: - return "+"; - case exports.EnumToken.GtTokenType: - case exports.EnumToken.ChildCombinatorTokenType: - return ">"; - case exports.EnumToken.GteTokenType: - return ">="; - case exports.EnumToken.ColumnCombinatorTokenType: - return "||"; - case exports.EnumToken.EndParensTokenType: - return ")"; - case exports.EnumToken.AttrStartTokenType: - return "["; - case exports.EnumToken.AttrEndTokenType: - return "]"; - case exports.EnumToken.DescendantCombinatorTokenType: - case exports.EnumToken.WhitespaceTokenType: - return " "; - case exports.EnumToken.ColonTokenType: - return ":"; - case exports.EnumToken.DoubleColonTokenType: - return "::"; - case exports.EnumToken.SemiColonTokenType: - return ";"; - case exports.EnumToken.CommaTokenType: - return ","; - case exports.EnumToken.ImportantTokenType: - return "!important"; - case exports.EnumToken.Pipe: - return "|"; - case exports.EnumToken.AttrTokenType: - case exports.EnumToken.IdenListTokenType: - return "[" + token.chi.reduce(reducer, "") + "]"; - case exports.EnumToken.TimeTokenType: - case exports.EnumToken.AngleTokenType: - case exports.EnumToken.LengthTokenType: - case exports.EnumToken.DimensionTokenType: - case exports.EnumToken.FrequencyTokenType: - case exports.EnumToken.ResolutionTokenType: - let val = token.val.typ == exports.EnumToken.FractionTokenType - ? renderValue(token.val, options, cache) - : minifyNumber(token.val); - let unit = token.unit; - if (token.typ == exports.EnumToken.AngleTokenType && !val.includes("/")) { - const angle = getAngle(token); - let v; - let value = val + unit; - for (const u of ["deg", "turn", "rad", "grad"]) { - if (token.unit == u) { + } + for (const token2 of raw2) { + for (const t of token2) { + if (t.includes(":")) { + const matches = t.match(/::?-([a-z]+)-/); + if (matches == null) { continue; } - switch (u) { - case "deg": - v = minifyNumber(toPrecisionAngle(angle * 360, anglePrecision, false).toFixed(anglePrecision)); - if (v.length + 3 < value.length) { - val = v; - unit = u; - value = v + u; - } - break; - case "turn": - v = minifyNumber(toPrecisionAngle(angle, anglePrecision, false).toFixed(anglePrecision)); - if (v.length + 4 < value.length) { - val = v; - unit = u; - value = v + u; - } - break; - case "rad": - v = minifyNumber(toPrecisionAngle(angle * (2 * Math.PI), anglePrecision, false).toFixed(anglePrecision)); - if (v.length + 3 < value.length) { - val = v; - unit = u; - value = v + u; - } - break; - case "grad": - v = minifyNumber(toPrecisionAngle(angle * 400, anglePrecision, false).toFixed(anglePrecision)); - if (v.length + 4 < value.length) { - val = v; - unit = u; - value = v + u; - } - break; + prefixes2.add(matches[1]); + if (prefixes2.size > 1) { + break; } } } - if (val === "0") { - if (token.typ == exports.EnumToken.TimeTokenType) { - return "0s"; - } - if (token.typ == exports.EnumToken.FrequencyTokenType) { - return "0Hz"; - } - // @ts-ignore - if (token.typ == exports.EnumToken.ResolutionTokenType) { - return "0x"; - } - return "0"; + if (prefixes2.size > 1) { + break; } - if (token.typ == exports.EnumToken.TimeTokenType) { - if (unit == "ms") { - // @ts-ignore - const v = minifyNumber(val / 1000); - if (v.length + 1 <= val.length) { - return v + "s"; - } - return val + "ms"; - } - return val + "s"; + } + if (prefixes1.size != prefixes2.size) { + return null; + } + for (const prefix of prefixes1) { + if (!prefixes2.has(prefix)) { + return null; } - if (token.typ == exports.EnumToken.ResolutionTokenType && unit == "dppx") { - unit = "x"; + } + } + const css1 = options.cache.get(node1); + const css2 = options.cache.get(node2); + node1 = { ...node1, chi: node1.chi.slice() }; + node2 = { ...node2, chi: node2.chi.slice() }; + if (css1 != null) { + options.cache.set(node1, css1); + } + if (css2 != null) { + options.cache.set(node2, css2); + } + if (raw1 != null) { + node1[RAW] = raw1; + } + if (raw2 != null) { + node2[RAW] = raw2; + } + const intersect = []; + while (i--) { + if (node1.chi[i].typ == exports.EnumToken.CommentNodeType) { + continue; + } + j = node2.chi.length; + while (j--) { + if (node2.chi[j].typ == exports.EnumToken.CommentNodeType) { + continue; } - return val.includes("/") ? val.replace("/", unit + "/") : minifyNumber(toPrecisionValue(val)) + unit; - case exports.EnumToken.FlexTokenType: - case exports.EnumToken.PercentageTokenType: - const uni = token.typ == exports.EnumToken.PercentageTokenType ? "%" : "fr"; - const perc = token.val.typ == exports.EnumToken.FractionTokenType - ? renderValue(token.val, options, cache) - : minifyNumber(token.val); - return options.minifyValues && perc == "0" - ? "0" - : perc.includes("/") - ? perc.replace("/", uni + "/") - : perc + uni; - case exports.EnumToken.NumberTokenType: - return token.val.typ == exports.EnumToken.FractionTokenType - ? renderValue(token.val, options, cache) - : minifyNumber(token.val); - case exports.EnumToken.InfinityTokenType: - return "1/0"; - case exports.EnumToken.NegativeInfinityTokenType: - return "-1/0"; - case exports.EnumToken.NaNTokenType: - return "NaN"; - case exports.EnumToken.AtRuleTokenType: - return "@" + token.nam; - case exports.EnumToken.CommentTokenType: - case exports.EnumToken.CDOCOMMNodeType: - if (options.removeComments && - (!options.preserveLicense || !token.val.startsWith("/*!"))) { - return ""; + if (node1.chi[i].nam == node2.chi[j].nam) { + if (node1.chi[i].typ == node2.chi[j].typ && eq(node1.chi[i], node2.chi[j])) { + intersect.push(node1.chi[i]); + node1.chi.splice(i, 1); + node2.chi.splice(j, 1); + options.cache.delete(node1); + options.cache.delete(node2); + break; + } } - case exports.EnumToken.PseudoClassTokenType: - case exports.EnumToken.PseudoElementTokenType: - // https://www.w3.org/TR/selectors-4/#single-colon-pseudos - if (token.typ == exports.EnumToken.PseudoElementTokenType && - pseudoElements.includes(token.val.slice(1))) { - return token.val.slice(1); + } + } + const result = intersect.length === 0 && (node1.chi.length > 0 || node2.chi.length > 0) + ? null + : { + ...node1, + // @ts-ignore + sel: [ + ...new Set(splitRule(node1.sel) + .concat(splitRule(node2.sel)) + .map((s) => s.join(""))), + ].join(","), + // @ts-ignore + chi: intersect.reverse(), + }; + let op = { level: 0, ...options }; + if (result == null || + [n1, n2].reduce((acc, curr) => { + let css = options.cache.get(curr); + if (css == null) { + let level = 0; + let parent = curr[PARENT]; + while (parent != null && parent.typ != exports.EnumToken.StyleSheetNodeType) { + level++; + parent = parent[PARENT]; + } + op.level = level; + css = doRender(curr, op).code; + options.cache.set(curr, css); } - case exports.EnumToken.UrlTokenTokenType: - case exports.EnumToken.HashTokenType: - case exports.EnumToken.IdenTokenType: - case exports.EnumToken.StringTokenType: - case exports.EnumToken.LiteralTokenType: - case exports.EnumToken.DashedIdenTokenType: - case exports.EnumToken.PseudoPageTokenType: - case exports.EnumToken.ClassSelectorTokenType: - return token.val; - case exports.EnumToken.NestingSelectorTokenType: - return "&"; - case exports.EnumToken.InvalidAttrTokenType: - return ("[" + - token.chi.reduce((acc, curr) => acc + renderValue(curr, options, cache), "")); - case exports.EnumToken.InvalidClassSelectorTokenType: - return token.val; - case exports.EnumToken.SupportsQueryUnaryConditionTokenType: - case exports.EnumToken.WhenElseUnaryConditionTokenType: - return (renderValue(token.l, options, cache, reducer) + - " " + - token.r.reduce((acc, curr) => acc + renderValue(curr, options, cache, reducer), "")); - case exports.EnumToken.SupportsQueryConditionTokenType: - case exports.EnumToken.WhenElseQueryConditionTokenType: - return (token.l.reduce((acc, curr) => acc + renderValue(curr, options, cache, reducer), "") + - " " + - renderValue(token.op, options, cache, reducer) + - " " + - token.r.reduce((acc, curr) => acc + renderValue(curr, options, cache, reducer), "")); - case exports.EnumToken.IfConditionTokenType: - return token.l.length == 0 - ? "" - : token.l.reduce((acc, curr) => acc + renderValue(curr, options, cache, reducer), "") + - ":" + - (options.indent ?? "") + - token.r.reduce((acc, curr) => acc + renderValue(curr, options, cache, reducer), ""); - case exports.EnumToken.IfElseConditionTokenType: - return (renderValue(token.l, options, cache, reducer) + - (options.indent ?? "") + - renderValue(token.r, options, cache, reducer)); - case exports.EnumToken.DeclarationNodeType: - return (token.nam + - ":" + - (options.minifyValues ? filterValues(token.val) : token.val).reduce((acc, curr) => acc + renderValue(curr, options, cache, reducer), "")); - case exports.EnumToken.MediaQueryUnaryFeatureTokenType: - return (renderValue(token.l, options, cache, reducer) + - " " + - token.r.reduce((acc, curr) => acc + renderValue(curr, options, cache, reducer), "")); - case exports.EnumToken.MediaQueryConditionTokenType: { - const indent = token.op.typ == exports.EnumToken.LtTokenType || - token.op.typ == exports.EnumToken.GtTokenType || - token.op.typ == exports.EnumToken.ColonTokenType || - token.op.typ == exports.EnumToken.DelimTokenType || - token.op.typ == exports.EnumToken.LteTokenType || - token.op.typ == exports.EnumToken.GteTokenType - ? "" - : " "; - return (token.l.reduce((acc, curr) => acc + renderValue(curr, options, cache, reducer), "") + - indent + - renderValue(token.op, options, cache, reducer) + - indent + - token.r.reduce((acc, curr) => acc + renderValue(curr, options, cache, reducer), "")); + return curr.chi.length == 0 ? acc : acc + css.length; + }, 0) <= + [node1, node2, result].reduce((acc, curr) => { + let css = options.cache.get(curr); + if (css != null) { + return curr.chi.length == 0 ? acc : acc + css.length; + } + let level = 0; + let parent = curr[PARENT]; + while (parent != null && parent.typ != exports.EnumToken.StyleSheetNodeType) { + level++; + parent = parent[PARENT]; + } + op.level = level; + css = doRender(curr, op).code; + return curr.chi.length == 0 ? acc : acc + css.length; + }, 0)) { + if (node1.chi.length != 0 && node2.chi.length != 0) { + return null; } - case exports.EnumToken.MediaRangeQueryTokenType: - return (token.l.reduce((acc, curr) => acc + renderValue(curr, options, cache, reducer), "") + - renderValue(token.op1, options, cache, reducer) + - token.val.reduce((acc, curr) => acc + renderValue(curr, options, cache, reducer), "") + - renderValue(token.op2, options, cache, reducer) + - token.r.reduce((acc, curr) => acc + renderValue(curr, options, cache, reducer), "")); - case exports.EnumToken.MediaFeatureTokenType: - return token.val; - case exports.EnumToken.NotTokenType: - return "not"; - case exports.EnumToken.OnlyTokenType: - return "only"; - case exports.EnumToken.AndTokenType: - return "and"; - case exports.EnumToken.OrTokenType: - return "or"; - case exports.EnumToken.InvalidMediaQueryTokenType: - case exports.EnumToken.InvalidCommentTokenType: - case exports.EnumToken.BadCommentTokenType: - case exports.EnumToken.BadCdoTokenType: - case exports.EnumToken.BadStringTokenType: - case exports.EnumToken.BadUrlTokenType: - case exports.EnumToken.EOFTokenType: - return ""; - default: - console.debug({ token }); - throw new Error(`Unsupported token type for ${exports.EnumToken[token.typ]}`); } - // errors?.push({ action: "ignore", message: `render: unexpected token ${JSON.stringify(token, null, 1)}` }); - // return ""; + if (result != null) { + result[TOKENS] = null; + result[RAW] = null; + const optimized = optimizeSelector(splitRule(result.sel)); + if (optimized?.match) { + const rule = optimized.selector.reduce((acc, curr) => { + if (acc.length > 0) { + acc += ","; + } + if (curr.length > 2 && curr[0] === "&" && curr[1] === " ") { + return acc + curr.slice(2).join(""); + } + else if (curr.length > 1 && curr[0] === "&") { + return acc + curr.slice(1).join(""); + } + return acc + curr.join(""); + }, ""); + const match = optimized.optimized.join(""); + const sel = match + ":is(" + replaceCompound(rule, match) + ")"; + if (sel.length < result.sel.length) { + result.sel = sel; + result[TOKENS] = null; + } + } + } + return { result, node1: exchanged ? node2 : node1, node2: exchanged ? node1 : node2 }; } /** - * Remove whitespace tokens that are not needed - * @param values + * Reduce rule selector + * @param node * - * @internal + * @private */ -function filterValues(values) { - let i = 0; - for (; i < values.length; i++) { - if (values[i].typ == exports.EnumToken.ImportantTokenType && values[i - 1]?.typ === exports.EnumToken.WhitespaceTokenType) { - values.splice(i - 1, 1); +function reduceRuleSelector(node) { + if (node[RAW] == null) { + node[RAW] = splitRule(node.sel); + } + let optimized = optimizeSelector(node[RAW].reduce((acc, curr) => { + acc.push(curr.slice()); + return acc; + }, [])); + if (optimized != null) { + node[OPTIMIZED] = optimized; + } + if (optimized != null && optimized.match && optimized.reducible && optimized.selector.length > 1) { + for (const selector of optimized.selector) { + if (selector.length > 1 && + selector[0] == "&" && + (combinators.includes(selector[1]) || !/^[a-zA-Z:]/.test(selector[1]))) { + const all = new Array(Math.max(selector.length - 1, 0)); + let write = 0; + for (let read = 1; read < selector.length; read++) { + all[write++] = selector[read]; + } + selector.length = 0; + for (let index = 0; index < write; index++) { + selector[index] = all[index]; + } + } } - else if (tokensfuncSet.has(values[i].typ) && - "chi" in values[i] && - values[i].typ != exports.EnumToken.WildCardFunctionTokenType && - values[i + 1]?.typ == exports.EnumToken.WhitespaceTokenType) { - values.splice(i + 1, 1); + const unique = new Set(); + const reduced = optimized.selector.reduce((acc, curr) => { + const sig = curr.join(""); + if (!unique.has(sig)) { + if (acc.length > 0) { + acc.push(","); + } + unique.add(sig); + for (const c of curr) { + acc.push(c); + } + } + return acc; + }, []); + const raw = [ + [optimized.optimized[0], reduced.length === 1 ? reduced.join("") : ":is("].concat(reduced).concat(")"), + ]; + const sel = raw[0].join(""); + if (sel.length < node.sel.length) { + node.sel = sel; + node[RAW] = raw; + node[TOKENS] = null; } } - return values; } const SymbolsMapTokens = Object.create(null); @@ -28523,33 +29494,39 @@ class Tokenizer { * parse selector */ function parseSelector(tokens, context, options, errors) { - if (context?.typ === exports.EnumToken.KeyframesAtRuleNodeType) { - const result = matchAllSyntaxes(getParsedSyntax(ValidationSyntaxGroupEnum.Syntaxes, "keyframe-selectors"), createValidationContext(tokens), options); - const parts = splitTokenList(tokens); + const arena = options.arena; + const contextTyp = arena.getType(context); + let typ; + if (arena.getType(context) === exports.EnumToken.KeyframesAtRuleNodeType) { + const result = matchAllSyntaxes(getParsedSyntax(ValidationSyntaxGroupEnum.Syntaxes, "keyframe-selectors"), createValidationContext(tokens, options), options); + const parts = splitTokenList(tokens, options); for (const part of parts) { - trimArray(part); + trimArray(part, options); if (options.minify) { - const filtered = part.filter((token) => token.typ !== exports.EnumToken.WhitespaceTokenType && token.typ !== exports.EnumToken.CommentTokenType); + const filtered = part.filter((token) => { + typ = arena.getType(token); + }); if (filtered.length === 1) { - if (filtered[0].typ === exports.EnumToken.IdenTokenType && - "from" === filtered[0].val.toLowerCase()) { - filtered[0] = { - typ: exports.EnumToken.PercentageTokenType, - val: 0, - [LOCSRCID]: filtered[0][LOCSRCID], - [LOCSTA]: filtered[0][LOCSTA], - [LOCEND]: filtered[0][LOCEND], - }; - } - else if (filtered[0].typ === exports.EnumToken.PercentageTokenType && - 100 === filtered[0].val) { - filtered[0] = { - typ: exports.EnumToken.IdenTokenType, - val: "to", - [LOCSRCID]: filtered[0][LOCSRCID], - [LOCSTA]: filtered[0][LOCSTA], - [LOCEND]: filtered[0][LOCEND], - }; + typ = arena.getType(filtered[0]); + if (typ === exports.EnumToken.IdenTokenType && arena.equalsIgnoreCase(filtered[0], "val", "from")) { + arena.set(filtered[0], exports.EnumToken.PercentageTokenType, 0, 0); + // filtered[0] = { + // typ: EnumToken.PercentageTokenType, + // val: 0, + // [LOCSRCID]: filtered[0][LOCSRCID], + // [LOCSTA]: filtered[0][LOCSTA], + // [LOCEND]: filtered[0][LOCEND], + // }; + } + else if (typ === exports.EnumToken.PercentageTokenType && 100 === arena.getNumerciValue(filtered[0])) { + arena.set(filtered[0], 0, exports.EnumToken.IdenTokenType, arena.internString("to"), 0); + // filtered[0] = { + // typ: EnumToken.IdenTokenType, + // val: "to", + // [LOCSRCID]: filtered[0][LOCSRCID], + // [LOCSTA]: filtered[0][LOCSTA], + // [LOCEND]: filtered[0][LOCEND], + // }; } part.splice(0, part.length, ...filtered); } @@ -28557,18 +29534,22 @@ function parseSelector(tokens, context, options, errors) { } tokens.splice(0, tokens.length, ...parts.reduce((acc, curr) => { if (acc.length > 0) { - acc.push({ typ: exports.EnumToken.CommaTokenType }); + const id = arena.allocate(); + arena.set(id, exports.EnumToken.CommaTokenType); + acc.push(id); } for (const c of curr) { acc.push(c); } return acc; }, [])); + const node = arena.allocate(); + arena.set(node, exports.EnumToken.KeyframesRuleNodeType); return { typ: exports.EnumToken.KeyframesRuleNodeType, sel: [ - ...splitTokenList(trimArray(tokens)).reduce((acc, curr) => { - acc.add(curr.reduce((acc, curr) => acc + renderValue(curr, { minify: false }), "")); + ...splitTokenList(trimArray(tokens, options), options).reduce((acc, curr) => { + acc.add(curr.reduce((acc, curr) => acc + printValue$1(curr, { minify: false }), "")); return acc; }, new Set()), ].join(), @@ -28588,10 +29569,10 @@ function parseSelector(tokens, context, options, errors) { let parent = context; let nested = false; let val; - if (context?.typ !== exports.EnumToken.StyleSheetNodeType && context?.typ !== exports.EnumToken.RuleNodeType) { + if (contextTyp !== exports.EnumToken.StyleSheetNodeType && contextTyp !== exports.EnumToken.RuleNodeType) { allowed = false; - if (context?.typ === exports.EnumToken.AtRuleNodeType) { - const syntaxRule = getSyntaxRule(ValidationSyntaxGroupEnum.AtRules, "@" + context.nam); + if (contextTyp === exports.EnumToken.AtRuleNodeType) { + const syntaxRule = getSyntaxRule(ValidationSyntaxGroupEnum.AtRules, "@" + arena.getName(context)); allowed = syntaxRule?.acceptAnyRule ?? false; if (!allowed) { const rules = syntaxRule?.getBlockRules?.(); @@ -28610,129 +29591,201 @@ function parseSelector(tokens, context, options, errors) { } } do { - if (parent?.typ === exports.EnumToken.AtRuleNodeType && "media" === parent.nam) { - parent = parent[PARENT]; + if (contextTyp === exports.EnumToken.AtRuleNodeType && arena.equalsIgnoreCase(parent, "nam", "media")) { + parent = arena.getParent(parent); continue; } - nested = parent?.typ == exports.EnumToken.RuleNodeType; - parent = parent?.[PARENT]; - } while (!nested && parent != null); + nested = arena.getType(parent) == exports.EnumToken.RuleNodeType; + parent = arena.getParent(parent); + } while (!nested && parent != 0); const all = new Array(tokens.length); let write = 0; + let token; + let next; + let newToken; for (let read = 0; read < tokens.length; read++) { - let token = tokens[read]; - if (token.typ == exports.EnumToken.ColonTokenType) { - const next = tokens[read + 1]; - if (next?.typ == exports.EnumToken.IdenTokenType) { - all[write++] = { - ...token, - typ: exports.EnumToken.PseudoElementTokenType, - val: ":" + next.val, - [LOCEND]: next[LOCEND], - }; + token = tokens[read]; + typ = arena.getType(token); + if (typ == exports.EnumToken.ColonTokenType) { + next = tokens[read + 1]; + if (next != null && arena.getType(next) == exports.EnumToken.IdenTokenType) { + newToken = arena.allocate(); + all[write++] = newToken; + arena.set(newToken, exports.EnumToken.PseudoClassTokenType, arena.interString(":" + arena.getValue(next))); + arena.setSpan(newToken, arena.getSourceId(token), arena.getStart(token), arena.getEnd(next)); read++; continue; } - if (next?.typ == exports.EnumToken.FunctionTokenDefType) { - val = ":" + next.val; - all[write++] = { - ...token, - typ: val + "()" in getSyntaxConfig().selectors - ? exports.EnumToken.PseudoClassFunctionTokenDefType - : next.typ, - val, - [LOCEND]: next[LOCEND], - }; + if (next != null && arena.getType(next) == exports.EnumToken.FunctionTokenDefType) { + val = ":" + arena.getValue(next); + // all[write++] = { + // ...token, + // typ: + // val + "()" in getSyntaxConfig().selectors + // ? EnumToken.PseudoClassFunctionTokenDefType + // : next.typ, + // val, + // [LOCEND]: next[LOCEND], + // } as Token; + newToken = arena.allocate(); + all[write++] = newToken; + arena.set(newToken, val + "()" in getSyntaxConfig().selectors + ? exports.EnumToken.PseudoClassFunctionTokenDefType + : arena.getType(next), arena.interString(":" + arena.getValue(next))); + arena.setSpan(newToken, arena.getSourceId(token), arena.getStart(token), arena.getEnd(next)); read++; continue; } } - if (token.typ == exports.EnumToken.DoubleColonTokenType) { + if (arena.getType(token) == exports.EnumToken.DoubleColonTokenType) { const next = tokens[read + 1]; - val = ":" + next.val; - if (next?.typ == exports.EnumToken.IdenTokenType) { - all[write++] = { - ...token, - typ: exports.EnumToken.PseudoClassTokenType, - val: (pseudoElements.includes(val) ? "" : ":") + val, - [LOCEND]: next[LOCEND], - }; + val = ":" + arena.getValue(next); + if (arena.getType(next) == exports.EnumToken.IdenTokenType) { + // all[write++] = { + // ...token, + // typ: EnumToken.PseudoClassTokenType, + // val: (pseudoElements.includes(val) ? "" : ":") + val, + // [LOCEND]: next[LOCEND], + // } as Token; + newToken = arena.allocate(); + all[write++] = newToken; + arena.set(newToken, exports.EnumToken.PseudoClassTokenType, arena.interString((pseudoElements.includes(val) ? "" : ":") + val)); + arena.setSpan(newToken, arena.getSourceId(token), arena.getStart(token), arena.getEnd(next)); read++; continue; } - if (next?.typ == exports.EnumToken.FunctionTokenDefType) { - val = "::" + next.val; - all[write++] = { - ...token, - typ: val + "()" in getSyntaxConfig().selectors - ? exports.EnumToken.PseudoClassFunctionTokenDefType - : exports.EnumToken.FunctionTokenDefType, - val, - [LOCEND]: next[LOCEND], - }; + if (arena.getType(next) == exports.EnumToken.FunctionTokenDefType) { + val = "::" + arena.getValue(next); + // all[write++] = { + // ...token, + // typ: + // val + "()" in getSyntaxConfig().selectors + // ? EnumToken.PseudoClassFunctionTokenDefType + // : EnumToken.FunctionTokenDefType, + // val, + // [LOCEND]: next[LOCEND], + // } as Token; + newToken = arena.allocate(); + all[write++] = newToken; + arena.set(newToken, val + "()" in getSyntaxConfig().selectors + ? exports.EnumToken.PseudoClassFunctionTokenDefType + : exports.EnumToken.FunctionTokenDefType, arena.interString(val)); + arena.setSpan(newToken, arena.getSourceId(token), arena.getStart(token), arena.getEnd(next)); read++; continue; } } - if (token.typ == exports.EnumToken.ColorTokenType) { - if (isHash(token.val)) { - token.typ = exports.EnumToken.HashTokenType; + if (arena.getType(token) == exports.EnumToken.ColorTokenType) { + if (isHash(arena.getValue(token))) { + arena.set(token, exports.EnumToken.HashTokenType); all[write++] = token; continue; } - return { - typ: exports.EnumToken.RuleNodeType, - sel: [ - ...tokens - .reduce((acc, curr, index, array) => { - if (curr.typ == exports.EnumToken.WhitespaceTokenType) { - if (trimWhiteSpace.includes(array[index - 1]?.typ) || - trimWhiteSpace.includes(array[index + 1]?.typ) || - combinators.includes(array[index - 1]?.val) || - combinators.includes(array[index + 1]?.val)) { - return acc; - } - } - let t = renderValue(curr, { minify: false }); - if (t == ",") { - acc.push([]); - } - else { - acc[acc.length - 1].push(t); + const node = arena.allocate(); + node.set(node, exports.EnumToken.RuleNodeType, arena.internString([ + ...all + .reduce((acc, curr, index, array) => { + if (arena.getType(curr) == exports.EnumToken.WhitespaceTokenType) { + if (trimWhiteSpace.includes(arena.getType(array[index - 1])) || + trimWhiteSpace.includes(arena.getType(array[index + 1])) || + combinators.includes(arena.getValue(array[index - 1])) || + combinators.includes(arena.getValue(array[index + 1]))) { + return acc; } - return acc; - }, [[]]) - .reduce((acc, curr) => { - let i = 0; - for (; i < curr.length; i++) { - if (i + 1 < curr.length && curr[i] == "*") { - if (curr[i] == "*") { - let index = curr[i + 1] == " " ? 2 : 1; - if (![">", "~", "+"].includes(curr[index])) { - curr.splice(i, index); - } + } + let t = printValue$1(curr, { minify: false, arena }); + if (t == ",") { + acc.push([]); + } + else { + acc[acc.length - 1].push(t); + } + return acc; + }, [[]]) + .reduce((acc, curr) => { + let i = 0; + for (; i < curr.length; i++) { + if (i + 1 < curr.length && curr[i] == "*") { + if (curr[i] == "*") { + let index = curr[i + 1] == " " ? 2 : 1; + if (![">", "~", "+"].includes(curr[index])) { + curr.splice(i, index); } } } - acc.set(curr.join(""), curr); - return acc; - }, uniq) - .keys(), - ].join(","), - chi: [], - [LOCSRCID]: tokens[0][LOCSRCID], - [LOCSTA]: tokens[0][LOCSTA], - [LOCEND]: tokens[tokens.length - 1][LOCEND], - [TOKENS]: tokens, - [STATE]: exports.EnumAstNodeStatus.Invalid, - [ERRORS]: [ - { - action: "drop", - node: token, - message: "invalid hash id", - }, - ], - }; + } + acc.set(curr.join(""), curr); + return acc; + }, uniq) + .keys(), + ].join(","))); + arena.setTokens(node, all); + arena.setFlags(node, exports.EnumAstNodeStatus.HasChildren | exports.EnumAstNodeStatus.Invalid); + arena.setErrors(node, [ + { + action: "drop", + node: node, + message: "invalid hash id", + }, + ]); + return node; + // return { + // typ: EnumToken.RuleNodeType, + // sel: [ + // ...tokens + // .reduce( + // (acc: string[][], curr: Token, index: number, array: Token[]) => { + // if (curr.typ == EnumToken.WhitespaceTokenType) { + // if ( + // trimWhiteSpace.includes(array[index - 1]?.typ) || + // trimWhiteSpace.includes(array[index + 1]?.typ) || + // combinators.includes((array[index - 1])?.val) || + // combinators.includes((array[index + 1])?.val) + // ) { + // return acc; + // } + // } + // let t: string = printValue(curr, { minify: false }); + // if (t == ",") { + // acc.push([]); + // } else { + // acc[acc.length - 1].push(t); + // } + // return acc; + // }, + // [[]], + // ) + // .reduce((acc: Map, curr: string[]) => { + // let i: number = 0; + // for (; i < curr.length; i++) { + // if (i + 1 < curr.length && curr[i] == "*") { + // if (curr[i] == "*") { + // let index: number = curr[i + 1] == " " ? 2 : 1; + // if (![">", "~", "+"].includes(curr[index])) { + // curr.splice(i, index); + // } + // } + // } + // } + // acc.set(curr.join(""), curr); + // return acc; + // }, uniq) + // .keys(), + // ].join(","), + // chi: [], + // [LOCSRCID]: tokens[0][LOCSRCID], + // [LOCSTA]: tokens[0][LOCSTA], + // [LOCEND]: tokens[tokens.length - 1][LOCEND], + // [TOKENS]: tokens, + // [STATE]: EnumAstNodeStatus.Invalid, + // [ERRORS]: [ + // { + // action: "drop", + // node: token, + // message: "invalid hash id", + // }, + // ], + // } as AstRule; } all[write++] = token; } @@ -28743,194 +29796,211 @@ function parseSelector(tokens, context, options, errors) { tokens.length = write; } const result = matchSelectorSyntax(tokens, errors, options, nested); - trimArray(tokens); - if (result.success) { - for (let i = 0; i < tokens.length; i++) { - const token = tokens[i]; - switch (token.typ) { - case exports.EnumToken.AttrStartTokenType: - stack.push(token); - break; - case exports.EnumToken.AttrEndTokenType: - if (stack.at(-1)?.typ == exports.EnumToken.AttrStartTokenType) { - index = tokens.indexOf(stack.at(-1)); - // @ts-expect-error - const { val, ...attr } = stack.at(-1); - attr[LOCSRCID] = stack.at(-1)[LOCSRCID]; - attr[LOCSTA] = stack.at(-1)[LOCSTA]; - attr[LOCEND] = token[LOCEND]; - tokens.splice(i, 1); - Object.assign(attr, { - typ: exports.EnumToken.AttrTokenType, - chi: tokens.splice(index + 1, i - index - 1), - }); - tokens[index] = attr; - i = index; - stack.pop(); - } - break; - case exports.EnumToken.PseudoClassFunctionTokenDefType: - stack.push(token); - break; - case exports.EnumToken.EndParensTokenType: - if (stack.at(-1)?.typ == exports.EnumToken.PseudoClassFunctionTokenDefType) { - const func = stack.at(-1); - index = tokens.indexOf(func); - stack.at(-1)[LOCEND] = token[LOCEND]; - tokens.splice(i, 1); - if (tokensfuncDefMap.has(func.typ)) { - // @ts-expect-error - func.typ = tokensfuncDefMap.get(func.typ); - func.chi = trimArray(tokens.splice(index + 1, i - index - 1)); - } - if (result.success && options.minify) { - // parse an+b - // an+b produces an ident such as 'n-0', a literal such as '+2n-0' or a list of tokens such as[2n\s?[+-]\s?3] - if (func.val == ":nth-child" || - func.val == ":nth-last-child" || - func.val == ":nth-of-type" || - func.val == ":nth-last-of-type") { - const list = []; - let index; - for (index = 0; index < func.chi.length; index++) { - if (func.chi[index].typ == exports.EnumToken.CommentTokenType || - func.chi[index].typ == exports.EnumToken.WhitespaceTokenType) { - continue; - } - if (func.chi[index].typ == exports.EnumToken.IdenTokenType && - equalsIgnoreCase("of", func.chi[index].val)) { - index--; - break; - } - list.push(func.chi[index]); + trimArray(tokens, options); + // if (result.success) { + for (let i = 0; i < tokens.length; i++) { + token = tokens[i]; + typ = arena.getType(token); + switch (token) { + case exports.EnumToken.AttrStartTokenType: + stack.push(token); + break; + case exports.EnumToken.AttrEndTokenType: + if (arena.getType(stack.at(-1)) == exports.EnumToken.AttrStartTokenType) { + index = tokens.indexOf(stack.at(-1)); + // const { val, ...attr } = stack.at(-1) as AttrStartToken; + // attr[LOCSRCID] = stack.at(-1)![LOCSRCID]; + // attr[LOCSTA] = stack.at(-1)![LOCSTA]; + // attr[LOCEND] = token[LOCEND]; + arena.setSpanEnd(tokens[index], arena.getSpanEnd(token)); + arena.setChildren(tokens[index], ...tokens.splice(index + 1, i - index - 1)); + tokens.splice(i, 1); + // Object.assign(attr, { + // typ: EnumToken.AttrTokenType, + // chi: tokens.splice(index + 1, i - index - 1), + // }); + // tokens[index] = attr; + i = index; + stack.pop(); + } + break; + case exports.EnumToken.PseudoClassFunctionTokenDefType: + stack.push(token); + break; + case exports.EnumToken.EndParensTokenType: + if (stack.length > 0 && arena.getType(stack.at(-1)) == exports.EnumToken.PseudoClassFunctionTokenDefType) { + const func = stack.at(-1); + const funcType = arena.getType(func); + index = tokens.indexOf(func); + arena.setSpanEnd(tokens[index], arena.getSpanEnd(token)); + // (stack.at(-1) as AttrStartToken)[LOCEND] = token[LOCEND]; + tokens.splice(i, 1); + if (tokensfuncDefMap.has(funcType)) { + arena.setType(func, tokensfuncDefMap.get(funcType)); + arena.setChildren(func, ...trimArray(tokens.splice(index + 1, i - index - 1), options)); + } + if (result.success && options.minify) { + val = arena.getValue(func); + // parse an+b + // an+b produces an ident such as 'n-0', a literal such as '+2n-0' or a list of tokens such as[2n\s?[+-]\s?3] + if (val == ":nth-child" || + val == ":nth-last-child" || + val == ":nth-of-type" || + val == ":nth-last-of-type") { + const list = []; + const children = arena.children(func); + let index = 0; + let child = 0; + for (index = 0; index < children.length; index++) { + child = children[index]; + typ = arena.getType(child); + if (typ == exports.EnumToken.CommentTokenType || typ == exports.EnumToken.WhitespaceTokenType) { + continue; } - if (list.length == 2) { - if (list[1].typ == exports.EnumToken.NumberTokenType) { - if (list[1].val == 0) { - list.length = 1; - if (list[0].typ == exports.EnumToken.DimensionTokenType && - list[0].val == -2) { - list[0].val = 2; - } - } - else { - const sign = Math.sign(list[1].val); - // @ts-ignore - list[1].val *= sign; - list.splice(1, 0, { - typ: exports.EnumToken.LiteralTokenType, - val: sign > 0 ? "+" : "-", - }); + if (typ == exports.EnumToken.IdenTokenType && arena.equalsIgnoreCase(child, "val", "of")) { + index--; + break; + } + list.push(child); + } + if (list.length == 2) { + if (arena.getType(list[1]) == exports.EnumToken.NumberTokenType) { + if (arena.getNumericValue(list[1]) == 0) { + list.length = 1; + if (arena.getType(list[0]) == exports.EnumToken.DimensionTokenType && + arena.getNumericValue(list[0]) == -2) { + arena.setNumericValue(list[0], 2); } } - if (list.length == 3 && - list[2].typ == exports.EnumToken.NumberTokenType && - list[0].typ == exports.EnumToken.DimensionTokenType && - (list[0].val == 2 || - list[0].val == -2)) { - if (1 == list[2].val) { - list.splice(0, 3, { - typ: exports.EnumToken.IdenTokenType, - val: "odd", - [LOCSRCID]: list[0][LOCSRCID], - [LOCSTA]: list[0][LOCSTA], - [LOCEND]: list[0][LOCEND], - }); + else { + const sign = Math.sign(arena.getNumericValue(list[1])); + // @ts-ignore + list[1].val *= sign; + const literal = arena.allocate(); + arena.setType(literal, exports.EnumToken.LiteralTokenType, arena.internString(sign < 0 ? "-" : "+")); + list.splice(1, 0, literal); + } + } + if (list.length == 3 && + arena.getType(list[2]) == exports.EnumToken.NumberTokenType && + arena.getType(list[0]) == exports.EnumToken.DimensionTokenType) { + const value = arena.getNumericValue(list[0]); + if (value == 2 || value == -2) { + const value0 = arena.getNumericValue(list[2]); + if (1 == value0) { + const literal = arena.allocate(); + arena.setType(literal, exports.EnumToken.IdenTokenType, arena.internString("odd")); + arena.setSpan(literal, arena.getSourceId(list[0]), arena.getSpanStart(list[0]), arena.getSpanEnd(list[0])); + list.splice(0, 3, literal); } - else if (0 == list[2].val) { - list.splice(0, 3, { - typ: exports.EnumToken.IdenTokenType, - val: "even", - [LOCSRCID]: list[0][LOCSRCID], - [LOCSTA]: list[0][LOCSTA], - [LOCEND]: list[0][LOCEND], - }); + else if (0 == value0) { + const literal = arena.allocate(); + arena.setType(literal, exports.EnumToken.IdenTokenType, arena.internString("even")); + arena.setSpan(literal, arena.getSourceId(list[0]), arena.getSpanStart(list[0]), arena.getSpanEnd(list[0])); + list.splice(0, 3, literal); } } - func.chi.splice(0, index, ...list); } - if (list.length == 1) { - if (list[0].typ == exports.EnumToken.IdenTokenType && - equalsIgnoreCase("-n", list[0].val)) { - list[0].val = "n"; - } + arena.setChildren(func, arena.children(func).splice(0, index, ...list)); + } + if (list.length == 1) { + if (arena.getType(list[0]) == exports.EnumToken.IdenTokenType && + arena.equalsIgnoreCase(list[0], "val", "-n")) { + arena.setValue(list[0], arena.interString("n")); } - if (list.length == 3) { - if (list[0].typ == exports.EnumToken.IdenTokenType && - ("n" == list[0].val || - "-n" == list[0].val || - "+n" == list[0].val)) { - if (list[1].typ == exports.EnumToken.NextSiblingCombinatorTokenType) { - if (list[2].typ == exports.EnumToken.NumberTokenType && - 0 == list[2].val) { - list[0].val = "n"; - func.chi.splice(0, index, list[0]); + } + if (list.length == 3) { + if (arena.getType(list[0]) == exports.EnumToken.IdenTokenType) { + val = arena.getValue(list[0]); + if ("n" == val || "-n" == val || "+n" == val) { + if (arena.getType(list[1]) == exports.EnumToken.NextSiblingCombinatorTokenType) { + if (arena.getType(list[2]) == exports.EnumToken.NumberTokenType && + 0 == arena.getNumericValue(list[2])) { + arena.setValue(list[0], "n"); + arena.setChildren(func, arena.children(func).splice(0, index, list[0])); + // func.chi.splice(0, index, list[0]); break; } } } } - const token = func.chi.find((t) => t.typ != exports.EnumToken.WhitespaceTokenType && t.typ != exports.EnumToken.CommentTokenType); - if (token?.typ == exports.EnumToken.IdenTokenType || token?.typ == exports.EnumToken.LiteralTokenType) { - if (token.typ == exports.EnumToken.IdenTokenType && - (token.val == "odd" || token.val == "even")) { - if (token.val == "even") { - Object.assign(token, { - typ: exports.EnumToken.DimensionTokenType, - val: 2, - unit: "n", - }); + } + const token = arena.findChild(func, (t) => { + typ = arena.getType(t); + return typ != exports.EnumToken.WhitespaceTokenType && typ != exports.EnumToken.CommentTokenType; + }); + if (token != null) { + typ = arena.getType(token); + if (typ == exports.EnumToken.IdenTokenType || typ == exports.EnumToken.LiteralTokenType) { + val = arena.getValue(token); + if (typ == exports.EnumToken.IdenTokenType) { + if (val == "even") { + arena.set(token, exports.EnumToken.DimensionTokenType, 0, 2, arena.internString("n")); } } else { - const matches = /^(([+-]?[0-9]*)?n)?([+-]?[0-9]+)?$/.exec(token.val); + const matches = /^(([+-]?[0-9]*)?n)?([+-]?[0-9]+)?$/.exec(val); if (matches != null) { const a1 = matches[2] === "" ? 1 : matches[2] === "-" ? -1 : +matches[2]; const b1 = +matches[3]; if (b1 === 0) { - Object.assign(token, Math.abs(a1) === 1 - ? { - typ: exports.EnumToken.IdenTokenType, - val: "n", - } - : { - typ: exports.EnumToken.DimensionTokenType, - val: a1 * Math.sign(a1), - unit: "n", - }); + if (Math.abs(a1) === 1) { + arena.set(token, exports.EnumToken.IdenTokenType, arena.internString("n")); + } + else { + arena.set(token, exports.EnumToken.DimensionTokenType, 0, a1 * Math.sign(a1), arena.internString("n")); + } + // Object.assign( + // token, + // Math.abs(a1) === 1 + // ? { + // typ: EnumToken.IdenTokenType, + // val: "n", + // } + // : { + // typ: EnumToken.DimensionTokenType, + // val: a1 * Math.sign(a1), + // unit: "n", + // }, + // ); } } } } - else if (token?.typ === exports.EnumToken.DimensionTokenType) { - const index = func.chi.indexOf(token); + else if (typ === exports.EnumToken.DimensionTokenType) { + const children = arena.children(func); + const index = children.indexOf(token); let num = null; let i = index + 1; - for (; i < func.chi.length; i++) { - if (func.chi[i].typ == exports.EnumToken.WhitespaceTokenType || - func.chi[i].typ == exports.EnumToken.CommentTokenType) { + for (; i < children.length; i++) { + typ = arena.getType(children[i]); + if (typ == exports.EnumToken.WhitespaceTokenType || + typ == exports.EnumToken.CommentTokenType) { continue; } - if (func.chi[i].typ == exports.EnumToken.NumberTokenType) { - num = func.chi[i]; + if (typ == exports.EnumToken.NumberTokenType) { + num = children[i]; break; } } if (num != null) { - if (num.val === 0) { - func.chi.splice(index + 1, i - index); - if (token.val < 0) { - token.val *= -1; + if (arena.getNumericValue(num) === 0) { + children.splice(index + 1, i - index); + if (arena.getNumericValue(token) < 0) { + arena.setNumericValue(token, arena.getNumericValue(token) * -1); } + arena.setChildren(func, children); break; } - else if (token.val === 2 && - Math.abs(num.val) === 1) { - Object.assign(token, { - typ: exports.EnumToken.IdenTokenType, - val: "odd", - }); - func.chi.splice(index + 1, i - index); + else if (arena.getNumericValue(token) === 2 && + Math.abs(arena.getNumericValue(num)) === 1) { + arena.set(token, exports.EnumToken.IdenTokenType, arena.internString("n")); + // Object.assign(token, { + // typ: EnumToken.IdenTokenType, + // val: "odd", + // }); + children.splice(index + 1, i - index); + arena.setChildren(func, children); break; } // if (((token as DimensionToken).val as number) === 1) { @@ -28955,67 +30025,117 @@ function parseSelector(tokens, context, options, errors) { } } } - i = index; - stack.pop(); } - break; - } + i = index; + stack.pop(); + } + break; } } - return { - typ: exports.EnumToken.RuleNodeType, - sel: [ - ...tokens - .reduce((acc, curr, index, array) => { - // if (curr.typ == EnumToken.CommentTokenType) { - // return acc; - // } - if (curr.typ == exports.EnumToken.WhitespaceTokenType) { - if (trimWhiteSpace.includes(array[index - 1]?.typ) || - trimWhiteSpace.includes(array[index + 1]?.typ) || - combinators.includes(array[index - 1]?.val) || - combinators.includes(array[index + 1]?.val)) { - return acc; - } - } - let t = renderValue(curr, { minify: false }); - if (t == ",") { - acc.push([]); - } - else { - acc[acc.length - 1].push(t); + // } + const node = arena.allocate(); + arena.set(node, exports.EnumToken.RuleNodeType, arena.internString([ + ...trimArray(all, options) + .reduce((acc, curr, index, array) => { + // if (curr.typ == EnumToken.CommentTokenType) { + // return acc; + // } + if (arena.getType(curr) == exports.EnumToken.WhitespaceTokenType) { + if (trimWhiteSpace.includes(arena.getType(array[index - 1])) || + trimWhiteSpace.includes(arena.getType(array[index + 1])) || + combinators.includes(arena.getValue(array[index - 1])) || + combinators.includes(arena.getValue(array[index + 1]))) { + return acc; } - return acc; - }, [[]]) - .reduce((acc, curr) => { - let i = 0; - for (; i < curr.length; i++) { - if (i + 1 < curr.length && curr[i] == "*") { - if (curr[i] == "*") { - let index = curr[i + 1] == " " ? 2 : 1; - if (![">", "~", "+"].includes(curr[index])) { - curr.splice(i, index); - } + } + let t = printValue$1(curr, arena, { minify: false, arena }); + if (t == ",") { + acc.push([]); + } + else { + acc[acc.length - 1].push(t); + } + return acc; + }, [[]]) + .reduce((acc, curr) => { + let i = 0; + for (; i < curr.length; i++) { + if (i + 1 < curr.length && curr[i] == "*") { + if (curr[i] == "*") { + let index = curr[i + 1] == " " ? 2 : 1; + if (![">", "~", "+"].includes(curr[index])) { + curr.splice(i, index); } } } - acc.set(curr.join(""), curr); - return acc; - }, uniq) - .keys(), - ].join(","), - chi: [], - [LOCSRCID]: tokens[0][LOCSRCID], - [LOCSTA]: tokens[0][LOCSTA], - [LOCEND]: tokens[tokens.length - 1][LOCEND], - [TOKENS]: tokens, - [STATE]: result.success && allowed - ? exports.EnumAstNodeStatus.Validated - : allowed - ? exports.EnumAstNodeStatus.Invalid - : exports.EnumAstNodeStatus.Disallowed, - [ERRORS]: result.success ? [] : result.errors, - }; + } + acc.set(curr.join(""), curr); + return acc; + }, uniq) + .keys(), + ].join(","))); + arena.setSpan(node, arena.getSourceId(tokens[0]), arena.getSpanStart(tokens[0]), arena.getSpanEnd(tokens[tokens.length - 1])); + arena.setFlags(node, exports.EnumAstNodeStatus.HasChildren | exports.EnumAstNodeStatus.Validated); + return node; + // return { + // typ: EnumToken.RuleNodeType, + // sel: [ + // ...tokens + // .reduce( + // (acc: string[][], curr: Token, index: number, array: Token[]) => { + // // if (curr.typ == EnumToken.CommentTokenType) { + // // return acc; + // // } + // if (curr.typ == EnumToken.WhitespaceTokenType) { + // if ( + // trimWhiteSpace.includes(array[index - 1]?.typ) || + // trimWhiteSpace.includes(array[index + 1]?.typ) || + // combinators.includes((array[index - 1])?.val) || + // combinators.includes((array[index + 1])?.val) + // ) { + // return acc; + // } + // } + // let t: string = printValue(curr, { minify: false }); + // if (t == ",") { + // acc.push([]); + // } else { + // acc[acc.length - 1].push(t); + // } + // return acc; + // }, + // [[]], + // ) + // .reduce((acc: Map, curr: string[]) => { + // let i: number = 0; + // for (; i < curr.length; i++) { + // if (i + 1 < curr.length && curr[i] == "*") { + // if (curr[i] == "*") { + // let index: number = curr[i + 1] == " " ? 2 : 1; + // if (![">", "~", "+"].includes(curr[index])) { + // curr.splice(i, index); + // } + // } + // } + // } + // acc.set(curr.join(""), curr); + // return acc; + // }, uniq) + // .keys(), + // ].join(","), + // chi: [], + // [LOCSRCID]: tokens[0][LOCSRCID], + // [LOCSTA]: tokens[0][LOCSTA], + // [LOCEND]: tokens[tokens.length - 1][LOCEND], + // [TOKENS]: tokens, + // [STATE]: + // result.success && allowed + // ? EnumAstNodeStatus.Validated + // : allowed + // ? EnumAstNodeStatus.Invalid + // : EnumAstNodeStatus.Disallowed, + // [ERRORS]: result.success ? [] : result.errors, + // } as AstRule; } /** @@ -29060,69 +30180,91 @@ function parseGridTemplate(template) { */ function parseDeclaration(tokens, parent, options, errors) { const name = tokens.shift(); + const arena = options.arena; let i; let rules = null; let syntaxRules = null; let success = true; + let val = arena.getValue(name); + let token; + let typ; let hasImportantFlag = false; let validate = typeof options.validation === "boolean" ? options.validation : !!options.validation; - if (renamedStandardProperties.has(name.val)) { - name.val = renamedStandardProperties.get(name.val); + if (renamedStandardProperties.has(val)) { + arena.setValue(name, arena.internString(renamedStandardProperties.get(val))); } for (i = 0; i < tokens.length; i++) { - const token = tokens[i]; - if (token.typ == exports.EnumToken.WhitespaceTokenType || - token.typ == exports.EnumToken.CommentTokenType || - token.typ == exports.EnumToken.InvalidCommentTokenType) { + token = tokens[i]; + typ = arena.getType(token); + if (typ == exports.EnumToken.WhitespaceTokenType || + typ == exports.EnumToken.CommentTokenType || + typ == exports.EnumToken.InvalidCommentTokenType) { continue; } break; } - if ((name.typ !== exports.EnumToken.IdenTokenType && name.typ !== exports.EnumToken.DashedIdenTokenType) || - tokens[i]?.typ !== exports.EnumToken.ColonTokenType) { - if (tokens[tokens.length - 1]?.[LOCEND] != null) { - name[LOCEND] = tokens[tokens.length - 1]?.[LOCEND]; - } - name[STATE] = exports.EnumAstNodeStatus.Unparsed; - name[ERRORS] = [ + typ = arena.getType(name); + if ((typ !== exports.EnumToken.IdenTokenType && typ !== exports.EnumToken.DashedIdenTokenType) || + arena.getType(tokens[i]) !== exports.EnumToken.ColonTokenType) { + // if (tokens[tokens.length - 1]?.[LOCEND] != null) { + // name[LOCEND] = tokens[tokens.length - 1]?.[LOCEND]; + // } + const node = arena.allocate(); + arena.setType(node, exports.EnumToken.RawNodeTokenType); + arena.setFlags(node, exports.EnumAstNodeStatus.Unparsed); + arena.setErrors(node, [ { action: "drop", node: name, - location: options.source.getSourceLocation(name[LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(name)), message: "invalid declaration", }, - ]; - return Object.assign({ - typ: exports.EnumToken.RawNodeTokenType, - val: tokens, - }); - } - tokens = trimArray(tokens.slice(i + 1)); - if (validate && name.typ === exports.EnumToken.IdenTokenType) { + ]); + arena.setTokens(node, ...tokens); + arena.setSpan(node, arena.getSourceId(name), arena.getSpanStart(name), arena.getSpanEnd(tokens.at(-1) ?? name)); + return node; + // name[STATE] = EnumAstNodeStatus.Unparsed; + // name[ERRORS] = [ + // { + // action: "drop", + // node: name, + // location: options.source!.getSourceLocation(name[LOCSTA]!), + // message: "invalid declaration", + // } as ErrorDescription, + // ]; + // return Object.assign({ + // typ: EnumToken.RawNodeTokenType, + // val: tokens, + // }) as RawNodeToken; + } + tokens = trimArray(tokens.slice(i + 1), options); + if (validate && typ === exports.EnumToken.IdenTokenType) { + typ = arena.getType(parent); + val = arena.getValue(parent).toLowerCase(); if (parent != null && - parent.typ === exports.EnumToken.AtRuleNodeType && - parent.nam != "import" && - parent.nam != "supports" && - parent.nam != "media") { + typ === exports.EnumToken.AtRuleNodeType && + val != "import" && + val != "supports" && + val != "media") { // check @at-rule description // pick up the syntax or reject immediately // else do this - rules = getSyntaxRule(ValidationSyntaxGroupEnum.AtRules, "@" + parent.nam); + rules = getSyntaxRule(ValidationSyntaxGroupEnum.AtRules, "@" + val); if (rules != null) { const propertyDescriptors = rules.getPropertyDescriptors(); if (propertyDescriptors != null) { - syntaxRules = propertyDescriptors[name.val.toLowerCase()]; + syntaxRules = propertyDescriptors[val.toLowerCase()]; } else { syntaxRules = rules.acceptAnyDeclaration && rules.acceptAnyRule - ? getParsedSyntax(ValidationSyntaxGroupEnum.Declarations, name.val.toLowerCase()) + ? getParsedSyntax(ValidationSyntaxGroupEnum.Declarations, val) : rules.getBlockRules(); } } } else { - syntaxRules = getParsedSyntax(ValidationSyntaxGroupEnum.Declarations, name.val.toLowerCase()); + syntaxRules = getParsedSyntax(ValidationSyntaxGroupEnum.Declarations, val); } // or // else @@ -29131,61 +30273,73 @@ function parseDeclaration(tokens, parent, options, errors) { syntaxRules[0].typ === ValidationTokenEnum.PropertyType && (syntaxRules[0].val === "declaration-list" || syntaxRules[0].val === "declaration-rule-list"))) { - if (parent?.typ === exports.EnumToken.AtRuleNodeType && - (parent.nam === "swash" || - parent.nam === "annotation" || - parent.nam === "ornaments" || - parent.nam === "stylistic" || - parent.nam === "historical-forms")) { + typ = arena.getType(parent); + val = arena.getValue(parent).toLowerCase(); + if (parent != null && + typ === exports.EnumToken.AtRuleNodeType && + (val === "swash" || + val === "annotation" || + val === "ornaments" || + val === "stylistic" || + val === "historical-forms")) { syntaxRules = getParsedSyntax(ValidationSyntaxGroupEnum.Syntaxes, "font-feature-custom-ident"); } - else if (parent?.typ === exports.EnumToken.AtRuleNodeType && parent.nam === "styleset") { + else if (typ === exports.EnumToken.AtRuleNodeType && val === "styleset") { syntaxRules = getParsedSyntax(ValidationSyntaxGroupEnum.Syntaxes, "font-feature-custom-ident-list"); } - else if (parent?.typ === exports.EnumToken.AtRuleNodeType && parent.nam === "character-variant") { + else if (typ === exports.EnumToken.AtRuleNodeType && val === "character-variant") { syntaxRules = getParsedSyntax(ValidationSyntaxGroupEnum.Syntaxes, "font-feature-custom-ident-character-variant"); } else { - syntaxRules = getParsedSyntax(ValidationSyntaxGroupEnum.Declarations, name.val.toLowerCase()); + syntaxRules = getParsedSyntax(ValidationSyntaxGroupEnum.Declarations, val); } } } - if (tokens.length === 0 && name.typ !== exports.EnumToken.DashedIdenTokenType) { + typ = arena.getType(name); + if (tokens.length === 0 && typ !== exports.EnumToken.DashedIdenTokenType) { errors.push({ action: "drop", message: "declaration value missing", node: name, - location: options.source.getSourceLocation(name[LOCSTA]), - }); - if (tokens[tokens.length - 1]?.[LOCEND] != null) { - name[LOCEND] = tokens[tokens.length - 1][LOCEND]; - } - name[STATE] = exports.EnumAstNodeStatus.Invalid; - name[ERRORS] = [errors[errors.length - 1]]; - // @ts-expect-error - return Object.assign(name, { - typ: exports.EnumToken.DeclarationNodeType, - nam: name.val, - val: tokens, + location: options.source.getSourceLocation(arena.getSpanStart(name)), }); + // if (tokens[tokens.length - 1]?.[LOCEND] != null) { + // name[LOCEND] = tokens[tokens.length - 1][LOCEND]; + // } + arena.setFlags(name, exports.EnumAstNodeStatus.Invalid | exports.EnumAstNodeStatus.HasValues); + arena.setErrors(name, [errors[errors.length - 1]]); + arena.setName(name, arena.internString(arena.getValue(name))); + arena.set(name, exports.EnumToken.DeclarationNodeType, 0); + arena.setChildren(name, ...tokens); + return name; + // name[STATE] = EnumAstNodeStatus.Invalid; + // name[ERRORS] = [errors[errors.length - 1]]; + // return Object.assign(name, { + // typ: EnumToken.DeclarationNodeType, + // nam: name.val, + // val: tokens, + // }) as AstDeclaration; } const stack = []; let result = null; - let token; + // let token: Token; let index; if (syntaxRules != null) { const doNotValidate = options.validation === false; - result = doNotValidate ? null : matchAllSyntaxes(syntaxRules, createValidationContext(tokens), options); + result = doNotValidate + ? null + : matchAllSyntaxes(syntaxRules, createValidationContext(tokens, options), options); if (doNotValidate || result != null) { success = doNotValidate || result?.success; if (success) { - for (const { value } of walkValues(tokens)) { - if (value.typ === exports.EnumToken.IdenTokenType && isColor(value)) { - parseColor(value); - } - else if (value.typ === exports.EnumToken.ColorTokenType) { - if (isColor(value)) { - parseColor(value); + for (const value of arena.walkValues(tokens)) { + typ = arena.getType(value); + if (typ === exports.EnumToken.IdenTokenType && isColor(value, arena)) { + parseColor(value, arena); + } + else if (typ === exports.EnumToken.ColorTokenType) { + if (isColor(value, arena)) { + parseColor(value, arena); } } } @@ -29199,66 +30353,83 @@ function parseDeclaration(tokens, parent, options, errors) { } for (i = 0; i < tokens.length; i++) { token = tokens[i]; - if (tokensfuncDefMap.has(token.typ) || - token.typ == exports.EnumToken.StartParensTokenType || - token.typ == exports.EnumToken.BlockStartTokenType) { + typ = arena.getType(token); + val = arena.getValue(token); + if (tokensfuncDefMap.has(typ) || + typ == exports.EnumToken.StartParensTokenType || + typ == exports.EnumToken.BlockStartTokenType) { stack.push(token); continue; } - if (token.typ === exports.EnumToken.ColonTokenType && stack.length == 0) { + if (typ === exports.EnumToken.ColonTokenType && stack.length == 0) { stack.push(token); continue; } - switch (token.typ) { + const stackType = arena.getType(stack.at(-1)); + switch (typ) { case exports.EnumToken.Literal: - if (token.val === "/" && stack.at(-1)?.typ == exports.EnumToken.MathFunctionTokenDefType) { - Object.assign(token, { - typ: exports.EnumToken.Div, - }); - } - else if (token.val === "-" && - stack.at(-1)?.typ == exports.EnumToken.MathFunctionTokenDefType) { - Object.assign(token, { - typ: exports.EnumToken.Sub, - }); + if (arena.equalsIgnoreCase(token, "val", "/") && stackType == exports.EnumToken.MathFunctionTokenDefType) { + arena.setType(token, exports.EnumToken.Div); + // Object.assign(token, { + // typ: EnumToken.Div, + // }); + } + else if (arena.equalsIgnoreCase(token, "val", "-") && + stackType == exports.EnumToken.MathFunctionTokenDefType) { + arena.setType(token, exports.EnumToken.Sub); + // Object.assign(token, { + // typ: EnumToken.Sub, + // }); } break; case exports.EnumToken.Star: case exports.EnumToken.Plus: - Object.assign(token, { - typ: tokensMap.get(token.typ), - }); + arena.setType(token, tokensMap.get(typ)); + // Object.assign(token, { + // typ: tokensMap.get(typ), + // }); break; case exports.EnumToken.BlockEndTokenType: - if (stack.at(-1)?.typ === exports.EnumToken.BlockStartTokenType) { + if (stackType === exports.EnumToken.BlockStartTokenType) { // stack.pop();index = tokens.indexOf(stack.at(-1)!); index = tokens.indexOf(stack.at(-1)); tokens.splice(i, 1); - Object.assign(tokens[index], { - typ: exports.EnumToken.WrappedValuesTokenType, - chi: trimArray(tokens.splice(index + 1, i - index - 1)), - }); + arena.setType(tokens[index], exports.EnumToken.WrappedValuesTokenType); + arena.setChildren(tokens[index], ...trimArray(tokens.splice(index + 1, i - index - 1), options)); + // Object.assign(tokens[index], { + // typ: EnumToken.WrappedValuesTokenType, + // chi: trimArray(tokens.splice(index + 1, i - index - 1), options), + // }); i = index; stack.pop(); } break; case exports.EnumToken.EndParensTokenType: - if (stack.at(-1)?.typ === exports.EnumToken.StartParensTokenType || tokensfuncDefMap.has(stack.at(-1)?.typ)) { + if (stackType === exports.EnumToken.StartParensTokenType || tokensfuncDefMap.has(stackType)) { index = tokens.indexOf(stack.at(-1)); tokens.splice(i, 1); - Object.assign(tokens[index], { - typ: stack.at(-1)?.typ === exports.EnumToken.StartParensTokenType - ? exports.EnumToken.ParensTokenType - : tokensfuncDefMap.get(stack.at(-1)?.typ), - chi: trimArray(tokens.splice(index + 1, i - index - 1)), - }); - if (tokens[index].typ === exports.EnumToken.WildCardFunctionTokenType && - equalsIgnoreCase(tokens[index].val, "if")) { - if (tokens[index].chi.at(-1)?.typ === exports.EnumToken.SemiColonTokenType) { - tokens[index].chi.pop(); + arena.setType(tokens[index], stackType === exports.EnumToken.StartParensTokenType + ? exports.EnumToken.ParensTokenType + : tokensfuncDefMap.get(stackType)); + arena.setChildren(tokens[index], ...trimArray(tokens.splice(index + 1, i - index - 1), options)); + // Object.assign(tokens[index], { + // typ: + // stack.at(-1)?.typ === EnumToken.StartParensTokenType + // ? EnumToken.ParensTokenType + // : (tokensfuncDefMap.get(stack.at(-1)?.typ) as EnumToken), + // chi: trimArray(tokens.splice(index + 1, i - index - 1)), + // }); + if (arena.getType(tokens[index]) === exports.EnumToken.WildCardFunctionTokenType && + arena.equalsIgnoreCase(tokens[index], "val", "if")) { + const lastChild = arena.getLastChild(tokens[index]); + if (lastChild != 0 && arena.getType(lastChild) === exports.EnumToken.SemiColonTokenType) { + arena.detach(lastChild); } + // if ((tokens[index] as FunctionToken).chi.at(-1)?.typ === EnumToken.SemiColonTokenType) { + // (tokens[index] as FunctionToken).chi.pop(); + // } let foundElse = false; - tokens[index].chi = splitTokenList(tokens[index].chi, [exports.EnumToken.SemiColonTokenType], true).reduce((acc, curr) => { + splitTokenList(arena.children(tokens[index]), options, [exports.EnumToken.SemiColonTokenType], true).reduce((acc, curr) => { if (foundElse) { // else already found // ignore everything after else @@ -29266,70 +30437,107 @@ function parseDeclaration(tokens, parent, options, errors) { return acc; } let index = -1; + let typ = arena.getType(curr[index]); while (++index < curr.length) { - if (curr[index].typ == exports.EnumToken.IdenTokenType && - equalsIgnoreCase("else", curr[index].val)) { + if (typ == exports.EnumToken.IdenTokenType && + arena.equalsIgnoreCase(curr[index], "val", "else")) { foundElse = true; } - if (curr[index].typ === exports.EnumToken.ColonTokenType) { + if (typ === exports.EnumToken.ColonTokenType) { break; } } if (foundElse && acc.length > 0) { - acc[acc.length - 1] = { - typ: exports.EnumToken.IfElseConditionTokenType, - l: acc[acc.length - 1], - r: { - typ: exports.EnumToken.IfConditionTokenType, - l: trimArray(curr.slice(0, index)), - r: trimArray(curr.at(-1)?.typ === exports.EnumToken.SemiColonTokenType - ? curr.slice(index + 1, -1) - : curr.slice(index + 1)), - }, - }; + const leftChild = arena.allocate(); + arena.setType(leftChild, exports.EnumToken.ListTokenType); + arena.setChildren(leftChild, ...trimArray(curr.slice(0, index), options)); + const rightChild = arena.allocate(); + arena.setType(rightChild, exports.EnumToken.ListTokenType); + arena.setChildren(rightChild, ...trimArray(arena.getType(curr.at(-1)) === exports.EnumToken.SemiColonTokenType + ? curr.slice(index + 1, -1) + : curr.slice(index + 1), options)); + const ifCondition = arena.allocate(); + arena.setType(ifCondition, exports.EnumToken.IfConditionTokenType); + arena.setLeft(ifCondition, leftChild); + arena.setRight(ifCondition, rightChild); + const ifElseLeftCondition = arena.allocate(); + arena.setType(ifElseLeftCondition, exports.EnumToken.IfElseConditionTokenType); + arena.setLeft(ifElseLeftCondition, acc[acc.length - 1]); + arena.setRight(ifElseLeftCondition, ifCondition); + acc[acc.length - 1] = ifElseLeftCondition; + // acc[acc.length - 1] = { + // typ: EnumToken.IfElseConditionTokenType, + // l: acc[acc.length - 1] as IfConditionToken, + // r: { + // typ: EnumToken.IfConditionTokenType, + // l: trimArray(curr.slice(0, index)), + // r: trimArray( + // curr.at(-1)?.typ === EnumToken.SemiColonTokenType + // ? curr.slice(index + 1, -1) + // : curr.slice(index + 1), + // ), + // } as IfConditionToken, + // } as IfElseConditionToken; } else { - acc.push({ - typ: exports.EnumToken.IfConditionTokenType, - l: trimArray(curr.slice(0, index)), - r: trimArray(curr.slice(index + 1)), - }); + const ifCondition = arena.allocate(); + arena.setType(ifCondition, exports.EnumToken.IfConditionTokenType); + const leftChild = arena.allocate(); + arena.setType(leftChild, exports.EnumToken.ListTokenType); + arena.setChildren(leftChild, ...trimArray(curr.slice(0, index), options)); + const rightChild = arena.allocate(); + arena.setType(rightChild, exports.EnumToken.ListTokenType); + arena.setChildren(rightChild, ...trimArray(curr.slice(index + 1), options)); + arena.setLeft(ifCondition, leftChild); + arena.setRight(ifCondition, rightChild); + acc.push(ifCondition); + // acc.push({ + // typ: EnumToken.IfConditionTokenType, + // l: trimArray(curr.slice(0, index)), + // r: trimArray(curr.slice(index + 1)), + // } as IfConditionToken); } return acc; }, []); } - else if (tokens[index].typ === exports.EnumToken.UrlFunctionTokenType) { + else if (arena.getType(tokens[index]) === exports.EnumToken.UrlFunctionTokenType) { let l = -1; - while (++l < tokens[index].chi.length) { - if (tokens[index].chi[l].typ === exports.EnumToken.StringTokenType) { + const children = arena.children(tokens[index]); + while (++l < children.length) { + if (arena.getType(children[l]) === exports.EnumToken.StringTokenType) { break; } } - if (tokens[index].chi[l]?.typ === exports.EnumToken.StringTokenType && - /^[a-zA-Z0-0/_.-]+$/.test(tokens[index].chi[l].val.slice(1, -1))) { - Object.assign(tokens[index].chi[l], { - typ: exports.EnumToken.UrlTokenTokenType, - val: tokens[index].chi[l].val.slice(1, -1), - }); + if (l < children.length && arena.getType(children[l]) === exports.EnumToken.StringTokenType) { + const val = arena.getValue(children[l]).slice(1, -1); + if (/^[a-zA-Z0-0/_.-]+$/.test(val)) { + arena.setValue(children[l], arena.internString(val)); + arena.setType(children[l], exports.EnumToken.UrlTokenTokenType); + // Object.assign((tokens[index] as FunctionToken).chi[l], { + // typ: EnumToken.UrlTokenTokenType, + // val: ((tokens[index] as FunctionToken).chi[l] as StringToken).val.slice(1, -1), + // }); + } } } i = index; - if (tokens[index].typ === exports.EnumToken.MathFunctionTokenType && - equalsIgnoreCase("calc", tokens[index].val)) { - tokens[index].chi = [buildExpression(tokens[index].chi)]; - } - else if (tokens[index].typ === exports.EnumToken.ColorTokenType) { - if (isColor(tokens[index])) { - parseColor(tokens[index]); + if (arena.getType(tokens[index]) === exports.EnumToken.MathFunctionTokenType && + arena.equalsIgnoreCase(tokens[index], "val", "calc")) { + arena.setChildren(tokens[index], ...arena.children(tokens[index])); + // (tokens[index] as FunctionToken).chi = [buildExpression((tokens[index] as FunctionToken).chi)]; + } + else if (arena.getType(tokens[index]) === exports.EnumToken.ColorTokenType) { + if (isColor(tokens[index], arena)) { + parseColor(tokens[index], arena); } else { success = false; - tokens[index].typ = exports.EnumToken.FunctionTokenType; + arena.setType(tokens[index], exports.EnumToken.FunctionTokenType); errors.push({ action: "drop", message: `invalid color`, node: tokens[index], - location: options.source.getSourceLocation(tokens[index][LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(tokens[index])), }); } } @@ -29338,31 +30546,39 @@ function parseDeclaration(tokens, parent, options, errors) { break; case exports.EnumToken.IdenTokenType: { - const val = token.val.toLowerCase(); - if (val in COLORS_NAMES || val === "currentcolor") { - Object.assign(token, { - val, - typ: exports.EnumToken.ColorTokenType, - kin: exports.ColorType.LIT, - }); + const val = arena.getValue(token).toLowerCase(); + if (COLORS_NAMES[val] != null || val === "currentcolor") { + arena.setType(token, exports.EnumToken.ColorTokenType); + arena.setColorType(token, exports.ColorType.LIT); + // Object.assign(token, { + // val, + // typ: EnumToken.ColorTokenType, + // kin: ColorType.LIT, + // }); } else if (nonStandardColors.has(val)) { - Object.assign(token, { - typ: exports.EnumToken.ColorTokenType, - kin: exports.ColorType.NON_STD, - }); + arena.setType(token, exports.EnumToken.ColorTokenType); + arena.setColorType(token, exports.ColorType.NON_STD); + // Object.assign(token, { + // typ: EnumToken.ColorTokenType, + // kin: ColorType.NON_STD, + // }); } else if (systemColors.has(val)) { - Object.assign(token, { - typ: exports.EnumToken.ColorTokenType, - kin: exports.ColorType.SYS, - }); + arena.setType(token, exports.EnumToken.ColorTokenType); + arena.setColorType(token, exports.ColorType.SYS); + // Object.assign(token, { + // typ: EnumToken.ColorTokenType, + // kin: ColorType.SYS, + // }); } else if (deprecatedSystemColors.has(val)) { - Object.assign(token, { - typ: exports.EnumToken.ColorTokenType, - kin: exports.ColorType.DPSYS, - }); + arena.setType(token, exports.EnumToken.ColorTokenType); + arena.setColorType(token, exports.ColorType.DPSYS); + // Object.assign(token, { + // typ: EnumToken.ColorTokenType, + // kin: ColorType.DPSYS, + // }); } } break; @@ -29370,8 +30586,9 @@ function parseDeclaration(tokens, parent, options, errors) { hasImportantFlag = true; break; } - if (trimTokenSpace$1.has(token.typ)) { - if (tokens[i + 1]?.typ === exports.EnumToken.WhitespaceTokenType) { + typ = arena.getType(token); + if (trimTokenSpace$1.has(typ)) { + if (tokens.length > i + 1 && arena.getType(tokens[i + 1]) === exports.EnumToken.WhitespaceTokenType) { for (let index = i + 1; index < tokens.length - 1; index++) { tokens[index] = tokens[index + 1]; } @@ -29379,7 +30596,7 @@ function parseDeclaration(tokens, parent, options, errors) { i--; continue; } - if (tokens[i - 1]?.typ == exports.EnumToken.WhitespaceTokenType) { + if (tokens.length >= i && arena.getType(tokens[i - 1]) == exports.EnumToken.WhitespaceTokenType) { for (let index = i - 1; index < tokens.length - 1; index++) { tokens[index] = tokens[index + 1]; } @@ -29393,97 +30610,156 @@ function parseDeclaration(tokens, parent, options, errors) { action: "drop", message: "unbalanced token", node: stack[stack.length - 1], - location: options.source.getSourceLocation(stack[stack.length - 1][LOCSTA]), - }); - if (tokens[tokens.length - 1][LOCEND] != null) { - name[LOCEND] = tokens[tokens.length - 1][LOCEND]; - } - name[STATE] = exports.EnumAstNodeStatus.Invalid; - name[ERRORS] = result?.errors ?? []; - //@ts-expect-error - return Object.assign(name, { - typ: exports.EnumToken.DeclarationNodeType, - nam: name.val, - val: tokens, + location: options.source.getSourceLocation(arena.getSpanStart(stack[stack.length - 1])), }); - } - for (const { value } of walkValues(tokens)) { - if (value.typ === exports.EnumToken.IdenTokenType && isColor(value)) { - parseColor(value); - } - } - if (name.val === "grid" || - name.val === "grid-template-areas" || - name.val === "grid-template-rows" || - name.val === "grid-template-columns") { + if (tokens.length > 0) { + arena.setSpanEnd(name, arena.getSpanEnd(tokens[tokens.length - 1])); + // name[LOCEND] = tokens[tokens.length - 1][LOCEND]; + } + arena.setName(name, arena.internString(arena.getValue(name))); + arena.set(name, exports.EnumToken.DeclarationNodeType, 0); + arena.setFlags(name, exports.EnumAstNodeStatus.Invalid | exports.EnumAstNodeStatus.HasValues); + arena.setErrors(name, result?.errors ?? []); + arena.setChildren(name, ...tokens); + return name; + // name[STATE] = EnumAstNodeStatus.Invalid; + // name[ERRORS] = result?.errors ?? []; + // return Object.assign(name, { + // typ: EnumToken.DeclarationNodeType, + // nam: name.val, + // val: tokens, + // }) as AstDeclaration; + } + for (const value of arena.walkValues(tokens)) { + if (arena.getType(value) === exports.EnumToken.IdenTokenType && isColor(value, arena)) { + parseColor(value, arena); + } + } + val = arena.getValue(name); + if (val === "grid" || + val === "grid-template-areas" || + val === "grid-template-rows" || + val === "grid-template-columns") { let i = 0; for (; i < tokens.length; i++) { - if (tokens[i].typ === exports.EnumToken.StringTokenType) { - if (/[\s.]/.test(tokens[i].val)) { - tokens[i].val = parseGridTemplate(tokens[i].val); - } - if (tokens[i + 1]?.typ === exports.EnumToken.WhitespaceTokenType && - tokens[i + 2]?.typ === exports.EnumToken.StringTokenType) { + typ = arena.getType(tokens[i]); + if (typ === exports.EnumToken.StringTokenType) { + val = arena.getValue(tokens[i]); + if (/[\s.]/.test(val)) { + arena.setValue(tokens[i], arena.internString(parseGridTemplate(val))); + // (tokens[i] as StringToken).val = parseGridTemplate(val); + } + if (arena.getType(tokens[i + 1]) === exports.EnumToken.WhitespaceTokenType && + arena.getType(tokens[i + 2]) === exports.EnumToken.StringTokenType) { tokens.splice(i + 1, 1); } } } } - if (validate && syntaxRules == null && name.typ === exports.EnumToken.IdenTokenType) { - if (tokens[tokens.length - 1]?.[LOCEND] != null) { - name[LOCEND] = tokens[tokens.length - 1][LOCEND]; - } - name[STATE] = exports.EnumAstNodeStatus.Unknown; - name[ERRORS] = result?.errors ?? []; - if (hasImportantFlag) { - name[STATE] |= exports.EnumAstNodeStatus.ImportantDeclaration; - } - // @ts-expect-error - return Object.assign(name, { - typ: exports.EnumToken.DeclarationNodeType, - nam: name.val, - val: tokens, - }); - } - if (equalsIgnoreCase("composes", name.val)) { + if (validate && syntaxRules == null && arena.getType(name) === exports.EnumToken.IdenTokenType) { + if (tokens.length > 0 != null) { + arena.setSpanEnd(name, arena.getSpanEnd(tokens[tokens.length - 1])); + // name[LOCEND] = tokens[tokens.length - 1][LOCEND]; + } + arena.setName(name, arena.internString(arena.getValue(name))); + arena.set(name, exports.EnumToken.DeclarationNodeType, 0); + arena.setFlags(name, hasImportantFlag ? exports.EnumAstNodeStatus.ImportantDeclaration | exports.EnumAstNodeStatus.Unknown : exports.EnumAstNodeStatus.Unknown); + arena.setErrors(name, result?.errors ?? []); + arena.setChildren(name, ...tokens); + return name; + // name[STATE] = EnumAstNodeStatus.Unknown; + // name[ERRORS] = result?.errors ?? []; + // // @ts-expect-error + // return Object.assign(name, { + // typ: EnumToken.DeclarationNodeType, + // nam: name.val, + // val: tokens, + // }) as AstDeclaration; + // if (hasImportantFlag) { + // name[STATE] |= EnumAstNodeStatus.ImportantDeclaration; + // } + // // @ts-expect-error + // return Object.assign(name, { + // typ: EnumToken.DeclarationNodeType, + // nam: name.val, + // val: tokens, + // }) as AstDeclaration; + } + if (arena.equalsIgnoreCase(name, "val", "composes")) { let index = -1; for (let i = 0; i < tokens.length; i++) { - if (tokens[i].typ === exports.EnumToken.IdenTokenType && equalsIgnoreCase("from", tokens[i].val)) { + if (arena.getType(tokens[i]) === exports.EnumToken.IdenTokenType && + arena.equalsIgnoreCase(tokens[i], "val", "from")) { index = i; break; } } - const left = trimArray(index == -1 ? tokens : tokens.slice(0, index)); - const right = index == -1 ? null : trimArray(tokens.slice(index + 1)); - tokens = [ - { - typ: exports.EnumToken.ComposesSelectorNodeType, - l: left, - r: right?.[0] ?? null, - [LOCSRCID]: tokens[0][LOCSRCID], - [LOCSTA]: tokens[0][LOCSTA], - [LOCEND]: index != -1 ? right[right.length - 1]?.[LOCEND] : left[left.length - 1][LOCEND], - }, - ]; + const left = trimArray(index == -1 ? tokens : tokens.slice(0, index), options); + const right = index == -1 ? null : trimArray(tokens.slice(index + 1), options); + const composeSelector = arena.allocate(); + arena.set(composeSelector, exports.EnumToken.ComposesSelectorNodeType); + arena.setSpan(composeSelector, arena.getSourceId(tokens[0]), arena.getSpanStart(tokens[0]), arena.getSpanEnd(right?.[right.length - 1] ?? left[left.length - 1])); + const leftSide = arena.allocate(); + arena.set(leftSide, exports.EnumToken.ListTokenType); + arena.setChildren(leftSide, ...left); + arena.setLeft(composeSelector, leftSide); + if (right != null) { + const rightSide = arena.allocate(); + arena.set(rightSide, exports.EnumToken.ListTokenType); + arena.setChildren(rightSide, ...right); + arena.setRight(composeSelector, rightSide); + } + tokens.length = 0; + tokens.push(composeSelector); + // tokens = [ + // { + // typ: EnumToken.ComposesSelectorNodeType, + // l: left, + // r: right?.[0] ?? null, + // [LOCSRCID]: tokens[0][LOCSRCID], + // [LOCSTA]: tokens[0][LOCSTA], + // [LOCEND]: index != -1 ? right![right!.length - 1]?.[LOCEND] : left[left.length - 1][LOCEND], + // } as ComposesSelectorToken, + // ]; } - if (tokens[tokens.length - 1]?.[LOCEND] != null) { - name[LOCEND] = tokens[tokens.length - 1][LOCEND]; + if (tokens.length > 0) { + arena.setSpanEnd(name, arena.getSpanEnd(tokens[tokens.length - 1])); + // name[LOCEND] = tokens[tokens.length - 1][LOCEND]; } - name[STATE] = success + let flags = exports.EnumAstNodeStatus.HasValues | (success ? result == null ? exports.EnumAstNodeStatus.Unvalidated : exports.EnumAstNodeStatus.Validated - : exports.EnumAstNodeStatus.ValidationFailed; - name[ERRORS] = result?.errors ?? []; + : exports.EnumAstNodeStatus.ValidationFailed); if (hasImportantFlag) { - name[STATE] |= exports.EnumAstNodeStatus.ImportantDeclaration; - } - // @ts-expect-error - return Object.assign(name, { - typ: exports.EnumToken.DeclarationNodeType, - nam: name.val, - val: tokens, - }); + flags |= exports.EnumAstNodeStatus.ImportantDeclaration; + } + arena.setName(name, arena.internString(arena.getValue(name))); + arena.set(name, exports.EnumToken.DeclarationNodeType, 0); + arena.setFlags(name, flags); + arena.setErrors(name, result?.errors ?? []); + arena.setChildren(name, ...tokens); + return name; + // name[STATE] = success + // ? result == null + // ? EnumAstNodeStatus.Unvalidated + // : EnumAstNodeStatus.Validated + // : EnumAstNodeStatus.ValidationFailed; + // name[ERRORS] = result?.errors ?? []; + // return Object.assign(name, { + // typ: EnumToken.DeclarationNodeType, + // nam: name.val, + // val: tokens, + // }) as AstDeclaration; + // if (hasImportantFlag) { + // name[STATE] |= EnumAstNodeStatus.ImportantDeclaration; + // } + // // @ts-expect-error + // return Object.assign(name, { + // typ: EnumToken.DeclarationNodeType, + // nam: name.val, + // val: tokens, + // }) as AstDeclaration; } // https://drafts.csswg.org/mediaqueries/#media-descriptor-table:~:text=It%20is%20invalid%20to%20mix%20and%20and%20or%20and%20not%20at%20the%20same%20%E2%80%9Clevel%E2%80%9D%20of%20a%20media%20query%2E @@ -30899,8 +32175,8 @@ function renderTokens(tokens, options) { if (tokens == null || tokens.length === 0) return ""; if (options != null) - return tokens.map((t) => renderValue(t, options)).join(""); - return tokens.map((t) => renderValue(t)).join(""); + return tokens.map((t) => printValue(t, options)).join(""); + return tokens.map((t) => printValue(t)).join(""); } const trimWhiteSpace = [ exports.EnumToken.CommentTokenType, @@ -31361,140 +32637,159 @@ function doParseSync(tokenizer, options = {}) { imports: [], }; const invalidNodes = []; + const arena = options.arena; let ast = { typ: exports.EnumToken.StyleSheetNodeType, chi: [], }; let tokens = []; - let context = ast; - let item; + let context = arena.root; let node; // @ts-ignore ignore error let parensMatch = 0; let curlyBracketMatch = 0; + let tokenId; // let currentItemIndex: number; ast[LOCSRCID] = options.source.id; ast[LOCSTA] = 0; while (!tokenizer.done()) { tokenizer.next(); // item = (iter as Array)[currentItemIndex]; + tokenId = arena.allocate(); if (tokenizer.unit != null) { - item = { - typ: tokenizer.typ, - val: tokenizer.val, - unit: tokenizer.unit, - }; + arena.set(tokenId, tokenizer.typ, 0, tokenizer.val, arena.internString(tokenizer.unit)); + // item = { + // typ: tokenizer.typ as EnumToken, + // val: tokenizer.val, + // unit: tokenizer.unit, + // } as DimensionToken; } else if (tokenizer.nam != null) { - item = { - typ: tokenizer.typ, - nam: tokenizer.nam, - }; + arena.set(tokenId, tokenizer.typ); + arena.setName(tokenId, arena.internString(tokenizer.nam)); + // item = { + // typ: tokenizer.typ as EnumToken, + // nam: tokenizer.nam, + // } as Token; } else if (tokenizer.val === null) { - item = { - typ: tokenizer.typ, - }; + arena.set(tokenId, tokenizer.typ); + // item = { + // typ: tokenizer.typ as EnumToken, + // } as Token; } else if (tokenizer.kin != null) { - item = { - typ: tokenizer.typ, - val: tokenizer.val, - kin: tokenizer.kin, - }; + arena.set(tokenId, tokenizer.typ, arena.internString(tokenizer.val)); + arena.setColorType(tokenId, tokenizer.kin); + // item = { + // typ: tokenizer.typ as EnumToken, + // val: tokenizer.val, + // kin: tokenizer.kin, + // } as ColorToken; } else { - item = { - typ: tokenizer.typ, - val: tokenizer.val, - }; - } - item[LOCSRCID] = tokenizer.srcId; - item[LOCSTA] = tokenizer.sta; - item[LOCEND] = tokenizer.end; + arena.set(tokenId, tokenizer.typ, typeof tokenizer.val === "string" ? arena.internString(tokenizer.val) : 0, typeof tokenizer.val === "number" ? tokenizer.val : 0); + // item = { + // typ: tokenizer.typ as EnumToken, + // val: tokenizer.val, + // } as Token; + } + arena.setSpan(tokenId, tokenizer.srcId, tokenizer.sta, tokenizer.end); + // item[LOCSRCID] = tokenizer.srcId as number; + // item[LOCSTA] = tokenizer.sta as number; + // item[LOCEND] = tokenizer.end as number; stats.bytesIn = tokenizer.bytesIn; stats.tokensCount++; - if (BadTokensTypes.includes(item.typ)) { - tokens.push(item); + if (BadTokensTypes.includes(tokenizer.typ)) { + tokens.push(tokenId); errors.push({ action: "drop", message: "Bad token", syntax: null, - node: item, - location: options.source.getSourceLocation(item[LOCSTA]), + node: tokenId, + location: options.source.getSourceLocation(tokenizer.sta), }); // bad token continue; } - if (item.typ === exports.EnumToken.StartParensTokenType || tokensfuncDefMap.has(item.typ)) { + if (tokenizer.typ === exports.EnumToken.StartParensTokenType || tokensfuncDefMap.has(tokenizer.typ)) { parensMatch++; } - else if (item.typ === exports.EnumToken.EndParensTokenType && parensMatch > 0) { + else if (tokenizer.typ === exports.EnumToken.EndParensTokenType && parensMatch > 0) { parensMatch--; } - if (item.typ === exports.EnumToken.BlockStartTokenType) { + if (tokenizer.typ === exports.EnumToken.BlockStartTokenType) { curlyBracketMatch++; } - else if (item.typ === exports.EnumToken.BlockEndTokenType && curlyBracketMatch > 0) { + else if (tokenizer.typ === exports.EnumToken.BlockEndTokenType && curlyBracketMatch > 0) { curlyBracketMatch--; } - tokens.push(item); + tokens.push(tokenId); if (parensMatch === 0 && - (item.typ === exports.EnumToken.SemiColonTokenType || - item.typ === exports.EnumToken.BlockStartTokenType || - item.typ === exports.EnumToken.EOFTokenType)) { + (tokenizer.typ === exports.EnumToken.SemiColonTokenType || + tokenizer.typ === exports.EnumToken.BlockStartTokenType || + tokenizer.typ === exports.EnumToken.EOFTokenType)) { node = parseNode(tokens, context, options, errors, stats, invalidNodes); if (node != null) { - if ("chi" in node) { + if (arena.hasFlags(node, exports.EnumAstNodeStatus.HasChildren)) { stack.push(node); context = node; } } - else if (item.typ == exports.EnumToken.BlockStartTokenType) { + else if (tokenizer.typ == exports.EnumToken.BlockStartTokenType) { let inBlock = 1; tokens.length = 0; - tokens.push(item); + tokens.push(tokenId); do { tokenizer.next(); + tokenId = arena.allocate(); if (tokenizer.unit != null) { - item = { - typ: tokenizer.typ, - val: tokenizer.val, - unit: tokenizer.unit, - }; + arena.set(tokenId, tokenizer.typ, 0, tokenizer.val, arena.internString(tokenizer.unit)); + // item = { + // typ: tokenizer.typ as EnumToken, + // val: tokenizer.val, + // unit: tokenizer.unit, + // } as DimensionToken; } else if (tokenizer.nam != null) { - item = { - typ: tokenizer.typ, - nam: tokenizer.nam, - }; + arena.set(tokenId, tokenizer.typ); + arena.setName(tokenId, arena.internString(tokenizer.nam)); + // item = { + // typ: tokenizer.typ as EnumToken, + // nam: tokenizer.nam, + // } as Token; } else if (tokenizer.val === null) { - item = { - typ: tokenizer.typ, - }; + arena.set(tokenId, tokenizer.typ); + // item = { + // typ: tokenizer.typ as EnumToken, + // } as Token; } else if (tokenizer.kin != null) { - item = { - typ: tokenizer.typ, - val: tokenizer.val, - kin: tokenizer.kin, - }; + arena.set(tokenId, tokenizer.typ, arena.internString(tokenizer.val)); + arena.setColorType(tokenId, tokenizer.kin); + // item = { + // typ: tokenizer.typ as EnumToken, + // val: tokenizer.val, + // kin: tokenizer.kin, + // } as ColorToken; } else { - item = { - typ: tokenizer.typ, - val: tokenizer.val, - }; - } - item[LOCSRCID] = tokenizer.srcId; - item[LOCSTA] = tokenizer.sta; - item[LOCEND] = tokenizer.end; - tokens.push(item); - if (item.typ === exports.EnumToken.BlockStartTokenType) { + arena.set(tokenId, tokenizer.typ, arena.internString(tokenizer.val)); + // item = { + // typ: tokenizer.typ as EnumToken, + // val: tokenizer.val, + // } as Token; + } + arena.setSpan(tokenId, tokenizer.srcId, tokenizer.sta, tokenizer.end); + // item[LOCSRCID] = tokenizer.srcId as number; + // item[LOCSTA] = tokenizer.sta as number; + // item[LOCEND] = tokenizer.end as number; + tokens.push(tokenId); + if (tokenizer.typ === exports.EnumToken.BlockStartTokenType) { inBlock++; } - else if (item.typ === exports.EnumToken.BlockEndTokenType) { + else if (tokenizer.typ === exports.EnumToken.BlockEndTokenType) { inBlock--; } } while (inBlock != 0 && !tokenizer.done()); @@ -31502,22 +32797,22 @@ function doParseSync(tokenizer, options = {}) { errors.push({ action: "drop", message: "invalid block", - location: options.source.getSourceLocation(tokens[0][LOCSTA]), + location: options.source.getSourceLocation(tokenizer.sta), }); } } tokens.length = 0; } - else if ((parensMatch === 0 || curlyBracketMatch === 0) && item.typ === exports.EnumToken.BlockEndTokenType) { + else if ((parensMatch === 0 || curlyBracketMatch === 0) && tokenizer.typ === exports.EnumToken.BlockEndTokenType) { parseNode(tokens, context, options, errors, stats, invalidNodes); - context[LOCEND] = item[LOCEND]; + // context[LOCEND] = tokenizer.end as number; const previousNode = stack.pop(); - context = (stack[stack.length - 1] ?? ast); + context = stack[stack.length - 1] ?? arena.root; if (options.removeEmpty && previousNode != null && - previousNode.chi.length == 0 && - context.chi[context.chi.length - 1] == previousNode) { - context.chi.pop(); + arena.getFirstChild(previousNode) == 0 && + arena.getLastChild(context) == previousNode) { + arena.detach(previousNode); } tokens.length = 0; parensMatch = 0; @@ -31528,7 +32823,7 @@ function doParseSync(tokenizer, options = {}) { if (tokens.length > 0) { node = parseNode(tokens, context, options, errors, stats, invalidNodes); if (node != null) { - if ("chi" in node /* && node.typ != EnumToken.InvalidRuleNodeType */) { + if (arena.hasFlags(node, exports.EnumAstNodeStatus.HasChildren)) { stack.push(node); context = node; } @@ -31734,6 +33029,7 @@ function doParseSync(tokenizer, options = {}) { stats.bytesIn += stats.importedBytesIn; let endTime = performance.now(); const result = { + arena, ast, errors, stats: { @@ -32172,7 +33468,7 @@ function doParseSync(tokenizer, options = {}) { } node.sel = ""; for (const token of node[TOKENS]) { - node.sel += renderValue(token); + node.sel += printValue(token); } } else if (node.typ == exports.EnumToken.AtRuleNodeType || node.typ == exports.EnumToken.KeyframesAtRuleNodeType) { @@ -33278,7 +34574,7 @@ async function doParse(iter, options = {}) { } node.sel = ""; for (const token of node[TOKENS]) { - node.sel += renderValue(token); + node.sel += printValue(token); } } else if (node.typ == exports.EnumToken.AtRuleNodeType || node.typ == exports.EnumToken.KeyframesAtRuleNodeType) { @@ -33337,104 +34633,128 @@ async function doParse(iter, options = {}) { } function parseNode(tokens, context, options, errors, stats, invalidNodes) { let i = 0; - if (tokens.at(-1)?.typ === exports.EnumToken.EOFTokenType) { + const arena = options.arena; + const contextType = arena.getType(context); + let token; + let typ; + if (arena.getType(tokens.at(-1)) === exports.EnumToken.EOFTokenType) { tokens.pop(); // check parenthesis are balanced let matchCount = 0; for (let i = 0; i < tokens.length; i++) { - const token = tokens[i]; - if (token.typ === exports.EnumToken.StartParensTokenType || tokensfuncDefMap.has(token.typ)) { + token = tokens[i]; + typ = arena.getType(token); + if (typ === exports.EnumToken.StartParensTokenType || tokensfuncDefMap.has(typ)) { matchCount++; } - else if (token.typ === exports.EnumToken.EndParensTokenType) { + else if (typ === exports.EnumToken.EndParensTokenType) { matchCount--; } } if (matchCount > 0) { let k = tokens.length; - while (k-- > 0 && - (tokens[k].typ === exports.EnumToken.WhitespaceTokenType || tokens[k].typ === exports.EnumToken.CommentTokenType)) - ; - if (tokens[k]?.typ == exports.EnumToken.SemiColonTokenType) { - matchCount = 0; - } - while (matchCount > 0) { - tokens.push({ - typ: exports.EnumToken.EndParensTokenType, - [LOCSRCID]: tokens[k]?.[LOCSRCID], - [LOCSTA]: tokens[k]?.[LOCSTA], - [LOCEND]: tokens[k]?.[LOCEND], - }); - matchCount--; + if (k > 0) { + typ = arena.getType(tokens[k - 1]); + while ((k-- > 0 && typ === exports.EnumToken.WhitespaceTokenType) || typ === exports.EnumToken.CommentTokenType) { + typ = arena.getType(tokens[k - 1]); + } + if (typ == exports.EnumToken.SemiColonTokenType) { + matchCount = 0; + } + let tokenId; + let sourceId = arena.getSourceId(tokens[k]); + let spanStart = arena.getSpanStart(tokens[k]); + let spanEnd = arena.getSpanEnd(tokens[k]); + while (matchCount > 0) { + tokenId = arena.allocate(); + arena.set(tokenId, exports.EnumToken.EndParensTokenType); + arena.setSpan(tokenId, sourceId, spanStart, spanEnd); + // tokens.push({ + // typ: EnumToken.EndParensTokenType, + // [LOCSRCID]: tokens[k]?.[LOCSRCID], + // [LOCSTA]: tokens[k]?.[LOCSTA], + // [LOCEND]: tokens[k]?.[LOCEND], + // }); + matchCount--; + } } } } for (; i < tokens.length; i++) { - if (tokens[i].typ === exports.EnumToken.CDOCOMMTokenType && context.typ !== exports.EnumToken.StyleSheetNodeType) { + if (arena.getType(tokens[i]) === exports.EnumToken.CDOCOMMTokenType && contextType !== exports.EnumToken.StyleSheetNodeType) { errors.push({ action: "drop", message: `CDOCOMM not allowed here ${JSON.stringify(tokens[i], null, 1)}`, node: tokens[i], - location: options.source.getSourceLocation(tokens[i][LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(tokens[i])), }); - tokens[i].typ = exports.EnumToken.InvalidCommentTokenType; + arena.set(tokens[i], exports.EnumToken.InvalidCommentTokenType); continue; } - if (tokens[i].typ === exports.EnumToken.CommentTokenType || - tokens[i].typ === exports.EnumToken.CDOCOMMTokenType || - tokens[i].typ === exports.EnumToken.WhitespaceTokenType) { + typ = arena.getType(tokens[i]); + if (typ === exports.EnumToken.CommentTokenType || + typ === exports.EnumToken.CDOCOMMTokenType || + typ === exports.EnumToken.WhitespaceTokenType) { continue; } break; } if (i > 0) { - context.chi.push(...tokens.splice(0, i).filter((n) => n.typ !== exports.EnumToken.WhitespaceTokenType)); + arena.appendChildren(context, ...tokens.splice(0, i).filter((n) => arena.getType(n) !== exports.EnumToken.WhitespaceTokenType)); i = 0; } for (; i < tokens.length; i++) { - if (tokens[i].typ == exports.EnumToken.CommentTokenType || tokens[i].typ == exports.EnumToken.CDOCOMMTokenType) { - if (tokens[i].typ == exports.EnumToken.CDOCOMMTokenType && context.typ != exports.EnumToken.StyleSheetNodeType) { + typ = arena.getType(tokens[i]); + if (typ == exports.EnumToken.CommentTokenType || typ == exports.EnumToken.CDOCOMMTokenType) { + if (typ == exports.EnumToken.CDOCOMMTokenType && contextType != exports.EnumToken.StyleSheetNodeType) { errors.push({ action: "drop", message: `CDOCOMM not allowed here ${JSON.stringify(tokens[i], null, 1)}`, node: tokens[i], - location: options.source.getSourceLocation(tokens[i][LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(tokens[i])), }); - tokens[i].typ = exports.EnumToken.InvalidCommentTokenType; + arena.setType(tokens[i], exports.EnumToken.InvalidCommentTokenType); continue; } - tokens[i][ROOT] = context[ROOT]; - context.chi.push(tokens[i]); + // tokens[i][ROOT] = context[ROOT]; + arena.appendChildren(context, tokens[i]); + // context.chi!.push(tokens[i] as AstNode); stats.nodesCount++; } - else if (tokens[i].typ != exports.EnumToken.WhitespaceTokenType) { + else if (typ != exports.EnumToken.WhitespaceTokenType) { break; } } if (tokens.length == 0) { return null; } + // for (const token of tokens.slice(1)) { + // console.error(EnumToken[arena.getType(token)]); + // } let delim = tokens.at(-1); - if (delim.typ == exports.EnumToken.SemiColonTokenType || - delim.typ == exports.EnumToken.BlockStartTokenType || - delim.typ == exports.EnumToken.BlockEndTokenType) { + typ = arena.getType(delim); + if (typ == exports.EnumToken.SemiColonTokenType || + typ == exports.EnumToken.BlockStartTokenType || + typ == exports.EnumToken.BlockEndTokenType) { tokens.pop(); } if (tokens.length == 0) { return null; } - if (tokens[0]?.typ == exports.EnumToken.AtRuleTokenType) { + typ = arena.getType(tokens[0]); + if (typ == exports.EnumToken.AtRuleTokenType) { let nestedRule = false; let parent = context; let node; - while (parent != null) { - if (parent.typ == exports.EnumToken.RuleNodeType) { + while (parent != 0) { + typ = arena.getType(parent); + if (typ == exports.EnumToken.RuleNodeType) { nestedRule = true; break; } - parent = parent[PARENT]; + parent = arena.getParent(parent); } - node = parseAtRule(tokens, context, { ...options, nestedRule }, errors, delim.typ == exports.EnumToken.BlockStartTokenType); + node = parseAtRule(tokens, context, { ...options, nestedRule }, errors, arena.getType(delim) == exports.EnumToken.BlockStartTokenType); if (node == null) { return null; } @@ -33447,46 +34767,48 @@ function parseNode(tokens, context, options, errors, stats, invalidNodes) { invalidNodes.push(node); } stats.nodesCount++; - context.chi.push(node); - node[ROOT] = context[ROOT]; - node[PARENT] = context; + arena.appendChildren(context, node); + // context.chi!.push(node); + // arena.setParent(node, context); + // node[ROOT] = context[ROOT]; + // node[PARENT] = context; // @ts-ignore return node; } else { stats.nodesCount++; // rule - if (delim.typ == exports.EnumToken.BlockStartTokenType) { + if (arena.getType(delim) == exports.EnumToken.BlockStartTokenType) { const node = parseSelector(tokens, context, options, errors); - context.chi.push(node); - node[PARENT] = context; - node[ROOT] = context[ROOT]; - if (node[STATE] & exports.EnumAstNodeStatus.Invalid || - node[STATE] & exports.EnumAstNodeStatus.Disallowed || - node[STATE] & exports.EnumAstNodeStatus.Unknown || - node[STATE] & exports.EnumAstNodeStatus.Unparsed || - node[STATE] & exports.EnumAstNodeStatus.Malformed) { + arena.appendChildren(context, node); + // context.chi!.push(node); + // node[PARENT] = context; + // node[ROOT] = context[ROOT]; + if (arena.hasFlags(node, exports.EnumAstNodeStatus.Invalid | + exports.EnumAstNodeStatus.Disallowed | + exports.EnumAstNodeStatus.Unknown | + exports.EnumAstNodeStatus.Unparsed | + exports.EnumAstNodeStatus.Malformed)) { invalidNodes.push(node); } return node; } else { const node = parseDeclaration(tokens, context, options, errors); - node[PARENT] = context; - node[ROOT] = context[ROOT]; - if (context.typ === exports.EnumToken.StyleSheetNodeType && node.typ === exports.EnumToken.DeclarationNodeType) { - node[STATE] |= - exports.EnumAstNodeStatus.Invalid & - ~(exports.EnumAstNodeStatus.Validated | exports.EnumAstNodeStatus.Unvalidated | exports.EnumAstNodeStatus.Unparsed); + // node[PARENT] = context; + // node[ROOT] = context[ROOT]; + if (contextType === exports.EnumToken.StyleSheetNodeType && node.typ === exports.EnumToken.DeclarationNodeType) { + node[STATE] = exports.EnumAstNodeStatus.Invalid; errors.push({ message: " not allowed in ", action: "drop", node, - location: options.source.getSourceLocation(node[LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(node)), }); } else if (options.lenient || node.typ === exports.EnumToken.DeclarationNodeType) { - context.chi.push(node); + // context.chi!.push(node); + arena.appendChildren(context, node); } if (node[STATE] & (exports.EnumAstNodeStatus.Invalid | @@ -33821,7 +35143,7 @@ function parseAtRule(stream, context, options, errors, parseAsBlock = null) { stream[index + 1]?.typ === exports.EnumToken.CommentTokenType && (stream.length < index + 3 || stream[index + 2]?.typ === exports.EnumToken.WhitespaceTokenType)) ? "" - : renderValue(t, options)), ""), + : printValue(t, options)), ""), ...(parseAsBlock ? { chi: [] } : {}), }); } @@ -33851,7 +35173,7 @@ function parseAtRule(stream, context, options, errors, parseAsBlock = null) { stream[index + 1]?.typ === exports.EnumToken.CommentTokenType && (stream.length < index + 3 || stream[index + 2]?.typ === exports.EnumToken.WhitespaceTokenType)) ? "" - : renderValue(t, options)), ""), + : printValue(t, options)), ""), }); } case "supports": @@ -34668,6 +35990,7 @@ class Arena { nextSibling; previousSibling; firstToken; + lastToken; firstChild; lastChild; name; @@ -34675,15 +35998,25 @@ class Arena { numericValue; unit; flags; - left; - right; - op; - op2; - constructor(size = 1024) { + colorType; + /** + * token spans. 8 bits for source type. 28 bits for start position, 28 bits for end position. This allows us to store the start and end positions of tokens in a single 64-bit integer, which is more efficient than storing them separately. + */ + spans; + /** + * error cache + */ + errors = new Map(); + /** + * rendered tokens cache + */ + renderedTokens; + constructor(size = 4096) { this.dataTypes = new Uint8Array(size); this.parents = new Uint32Array(size); this.nextSibling = new Uint32Array(size); this.previousSibling = new Uint32Array(size); + this.lastToken = new Uint32Array(size); this.firstToken = new Uint32Array(size); this.firstChild = new Uint32Array(size); this.lastChild = new Uint32Array(size); @@ -34692,23 +36025,673 @@ class Arena { this.numericValue = new Float64Array(size); this.unit = new Uint32Array(size); this.flags = new Uint32Array(size); - this.left = new Uint32Array(size); - this.right = new Uint32Array(size); - this.op = new Uint32Array(size); - this.op2 = new Uint32Array(size); - } + // this.left = new Uint32Array(size); + // this.right = new Uint32Array(size); + // this.op = new Uint32Array(size); + // this.op2 = new Uint32Array(size); + this.renderedTokens = new Uint32Array(size); + this.colorType = new Uint8Array(size); + this.spans = new BigUint64Array(size); + this.dataTypes[this.root] = exports.EnumToken.StyleSheetNodeType; + } + allocate() { + const id = ++this.current; + if (this.current >= this.dataTypes.length) { + this.resize(this.dataTypes.length * 2); + } + return id; + } + // Interns a string and returns its unique ID. If the string has already been interned, it returns the existing ID. internString(str) { let id = this.stringIds.get(str); if (id != null) { return id; } - this.stringIds.set(str, this.strings.length); + id = this.strings.length; + this.stringIds.set(str, id); this.strings.push(str); - return this.strings.length - 1; + return id; + } + /** + * set node data for a given id. The first argument is the id, the second is the type, the third is the string value, the fourth is the numeric value, and the fifth is the unit. + * @param id + * @param args + */ + set(id, ...args) { + this.dataTypes[id] = args[0]; + if (args.length > 1) { + // 0 is null string, so we can use it to indicate that there is no string value for this node. + this.stringValue[id] = args[1]; + } + if (args.length > 2) { + this.numericValue[id] = args[2]; + } + if (args.length > 3) { + this.unit[id] = args[3]; + } + } + getType(id) { + return this.dataTypes[id]; + } + setType(id, type) { + this.dataTypes[id] = type; + } + setName(id, nameId) { + this.name[id] = nameId; + } + getName(id) { + return this.strings[this.name[id]] ?? null; + } + setValue(id, valueId) { + this.stringValue[id] = valueId; + } + getValue(id) { + return this.strings[this.stringValue[id]] ?? null; + } + setNumericValue(id, numericValue) { + this.numericValue[id] = numericValue; + } + setUnit(id, unit) { + this.unit[id] = unit; + } + getUnit(id) { + return this.strings[this.unit[id]] ?? null; + } + getNumericValue(id) { + return this.numericValue[id]; + } + setParent(id, parentId) { + this.parents[id] = parentId; + } + getFirstChild(id) { + return this.firstChild[id]; + } + setFirstChild(id, firstChildId) { + this.detach(firstChildId); + const firstChild = this.firstChild[id]; + if (this.firstChild[id] != 0) { + const nextSibling = this.nextSibling[firstChild]; + if (nextSibling != 0) { + this.nextSibling[firstChildId] = nextSibling; + this.previousSibling[nextSibling] = firstChildId; + } + this.detach(firstChild); + } + this.firstChild[id] = firstChildId; + this.parents[firstChild] = id; + } + getLastChild(id) { + return this.lastChild[id]; + } + setLastChild(id, lastChildId) { + this.detach(lastChildId); + const lastChild = this.lastChild[id]; + if (lastChild != 0) { + const previousSibling = this.previousSibling[lastChild]; + if (previousSibling != 0) { + this.previousSibling[lastChildId] = previousSibling; + this.nextSibling[previousSibling] = lastChildId; + } + this.detach(lastChild); + } + this.lastChild[id] = lastChildId; + this.nextSibling[lastChildId] = 0; + this.parents[lastChildId] = id; + } + setNextSibling(id, nextSiblingId) { + this.detach(nextSiblingId); + this.nextSibling[nextSiblingId] = this.nextSibling[id]; + this.nextSibling[id] = nextSiblingId; + this.previousSibling[nextSiblingId] = id; + this.parents[nextSiblingId] = this.parents[id]; + } + setPreviousSibling(id, previousSiblingId) { + this.detach(previousSiblingId); + if (this.previousSibling[id] != 0) { + const previousSibling = this.previousSibling[id]; + if (previousSibling != 0) { + this.nextSibling[previousSibling] = previousSiblingId; + this.previousSibling[previousSiblingId] = previousSibling; + } + this.nextSibling[previousSiblingId] = this.nextSibling[id]; + } + this.previousSibling[id] = previousSiblingId; + this.parents[previousSiblingId] = this.parents[id]; + } + setFirstToken(id, firstTokenId) { + this.detach(firstTokenId); + const firstToken = this.firstToken[id]; + this.previousSibling[firstTokenId] = 0; + this.nextSibling[firstTokenId] = this.nextSibling[firstTokenId]; + this.parents[firstTokenId] = id; + this.firstToken[id] = firstTokenId; + this.detach(firstToken); + } + setColorType(id, colorType) { + this.colorType[id] = colorType; + } + setFlags(id, flags) { + this.flags[id] = flags; + } + getFlags(id) { + return this.flags[id]; + } + hasFlags(id, flag) { + return (this.flags[id] & flag) != 0; + } + getChildAt(id, index) { + let child = this.firstChild[id]; + while (index > 0 && child != 0) { + child = this.nextSibling[child]; + index--; + } + return child == 0 ? null : child; + } + setChildAt(id, index, child) { + if (index == 0) { + if (child != this.firstChild[id]) { + this.detach(child); + if (this.firstChild[id] != 0) { + this.nextSibling[child] = this.nextSibling[this.firstChild[id]]; + this.previousSibling[this.nextSibling[this.firstChild[id]]] = child; + } + this.firstChild[id] = child; + this.parents[child] = id; + } + return; + } + index--; + let currentChild = this.firstChild[id]; + while (index > 0 && this.nextSibling[currentChild] != 0) { + currentChild = this.nextSibling[currentChild]; + index--; + } + this.detach(child); + if (index > 0) { + // append + this.nextSibling[currentChild] = child; + this.previousSibling[child] = currentChild; + this.nextSibling[child] = 0; + } + else { + // prepend + this.previousSibling[child] = this.previousSibling[currentChild]; + this.nextSibling[child] = this.nextSibling[currentChild]; + this.nextSibling[this.previousSibling[currentChild]] = child; + this.previousSibling[currentChild] = child; + this.previousSibling[currentChild] = 0; + this.nextSibling[currentChild] = 0; + this.parents[currentChild] = 0; + } + this.parents[child] = id; + } + /** + * let child should be of type EnumToken.ListToken or EnumToken.ParensToken + * @param id + * @param child + */ + setLeft(id, child) { + this.setChildAt(id, 0, child); + } + getLeft(id) { + return this.getChildAt(id, 0); + } + setRight(id, rightId) { + this.setChildAt(id, 1, rightId); + } + getRight(id) { + return this.getChildAt(id, 1); + } + setOp(id, opId) { + this.setChildAt(id, 2, opId); + } + getOp(id) { + return this.getChildAt(id, 2); + } + setOp2(id, op2Id) { + this.setChildAt(id, 3, op2Id); + } + getOp2(id) { + return this.getChildAt(id, 3); + } + setRenderedTokens(id, renderedTokensId) { + this.renderedTokens[id] = renderedTokensId; + } + setSpan(id, sourceId, start, end) { + this.spans[id] = BigInt(((sourceId & 0xff) << 56) | ((start & 0xfffffff) << 28) | (end & 0xfffffff)); + } + getSourceId(id) { + return Number((this.spans[id] >> 56n) & 0xffn); + } + getSpanStart(id) { + return Number((this.spans[id] >> 28n) & 0xfffffffn); + } + setSpandStart(id, start) { + this.spans[id] = + BigInt((this.getSourceId(id) & 0xff) << 56) | + BigInt((start & 0xfffffff) << 28) | + BigInt(this.getSpanEnd(id) & 0xfffffff); + } + getSpanEnd(id) { + return Number(this.spans[id] & 0xfffffffn); + } + setSpanEnd(id, end) { + this.spans[id] = + BigInt((this.getSourceId(id) & 0xff) << 56) | + BigInt((this.getSpanStart(id) & 0xfffffff) << 28) | + BigInt(end & 0xfffffff); + } + getParent(id) { + return this.parents[id]; } resolveString(id) { return this.strings[id]; } + detach(id) { + let parent = this.parents[id]; + if (this.lastChild[parent] == id) { + this.lastChild[parent] = this.previousSibling[id]; + } + if (this.nextSibling[id] != 0) { + // first child + if (this.previousSibling[id] == 0) { + this.firstChild[parent] = this.nextSibling[id]; + } + else { + // not first child + this.nextSibling[this.previousSibling[id]] = this.nextSibling[id]; + } + } + this.nextSibling[id] = 0; + this.previousSibling[id] = 0; + this.parents[id] = 0; + } + *walk(node, filter, reverse) { + const parents = [node]; + let options = filter; + let isNumeric = false; + let children = true; + let attributes = false; + let i = 0; + let firstChild; + if (options != null && typeof options == "object") { + filter = options.filter; + reverse = options.reverse; + attributes = options.attributes; + children = options.children ?? true; + } + while ((node = parents[i++])) { + let option = null; + if (filter != null) { + // @ts-expect-error + option = filter(node); + isNumeric = typeof option == "number"; + if (isNumeric) { + if (option & exports.WalkerOptionEnum.Ignore) { + continue; + } + if (option & exports.WalkerOptionEnum.Stop) { + break; + } + } + } + if (!isNumeric || (option & exports.WalkerOptionEnum.Children) === 0) { + yield node; + } + if (attributes) { + firstChild = + this.flags[node] & exports.EnumAstNodeStatus.HasValues ? this.firstChild[node] : this.firstToken[node]; + if (firstChild != 0) { + const siblings = this.siblings(firstChild); + parents.splice(i, 0, ...(reverse ? siblings.toReversed() : siblings)); + } + // else if (this.flags[node] & EnumAstNodeStatus.HasValues) { + // // @ts-expect-error + // for (const val of node.val) { + // if (val[PARENT] != node) { + // val[PARENT] = node; + // } + // } + // // @ts-expect-error + // parents.splice(i, 0, ...(reverse ? node.val.toReversed() : node.val)); + // } + } + if (children && + this.flags[node] & exports.EnumAstNodeStatus.HasChildren && + (!isNumeric || (option & exports.WalkerOptionEnum.IgnoreChildren) === 0)) { + firstChild = this.firstChild[node]; + if (firstChild != 0) { + const siblings = this.siblings(firstChild); + parents.splice(i, 0, ...(reverse ? siblings.toReversed() : siblings)); + } + // // @ts-expect-error + // parents.splice(i, 0, ...(reverse ? node.chi!.toReversed() : node.chi)); + // for (const child of (node).chi) { + // if (child[PARENT] != node) { + // child[PARENT] = node; + // } + // } + } + } + } + *walkValues(values, filter, reverse) { + const stack = values.slice(); + if (filter != null && typeof filter == "function") { + filter = { + event: exports.WalkerEvent.Enter, + fn: filter, + }; + } + else if (filter == null) { + filter = { + event: exports.WalkerEvent.Enter, + }; + } + let isNumeric = false; + let value; + let option; + let i = -1; + const eventType = filter.event ?? exports.WalkerEvent.Enter; + // @ts-ignore + // const result = { + // value: null, + // parent: null, + // previousValue: null, + // nextValue: null, + // // + // root: root ?? null, + // parents: function* () { + // // @ts-ignore + // let result = node?.[PARENT] ?? root; + // while (result != null) { + // yield result; + // result = result[PARENT] ?? null; + // } + // }, + // } as WalkAttributesResult; + while (++i < stack.length) { + value = stack[i]; + option = null; + if (filter.fn != null && eventType & exports.WalkerEvent.Enter) { + const isValid = filter.type == null || + this.dataTypes[value] == filter.type || + (Array.isArray(filter.type) && filter.type.includes(this.dataTypes[value])) || + (typeof filter.type == "function" && filter.type(value)); + if (isValid) { + option = filter.fn(value, exports.WalkerEvent.Enter); + isNumeric = typeof option == "number"; + if (isNumeric && option & exports.WalkerOptionEnum.Stop) { + return; + } + if (isNumeric && option & exports.WalkerOptionEnum.Ignore) { + continue; + } + // + if (option != null && Array.isArray(option)) { + // const op = Array.isArray(option) ? option : [option]; + stack.splice(i, 0, ...(reverse ? option.toReversed() : option)); + } + } + } + yield value; + if (!isNumeric || (option & exports.WalkerOptionEnum.IgnoreChildren) === 0) { + if (this.flags[value] & exports.EnumAstNodeStatus.HasChildren) { + for (const child of this.children(value)) { + if (reverse) { + stack.unshift(child); + } + else { + stack.push(child); + } + } + } + // else { + // const values: Token[] = []; + // if ("l" in value && value.l != null) { + // // @ts-expect-error + // values.push(value.l); + // // @ts-expect-error + // if (value.l[PARENT] != value) { + // // @ts-expect-error + // value.l[PARENT] = value; + // } + // } + // if ("op" in value && typeof value.op == "object") { + // // @ts-expect-error + // values.push(value.op); + // // @ts-expect-error + // if (value.op[PARENT] != value) { + // // @ts-expect-error + // value.op[PARENT] = value; + // } + // } + // if ("r" in value && value.r != null) { + // if (Array.isArray(value.r)) { + // for (const r of value.r) { + // // + // values.push(r); + // if (r[PARENT] != value) { + // // + // r[PARENT] = value; + // } + // } + // } else { + // // + // values.push(value.r); + // if (value.r[PARENT] != value) { + // // + // value.r[PARENT] = value; + // } + // } + // } + // if (values.length > 0) { + // for (const v of values) { + // if (reverse) { + // stack.unshift(v); + // } else { + // stack.push(v); + // } + // } + // } + // } + } + if (eventType & exports.WalkerEvent.Leave && filter.fn != null) { + const isValid = filter.type == null || + this.dataTypes[value] == filter.type || + (Array.isArray(filter.type) && filter.type.includes(this.dataTypes[value])) || + (typeof filter.type == "function" && filter.type(value)); + if (isValid) { + // @ts-ignore + option = filter.fn(value, value[PARENT], exports.WalkerEvent.Leave); + // @ts-ignore + if (option != null && Array.isArray(option)) { + // const op = Array.isArray(option) ? option : [option]; + stack.splice(i, 0, ...(reverse ? option.toReversed() : option)); + // for (const o of op) { + // if (o[PARENT] != value) { + // o[PARENT] = value; + // } + // } + } + } + } + } + } + findChild(id, predicate) { + let child = this.firstChild[id]; + while (child != 0) { + if (predicate(child)) { + return child; + } + child = this.nextSibling[child]; + } + return null; + } + siblings(id) { + let siblings = []; + let child = this.firstChild[this.parents[id]]; + while (child != 0) { + siblings.push(child); + child = this.nextSibling[child]; + } + return siblings; + } + children(id) { + let children = []; + let child = this.firstChild[id]; + while (child != 0) { + children.push(child); + child = this.nextSibling[child]; + } + return children; + } + setChildren(id, ...children) { + if (this.lastChild[id] != 0) { + this.lastChild[id] = 0; + } + if (this.firstChild[id] != 0) { + this.detach(this.firstChild[id]); + } + this.appendChildren(id, ...children); + } + setTokens(id, ...children) { + if (this.lastToken[id] != 0) { + this.lastToken[id] = 0; + } + if (this.firstToken[id] != 0) { + this.detach(this.firstToken[id]); + } + this.appendTokens(id, ...children); + } + appendChildren(id, ...children) { + if (children.length == 0) { + return; + } + this.detach(children[0]); + let lastChild = this.lastChild[id]; + if (lastChild == 0) { + this.firstChild[id] = children[0]; + } + else { + this.nextSibling[lastChild] = children[0]; + } + this.parents[children[0]] = id; + for (let i = 1; i < children.length - 1; i++) { + this.detach(children[i]); + this.previousSibling[children[i]] = children[i - 1]; + this.nextSibling[children[i - 1]] = children[i]; + this.parents[children[i]] = id; + } + this.lastChild[id] = children[children.length - 1]; + } + appendTokens(id, ...children) { + if (children.length == 0) { + return; + } + this.detach(children[0]); + let lastChild = this.lastToken[id]; + if (lastChild == 0) { + this.firstToken[id] = children[0]; + } + else { + this.nextSibling[lastChild] = children[0]; + } + this.parents[children[0]] = id; + for (let i = 1; i < children.length - 1; i++) { + this.detach(children[i]); + this.previousSibling[children[i]] = children[i - 1]; + this.nextSibling[children[i - 1]] = children[i]; + this.parents[children[i]] = id; + } + this.lastToken[id] = children[children.length - 1]; + } + equalsIgnoreCase(node, key, string) { + const stringId = this.stringIds.get(string); + if (stringId == null) { + return false; + } + const id = key == "nam" ? this.name[node] : this.stringValue[node]; + if (id == stringId) { + return true; + } + if (string.length != this.strings[id].length) { + return false; + } + return equalsIgnoreCase(this.strings[id], string); + } + startsWith(node, key, string) { + const stringId = this.stringIds.get(string); + if (stringId == null) { + return false; + } + const id = key == "nam" ? this.name[node] : this.stringValue[node]; + if (id == stringId) { + return true; + } + if (string.length != this.strings[id].length) { + return false; + } + return this.strings[id].startsWith(string); + } + resize(newSize) { + if (newSize <= this.dataTypes.length) { + return; + } + const newDataTypes = new Uint8Array(newSize); + newDataTypes.set(this.dataTypes); + this.dataTypes = newDataTypes; + const newParents = new Uint32Array(newSize); + newParents.set(this.parents); + this.parents = newParents; + const newNextSibling = new Uint32Array(newSize); + newNextSibling.set(this.nextSibling); + this.nextSibling = newNextSibling; + const newPreviousSibling = new Uint32Array(newSize); + newPreviousSibling.set(this.previousSibling); + this.previousSibling = newPreviousSibling; + const newFirstToken = new Uint32Array(newSize); + newFirstToken.set(this.firstToken); + this.firstToken = newFirstToken; + const newLastToken = new Uint32Array(newSize); + newLastToken.set(this.lastToken); + this.lastToken = newLastToken; + const newFirstChild = new Uint32Array(newSize); + newFirstChild.set(this.firstChild); + this.firstChild = newFirstChild; + const newLastChild = new Uint32Array(newSize); + newLastChild.set(this.lastChild); + this.lastChild = newLastChild; + const newName = new Uint32Array(newSize); + newName.set(this.name); + this.name = newName; + const newStringValue = new Uint32Array(newSize); + newStringValue.set(this.stringValue); + this.stringValue = newStringValue; + const newNumericValue = new Float64Array(newSize); + newNumericValue.set(this.numericValue); + this.numericValue = newNumericValue; + const newUnit = new Uint32Array(newSize); + newUnit.set(this.unit); + this.unit = newUnit; + const newFlags = new Uint32Array(newSize); + newFlags.set(this.flags); + this.flags = newFlags; + const newRenderedTokens = new Uint32Array(newSize); + newRenderedTokens.set(this.renderedTokens); + this.renderedTokens = newRenderedTokens; + const newColorType = new Uint8Array(newSize); + newColorType.set(this.colorType); + this.colorType = newColorType; + const newSpans = new BigUint64Array(newSize); + newSpans.set(this.spans); + this.spans = newSpans; + } + setErrors(id, errors) { + this.errors.set(id, errors); + } + getErrors(id) { + return this.errors.get(id) ?? null; + } } /** @@ -34760,7 +36743,7 @@ function setNodeProperty(node, key, value) { } } -var version = "1.6.7"; +var version = "2.0.0-alpha.1"; /** * Load file or url @@ -34905,7 +36888,7 @@ function parseSync(...args) { options ??= {}; options.src ??= ""; options.sourcesMap ??= new Map(); - options.arena ??= new Arena(); + options.arena ??= new Arena(2 << Math.ceil(2 + Math.log2(stream.length))); Object.assign(options, { resolve, dirname, @@ -35062,6 +37045,7 @@ async function parse(...args) { options ??= {}; options.src ??= ""; options.sourcesMap ??= new Map(); + options.arena ??= new Arena(typeof stream == "string" ? 2 << Math.ceil(2 + Math.log2(stream.length)) : 4096); Object.assign(options, { load, resolve, @@ -35238,8 +37222,8 @@ exports.parseDeclarations = parseDeclarations; exports.parseFile = parseFile; exports.parseString = parseString; exports.parseSync = parseSync; +exports.printValue = printValue$1; exports.render = render; -exports.renderToken = renderValue; exports.replaceNodeOrValue = replaceNodeOrValue; exports.resolve = resolve; exports.setNodeProperty = setNodeProperty; diff --git a/dist/index.d.ts b/dist/index.d.ts index 4a060c834..ea77b4057 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -46,47 +46,25 @@ declare enum EnumAstNodeStatus$1 { */ Malformed = 128, /** - * Important declaration + * Node has children */ - ImportantDeclaration = 256, + HasChildren = 256, /** - * Color component without unit + * Node has values */ - PowerlessColorComponent = 512 -} -/** - * Enum of validation levels - * @deprecated - */ -declare enum ValidationLevel { + HasValues = 512, /** - * disable validation + * Node has fraction value */ - None = 0, + HasFractionValue = 1024, /** - * validate selectors - */ - Selector = 1, - /** - * validate at-rules - */ - AtRule = 2, - /** - * validate declarations - */ - Declaration = 4, - /** - * validate selectors and at-rules - */ - Default = 3,// selectors + at-rules - /** - * validate selectors, at-rules and declarations + * Important declaration */ - All = 7,// selectors + at-rules + declarations + ImportantDeclaration = 2048, /** - * Report only. Apply validation and report nodes that are marked as invalid + * Color component without unit */ - ReportOnly = 8 + PowerlessColorComponent = 4096 } /** * Enum of all token types @@ -331,7 +309,7 @@ declare enum EnumToken { /** * token list token type */ - ListToken = 59, + ListTokenType = 59, /** * addition token type */ @@ -789,135 +767,135 @@ declare enum ColorType$1 { /** * deprecated system colors */ - SYS = 0, + SYS = 1, /** * deprecated system colors */ - DPSYS = 1, + DPSYS = 2, /** * named colors */ - LIT = 2, + LIT = 3, /** * colors as hex values */ - HEX = 3, + HEX = 4, /** * colors as rgb values */ - RGBA = 4, + RGBA = 5, /** * colors using rgb */ - HSLA = 5, + HSLA = 6, /** * colors using hwb */ - HWB = 6, + HWB = 7, /** * colors using cmyk */ - CMYK = 7, + CMYK = 8, /** * colors using oklab * */ - OKLAB = 8, + OKLAB = 9, /** * colors using oklch * */ - OKLCH = 9, + OKLCH = 10, /** * colors using lab */ - LAB = 10, + LAB = 11, /** * colors using lch */ - LCH = 11, + LCH = 12, /** * colors using color() function */ - COLOR = 12, + COLOR = 13, /** * color using srgb */ - SRGB = 13, + SRGB = 14, /** * color using prophoto-rgb */ - PROPHOTO_RGB = 14, + PROPHOTO_RGB = 15, /** * color using a98-rgb */ - A98_RGB = 15, + A98_RGB = 16, /** * color using rec2020 */ - REC2020 = 16, + REC2020 = 17, /** * color using display-p3 */ - DISPLAY_P3 = 17, + DISPLAY_P3 = 18, /** * color using srgb-linear */ - SRGB_LINEAR = 18, + SRGB_LINEAR = 19, /** * color using xyz-d50 */ - XYZ_D50 = 19, + XYZ_D50 = 20, /** * color using xyz-d65 */ - XYZ_D65 = 20, + XYZ_D65 = 21, /** * light-dark() color function */ - LIGHT_DARK = 21, + LIGHT_DARK = 22, /** * color-mix() color function */ - COLOR_MIX = 22, + COLOR_MIX = 23, /** * non-standard color */ - NON_STD = 23, + NON_STD = 24, /** * custom color */ - CUSTOM_COLOR = 24, + CUSTOM_COLOR = 25, /** * alpha() color function */ - ALPHA = 25, + ALPHA = 26, /** * color using display-p3-linear */ - DISPLAY_P3_LINEAR = 26, + DISPLAY_P3_LINEAR = 27, /** * Contrast color */ - CONTRAST_COLOR = 27, + CONTRAST_COLOR = 28, /** * color layers */ - COLOR_LAYERS = 28, + COLOR_LAYERS = 29, /** * alias for rgba */ - RGB = 4, + RGB = 5, /** * alias for hsl */ - HSL = 5, + HSL = 6, /** * alias for xyz-d65 */ - XYZ = 20, + XYZ = 21, /** * alias for cmyk */ - DEVICE_CMYK = 7 + DEVICE_CMYK = 8 } /** * Supported module case transform @@ -2435,7 +2413,7 @@ export declare interface ListToken extends BaseToken { /** * @inheritdoc */ - typ: EnumToken.ListToken; + typ: EnumToken.ListTokenType; /** * Children */ @@ -4498,6 +4476,138 @@ declare enum ResponseType { JSON = 3 } +declare class Arena { + stringIds: Map; + strings: string[]; + readonly root: number; + current: number; + dataTypes: Uint8Array; + parents: Uint32Array; + nextSibling: Uint32Array; + previousSibling: Uint32Array; + firstToken: Uint32Array; + lastToken: Uint32Array; + firstChild: Uint32Array; + lastChild: Uint32Array; + name: Uint32Array; + stringValue: Uint32Array; + numericValue: Float64Array; + unit: Uint32Array; + flags: Uint32Array; + colorType: Uint8Array; + /** + * token spans. 8 bits for source type. 28 bits for start position, 28 bits for end position. This allows us to store the start and end positions of tokens in a single 64-bit integer, which is more efficient than storing them separately. + */ + spans: BigUint64Array; + /** + * error cache + */ + errors: Map; + /** + * rendered tokens cache + */ + renderedTokens: Uint32Array; + constructor(size?: number); + allocate(): number; + internString(str: string): number; + /** + * set node data for a given id. The first argument is the id, the second is the type, the third is the string value, the fourth is the numeric value, and the fifth is the unit. + * @param id + * @param type + * @param stringValue + */ + set(id: number, type: EnumToken): void; + /** + * set node data for a given id. The first argument is the id, the second is the type, the third is the string value, the fourth is the numeric value, and the fifth is the unit. + * @param id + * @param type + * @param stringValue + */ + set(id: number, type: EnumToken, stringValue: number): void; + /** + * set node data for a given id. The first argument is the id, the second is the type, the third is the string value, and the fourth is the numeric value. + * @param id + * @param type + * @param stringValue + * @param numericValue + */ + set(id: number, type: EnumToken, stringValue: number, numericValue: number): void; + /** + * set node data for a given id. The first argument is the id, the second is the type, the third is the string value, the fourth is the numeric value, and the fifth is the unit. + * @param id + * @param type + * @param stringValue + * @param numericValue + * @param unit + */ + set(id: number, type: EnumToken, stringValue: number, numericValue: number, unit: number): void; + getType(id: number): EnumToken; + setType(id: number, type: EnumToken): void; + setName(id: number, nameId: number): void; + getName(id: number): string; + setValue(id: number, valueId: number): void; + getValue(id: number): string; + setNumericValue(id: number, numericValue: number): void; + setUnit(id: number, unit: number): void; + getUnit(id: number): string; + getNumericValue(id: number): number; + setParent(id: number, parentId: number): void; + getFirstChild(id: number): number; + setFirstChild(id: number, firstChildId: number): void; + getLastChild(id: number): number; + setLastChild(id: number, lastChildId: number): void; + setNextSibling(id: number, nextSiblingId: number): void; + setPreviousSibling(id: number, previousSiblingId: number): void; + setFirstToken(id: number, firstTokenId: number): void; + setColorType(id: number, colorType: number): void; + setFlags(id: number, flags: number): void; + getFlags(id: number): number; + hasFlags(id: number, flag: number): boolean; + getChildAt(id: number, index: number): number | null; + setChildAt(id: number, index: number, child: number): void; + /** + * let child should be of type EnumToken.ListToken or EnumToken.ParensToken + * @param id + * @param child + */ + setLeft(id: number, child: number): void; + getLeft(id: number): number; + setRight(id: number, rightId: number): void; + getRight(id: number): number; + setOp(id: number, opId: number): void; + getOp(id: number): number; + setOp2(id: number, op2Id: number): void; + getOp2(id: number): number; + setRenderedTokens(id: number, renderedTokensId: number): void; + setSpan(id: number, sourceId: number, start: number, end: number): void; + getSourceId(id: number): number; + getSpanStart(id: number): number; + setSpandStart(id: number, start: number): void; + getSpanEnd(id: number): number; + setSpanEnd(id: number, end: number): void; + getParent(id: number): number; + resolveString(id: number): string; + detach(id: number): void; + walk(node: number, filter?: WalkerFilter | WalkerOptions | null, reverse?: boolean): Generator; + walkValues(values: number[], filter?: WalkerValueFilter | null | { + event?: WalkerEvent; + fn?: WalkerValueFilter; + type?: EnumToken | EnumToken[] | ((token: number) => boolean); + }, reverse?: boolean): Generator; + findChild(id: number, predicate: (id: number) => boolean): number | null; + siblings(id: number): number[]; + children(id: number): number[]; + setChildren(id: number, ...children: number[]): void; + setTokens(id: number, ...children: number[]): void; + appendChildren(id: number, ...children: number[]): void; + appendTokens(id: number, ...children: number[]): void; + equalsIgnoreCase(node: number, key: "nam" | "val", string: string): boolean; + startsWith(node: number, key: "nam" | "val", string: string): boolean; + resize(newSize: number): void; + setErrors(id: number, errors: ErrorDescription$1[]): void; + getErrors(id: number): ErrorDescription$1[] | null; +} + interface PropertyType { shorthand: string; } @@ -5037,7 +5147,7 @@ export declare interface WalkerOptions { /** * node walker option */ -export declare type WalkerOption = WalkerOptionEnum | AstNode$1 | Token$1 | null; +export declare type WalkerOption = WalkerOptionEnum | number | null; /** * returned value: * - {@link WalkerOptionEnum.Ignore}: ignore this node and its children @@ -5058,12 +5168,7 @@ export declare type WalkerFilter = (node: AstNode$1) => WalkerOption; * - {@link AstNode}: * - {@link Token}: */ -export declare type WalkerValueFilter = ( - node: AstNode$1 | Token$1, - parent?: AstNode$1 | Token$1 | AstNode$1[] | Token$1[] | null, - event?: WalkerEvent, - parents?: Generator, -) => WalkerOption | AstNode$1 | Token$1 | AstNode$1[] | Token$1[] | null; +export declare type WalkerValueFilter = (node: number, event?: WalkerEvent) => WalkerOption | number[]; /** * walker result @@ -5137,7 +5242,7 @@ export declare interface ErrorDescription$1 { /** * Error node */ - node?: Token$1 | AstNode$1 | null; + node?: number | null; /** * Error location */ @@ -5156,6 +5261,12 @@ export declare interface ErrorDescription$1 { * CSS validation options */ interface ValidationOptions { + + /** + * Arena + */ + arena: Arena; + /** * Nested rule context * @private @@ -5163,11 +5274,10 @@ interface ValidationOptions { nestedRule?: boolean; /** - * Enable CSS validation. Using ValidationLevel as value is deprecated and will be removed + * Enable CSS validation * - * see {@link ValidationLevel} */ - validation?: boolean | ValidationLevel; + validation?: boolean ; /** * Lenient validation. retain nodes that failed validation @@ -5805,6 +5915,7 @@ export declare interface ResolvedPath { * Ast node render options */ export declare interface RenderOptions { + /** * Source file to be used as CSS input file for sourcemap resolution */ @@ -5967,6 +6078,12 @@ export declare interface ParseResultStats { * Parse result object */ export declare interface ParseResult { + + /** + * Arena memory + */ + arena: Arena; + /** * Parsed ast tree */ @@ -6370,19 +6487,6 @@ declare function parseString(src: string, options?: { parseColor?: boolean; } | null, errors?: ErrorDescription$1[]): Token$1[]; -/** - * render ast token - * @param token - * @param options - * @param cache - * @param reducer - * @param errors - * @private - */ -declare function renderValue(token: Token$1, options?: RenderOptions, cache?: { - [key: string]: any; -}, reducer?: null | ((acc: string, curr: Token$1) => string), errors?: ErrorDescription$1[]): string; - /** * Converts a color to another color space * @param token @@ -6579,6 +6683,19 @@ declare function replaceNodeOrValue(parent: BinaryExpressionToken | (AstNode$1 & val: Token$1[]; })), node: Token$1, replacement: Token$1 | Token$1[]): boolean; +/** + * render ast token + * @param token + * @param options + * @param cache + * @param reducer + * @param errors + * @private + */ +declare function printValue(token: number, arena: Arena, options: RenderOptions, cache?: { + [key: string]: any; +}, reducer?: null | ((acc: string, curr: number) => string), errors?: ErrorDescription$1[]): string; + /** * * @param node @@ -7034,5 +7151,5 @@ declare function transform(options: ParseInputStreamOptions & TransformOptions): */ declare function transform(options: ParseInputFileOptions & TransformOptions): Promise; -export { ColorType$1 as ColorType, EnumAstNodeStatus$1 as EnumAstNodeStatus, EnumToken, FeatureWalkMode, ModuleCaseTransformEnum, ModuleScopeEnumOptions, ResponseType, SourceMap, ValidationLevel, WalkerEvent, WalkerOptionEnum, cloneNode, convertColor, dirname, expand, find, findAll, findByValue, findLast, getNodeProperty, isOkLabClose, load, minify, okLabDistance, parse, parseDeclarations, parseFile, parseString, parseSync, render, renderValue as renderToken, replaceNodeOrValue, resolve, setNodeProperty, transform, transformFile, transformSync, version, walk, walkValues }; +export { ColorType$1 as ColorType, EnumAstNodeStatus$1 as EnumAstNodeStatus, EnumToken, FeatureWalkMode, ModuleCaseTransformEnum, ModuleScopeEnumOptions, ResponseType, SourceMap, WalkerEvent, WalkerOptionEnum, cloneNode, convertColor, dirname, expand, find, findAll, findByValue, findLast, getNodeProperty, isOkLabClose, load, minify, okLabDistance, parse, parseDeclarations, parseFile, parseString, parseSync, printValue, render, replaceNodeOrValue, resolve, setNodeProperty, transform, transformFile, transformSync, version, walk, walkValues }; export type { AddToken, AndToken, AngleToken, AstAtRule, AstComment, AstDeclaration, AstInvalidAtRule, AstInvalidDeclaration, AstInvalidRule, AstKeyframesAtRule, AstKeyframesRule, AstNode$1 as AstNode, AstNodeStatus, AstRule, AstRuleList, AstStyleSheet, AstValueMatcher, AtRuleToken, AtRuleVisitorHandler, AttrEndToken, AttrStartToken, AttrToken, Background, BackgroundAttachmentMapping, BackgroundPosition, BackgroundPositionClass, BackgroundPositionConstraints, BackgroundPositionMapping, BackgroundProperties, BackgroundRepeat, BackgroundRepeatMapping, BackgroundSize, BackgroundSizeMapping, BadCDOCommentToken, BadCommentToken, BadStringToken, BadUrlToken, BaseToken, BinaryExpressionNode, BinaryExpressionToken, BlockEndToken, BlockStartToken, Border, BorderColor, BorderColorClass, BorderProperties, BorderRadius, CDOCommentToken, ChildCombinatorToken, ClassSelectorToken, ColonToken, ColorToken, ColumnCombinatorToken, CommaToken, CommentToken, ComposesSelectorToken, ConstraintsMapping, ContainMatchToken, ContainerStyleRangeToken, CssVariableImportTokenType, CssVariableMapTokenType, CssVariableToken, DashMatchToken, DashedIdentToken, DeclarationVisitorHandler, DelimToken, DescendantCombinatorToken, DimensionToken, DivToken, DoubleColonToken, EOFToken, EndMatchToken, EqualMatchToken, ErrorDescription$1 as ErrorDescription, FlexToken, Font, FontFamily, FontProperties, FontWeight, FontWeightConstraints, FontWeightMapping, FractionToken, FrequencyToken, FunctionDefToken, FunctionImageToken, FunctionToken, FunctionURLToken, GenericVisitorAstNodeHandlerMap, GenericVisitorAstNodeSyncHandlerMap, GenericVisitorAsyncResult, GenericVisitorHandler, GenericVisitorResult, GenericVisitorSyncHandler, GenericVisitorSyncResult, GreaterThanOrEqualToken, GreaterThanToken, GridTemplateFuncToken, HashToken, IdentListToken, IdentToken, IfConditionToken, IfElseConditionToken, ImportantToken, IncludeMatchToken, InfinityToken, InvalidAttrToken, InvalidClassSelectorToken, InvalidMediaQueryToken, LengthToken, LessThanOrEqualToken, LessThanToken, LineHeight, ListToken, LiteralToken, LoadResult, Map$1 as Map, MatchExpressionToken, MatchedSelector, MediaFeatureOnlyToken, MediaFeatureToken, MediaQueryConditionToken, MediaQueryUnaryFeatureToken, MediaRangeQueryToken, MinifyFeature, MinifyFeatureOptions, MinifyOptions, ModuleAsyncOptions, ModuleSyncOptions, MulToken, NaNToken, NameSpaceAttributeToken, NegativeInfinityToken, NestingSelectorToken, NextSiblingCombinatorToken, NotToken, NumberToken, OptimizedSelector, OptimizedSelectorToken, OrToken, Outline, OutlineProperties, ParensEndToken, ParensStartToken, ParensToken, ParseInfo$1 as ParseInfo, ParseInputFileOptions, ParseInputOptions, ParseInputStreamOptions, ParseResult, ParseResultStats, ParseSourceOptions, ParseTokenOptions, ParserOptions, ParserSourceMapOptions, ParserSyncOptions, PercentageToken, Prefix, PropertiesConfig, PropertiesConfigProperties, PropertyListOptions, PropertyMapType, PropertySetType, PropertyType, PseudoClassFunctionToken, PseudoClassToken, PseudoElementToken, PseudoPageToken, PurpleBackgroundAttachment, RawNodeToken, RawSelectorTokens, RenderOptions, RenderResult, ResolutionToken, ResolvedPath, RuleVisitorHandler, SemiColonToken, Separator, ShorthandDef, ShorthandMapType, ShorthandProperties, ShorthandPropertyType, ShorthandType, SinglePropertyType, SinglePropertyTypeMapping, SourceLocation, SourceMapObject, StartMatchToken, StringToken, SubToken, SubsequentCombinatorToken, SupportsQueryConditionToken, SupportsQueryUnaryConditionToken, TimeToken, TimelineFunctionToken, TimingFunctionToken, Token$1 as Token, TokenSearchResult, TokenizeResult, TransformOptions, TransformResult, TransformSyncOptions, UnaryExpression, UnaryExpressionNode, UnclosedStringToken, UniversalSelectorToken, UrlToken, ValidationConfiguration, ValidationMediaFeature, ValidationOptions, ValidationResult, ValidationSelectorOptions, ValidationSyntaxNode, ValidationSyntaxResult, ValidationToken$1 as ValidationToken, Value, ValueVisitorHandler, ValueVisitorSyncHandler, VariableScopeInfo, VisitorNodeMap, VisitorSyncNodeMap, WalkAttributesResult, WalkResult, WalkerFilter, WalkerOption, WalkerOptions, WalkerValueFilter, WhenElseQueryConditionToken, WhenElseUnaryConditionToken, WhitespaceToken, WrappedValuesToken }; diff --git a/dist/package.json.js b/dist/package.json.js index 809e572c4..111db9075 100644 --- a/dist/package.json.js +++ b/dist/package.json.js @@ -2,9 +2,9 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ -var version = "1.6.7"; +var version = "2.0.0-alpha.1"; export { version }; diff --git a/dist/src/data/properties.json.js b/dist/src/data/properties.json.js index 96287abcd..9e273588e 100644 --- a/dist/src/data/properties.json.js +++ b/dist/src/data/properties.json.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ var properties = { diff --git a/dist/src/data/validation.json.js b/dist/src/data/validation.json.js index ca3afdca9..ea67c97c8 100644 --- a/dist/src/data/validation.json.js +++ b/dist/src/data/validation.json.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ var declarations = { diff --git a/dist/src/lib/ast/clone.js b/dist/src/lib/ast/clone.js index 64a27f81a..a828449e4 100644 --- a/dist/src/lib/ast/clone.js +++ b/dist/src/lib/ast/clone.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { EnumToken } from './types.js'; diff --git a/dist/src/lib/ast/expand.js b/dist/src/lib/ast/expand.js index adb87328f..56ed51802 100644 --- a/dist/src/lib/ast/expand.js +++ b/dist/src/lib/ast/expand.js @@ -2,14 +2,13 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { splitRule } from './minify.js'; import { STATE, PARENT, combinators, RAW } from '../syntax/constants.js'; import { parseString } from '../parser/parse.js'; import { walkValues } from './walk.js'; -import { renderValue } from '../printer/render.js'; import { EnumAstNodeStatus, EnumToken } from './types.js'; import { cloneNode } from './clone.js'; @@ -288,7 +287,7 @@ function replaceCompound(input, replace) { }); } } - return tokens.reduce((acc, curr) => acc + renderValue(curr), ""); + return tokens.reduce((acc, curr) => acc + printValue(curr), ""); } function replaceCompoundLiteral(selector, replace) { const tokens = [""]; diff --git a/dist/src/lib/ast/features/calc.js b/dist/src/lib/ast/features/calc.js index 3f09e294c..1fc4187f9 100644 --- a/dist/src/lib/ast/features/calc.js +++ b/dist/src/lib/ast/features/calc.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { EnumToken } from '../types.js'; diff --git a/dist/src/lib/ast/features/if.js b/dist/src/lib/ast/features/if.js index cdcf3f744..c5ef86f2f 100644 --- a/dist/src/lib/ast/features/if.js +++ b/dist/src/lib/ast/features/if.js @@ -2,11 +2,10 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { EnumToken } from '../types.js'; -import { renderValue } from '../../printer/render.js'; import { FeatureWalkMode } from './type.js'; import { PARENT, LOCSRCID, LOCSTA, LOCEND, TOKENS } from '../../syntax/constants.js'; import { equalsIgnoreCase } from '../../parser/utils/text.js'; @@ -104,7 +103,7 @@ function substituteIfElseNode(declaration, node, wrapper, parentWrapper, cache) minify, convertColor: minify, }; - atRule.val = atRule[TOKENS].reduce((acc, curr) => acc + renderValue(curr, options), ""); + atRule.val = atRule[TOKENS].reduce((acc, curr) => acc + printValue(curr, options), ""); clonedDeclaration = cloneNode(declaration, true, nodeMap); replaceNodeOrValue(nodeMap.get(targetWrapper), nodeMap.get(node), node.r.at(-1)?.typ === EnumToken.SemiColonTokenType ? trimArray(node.r.slice(0, -1)) : node.r); clonedDeclaration[PARENT] = atRule; @@ -119,7 +118,7 @@ function substituteIfElseNode(declaration, node, wrapper, parentWrapper, cache) chi: [], }); atRule[TOKENS] = [left]; - atRule.val = atRule[TOKENS].reduce((acc, curr) => acc + renderValue(curr), ""); + atRule.val = atRule[TOKENS].reduce((acc, curr) => acc + printValue(curr), ""); if (declaration[PARENT] != null) { atRule[LOCSRCID] = declaration[PARENT][LOCSRCID]; atRule[LOCSTA] = declaration[PARENT][LOCSTA]; diff --git a/dist/src/lib/ast/features/index.js b/dist/src/lib/ast/features/index.js index be2b0de95..08b4d4f66 100644 --- a/dist/src/lib/ast/features/index.js +++ b/dist/src/lib/ast/features/index.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ export { ComputePrefixFeature } from './prefix.js'; diff --git a/dist/src/lib/ast/features/inlinecssvariables.js b/dist/src/lib/ast/features/inlinecssvariables.js index f84b262fb..0f5256ca5 100644 --- a/dist/src/lib/ast/features/inlinecssvariables.js +++ b/dist/src/lib/ast/features/inlinecssvariables.js @@ -2,12 +2,11 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { EnumToken } from '../types.js'; import { walkValues } from '../walk.js'; -import { renderValue } from '../../printer/render.js'; import { splitRule } from '../minify.js'; import { FeatureWalkMode } from './type.js'; import { RAW, mathFuncs } from '../../syntax/constants.js'; @@ -143,7 +142,7 @@ class InlineCssVariablesFeature { // @ts-ignore parent.chi.splice(i, 1, { typ: EnumToken.CommentTokenType, - val: `/* ${info.node.nam}: ${info.values.reduce((acc, curr) => acc + renderValue(curr, { convertColor: false }), "")} */`, + val: `/* ${info.node.nam}: ${info.values.reduce((acc, curr) => acc + printNode(curr, { convertColor: false }), "")} */`, }); break; } diff --git a/dist/src/lib/ast/features/prefix.js b/dist/src/lib/ast/features/prefix.js index 3b9f69ce0..29be16dbf 100644 --- a/dist/src/lib/ast/features/prefix.js +++ b/dist/src/lib/ast/features/prefix.js @@ -2,14 +2,13 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { EnumAstNodeStatus, EnumToken } from '../types.js'; import { walkValues } from '../walk.js'; import { pseudoAliasMap } from '../../syntax/syntax.js'; import { splitRule } from '../minify.js'; -import { renderValue } from '../../printer/render.js'; import { STATE, TOKENS, funcLike, regMatchLinearGradient, regMatchRadialGradient } from '../../syntax/constants.js'; import { FeatureWalkMode } from './type.js'; import { ValidationSyntaxGroupEnum } from '../../validation/parser/typedef.js'; @@ -17,6 +16,7 @@ import { getSyntaxConfig } from '../../validation/config.js'; import { splitTokenList } from '../../validation/utils/list.js'; import { equalsIgnoreCase } from '../../parser/utils/text.js'; import { toDegrees } from '../../parser/utils/angle.js'; +import { printValue } from '../../printer/print.js'; const config = getSyntaxConfig(); function replacePseudo(tokens) { @@ -196,7 +196,7 @@ class ComputePrefixFeature { } if (node.typ == EnumToken.AtRuleNodeType && node.val !== "") { if (replaceAstNodes(node[TOKENS])) { - node.val = node[TOKENS].reduce((acc, curr) => acc + renderValue(curr), ""); + node.val = node[TOKENS].reduce((acc, curr) => acc + printValue(curr), ""); } } } diff --git a/dist/src/lib/ast/features/shorthand.js b/dist/src/lib/ast/features/shorthand.js index fe311d5f3..5fdf209a8 100644 --- a/dist/src/lib/ast/features/shorthand.js +++ b/dist/src/lib/ast/features/shorthand.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { PropertyList } from '../../parser/declaration/list.js'; diff --git a/dist/src/lib/ast/features/transform.js b/dist/src/lib/ast/features/transform.js index 30609fbe8..40ff75955 100644 --- a/dist/src/lib/ast/features/transform.js +++ b/dist/src/lib/ast/features/transform.js @@ -2,15 +2,15 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { EnumToken, EnumAstNodeStatus } from '../types.js'; import { compute } from '../transform/compute.js'; -import { filterValues, renderValue } from '../../printer/render.js'; import { minifyTransformFunctions, eqMatrix } from '../transform/minify.js'; import { FeatureWalkMode } from './type.js'; import { STATE } from '../../syntax/constants.js'; +import { filterValues, printValue } from '../../printer/print.js'; class TransformCssFeature { accept = new Set([ @@ -70,9 +70,9 @@ class TransformCssFeature { if (eqMatrix(matrix, minified)) { r.push(minified); } - const l = renderValue(matrix).length; + const l = printValue(matrix).length; node.val = r.reduce((acc, curr) => { - if (curr.reduce((acc, t) => acc + renderValue(t), "").length < l) { + if (curr.reduce((acc, t) => acc + printValue(t), "").length < l) { return curr; } return acc; diff --git a/dist/src/lib/ast/features/type.js b/dist/src/lib/ast/features/type.js index 2314edfd1..85b8be3ba 100644 --- a/dist/src/lib/ast/features/type.js +++ b/dist/src/lib/ast/features/type.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ /** diff --git a/dist/src/lib/ast/find.js b/dist/src/lib/ast/find.js index 8490ad390..59b796daf 100644 --- a/dist/src/lib/ast/find.js +++ b/dist/src/lib/ast/find.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { EnumToken } from './types.js'; diff --git a/dist/src/lib/ast/math/expression.js b/dist/src/lib/ast/math/expression.js index 5c0a3aa50..97fd5ae0b 100644 --- a/dist/src/lib/ast/math/expression.js +++ b/dist/src/lib/ast/math/expression.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { equalsIgnoreCase } from '../../parser/utils/text.js'; @@ -90,7 +90,7 @@ function evaluate(tokens) { if (token.typ == EnumToken.Sub) { if (!isScalarToken(nodes[i + 1])) { token = { - typ: EnumToken.ListToken, + typ: EnumToken.ListTokenType, chi: [nodes[i], nodes[i + 1]], [LOCSRCID]: nodes[i][LOCSRCID], [LOCSTA]: nodes[i][LOCSTA], @@ -138,7 +138,7 @@ function evaluate(tokens) { return acc; } } - if (acc.length > 0 && curr[0] != EnumToken.ListToken) { + if (acc.length > 0 && curr[0] != EnumToken.ListTokenType) { acc.push({ typ: EnumToken.Add, [LOCSRCID]: token[LOCSRCID], @@ -829,7 +829,7 @@ function factor(tokens, ops) { return [factorToken(tokens[0])]; } for (let i = 0; i < tokens.length; i++) { - if (tokens[i].typ == EnumToken.ListToken) { + if (tokens[i].typ == EnumToken.ListTokenType) { // @ts-ignore tokens.splice(i, 1, ...tokens[i].chi); } diff --git a/dist/src/lib/ast/math/math.js b/dist/src/lib/ast/math/math.js index f7275be67..06d2fedb5 100644 --- a/dist/src/lib/ast/math/math.js +++ b/dist/src/lib/ast/math/math.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { minifyNumber } from '../../syntax/syntax.js'; diff --git a/dist/src/lib/ast/minify.js b/dist/src/lib/ast/minify.js index eb1c1e92c..a02d6bc82 100644 --- a/dist/src/lib/ast/minify.js +++ b/dist/src/lib/ast/minify.js @@ -2,11 +2,11 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { eq } from '../parser/utils/eq.js'; -import { renderValue, doRender } from '../printer/render.js'; +import { doRender } from '../printer/render.js'; import * as index from './features/index.js'; import { walkValues } from './walk.js'; import { EnumToken } from './types.js'; @@ -280,7 +280,7 @@ function minifyAtRuleMedia(tokens) { if (result.hasUpdates) { hasUpdates = true; } - acc.set(values.reduce((acc, t) => acc + renderValue(t), ""), result.values); + acc.set(values.reduce((acc, t) => acc + printValue(t), ""), result.values); return acc; }, new Map()); if (sections.has("all")) { @@ -428,7 +428,7 @@ function doMinify(ast, options = {}, recursive = false, errors, nestingContent, (index + 3 < arr.length || arr[index + 2]?.typ === EnumToken.WhitespaceTokenType)) ? "" - : renderValue(curr)), ""); + : printValue(curr)), ""); } } if (["all", "", null].includes(node.val)) { @@ -456,7 +456,7 @@ function doMinify(ast, options = {}, recursive = false, errors, nestingContent, arr[index + 1]?.typ === EnumToken.CommentTokenType && (index + 3 < arr.length || arr[index + 2].typ === EnumToken.WhitespaceTokenType)) ? "" - : renderValue(curr)), ""); + : printValue(curr)), ""); } } else if (ast.typ === node.typ && @@ -1247,7 +1247,7 @@ function fixSelector(node) { } } } - node.sel = attributes.reduce((acc, curr) => acc + renderValue(curr), ""); + node.sel = attributes.reduce((acc, curr) => acc + printValue(curr), ""); node[TOKENS] = null; } } diff --git a/dist/src/lib/ast/node.js b/dist/src/lib/ast/node.js index 42312277b..c4f035baf 100644 --- a/dist/src/lib/ast/node.js +++ b/dist/src/lib/ast/node.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { TOKENS, ERRORS, STATE, LOCSRCID, LOCSTA, LOCEND, PARENT } from '../syntax/constants.js'; diff --git a/dist/src/lib/ast/transform/compute.js b/dist/src/lib/ast/transform/compute.js index e942fbb62..ec2547379 100644 --- a/dist/src/lib/ast/transform/compute.js +++ b/dist/src/lib/ast/transform/compute.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { identity, multiply, toZero } from './utils.js'; diff --git a/dist/src/lib/ast/transform/matrix.js b/dist/src/lib/ast/transform/matrix.js index 2cb530052..3fdf53cbb 100644 --- a/dist/src/lib/ast/transform/matrix.js +++ b/dist/src/lib/ast/transform/matrix.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { identity, is2DMatrix } from './utils.js'; diff --git a/dist/src/lib/ast/transform/minify.js b/dist/src/lib/ast/transform/minify.js index 148abe4e8..92c9b080f 100644 --- a/dist/src/lib/ast/transform/minify.js +++ b/dist/src/lib/ast/transform/minify.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { identity, multiply, decompose, round, toZero } from './utils.js'; diff --git a/dist/src/lib/ast/transform/perspective.js b/dist/src/lib/ast/transform/perspective.js index 518975d96..e9326f295 100644 --- a/dist/src/lib/ast/transform/perspective.js +++ b/dist/src/lib/ast/transform/perspective.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { identity, multiply } from './utils.js'; diff --git a/dist/src/lib/ast/transform/rotate.js b/dist/src/lib/ast/transform/rotate.js index 815919512..1143cd609 100644 --- a/dist/src/lib/ast/transform/rotate.js +++ b/dist/src/lib/ast/transform/rotate.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { identity, multiply } from './utils.js'; diff --git a/dist/src/lib/ast/transform/scale.js b/dist/src/lib/ast/transform/scale.js index 449b6d33d..e7324f949 100644 --- a/dist/src/lib/ast/transform/scale.js +++ b/dist/src/lib/ast/transform/scale.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { identity, multiply } from './utils.js'; diff --git a/dist/src/lib/ast/transform/skew.js b/dist/src/lib/ast/transform/skew.js index 2661a11f0..54260a3fe 100644 --- a/dist/src/lib/ast/transform/skew.js +++ b/dist/src/lib/ast/transform/skew.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { identity, multiply } from './utils.js'; diff --git a/dist/src/lib/ast/transform/translate.js b/dist/src/lib/ast/transform/translate.js index 3b480ad19..f97f6aed5 100644 --- a/dist/src/lib/ast/transform/translate.js +++ b/dist/src/lib/ast/transform/translate.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { identity, multiply } from './utils.js'; diff --git a/dist/src/lib/ast/transform/utils.js b/dist/src/lib/ast/transform/utils.js index 1653f753a..df34cd28f 100644 --- a/dist/src/lib/ast/transform/utils.js +++ b/dist/src/lib/ast/transform/utils.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { epsilon } from '../../syntax/constants.js'; diff --git a/dist/src/lib/ast/types.js b/dist/src/lib/ast/types.js index ff42c9b52..29ba74bda 100644 --- a/dist/src/lib/ast/types.js +++ b/dist/src/lib/ast/types.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ /** @@ -55,49 +55,26 @@ var EnumAstNodeStatus; */ EnumAstNodeStatus[EnumAstNodeStatus["Malformed"] = 128] = "Malformed"; /** - * Important declaration - */ - EnumAstNodeStatus[EnumAstNodeStatus["ImportantDeclaration"] = 256] = "ImportantDeclaration"; - /** - * Color component without unit - */ - EnumAstNodeStatus[EnumAstNodeStatus["PowerlessColorComponent"] = 512] = "PowerlessColorComponent"; -})(EnumAstNodeStatus || (EnumAstNodeStatus = {})); -/** - * Enum of validation levels - * @deprecated - */ -var ValidationLevel; -(function (ValidationLevel) { - /** - * disable validation - */ - ValidationLevel[ValidationLevel["None"] = 0] = "None"; - /** - * validate selectors - */ - ValidationLevel[ValidationLevel["Selector"] = 1] = "Selector"; - /** - * validate at-rules + * Node has children */ - ValidationLevel[ValidationLevel["AtRule"] = 2] = "AtRule"; + EnumAstNodeStatus[EnumAstNodeStatus["HasChildren"] = 256] = "HasChildren"; /** - * validate declarations + * Node has values */ - ValidationLevel[ValidationLevel["Declaration"] = 4] = "Declaration"; + EnumAstNodeStatus[EnumAstNodeStatus["HasValues"] = 512] = "HasValues"; /** - * validate selectors and at-rules + * Node has fraction value */ - ValidationLevel[ValidationLevel["Default"] = 3] = "Default"; + EnumAstNodeStatus[EnumAstNodeStatus["HasFractionValue"] = 1024] = "HasFractionValue"; /** - * validate selectors, at-rules and declarations + * Important declaration */ - ValidationLevel[ValidationLevel["All"] = 7] = "All"; + EnumAstNodeStatus[EnumAstNodeStatus["ImportantDeclaration"] = 2048] = "ImportantDeclaration"; /** - * Report only. Apply validation and report nodes that are marked as invalid + * Color component without unit */ - ValidationLevel[ValidationLevel["ReportOnly"] = 8] = "ReportOnly"; -})(ValidationLevel || (ValidationLevel = {})); + EnumAstNodeStatus[EnumAstNodeStatus["PowerlessColorComponent"] = 4096] = "PowerlessColorComponent"; +})(EnumAstNodeStatus || (EnumAstNodeStatus = {})); /** * Enum of all token types */ @@ -342,7 +319,7 @@ var EnumToken; /** * token list token type */ - EnumToken[EnumToken["ListToken"] = 59] = "ListToken"; + EnumToken[EnumToken["ListTokenType"] = 59] = "ListTokenType"; /* arithmetic tokens */ /** * addition token type @@ -805,135 +782,135 @@ var ColorType; /** * deprecated system colors */ - ColorType[ColorType["SYS"] = 0] = "SYS"; + ColorType[ColorType["SYS"] = 1] = "SYS"; /** * deprecated system colors */ - ColorType[ColorType["DPSYS"] = 1] = "DPSYS"; + ColorType[ColorType["DPSYS"] = 2] = "DPSYS"; /** * named colors */ - ColorType[ColorType["LIT"] = 2] = "LIT"; + ColorType[ColorType["LIT"] = 3] = "LIT"; /** * colors as hex values */ - ColorType[ColorType["HEX"] = 3] = "HEX"; + ColorType[ColorType["HEX"] = 4] = "HEX"; /** * colors as rgb values */ - ColorType[ColorType["RGBA"] = 4] = "RGBA"; + ColorType[ColorType["RGBA"] = 5] = "RGBA"; /** * colors using rgb */ - ColorType[ColorType["HSLA"] = 5] = "HSLA"; + ColorType[ColorType["HSLA"] = 6] = "HSLA"; /** * colors using hwb */ - ColorType[ColorType["HWB"] = 6] = "HWB"; + ColorType[ColorType["HWB"] = 7] = "HWB"; /** * colors using cmyk */ - ColorType[ColorType["CMYK"] = 7] = "CMYK"; + ColorType[ColorType["CMYK"] = 8] = "CMYK"; /** * colors using oklab * */ - ColorType[ColorType["OKLAB"] = 8] = "OKLAB"; + ColorType[ColorType["OKLAB"] = 9] = "OKLAB"; /** * colors using oklch * */ - ColorType[ColorType["OKLCH"] = 9] = "OKLCH"; + ColorType[ColorType["OKLCH"] = 10] = "OKLCH"; /** * colors using lab */ - ColorType[ColorType["LAB"] = 10] = "LAB"; + ColorType[ColorType["LAB"] = 11] = "LAB"; /** * colors using lch */ - ColorType[ColorType["LCH"] = 11] = "LCH"; + ColorType[ColorType["LCH"] = 12] = "LCH"; /** * colors using color() function */ - ColorType[ColorType["COLOR"] = 12] = "COLOR"; + ColorType[ColorType["COLOR"] = 13] = "COLOR"; /** * color using srgb */ - ColorType[ColorType["SRGB"] = 13] = "SRGB"; + ColorType[ColorType["SRGB"] = 14] = "SRGB"; /** * color using prophoto-rgb */ - ColorType[ColorType["PROPHOTO_RGB"] = 14] = "PROPHOTO_RGB"; + ColorType[ColorType["PROPHOTO_RGB"] = 15] = "PROPHOTO_RGB"; /** * color using a98-rgb */ - ColorType[ColorType["A98_RGB"] = 15] = "A98_RGB"; + ColorType[ColorType["A98_RGB"] = 16] = "A98_RGB"; /** * color using rec2020 */ - ColorType[ColorType["REC2020"] = 16] = "REC2020"; + ColorType[ColorType["REC2020"] = 17] = "REC2020"; /** * color using display-p3 */ - ColorType[ColorType["DISPLAY_P3"] = 17] = "DISPLAY_P3"; + ColorType[ColorType["DISPLAY_P3"] = 18] = "DISPLAY_P3"; /** * color using srgb-linear */ - ColorType[ColorType["SRGB_LINEAR"] = 18] = "SRGB_LINEAR"; + ColorType[ColorType["SRGB_LINEAR"] = 19] = "SRGB_LINEAR"; /** * color using xyz-d50 */ - ColorType[ColorType["XYZ_D50"] = 19] = "XYZ_D50"; + ColorType[ColorType["XYZ_D50"] = 20] = "XYZ_D50"; /** * color using xyz-d65 */ - ColorType[ColorType["XYZ_D65"] = 20] = "XYZ_D65"; + ColorType[ColorType["XYZ_D65"] = 21] = "XYZ_D65"; /** * light-dark() color function */ - ColorType[ColorType["LIGHT_DARK"] = 21] = "LIGHT_DARK"; + ColorType[ColorType["LIGHT_DARK"] = 22] = "LIGHT_DARK"; /** * color-mix() color function */ - ColorType[ColorType["COLOR_MIX"] = 22] = "COLOR_MIX"; + ColorType[ColorType["COLOR_MIX"] = 23] = "COLOR_MIX"; /** * non-standard color */ - ColorType[ColorType["NON_STD"] = 23] = "NON_STD"; + ColorType[ColorType["NON_STD"] = 24] = "NON_STD"; /** * custom color */ - ColorType[ColorType["CUSTOM_COLOR"] = 24] = "CUSTOM_COLOR"; + ColorType[ColorType["CUSTOM_COLOR"] = 25] = "CUSTOM_COLOR"; /** * alpha() color function */ - ColorType[ColorType["ALPHA"] = 25] = "ALPHA"; + ColorType[ColorType["ALPHA"] = 26] = "ALPHA"; /** * color using display-p3-linear */ - ColorType[ColorType["DISPLAY_P3_LINEAR"] = 26] = "DISPLAY_P3_LINEAR"; + ColorType[ColorType["DISPLAY_P3_LINEAR"] = 27] = "DISPLAY_P3_LINEAR"; /** * Contrast color */ - ColorType[ColorType["CONTRAST_COLOR"] = 27] = "CONTRAST_COLOR"; + ColorType[ColorType["CONTRAST_COLOR"] = 28] = "CONTRAST_COLOR"; /** * color layers */ - ColorType[ColorType["COLOR_LAYERS"] = 28] = "COLOR_LAYERS"; + ColorType[ColorType["COLOR_LAYERS"] = 29] = "COLOR_LAYERS"; /** * alias for rgba */ - ColorType[ColorType["RGB"] = 4] = "RGB"; + ColorType[ColorType["RGB"] = 5] = "RGB"; /** * alias for hsl */ - ColorType[ColorType["HSL"] = 5] = "HSL"; + ColorType[ColorType["HSL"] = 6] = "HSL"; /** * alias for xyz-d65 */ - ColorType[ColorType["XYZ"] = 20] = "XYZ"; + ColorType[ColorType["XYZ"] = 21] = "XYZ"; /** * alias for cmyk */ - ColorType[ColorType["DEVICE_CMYK"] = 7] = "DEVICE_CMYK"; + ColorType[ColorType["DEVICE_CMYK"] = 8] = "DEVICE_CMYK"; })(ColorType || (ColorType = {})); /** * Supported module case transform @@ -1004,4 +981,4 @@ var ModuleScopeEnumOptions; ModuleScopeEnumOptions[ModuleScopeEnumOptions["Shortest"] = 512] = "Shortest"; })(ModuleScopeEnumOptions || (ModuleScopeEnumOptions = {})); -export { ColorType, EnumAstNodeStatus, EnumToken, ModuleCaseTransformEnum, ModuleScopeEnumOptions, SyntaxValidationResult, ValidationLevel }; +export { ColorType, EnumAstNodeStatus, EnumToken, ModuleCaseTransformEnum, ModuleScopeEnumOptions, SyntaxValidationResult }; diff --git a/dist/src/lib/ast/walk.js b/dist/src/lib/ast/walk.js index 4dbdfc556..5405b3f04 100644 --- a/dist/src/lib/ast/walk.js +++ b/dist/src/lib/ast/walk.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { PARENT, TOKENS } from '../syntax/constants.js'; @@ -271,7 +271,6 @@ function* walkValues(values, root = null, filter, reverse) { let isNumeric = false; let value; let option; - let node; let i = -1; const eventType = filter.event ?? WalkerEvent.Enter; // @ts-ignore @@ -305,16 +304,7 @@ function* walkValues(values, root = null, filter, reverse) { (typeof filter.type == "function" && filter.type(value)); if (isValid) { // @ts-expect-error - option = filter.fn(value, value[PARENT] ?? root, WalkerEvent.Enter, - // @ts-expect-error - function* () { - // @ts-expect-error - let parent = node[PARENT]; - while (parent != null) { - yield parent; - parent = parent[PARENT] ?? null; - } - }); + option = filter.fn(value); isNumeric = typeof option == "number"; if (isNumeric && option & WalkerOptionEnum.Stop) { return; diff --git a/dist/src/lib/fs/resolve.js b/dist/src/lib/fs/resolve.js index 98808a1b6..db60a9488 100644 --- a/dist/src/lib/fs/resolve.js +++ b/dist/src/lib/fs/resolve.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { memoize } from '../parser/utils/cache.js'; diff --git a/dist/src/lib/parser/arena.js b/dist/src/lib/parser/arena.js index 479fc3d40..e59b834e3 100644 --- a/dist/src/lib/parser/arena.js +++ b/dist/src/lib/parser/arena.js @@ -1,3 +1,15 @@ +/* + CSS parser, minifier and validator for node and the browser + + Author(s): Thierry Bela + Version: 2.0.0-alpha.1 + License: MIT + */ +import { EnumToken, EnumAstNodeStatus } from '../ast/types.js'; +import { PARENT } from '../syntax/constants.js'; +import { equalsIgnoreCase } from './utils/text.js'; +import { WalkerOptionEnum, WalkerEvent } from '../ast/walk.js'; + class Arena { stringIds = new Map(); strings = []; @@ -8,6 +20,7 @@ class Arena { nextSibling; previousSibling; firstToken; + lastToken; firstChild; lastChild; name; @@ -15,15 +28,25 @@ class Arena { numericValue; unit; flags; - left; - right; - op; - op2; - constructor(size = 1024) { + colorType; + /** + * token spans. 8 bits for source type. 28 bits for start position, 28 bits for end position. This allows us to store the start and end positions of tokens in a single 64-bit integer, which is more efficient than storing them separately. + */ + spans; + /** + * error cache + */ + errors = new Map(); + /** + * rendered tokens cache + */ + renderedTokens; + constructor(size = 4096) { this.dataTypes = new Uint8Array(size); this.parents = new Uint32Array(size); this.nextSibling = new Uint32Array(size); this.previousSibling = new Uint32Array(size); + this.lastToken = new Uint32Array(size); this.firstToken = new Uint32Array(size); this.firstChild = new Uint32Array(size); this.lastChild = new Uint32Array(size); @@ -32,23 +55,673 @@ class Arena { this.numericValue = new Float64Array(size); this.unit = new Uint32Array(size); this.flags = new Uint32Array(size); - this.left = new Uint32Array(size); - this.right = new Uint32Array(size); - this.op = new Uint32Array(size); - this.op2 = new Uint32Array(size); + // this.left = new Uint32Array(size); + // this.right = new Uint32Array(size); + // this.op = new Uint32Array(size); + // this.op2 = new Uint32Array(size); + this.renderedTokens = new Uint32Array(size); + this.colorType = new Uint8Array(size); + this.spans = new BigUint64Array(size); + this.dataTypes[this.root] = EnumToken.StyleSheetNodeType; + } + allocate() { + const id = ++this.current; + if (this.current >= this.dataTypes.length) { + this.resize(this.dataTypes.length * 2); + } + return id; } + // Interns a string and returns its unique ID. If the string has already been interned, it returns the existing ID. internString(str) { let id = this.stringIds.get(str); if (id != null) { return id; } - this.stringIds.set(str, this.strings.length); + id = this.strings.length; + this.stringIds.set(str, id); this.strings.push(str); - return this.strings.length - 1; + return id; + } + /** + * set node data for a given id. The first argument is the id, the second is the type, the third is the string value, the fourth is the numeric value, and the fifth is the unit. + * @param id + * @param args + */ + set(id, ...args) { + this.dataTypes[id] = args[0]; + if (args.length > 1) { + // 0 is null string, so we can use it to indicate that there is no string value for this node. + this.stringValue[id] = args[1]; + } + if (args.length > 2) { + this.numericValue[id] = args[2]; + } + if (args.length > 3) { + this.unit[id] = args[3]; + } + } + getType(id) { + return this.dataTypes[id]; + } + setType(id, type) { + this.dataTypes[id] = type; + } + setName(id, nameId) { + this.name[id] = nameId; + } + getName(id) { + return this.strings[this.name[id]] ?? null; + } + setValue(id, valueId) { + this.stringValue[id] = valueId; + } + getValue(id) { + return this.strings[this.stringValue[id]] ?? null; + } + setNumericValue(id, numericValue) { + this.numericValue[id] = numericValue; + } + setUnit(id, unit) { + this.unit[id] = unit; + } + getUnit(id) { + return this.strings[this.unit[id]] ?? null; + } + getNumericValue(id) { + return this.numericValue[id]; + } + setParent(id, parentId) { + this.parents[id] = parentId; + } + getFirstChild(id) { + return this.firstChild[id]; + } + setFirstChild(id, firstChildId) { + this.detach(firstChildId); + const firstChild = this.firstChild[id]; + if (this.firstChild[id] != 0) { + const nextSibling = this.nextSibling[firstChild]; + if (nextSibling != 0) { + this.nextSibling[firstChildId] = nextSibling; + this.previousSibling[nextSibling] = firstChildId; + } + this.detach(firstChild); + } + this.firstChild[id] = firstChildId; + this.parents[firstChild] = id; + } + getLastChild(id) { + return this.lastChild[id]; + } + setLastChild(id, lastChildId) { + this.detach(lastChildId); + const lastChild = this.lastChild[id]; + if (lastChild != 0) { + const previousSibling = this.previousSibling[lastChild]; + if (previousSibling != 0) { + this.previousSibling[lastChildId] = previousSibling; + this.nextSibling[previousSibling] = lastChildId; + } + this.detach(lastChild); + } + this.lastChild[id] = lastChildId; + this.nextSibling[lastChildId] = 0; + this.parents[lastChildId] = id; + } + setNextSibling(id, nextSiblingId) { + this.detach(nextSiblingId); + this.nextSibling[nextSiblingId] = this.nextSibling[id]; + this.nextSibling[id] = nextSiblingId; + this.previousSibling[nextSiblingId] = id; + this.parents[nextSiblingId] = this.parents[id]; + } + setPreviousSibling(id, previousSiblingId) { + this.detach(previousSiblingId); + if (this.previousSibling[id] != 0) { + const previousSibling = this.previousSibling[id]; + if (previousSibling != 0) { + this.nextSibling[previousSibling] = previousSiblingId; + this.previousSibling[previousSiblingId] = previousSibling; + } + this.nextSibling[previousSiblingId] = this.nextSibling[id]; + } + this.previousSibling[id] = previousSiblingId; + this.parents[previousSiblingId] = this.parents[id]; + } + setFirstToken(id, firstTokenId) { + this.detach(firstTokenId); + const firstToken = this.firstToken[id]; + this.previousSibling[firstTokenId] = 0; + this.nextSibling[firstTokenId] = this.nextSibling[firstTokenId]; + this.parents[firstTokenId] = id; + this.firstToken[id] = firstTokenId; + this.detach(firstToken); + } + setColorType(id, colorType) { + this.colorType[id] = colorType; + } + setFlags(id, flags) { + this.flags[id] = flags; + } + getFlags(id) { + return this.flags[id]; + } + hasFlags(id, flag) { + return (this.flags[id] & flag) != 0; + } + getChildAt(id, index) { + let child = this.firstChild[id]; + while (index > 0 && child != 0) { + child = this.nextSibling[child]; + index--; + } + return child == 0 ? null : child; + } + setChildAt(id, index, child) { + if (index == 0) { + if (child != this.firstChild[id]) { + this.detach(child); + if (this.firstChild[id] != 0) { + this.nextSibling[child] = this.nextSibling[this.firstChild[id]]; + this.previousSibling[this.nextSibling[this.firstChild[id]]] = child; + } + this.firstChild[id] = child; + this.parents[child] = id; + } + return; + } + index--; + let currentChild = this.firstChild[id]; + while (index > 0 && this.nextSibling[currentChild] != 0) { + currentChild = this.nextSibling[currentChild]; + index--; + } + this.detach(child); + if (index > 0) { + // append + this.nextSibling[currentChild] = child; + this.previousSibling[child] = currentChild; + this.nextSibling[child] = 0; + } + else { + // prepend + this.previousSibling[child] = this.previousSibling[currentChild]; + this.nextSibling[child] = this.nextSibling[currentChild]; + this.nextSibling[this.previousSibling[currentChild]] = child; + this.previousSibling[currentChild] = child; + this.previousSibling[currentChild] = 0; + this.nextSibling[currentChild] = 0; + this.parents[currentChild] = 0; + } + this.parents[child] = id; + } + /** + * let child should be of type EnumToken.ListToken or EnumToken.ParensToken + * @param id + * @param child + */ + setLeft(id, child) { + this.setChildAt(id, 0, child); + } + getLeft(id) { + return this.getChildAt(id, 0); + } + setRight(id, rightId) { + this.setChildAt(id, 1, rightId); + } + getRight(id) { + return this.getChildAt(id, 1); + } + setOp(id, opId) { + this.setChildAt(id, 2, opId); + } + getOp(id) { + return this.getChildAt(id, 2); + } + setOp2(id, op2Id) { + this.setChildAt(id, 3, op2Id); + } + getOp2(id) { + return this.getChildAt(id, 3); + } + setRenderedTokens(id, renderedTokensId) { + this.renderedTokens[id] = renderedTokensId; + } + setSpan(id, sourceId, start, end) { + this.spans[id] = BigInt(((sourceId & 0xff) << 56) | ((start & 0xfffffff) << 28) | (end & 0xfffffff)); + } + getSourceId(id) { + return Number((this.spans[id] >> 56n) & 0xffn); + } + getSpanStart(id) { + return Number((this.spans[id] >> 28n) & 0xfffffffn); + } + setSpandStart(id, start) { + this.spans[id] = + BigInt((this.getSourceId(id) & 0xff) << 56) | + BigInt((start & 0xfffffff) << 28) | + BigInt(this.getSpanEnd(id) & 0xfffffff); + } + getSpanEnd(id) { + return Number(this.spans[id] & 0xfffffffn); + } + setSpanEnd(id, end) { + this.spans[id] = + BigInt((this.getSourceId(id) & 0xff) << 56) | + BigInt((this.getSpanStart(id) & 0xfffffff) << 28) | + BigInt(end & 0xfffffff); + } + getParent(id) { + return this.parents[id]; } resolveString(id) { return this.strings[id]; } + detach(id) { + let parent = this.parents[id]; + if (this.lastChild[parent] == id) { + this.lastChild[parent] = this.previousSibling[id]; + } + if (this.nextSibling[id] != 0) { + // first child + if (this.previousSibling[id] == 0) { + this.firstChild[parent] = this.nextSibling[id]; + } + else { + // not first child + this.nextSibling[this.previousSibling[id]] = this.nextSibling[id]; + } + } + this.nextSibling[id] = 0; + this.previousSibling[id] = 0; + this.parents[id] = 0; + } + *walk(node, filter, reverse) { + const parents = [node]; + let options = filter; + let isNumeric = false; + let children = true; + let attributes = false; + let i = 0; + let firstChild; + if (options != null && typeof options == "object") { + filter = options.filter; + reverse = options.reverse; + attributes = options.attributes; + children = options.children ?? true; + } + while ((node = parents[i++])) { + let option = null; + if (filter != null) { + // @ts-expect-error + option = filter(node); + isNumeric = typeof option == "number"; + if (isNumeric) { + if (option & WalkerOptionEnum.Ignore) { + continue; + } + if (option & WalkerOptionEnum.Stop) { + break; + } + } + } + if (!isNumeric || (option & WalkerOptionEnum.Children) === 0) { + yield node; + } + if (attributes) { + firstChild = + this.flags[node] & EnumAstNodeStatus.HasValues ? this.firstChild[node] : this.firstToken[node]; + if (firstChild != 0) { + const siblings = this.siblings(firstChild); + parents.splice(i, 0, ...(reverse ? siblings.toReversed() : siblings)); + } + // else if (this.flags[node] & EnumAstNodeStatus.HasValues) { + // // @ts-expect-error + // for (const val of node.val) { + // if (val[PARENT] != node) { + // val[PARENT] = node; + // } + // } + // // @ts-expect-error + // parents.splice(i, 0, ...(reverse ? node.val.toReversed() : node.val)); + // } + } + if (children && + this.flags[node] & EnumAstNodeStatus.HasChildren && + (!isNumeric || (option & WalkerOptionEnum.IgnoreChildren) === 0)) { + firstChild = this.firstChild[node]; + if (firstChild != 0) { + const siblings = this.siblings(firstChild); + parents.splice(i, 0, ...(reverse ? siblings.toReversed() : siblings)); + } + // // @ts-expect-error + // parents.splice(i, 0, ...(reverse ? node.chi!.toReversed() : node.chi)); + // for (const child of (node).chi) { + // if (child[PARENT] != node) { + // child[PARENT] = node; + // } + // } + } + } + } + *walkValues(values, filter, reverse) { + const stack = values.slice(); + if (filter != null && typeof filter == "function") { + filter = { + event: WalkerEvent.Enter, + fn: filter, + }; + } + else if (filter == null) { + filter = { + event: WalkerEvent.Enter, + }; + } + let isNumeric = false; + let value; + let option; + let i = -1; + const eventType = filter.event ?? WalkerEvent.Enter; + // @ts-ignore + // const result = { + // value: null, + // parent: null, + // previousValue: null, + // nextValue: null, + // // + // root: root ?? null, + // parents: function* () { + // // @ts-ignore + // let result = node?.[PARENT] ?? root; + // while (result != null) { + // yield result; + // result = result[PARENT] ?? null; + // } + // }, + // } as WalkAttributesResult; + while (++i < stack.length) { + value = stack[i]; + option = null; + if (filter.fn != null && eventType & WalkerEvent.Enter) { + const isValid = filter.type == null || + this.dataTypes[value] == filter.type || + (Array.isArray(filter.type) && filter.type.includes(this.dataTypes[value])) || + (typeof filter.type == "function" && filter.type(value)); + if (isValid) { + option = filter.fn(value, WalkerEvent.Enter); + isNumeric = typeof option == "number"; + if (isNumeric && option & WalkerOptionEnum.Stop) { + return; + } + if (isNumeric && option & WalkerOptionEnum.Ignore) { + continue; + } + // + if (option != null && Array.isArray(option)) { + // const op = Array.isArray(option) ? option : [option]; + stack.splice(i, 0, ...(reverse ? option.toReversed() : option)); + } + } + } + yield value; + if (!isNumeric || (option & WalkerOptionEnum.IgnoreChildren) === 0) { + if (this.flags[value] & EnumAstNodeStatus.HasChildren) { + for (const child of this.children(value)) { + if (reverse) { + stack.unshift(child); + } + else { + stack.push(child); + } + } + } + // else { + // const values: Token[] = []; + // if ("l" in value && value.l != null) { + // // @ts-expect-error + // values.push(value.l); + // // @ts-expect-error + // if (value.l[PARENT] != value) { + // // @ts-expect-error + // value.l[PARENT] = value; + // } + // } + // if ("op" in value && typeof value.op == "object") { + // // @ts-expect-error + // values.push(value.op); + // // @ts-expect-error + // if (value.op[PARENT] != value) { + // // @ts-expect-error + // value.op[PARENT] = value; + // } + // } + // if ("r" in value && value.r != null) { + // if (Array.isArray(value.r)) { + // for (const r of value.r) { + // // + // values.push(r); + // if (r[PARENT] != value) { + // // + // r[PARENT] = value; + // } + // } + // } else { + // // + // values.push(value.r); + // if (value.r[PARENT] != value) { + // // + // value.r[PARENT] = value; + // } + // } + // } + // if (values.length > 0) { + // for (const v of values) { + // if (reverse) { + // stack.unshift(v); + // } else { + // stack.push(v); + // } + // } + // } + // } + } + if (eventType & WalkerEvent.Leave && filter.fn != null) { + const isValid = filter.type == null || + this.dataTypes[value] == filter.type || + (Array.isArray(filter.type) && filter.type.includes(this.dataTypes[value])) || + (typeof filter.type == "function" && filter.type(value)); + if (isValid) { + // @ts-ignore + option = filter.fn(value, value[PARENT], WalkerEvent.Leave); + // @ts-ignore + if (option != null && Array.isArray(option)) { + // const op = Array.isArray(option) ? option : [option]; + stack.splice(i, 0, ...(reverse ? option.toReversed() : option)); + // for (const o of op) { + // if (o[PARENT] != value) { + // o[PARENT] = value; + // } + // } + } + } + } + } + } + findChild(id, predicate) { + let child = this.firstChild[id]; + while (child != 0) { + if (predicate(child)) { + return child; + } + child = this.nextSibling[child]; + } + return null; + } + siblings(id) { + let siblings = []; + let child = this.firstChild[this.parents[id]]; + while (child != 0) { + siblings.push(child); + child = this.nextSibling[child]; + } + return siblings; + } + children(id) { + let children = []; + let child = this.firstChild[id]; + while (child != 0) { + children.push(child); + child = this.nextSibling[child]; + } + return children; + } + setChildren(id, ...children) { + if (this.lastChild[id] != 0) { + this.lastChild[id] = 0; + } + if (this.firstChild[id] != 0) { + this.detach(this.firstChild[id]); + } + this.appendChildren(id, ...children); + } + setTokens(id, ...children) { + if (this.lastToken[id] != 0) { + this.lastToken[id] = 0; + } + if (this.firstToken[id] != 0) { + this.detach(this.firstToken[id]); + } + this.appendTokens(id, ...children); + } + appendChildren(id, ...children) { + if (children.length == 0) { + return; + } + this.detach(children[0]); + let lastChild = this.lastChild[id]; + if (lastChild == 0) { + this.firstChild[id] = children[0]; + } + else { + this.nextSibling[lastChild] = children[0]; + } + this.parents[children[0]] = id; + for (let i = 1; i < children.length - 1; i++) { + this.detach(children[i]); + this.previousSibling[children[i]] = children[i - 1]; + this.nextSibling[children[i - 1]] = children[i]; + this.parents[children[i]] = id; + } + this.lastChild[id] = children[children.length - 1]; + } + appendTokens(id, ...children) { + if (children.length == 0) { + return; + } + this.detach(children[0]); + let lastChild = this.lastToken[id]; + if (lastChild == 0) { + this.firstToken[id] = children[0]; + } + else { + this.nextSibling[lastChild] = children[0]; + } + this.parents[children[0]] = id; + for (let i = 1; i < children.length - 1; i++) { + this.detach(children[i]); + this.previousSibling[children[i]] = children[i - 1]; + this.nextSibling[children[i - 1]] = children[i]; + this.parents[children[i]] = id; + } + this.lastToken[id] = children[children.length - 1]; + } + equalsIgnoreCase(node, key, string) { + const stringId = this.stringIds.get(string); + if (stringId == null) { + return false; + } + const id = key == "nam" ? this.name[node] : this.stringValue[node]; + if (id == stringId) { + return true; + } + if (string.length != this.strings[id].length) { + return false; + } + return equalsIgnoreCase(this.strings[id], string); + } + startsWith(node, key, string) { + const stringId = this.stringIds.get(string); + if (stringId == null) { + return false; + } + const id = key == "nam" ? this.name[node] : this.stringValue[node]; + if (id == stringId) { + return true; + } + if (string.length != this.strings[id].length) { + return false; + } + return this.strings[id].startsWith(string); + } + resize(newSize) { + if (newSize <= this.dataTypes.length) { + return; + } + const newDataTypes = new Uint8Array(newSize); + newDataTypes.set(this.dataTypes); + this.dataTypes = newDataTypes; + const newParents = new Uint32Array(newSize); + newParents.set(this.parents); + this.parents = newParents; + const newNextSibling = new Uint32Array(newSize); + newNextSibling.set(this.nextSibling); + this.nextSibling = newNextSibling; + const newPreviousSibling = new Uint32Array(newSize); + newPreviousSibling.set(this.previousSibling); + this.previousSibling = newPreviousSibling; + const newFirstToken = new Uint32Array(newSize); + newFirstToken.set(this.firstToken); + this.firstToken = newFirstToken; + const newLastToken = new Uint32Array(newSize); + newLastToken.set(this.lastToken); + this.lastToken = newLastToken; + const newFirstChild = new Uint32Array(newSize); + newFirstChild.set(this.firstChild); + this.firstChild = newFirstChild; + const newLastChild = new Uint32Array(newSize); + newLastChild.set(this.lastChild); + this.lastChild = newLastChild; + const newName = new Uint32Array(newSize); + newName.set(this.name); + this.name = newName; + const newStringValue = new Uint32Array(newSize); + newStringValue.set(this.stringValue); + this.stringValue = newStringValue; + const newNumericValue = new Float64Array(newSize); + newNumericValue.set(this.numericValue); + this.numericValue = newNumericValue; + const newUnit = new Uint32Array(newSize); + newUnit.set(this.unit); + this.unit = newUnit; + const newFlags = new Uint32Array(newSize); + newFlags.set(this.flags); + this.flags = newFlags; + const newRenderedTokens = new Uint32Array(newSize); + newRenderedTokens.set(this.renderedTokens); + this.renderedTokens = newRenderedTokens; + const newColorType = new Uint8Array(newSize); + newColorType.set(this.colorType); + this.colorType = newColorType; + const newSpans = new BigUint64Array(newSize); + newSpans.set(this.spans); + this.spans = newSpans; + } + setErrors(id, errors) { + this.errors.set(id, errors); + } + getErrors(id) { + return this.errors.get(id) ?? null; + } } export { Arena }; diff --git a/dist/src/lib/parser/declaration/list.js b/dist/src/lib/parser/declaration/list.js index 26ea64de9..8a241af08 100644 --- a/dist/src/lib/parser/declaration/list.js +++ b/dist/src/lib/parser/declaration/list.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { PropertySet } from './set.js'; diff --git a/dist/src/lib/parser/declaration/map.js b/dist/src/lib/parser/declaration/map.js index b055b96ca..2cf25162d 100644 --- a/dist/src/lib/parser/declaration/map.js +++ b/dist/src/lib/parser/declaration/map.js @@ -2,18 +2,18 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { eq } from '../utils/eq.js'; import { getConfig } from '../utils/config.js'; import { matchType } from '../utils/type.js'; import { EnumToken } from '../../ast/types.js'; -import { renderValue } from '../../printer/render.js'; import { parseString } from '../parse.js'; import { PropertySet } from './set.js'; import { PROPERTYNAME } from '../../syntax/constants.js'; import { cloneNode } from '../../ast/clone.js'; +import { printValue } from '../../printer/print.js'; const propertiesConfig = getConfig(); class PropertyMap { @@ -284,7 +284,7 @@ class PropertyMap { }; // @ts-ignore const separator = this.config.separator - ? renderValue({ + ? printValue({ ...this.config.separator, typ: EnumToken[this.config.separator.typ], }) @@ -294,7 +294,7 @@ class PropertyMap { for (i = 0; i < declaration.val.length; i++) { t = declaration.val[i]; if (!cache.has(t)) { - cache.set(t, renderValue(t, { minify: true })); + cache.set(t, printValue(t, { minify: true })); } if (t.typ == typ && separator == cache.get(t)) { this.removeDefaults(map, value); @@ -589,7 +589,7 @@ class PropertyMap { // [], // ); if (props.default.includes(curr[1][i] - .reduce((acc, curr) => acc + renderValue(curr) + " ", "") + .reduce((acc, curr) => acc + printNode(curr) + " ", "") .trimEnd())) { if (!propertiesMapping[curr[0]].required) { continue; @@ -691,7 +691,7 @@ class PropertyMap { }, []); if (this.config.mapping != null) { const val = values.reduce((acc, curr) => acc + - renderValue(curr, { + printNode(curr, { removeComments: true, minify: true, }), ""); diff --git a/dist/src/lib/parser/declaration/set.js b/dist/src/lib/parser/declaration/set.js index e2be699b9..5ea262509 100644 --- a/dist/src/lib/parser/declaration/set.js +++ b/dist/src/lib/parser/declaration/set.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { eq } from '../utils/eq.js'; diff --git a/dist/src/lib/parser/linesmap.js b/dist/src/lib/parser/linesmap.js index 398543ea8..3fdb3a289 100644 --- a/dist/src/lib/parser/linesmap.js +++ b/dist/src/lib/parser/linesmap.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ /** diff --git a/dist/src/lib/parser/parse.js b/dist/src/lib/parser/parse.js index 00e9f95ab..9a7b5ee8d 100644 --- a/dist/src/lib/parser/parse.js +++ b/dist/src/lib/parser/parse.js @@ -2,18 +2,17 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { isIdentColor, parseColor, isColor } from '../syntax/syntax.js'; import { camelize, equalsIgnoreCase, dasherize } from './utils/text.js'; -import { renderValue } from '../printer/render.js'; import { EnumToken, EnumAstNodeStatus, ModuleCaseTransformEnum, ModuleScopeEnumOptions } from '../ast/types.js'; import { minify } from '../ast/minify.js'; import { expand } from '../ast/expand.js'; import { walk, walkValues, WalkerEvent } from '../ast/walk.js'; import { Tokenizer } from './tokenize.js'; -import { LOCSRCID, LOCSTA, LOCEND, tokensfuncDefMap, STATE, PARENT, TOKENS, ROOT, ERRORS, pageMarginBoxType } from '../syntax/constants.js'; +import { LOCSRCID, LOCSTA, LOCEND, tokensfuncDefMap, STATE, PARENT, TOKENS, ERRORS, pageMarginBoxType } from '../syntax/constants.js'; import { hashAlgorithms, hash, syncHash } from './utils/hash.js'; import { parseSelector } from './utils/selector.js'; import { parseDeclaration } from './utils/declaration.js'; @@ -37,8 +36,8 @@ function renderTokens(tokens, options) { if (tokens == null || tokens.length === 0) return ""; if (options != null) - return tokens.map((t) => renderValue(t, options)).join(""); - return tokens.map((t) => renderValue(t)).join(""); + return tokens.map((t) => printValue(t, options)).join(""); + return tokens.map((t) => printValue(t)).join(""); } const trimWhiteSpace = [ EnumToken.CommentTokenType, @@ -499,140 +498,159 @@ function doParseSync(tokenizer, options = {}) { imports: [], }; const invalidNodes = []; + const arena = options.arena; let ast = { typ: EnumToken.StyleSheetNodeType, chi: [], }; let tokens = []; - let context = ast; - let item; + let context = arena.root; let node; // @ts-ignore ignore error let parensMatch = 0; let curlyBracketMatch = 0; + let tokenId; // let currentItemIndex: number; ast[LOCSRCID] = options.source.id; ast[LOCSTA] = 0; while (!tokenizer.done()) { tokenizer.next(); // item = (iter as Array)[currentItemIndex]; + tokenId = arena.allocate(); if (tokenizer.unit != null) { - item = { - typ: tokenizer.typ, - val: tokenizer.val, - unit: tokenizer.unit, - }; + arena.set(tokenId, tokenizer.typ, 0, tokenizer.val, arena.internString(tokenizer.unit)); + // item = { + // typ: tokenizer.typ as EnumToken, + // val: tokenizer.val, + // unit: tokenizer.unit, + // } as DimensionToken; } else if (tokenizer.nam != null) { - item = { - typ: tokenizer.typ, - nam: tokenizer.nam, - }; + arena.set(tokenId, tokenizer.typ); + arena.setName(tokenId, arena.internString(tokenizer.nam)); + // item = { + // typ: tokenizer.typ as EnumToken, + // nam: tokenizer.nam, + // } as Token; } else if (tokenizer.val === null) { - item = { - typ: tokenizer.typ, - }; + arena.set(tokenId, tokenizer.typ); + // item = { + // typ: tokenizer.typ as EnumToken, + // } as Token; } else if (tokenizer.kin != null) { - item = { - typ: tokenizer.typ, - val: tokenizer.val, - kin: tokenizer.kin, - }; + arena.set(tokenId, tokenizer.typ, arena.internString(tokenizer.val)); + arena.setColorType(tokenId, tokenizer.kin); + // item = { + // typ: tokenizer.typ as EnumToken, + // val: tokenizer.val, + // kin: tokenizer.kin, + // } as ColorToken; } else { - item = { - typ: tokenizer.typ, - val: tokenizer.val, - }; - } - item[LOCSRCID] = tokenizer.srcId; - item[LOCSTA] = tokenizer.sta; - item[LOCEND] = tokenizer.end; + arena.set(tokenId, tokenizer.typ, typeof tokenizer.val === "string" ? arena.internString(tokenizer.val) : 0, typeof tokenizer.val === "number" ? tokenizer.val : 0); + // item = { + // typ: tokenizer.typ as EnumToken, + // val: tokenizer.val, + // } as Token; + } + arena.setSpan(tokenId, tokenizer.srcId, tokenizer.sta, tokenizer.end); + // item[LOCSRCID] = tokenizer.srcId as number; + // item[LOCSTA] = tokenizer.sta as number; + // item[LOCEND] = tokenizer.end as number; stats.bytesIn = tokenizer.bytesIn; stats.tokensCount++; - if (BadTokensTypes.includes(item.typ)) { - tokens.push(item); + if (BadTokensTypes.includes(tokenizer.typ)) { + tokens.push(tokenId); errors.push({ action: "drop", message: "Bad token", syntax: null, - node: item, - location: options.source.getSourceLocation(item[LOCSTA]), + node: tokenId, + location: options.source.getSourceLocation(tokenizer.sta), }); // bad token continue; } - if (item.typ === EnumToken.StartParensTokenType || tokensfuncDefMap.has(item.typ)) { + if (tokenizer.typ === EnumToken.StartParensTokenType || tokensfuncDefMap.has(tokenizer.typ)) { parensMatch++; } - else if (item.typ === EnumToken.EndParensTokenType && parensMatch > 0) { + else if (tokenizer.typ === EnumToken.EndParensTokenType && parensMatch > 0) { parensMatch--; } - if (item.typ === EnumToken.BlockStartTokenType) { + if (tokenizer.typ === EnumToken.BlockStartTokenType) { curlyBracketMatch++; } - else if (item.typ === EnumToken.BlockEndTokenType && curlyBracketMatch > 0) { + else if (tokenizer.typ === EnumToken.BlockEndTokenType && curlyBracketMatch > 0) { curlyBracketMatch--; } - tokens.push(item); + tokens.push(tokenId); if (parensMatch === 0 && - (item.typ === EnumToken.SemiColonTokenType || - item.typ === EnumToken.BlockStartTokenType || - item.typ === EnumToken.EOFTokenType)) { + (tokenizer.typ === EnumToken.SemiColonTokenType || + tokenizer.typ === EnumToken.BlockStartTokenType || + tokenizer.typ === EnumToken.EOFTokenType)) { node = parseNode(tokens, context, options, errors, stats, invalidNodes); if (node != null) { - if ("chi" in node) { + if (arena.hasFlags(node, EnumAstNodeStatus.HasChildren)) { stack.push(node); context = node; } } - else if (item.typ == EnumToken.BlockStartTokenType) { + else if (tokenizer.typ == EnumToken.BlockStartTokenType) { let inBlock = 1; tokens.length = 0; - tokens.push(item); + tokens.push(tokenId); do { tokenizer.next(); + tokenId = arena.allocate(); if (tokenizer.unit != null) { - item = { - typ: tokenizer.typ, - val: tokenizer.val, - unit: tokenizer.unit, - }; + arena.set(tokenId, tokenizer.typ, 0, tokenizer.val, arena.internString(tokenizer.unit)); + // item = { + // typ: tokenizer.typ as EnumToken, + // val: tokenizer.val, + // unit: tokenizer.unit, + // } as DimensionToken; } else if (tokenizer.nam != null) { - item = { - typ: tokenizer.typ, - nam: tokenizer.nam, - }; + arena.set(tokenId, tokenizer.typ); + arena.setName(tokenId, arena.internString(tokenizer.nam)); + // item = { + // typ: tokenizer.typ as EnumToken, + // nam: tokenizer.nam, + // } as Token; } else if (tokenizer.val === null) { - item = { - typ: tokenizer.typ, - }; + arena.set(tokenId, tokenizer.typ); + // item = { + // typ: tokenizer.typ as EnumToken, + // } as Token; } else if (tokenizer.kin != null) { - item = { - typ: tokenizer.typ, - val: tokenizer.val, - kin: tokenizer.kin, - }; + arena.set(tokenId, tokenizer.typ, arena.internString(tokenizer.val)); + arena.setColorType(tokenId, tokenizer.kin); + // item = { + // typ: tokenizer.typ as EnumToken, + // val: tokenizer.val, + // kin: tokenizer.kin, + // } as ColorToken; } else { - item = { - typ: tokenizer.typ, - val: tokenizer.val, - }; - } - item[LOCSRCID] = tokenizer.srcId; - item[LOCSTA] = tokenizer.sta; - item[LOCEND] = tokenizer.end; - tokens.push(item); - if (item.typ === EnumToken.BlockStartTokenType) { + arena.set(tokenId, tokenizer.typ, arena.internString(tokenizer.val)); + // item = { + // typ: tokenizer.typ as EnumToken, + // val: tokenizer.val, + // } as Token; + } + arena.setSpan(tokenId, tokenizer.srcId, tokenizer.sta, tokenizer.end); + // item[LOCSRCID] = tokenizer.srcId as number; + // item[LOCSTA] = tokenizer.sta as number; + // item[LOCEND] = tokenizer.end as number; + tokens.push(tokenId); + if (tokenizer.typ === EnumToken.BlockStartTokenType) { inBlock++; } - else if (item.typ === EnumToken.BlockEndTokenType) { + else if (tokenizer.typ === EnumToken.BlockEndTokenType) { inBlock--; } } while (inBlock != 0 && !tokenizer.done()); @@ -640,22 +658,22 @@ function doParseSync(tokenizer, options = {}) { errors.push({ action: "drop", message: "invalid block", - location: options.source.getSourceLocation(tokens[0][LOCSTA]), + location: options.source.getSourceLocation(tokenizer.sta), }); } } tokens.length = 0; } - else if ((parensMatch === 0 || curlyBracketMatch === 0) && item.typ === EnumToken.BlockEndTokenType) { + else if ((parensMatch === 0 || curlyBracketMatch === 0) && tokenizer.typ === EnumToken.BlockEndTokenType) { parseNode(tokens, context, options, errors, stats, invalidNodes); - context[LOCEND] = item[LOCEND]; + // context[LOCEND] = tokenizer.end as number; const previousNode = stack.pop(); - context = (stack[stack.length - 1] ?? ast); + context = stack[stack.length - 1] ?? arena.root; if (options.removeEmpty && previousNode != null && - previousNode.chi.length == 0 && - context.chi[context.chi.length - 1] == previousNode) { - context.chi.pop(); + arena.getFirstChild(previousNode) == 0 && + arena.getLastChild(context) == previousNode) { + arena.detach(previousNode); } tokens.length = 0; parensMatch = 0; @@ -666,7 +684,7 @@ function doParseSync(tokenizer, options = {}) { if (tokens.length > 0) { node = parseNode(tokens, context, options, errors, stats, invalidNodes); if (node != null) { - if ("chi" in node /* && node.typ != EnumToken.InvalidRuleNodeType */) { + if (arena.hasFlags(node, EnumAstNodeStatus.HasChildren)) { stack.push(node); context = node; } @@ -872,6 +890,7 @@ function doParseSync(tokenizer, options = {}) { stats.bytesIn += stats.importedBytesIn; let endTime = performance.now(); const result = { + arena, ast, errors, stats: { @@ -1310,7 +1329,7 @@ function doParseSync(tokenizer, options = {}) { } node.sel = ""; for (const token of node[TOKENS]) { - node.sel += renderValue(token); + node.sel += printValue(token); } } else if (node.typ == EnumToken.AtRuleNodeType || node.typ == EnumToken.KeyframesAtRuleNodeType) { @@ -2416,7 +2435,7 @@ async function doParse(iter, options = {}) { } node.sel = ""; for (const token of node[TOKENS]) { - node.sel += renderValue(token); + node.sel += printValue(token); } } else if (node.typ == EnumToken.AtRuleNodeType || node.typ == EnumToken.KeyframesAtRuleNodeType) { @@ -2475,104 +2494,128 @@ async function doParse(iter, options = {}) { } function parseNode(tokens, context, options, errors, stats, invalidNodes) { let i = 0; - if (tokens.at(-1)?.typ === EnumToken.EOFTokenType) { + const arena = options.arena; + const contextType = arena.getType(context); + let token; + let typ; + if (arena.getType(tokens.at(-1)) === EnumToken.EOFTokenType) { tokens.pop(); // check parenthesis are balanced let matchCount = 0; for (let i = 0; i < tokens.length; i++) { - const token = tokens[i]; - if (token.typ === EnumToken.StartParensTokenType || tokensfuncDefMap.has(token.typ)) { + token = tokens[i]; + typ = arena.getType(token); + if (typ === EnumToken.StartParensTokenType || tokensfuncDefMap.has(typ)) { matchCount++; } - else if (token.typ === EnumToken.EndParensTokenType) { + else if (typ === EnumToken.EndParensTokenType) { matchCount--; } } if (matchCount > 0) { let k = tokens.length; - while (k-- > 0 && - (tokens[k].typ === EnumToken.WhitespaceTokenType || tokens[k].typ === EnumToken.CommentTokenType)) - ; - if (tokens[k]?.typ == EnumToken.SemiColonTokenType) { - matchCount = 0; - } - while (matchCount > 0) { - tokens.push({ - typ: EnumToken.EndParensTokenType, - [LOCSRCID]: tokens[k]?.[LOCSRCID], - [LOCSTA]: tokens[k]?.[LOCSTA], - [LOCEND]: tokens[k]?.[LOCEND], - }); - matchCount--; + if (k > 0) { + typ = arena.getType(tokens[k - 1]); + while ((k-- > 0 && typ === EnumToken.WhitespaceTokenType) || typ === EnumToken.CommentTokenType) { + typ = arena.getType(tokens[k - 1]); + } + if (typ == EnumToken.SemiColonTokenType) { + matchCount = 0; + } + let tokenId; + let sourceId = arena.getSourceId(tokens[k]); + let spanStart = arena.getSpanStart(tokens[k]); + let spanEnd = arena.getSpanEnd(tokens[k]); + while (matchCount > 0) { + tokenId = arena.allocate(); + arena.set(tokenId, EnumToken.EndParensTokenType); + arena.setSpan(tokenId, sourceId, spanStart, spanEnd); + // tokens.push({ + // typ: EnumToken.EndParensTokenType, + // [LOCSRCID]: tokens[k]?.[LOCSRCID], + // [LOCSTA]: tokens[k]?.[LOCSTA], + // [LOCEND]: tokens[k]?.[LOCEND], + // }); + matchCount--; + } } } } for (; i < tokens.length; i++) { - if (tokens[i].typ === EnumToken.CDOCOMMTokenType && context.typ !== EnumToken.StyleSheetNodeType) { + if (arena.getType(tokens[i]) === EnumToken.CDOCOMMTokenType && contextType !== EnumToken.StyleSheetNodeType) { errors.push({ action: "drop", message: `CDOCOMM not allowed here ${JSON.stringify(tokens[i], null, 1)}`, node: tokens[i], - location: options.source.getSourceLocation(tokens[i][LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(tokens[i])), }); - tokens[i].typ = EnumToken.InvalidCommentTokenType; + arena.set(tokens[i], EnumToken.InvalidCommentTokenType); continue; } - if (tokens[i].typ === EnumToken.CommentTokenType || - tokens[i].typ === EnumToken.CDOCOMMTokenType || - tokens[i].typ === EnumToken.WhitespaceTokenType) { + typ = arena.getType(tokens[i]); + if (typ === EnumToken.CommentTokenType || + typ === EnumToken.CDOCOMMTokenType || + typ === EnumToken.WhitespaceTokenType) { continue; } break; } if (i > 0) { - context.chi.push(...tokens.splice(0, i).filter((n) => n.typ !== EnumToken.WhitespaceTokenType)); + arena.appendChildren(context, ...tokens.splice(0, i).filter((n) => arena.getType(n) !== EnumToken.WhitespaceTokenType)); i = 0; } for (; i < tokens.length; i++) { - if (tokens[i].typ == EnumToken.CommentTokenType || tokens[i].typ == EnumToken.CDOCOMMTokenType) { - if (tokens[i].typ == EnumToken.CDOCOMMTokenType && context.typ != EnumToken.StyleSheetNodeType) { + typ = arena.getType(tokens[i]); + if (typ == EnumToken.CommentTokenType || typ == EnumToken.CDOCOMMTokenType) { + if (typ == EnumToken.CDOCOMMTokenType && contextType != EnumToken.StyleSheetNodeType) { errors.push({ action: "drop", message: `CDOCOMM not allowed here ${JSON.stringify(tokens[i], null, 1)}`, node: tokens[i], - location: options.source.getSourceLocation(tokens[i][LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(tokens[i])), }); - tokens[i].typ = EnumToken.InvalidCommentTokenType; + arena.setType(tokens[i], EnumToken.InvalidCommentTokenType); continue; } - tokens[i][ROOT] = context[ROOT]; - context.chi.push(tokens[i]); + // tokens[i][ROOT] = context[ROOT]; + arena.appendChildren(context, tokens[i]); + // context.chi!.push(tokens[i] as AstNode); stats.nodesCount++; } - else if (tokens[i].typ != EnumToken.WhitespaceTokenType) { + else if (typ != EnumToken.WhitespaceTokenType) { break; } } if (tokens.length == 0) { return null; } + // for (const token of tokens.slice(1)) { + // console.error(EnumToken[arena.getType(token)]); + // } let delim = tokens.at(-1); - if (delim.typ == EnumToken.SemiColonTokenType || - delim.typ == EnumToken.BlockStartTokenType || - delim.typ == EnumToken.BlockEndTokenType) { + typ = arena.getType(delim); + if (typ == EnumToken.SemiColonTokenType || + typ == EnumToken.BlockStartTokenType || + typ == EnumToken.BlockEndTokenType) { tokens.pop(); } if (tokens.length == 0) { return null; } - if (tokens[0]?.typ == EnumToken.AtRuleTokenType) { + typ = arena.getType(tokens[0]); + if (typ == EnumToken.AtRuleTokenType) { let nestedRule = false; let parent = context; let node; - while (parent != null) { - if (parent.typ == EnumToken.RuleNodeType) { + while (parent != 0) { + typ = arena.getType(parent); + if (typ == EnumToken.RuleNodeType) { nestedRule = true; break; } - parent = parent[PARENT]; + parent = arena.getParent(parent); } - node = parseAtRule(tokens, context, { ...options, nestedRule }, errors, delim.typ == EnumToken.BlockStartTokenType); + node = parseAtRule(tokens, context, { ...options, nestedRule }, errors, arena.getType(delim) == EnumToken.BlockStartTokenType); if (node == null) { return null; } @@ -2585,46 +2628,48 @@ function parseNode(tokens, context, options, errors, stats, invalidNodes) { invalidNodes.push(node); } stats.nodesCount++; - context.chi.push(node); - node[ROOT] = context[ROOT]; - node[PARENT] = context; + arena.appendChildren(context, node); + // context.chi!.push(node); + // arena.setParent(node, context); + // node[ROOT] = context[ROOT]; + // node[PARENT] = context; // @ts-ignore return node; } else { stats.nodesCount++; // rule - if (delim.typ == EnumToken.BlockStartTokenType) { + if (arena.getType(delim) == EnumToken.BlockStartTokenType) { const node = parseSelector(tokens, context, options, errors); - context.chi.push(node); - node[PARENT] = context; - node[ROOT] = context[ROOT]; - if (node[STATE] & EnumAstNodeStatus.Invalid || - node[STATE] & EnumAstNodeStatus.Disallowed || - node[STATE] & EnumAstNodeStatus.Unknown || - node[STATE] & EnumAstNodeStatus.Unparsed || - node[STATE] & EnumAstNodeStatus.Malformed) { + arena.appendChildren(context, node); + // context.chi!.push(node); + // node[PARENT] = context; + // node[ROOT] = context[ROOT]; + if (arena.hasFlags(node, EnumAstNodeStatus.Invalid | + EnumAstNodeStatus.Disallowed | + EnumAstNodeStatus.Unknown | + EnumAstNodeStatus.Unparsed | + EnumAstNodeStatus.Malformed)) { invalidNodes.push(node); } return node; } else { const node = parseDeclaration(tokens, context, options, errors); - node[PARENT] = context; - node[ROOT] = context[ROOT]; - if (context.typ === EnumToken.StyleSheetNodeType && node.typ === EnumToken.DeclarationNodeType) { - node[STATE] |= - EnumAstNodeStatus.Invalid & - ~(EnumAstNodeStatus.Validated | EnumAstNodeStatus.Unvalidated | EnumAstNodeStatus.Unparsed); + // node[PARENT] = context; + // node[ROOT] = context[ROOT]; + if (contextType === EnumToken.StyleSheetNodeType && node.typ === EnumToken.DeclarationNodeType) { + node[STATE] = EnumAstNodeStatus.Invalid; errors.push({ message: " not allowed in ", action: "drop", node, - location: options.source.getSourceLocation(node[LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(node)), }); } else if (options.lenient || node.typ === EnumToken.DeclarationNodeType) { - context.chi.push(node); + // context.chi!.push(node); + arena.appendChildren(context, node); } if (node[STATE] & (EnumAstNodeStatus.Invalid | @@ -2959,7 +3004,7 @@ function parseAtRule(stream, context, options, errors, parseAsBlock = null) { stream[index + 1]?.typ === EnumToken.CommentTokenType && (stream.length < index + 3 || stream[index + 2]?.typ === EnumToken.WhitespaceTokenType)) ? "" - : renderValue(t, options)), ""), + : printValue(t, options)), ""), ...(parseAsBlock ? { chi: [] } : {}), }); } @@ -2989,7 +3034,7 @@ function parseAtRule(stream, context, options, errors, parseAsBlock = null) { stream[index + 1]?.typ === EnumToken.CommentTokenType && (stream.length < index + 3 || stream[index + 2]?.typ === EnumToken.WhitespaceTokenType)) ? "" - : renderValue(t, options)), ""), + : printValue(t, options)), ""), }); } case "supports": diff --git a/dist/src/lib/parser/source.js b/dist/src/lib/parser/source.js index a3dab5029..8f876a813 100644 --- a/dist/src/lib/parser/source.js +++ b/dist/src/lib/parser/source.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { SourceMap } from '../printer/sourcemap/sourcemap.js'; diff --git a/dist/src/lib/parser/tokenize.js b/dist/src/lib/parser/tokenize.js index 5f1e35c77..e73b46c4c 100644 --- a/dist/src/lib/parser/tokenize.js +++ b/dist/src/lib/parser/tokenize.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { EnumToken, ColorType } from '../ast/types.js'; diff --git a/dist/src/lib/parser/utils/angle.js b/dist/src/lib/parser/utils/angle.js index 33cc924be..896b99ce9 100644 --- a/dist/src/lib/parser/utils/angle.js +++ b/dist/src/lib/parser/utils/angle.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ /** diff --git a/dist/src/lib/parser/utils/at-rule-container.js b/dist/src/lib/parser/utils/at-rule-container.js index 2aa750fc3..039edb78b 100644 --- a/dist/src/lib/parser/utils/at-rule-container.js +++ b/dist/src/lib/parser/utils/at-rule-container.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { EnumToken } from '../../ast/types.js'; diff --git a/dist/src/lib/parser/utils/at-rule-font-feature-values.js b/dist/src/lib/parser/utils/at-rule-font-feature-values.js index 02f9d5e80..7db11c446 100644 --- a/dist/src/lib/parser/utils/at-rule-font-feature-values.js +++ b/dist/src/lib/parser/utils/at-rule-font-feature-values.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { getSyntaxRule } from '../../validation/config.js'; diff --git a/dist/src/lib/parser/utils/at-rule-generic.js b/dist/src/lib/parser/utils/at-rule-generic.js index f5875a679..cb4257746 100644 --- a/dist/src/lib/parser/utils/at-rule-generic.js +++ b/dist/src/lib/parser/utils/at-rule-generic.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { EnumToken } from '../../ast/types.js'; diff --git a/dist/src/lib/parser/utils/at-rule-import.js b/dist/src/lib/parser/utils/at-rule-import.js index a4d591c6d..169f93bfd 100644 --- a/dist/src/lib/parser/utils/at-rule-import.js +++ b/dist/src/lib/parser/utils/at-rule-import.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { EnumToken } from '../../ast/types.js'; diff --git a/dist/src/lib/parser/utils/at-rule-media.js b/dist/src/lib/parser/utils/at-rule-media.js index c1ea6bba9..eff4c1bc4 100644 --- a/dist/src/lib/parser/utils/at-rule-media.js +++ b/dist/src/lib/parser/utils/at-rule-media.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { EnumToken } from '../../ast/types.js'; diff --git a/dist/src/lib/parser/utils/at-rule-support.js b/dist/src/lib/parser/utils/at-rule-support.js index b4a3092dc..75edc317a 100644 --- a/dist/src/lib/parser/utils/at-rule-support.js +++ b/dist/src/lib/parser/utils/at-rule-support.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { EnumToken } from '../../ast/types.js'; diff --git a/dist/src/lib/parser/utils/at-rule-when-else.js b/dist/src/lib/parser/utils/at-rule-when-else.js index 1aa91c91f..587afa129 100644 --- a/dist/src/lib/parser/utils/at-rule-when-else.js +++ b/dist/src/lib/parser/utils/at-rule-when-else.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { EnumToken } from '../../ast/types.js'; diff --git a/dist/src/lib/parser/utils/at-rule.js b/dist/src/lib/parser/utils/at-rule.js index 30afaff93..f617d34de 100644 --- a/dist/src/lib/parser/utils/at-rule.js +++ b/dist/src/lib/parser/utils/at-rule.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { ValidationSyntaxGroupEnum } from '../../validation/parser/typedef.js'; diff --git a/dist/src/lib/parser/utils/base64.js b/dist/src/lib/parser/utils/base64.js index bc2cbc1b2..c2c42a423 100644 --- a/dist/src/lib/parser/utils/base64.js +++ b/dist/src/lib/parser/utils/base64.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ /** diff --git a/dist/src/lib/parser/utils/cache.js b/dist/src/lib/parser/utils/cache.js index 51dd6d729..767182620 100644 --- a/dist/src/lib/parser/utils/cache.js +++ b/dist/src/lib/parser/utils/cache.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ /** diff --git a/dist/src/lib/parser/utils/config.js b/dist/src/lib/parser/utils/config.js index 461a8caad..68e9b684c 100644 --- a/dist/src/lib/parser/utils/config.js +++ b/dist/src/lib/parser/utils/config.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import config from '../../../data/properties.json.js'; diff --git a/dist/src/lib/parser/utils/declaration.js b/dist/src/lib/parser/utils/declaration.js index e350b7e6e..318840104 100644 --- a/dist/src/lib/parser/utils/declaration.js +++ b/dist/src/lib/parser/utils/declaration.js @@ -2,18 +2,15 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { EnumToken, EnumAstNodeStatus, ColorType } from '../../ast/types.js'; -import { LOCEND, STATE, ERRORS, LOCSTA, tokensfuncDefMap, COLORS_NAMES, nonStandardColors, systemColors, deprecatedSystemColors, tokensMap, trimTokenSpace, LOCSRCID } from '../../syntax/constants.js'; +import { tokensfuncDefMap, COLORS_NAMES, nonStandardColors, systemColors, deprecatedSystemColors, tokensMap, trimTokenSpace } from '../../syntax/constants.js'; import { renamedStandardProperties, isColor, parseColor, isWhiteSpace } from '../../syntax/syntax.js'; import { getSyntaxRule, getParsedSyntax } from '../../validation/config.js'; import { trimArray, matchAllSyntaxes, createValidationContext } from '../../validation/match.js'; import { ValidationSyntaxGroupEnum, ValidationTokenEnum } from '../../validation/parser/typedef.js'; -import { walkValues } from '../../ast/walk.js'; -import { equalsIgnoreCase } from './text.js'; -import { buildExpression } from '../../ast/math/expression.js'; import { splitTokenList } from '../../validation/utils/list.js'; /** @@ -58,69 +55,91 @@ function parseGridTemplate(template) { */ function parseDeclaration(tokens, parent, options, errors) { const name = tokens.shift(); + const arena = options.arena; let i; let rules = null; let syntaxRules = null; let success = true; + let val = arena.getValue(name); + let token; + let typ; let hasImportantFlag = false; let validate = typeof options.validation === "boolean" ? options.validation : !!options.validation; - if (renamedStandardProperties.has(name.val)) { - name.val = renamedStandardProperties.get(name.val); + if (renamedStandardProperties.has(val)) { + arena.setValue(name, arena.internString(renamedStandardProperties.get(val))); } for (i = 0; i < tokens.length; i++) { - const token = tokens[i]; - if (token.typ == EnumToken.WhitespaceTokenType || - token.typ == EnumToken.CommentTokenType || - token.typ == EnumToken.InvalidCommentTokenType) { + token = tokens[i]; + typ = arena.getType(token); + if (typ == EnumToken.WhitespaceTokenType || + typ == EnumToken.CommentTokenType || + typ == EnumToken.InvalidCommentTokenType) { continue; } break; } - if ((name.typ !== EnumToken.IdenTokenType && name.typ !== EnumToken.DashedIdenTokenType) || - tokens[i]?.typ !== EnumToken.ColonTokenType) { - if (tokens[tokens.length - 1]?.[LOCEND] != null) { - name[LOCEND] = tokens[tokens.length - 1]?.[LOCEND]; - } - name[STATE] = EnumAstNodeStatus.Unparsed; - name[ERRORS] = [ + typ = arena.getType(name); + if ((typ !== EnumToken.IdenTokenType && typ !== EnumToken.DashedIdenTokenType) || + arena.getType(tokens[i]) !== EnumToken.ColonTokenType) { + // if (tokens[tokens.length - 1]?.[LOCEND] != null) { + // name[LOCEND] = tokens[tokens.length - 1]?.[LOCEND]; + // } + const node = arena.allocate(); + arena.setType(node, EnumToken.RawNodeTokenType); + arena.setFlags(node, EnumAstNodeStatus.Unparsed); + arena.setErrors(node, [ { action: "drop", node: name, - location: options.source.getSourceLocation(name[LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(name)), message: "invalid declaration", }, - ]; - return Object.assign({ - typ: EnumToken.RawNodeTokenType, - val: tokens, - }); + ]); + arena.setTokens(node, ...tokens); + arena.setSpan(node, arena.getSourceId(name), arena.getSpanStart(name), arena.getSpanEnd(tokens.at(-1) ?? name)); + return node; + // name[STATE] = EnumAstNodeStatus.Unparsed; + // name[ERRORS] = [ + // { + // action: "drop", + // node: name, + // location: options.source!.getSourceLocation(name[LOCSTA]!), + // message: "invalid declaration", + // } as ErrorDescription, + // ]; + // return Object.assign({ + // typ: EnumToken.RawNodeTokenType, + // val: tokens, + // }) as RawNodeToken; } - tokens = trimArray(tokens.slice(i + 1)); - if (validate && name.typ === EnumToken.IdenTokenType) { + tokens = trimArray(tokens.slice(i + 1), options); + if (validate && typ === EnumToken.IdenTokenType) { + typ = arena.getType(parent); + val = arena.getValue(parent).toLowerCase(); if (parent != null && - parent.typ === EnumToken.AtRuleNodeType && - parent.nam != "import" && - parent.nam != "supports" && - parent.nam != "media") { + typ === EnumToken.AtRuleNodeType && + val != "import" && + val != "supports" && + val != "media") { // check @at-rule description // pick up the syntax or reject immediately // else do this - rules = getSyntaxRule(ValidationSyntaxGroupEnum.AtRules, "@" + parent.nam); + rules = getSyntaxRule(ValidationSyntaxGroupEnum.AtRules, "@" + val); if (rules != null) { const propertyDescriptors = rules.getPropertyDescriptors(); if (propertyDescriptors != null) { - syntaxRules = propertyDescriptors[name.val.toLowerCase()]; + syntaxRules = propertyDescriptors[val.toLowerCase()]; } else { syntaxRules = rules.acceptAnyDeclaration && rules.acceptAnyRule - ? getParsedSyntax(ValidationSyntaxGroupEnum.Declarations, name.val.toLowerCase()) + ? getParsedSyntax(ValidationSyntaxGroupEnum.Declarations, val) : rules.getBlockRules(); } } } else { - syntaxRules = getParsedSyntax(ValidationSyntaxGroupEnum.Declarations, name.val.toLowerCase()); + syntaxRules = getParsedSyntax(ValidationSyntaxGroupEnum.Declarations, val); } // or // else @@ -129,61 +148,73 @@ function parseDeclaration(tokens, parent, options, errors) { syntaxRules[0].typ === ValidationTokenEnum.PropertyType && (syntaxRules[0].val === "declaration-list" || syntaxRules[0].val === "declaration-rule-list"))) { - if (parent?.typ === EnumToken.AtRuleNodeType && - (parent.nam === "swash" || - parent.nam === "annotation" || - parent.nam === "ornaments" || - parent.nam === "stylistic" || - parent.nam === "historical-forms")) { + typ = arena.getType(parent); + val = arena.getValue(parent).toLowerCase(); + if (parent != null && + typ === EnumToken.AtRuleNodeType && + (val === "swash" || + val === "annotation" || + val === "ornaments" || + val === "stylistic" || + val === "historical-forms")) { syntaxRules = getParsedSyntax(ValidationSyntaxGroupEnum.Syntaxes, "font-feature-custom-ident"); } - else if (parent?.typ === EnumToken.AtRuleNodeType && parent.nam === "styleset") { + else if (typ === EnumToken.AtRuleNodeType && val === "styleset") { syntaxRules = getParsedSyntax(ValidationSyntaxGroupEnum.Syntaxes, "font-feature-custom-ident-list"); } - else if (parent?.typ === EnumToken.AtRuleNodeType && parent.nam === "character-variant") { + else if (typ === EnumToken.AtRuleNodeType && val === "character-variant") { syntaxRules = getParsedSyntax(ValidationSyntaxGroupEnum.Syntaxes, "font-feature-custom-ident-character-variant"); } else { - syntaxRules = getParsedSyntax(ValidationSyntaxGroupEnum.Declarations, name.val.toLowerCase()); + syntaxRules = getParsedSyntax(ValidationSyntaxGroupEnum.Declarations, val); } } } - if (tokens.length === 0 && name.typ !== EnumToken.DashedIdenTokenType) { + typ = arena.getType(name); + if (tokens.length === 0 && typ !== EnumToken.DashedIdenTokenType) { errors.push({ action: "drop", message: "declaration value missing", node: name, - location: options.source.getSourceLocation(name[LOCSTA]), - }); - if (tokens[tokens.length - 1]?.[LOCEND] != null) { - name[LOCEND] = tokens[tokens.length - 1][LOCEND]; - } - name[STATE] = EnumAstNodeStatus.Invalid; - name[ERRORS] = [errors[errors.length - 1]]; - // @ts-expect-error - return Object.assign(name, { - typ: EnumToken.DeclarationNodeType, - nam: name.val, - val: tokens, + location: options.source.getSourceLocation(arena.getSpanStart(name)), }); + // if (tokens[tokens.length - 1]?.[LOCEND] != null) { + // name[LOCEND] = tokens[tokens.length - 1][LOCEND]; + // } + arena.setFlags(name, EnumAstNodeStatus.Invalid | EnumAstNodeStatus.HasValues); + arena.setErrors(name, [errors[errors.length - 1]]); + arena.setName(name, arena.internString(arena.getValue(name))); + arena.set(name, EnumToken.DeclarationNodeType, 0); + arena.setChildren(name, ...tokens); + return name; + // name[STATE] = EnumAstNodeStatus.Invalid; + // name[ERRORS] = [errors[errors.length - 1]]; + // return Object.assign(name, { + // typ: EnumToken.DeclarationNodeType, + // nam: name.val, + // val: tokens, + // }) as AstDeclaration; } const stack = []; let result = null; - let token; + // let token: Token; let index; if (syntaxRules != null) { const doNotValidate = options.validation === false; - result = doNotValidate ? null : matchAllSyntaxes(syntaxRules, createValidationContext(tokens), options); + result = doNotValidate + ? null + : matchAllSyntaxes(syntaxRules, createValidationContext(tokens, options), options); if (doNotValidate || result != null) { success = doNotValidate || result?.success; if (success) { - for (const { value } of walkValues(tokens)) { - if (value.typ === EnumToken.IdenTokenType && isColor(value)) { - parseColor(value); + for (const value of arena.walkValues(tokens)) { + typ = arena.getType(value); + if (typ === EnumToken.IdenTokenType && isColor(value, arena)) { + parseColor(value, arena); } - else if (value.typ === EnumToken.ColorTokenType) { - if (isColor(value)) { - parseColor(value); + else if (typ === EnumToken.ColorTokenType) { + if (isColor(value, arena)) { + parseColor(value, arena); } } } @@ -197,66 +228,83 @@ function parseDeclaration(tokens, parent, options, errors) { } for (i = 0; i < tokens.length; i++) { token = tokens[i]; - if (tokensfuncDefMap.has(token.typ) || - token.typ == EnumToken.StartParensTokenType || - token.typ == EnumToken.BlockStartTokenType) { + typ = arena.getType(token); + val = arena.getValue(token); + if (tokensfuncDefMap.has(typ) || + typ == EnumToken.StartParensTokenType || + typ == EnumToken.BlockStartTokenType) { stack.push(token); continue; } - if (token.typ === EnumToken.ColonTokenType && stack.length == 0) { + if (typ === EnumToken.ColonTokenType && stack.length == 0) { stack.push(token); continue; } - switch (token.typ) { + const stackType = arena.getType(stack.at(-1)); + switch (typ) { case EnumToken.Literal: - if (token.val === "/" && stack.at(-1)?.typ == EnumToken.MathFunctionTokenDefType) { - Object.assign(token, { - typ: EnumToken.Div, - }); + if (arena.equalsIgnoreCase(token, "val", "/") && stackType == EnumToken.MathFunctionTokenDefType) { + arena.setType(token, EnumToken.Div); + // Object.assign(token, { + // typ: EnumToken.Div, + // }); } - else if (token.val === "-" && - stack.at(-1)?.typ == EnumToken.MathFunctionTokenDefType) { - Object.assign(token, { - typ: EnumToken.Sub, - }); + else if (arena.equalsIgnoreCase(token, "val", "-") && + stackType == EnumToken.MathFunctionTokenDefType) { + arena.setType(token, EnumToken.Sub); + // Object.assign(token, { + // typ: EnumToken.Sub, + // }); } break; case EnumToken.Star: case EnumToken.Plus: - Object.assign(token, { - typ: tokensMap.get(token.typ), - }); + arena.setType(token, tokensMap.get(typ)); + // Object.assign(token, { + // typ: tokensMap.get(typ), + // }); break; case EnumToken.BlockEndTokenType: - if (stack.at(-1)?.typ === EnumToken.BlockStartTokenType) { + if (stackType === EnumToken.BlockStartTokenType) { // stack.pop();index = tokens.indexOf(stack.at(-1)!); index = tokens.indexOf(stack.at(-1)); tokens.splice(i, 1); - Object.assign(tokens[index], { - typ: EnumToken.WrappedValuesTokenType, - chi: trimArray(tokens.splice(index + 1, i - index - 1)), - }); + arena.setType(tokens[index], EnumToken.WrappedValuesTokenType); + arena.setChildren(tokens[index], ...trimArray(tokens.splice(index + 1, i - index - 1), options)); + // Object.assign(tokens[index], { + // typ: EnumToken.WrappedValuesTokenType, + // chi: trimArray(tokens.splice(index + 1, i - index - 1), options), + // }); i = index; stack.pop(); } break; case EnumToken.EndParensTokenType: - if (stack.at(-1)?.typ === EnumToken.StartParensTokenType || tokensfuncDefMap.has(stack.at(-1)?.typ)) { + if (stackType === EnumToken.StartParensTokenType || tokensfuncDefMap.has(stackType)) { index = tokens.indexOf(stack.at(-1)); tokens.splice(i, 1); - Object.assign(tokens[index], { - typ: stack.at(-1)?.typ === EnumToken.StartParensTokenType - ? EnumToken.ParensTokenType - : tokensfuncDefMap.get(stack.at(-1)?.typ), - chi: trimArray(tokens.splice(index + 1, i - index - 1)), - }); - if (tokens[index].typ === EnumToken.WildCardFunctionTokenType && - equalsIgnoreCase(tokens[index].val, "if")) { - if (tokens[index].chi.at(-1)?.typ === EnumToken.SemiColonTokenType) { - tokens[index].chi.pop(); + arena.setType(tokens[index], stackType === EnumToken.StartParensTokenType + ? EnumToken.ParensTokenType + : tokensfuncDefMap.get(stackType)); + arena.setChildren(tokens[index], ...trimArray(tokens.splice(index + 1, i - index - 1), options)); + // Object.assign(tokens[index], { + // typ: + // stack.at(-1)?.typ === EnumToken.StartParensTokenType + // ? EnumToken.ParensTokenType + // : (tokensfuncDefMap.get(stack.at(-1)?.typ) as EnumToken), + // chi: trimArray(tokens.splice(index + 1, i - index - 1)), + // }); + if (arena.getType(tokens[index]) === EnumToken.WildCardFunctionTokenType && + arena.equalsIgnoreCase(tokens[index], "val", "if")) { + const lastChild = arena.getLastChild(tokens[index]); + if (lastChild != 0 && arena.getType(lastChild) === EnumToken.SemiColonTokenType) { + arena.detach(lastChild); } + // if ((tokens[index] as FunctionToken).chi.at(-1)?.typ === EnumToken.SemiColonTokenType) { + // (tokens[index] as FunctionToken).chi.pop(); + // } let foundElse = false; - tokens[index].chi = splitTokenList(tokens[index].chi, [EnumToken.SemiColonTokenType], true).reduce((acc, curr) => { + splitTokenList(arena.children(tokens[index]), options, [EnumToken.SemiColonTokenType], true).reduce((acc, curr) => { if (foundElse) { // else already found // ignore everything after else @@ -264,70 +312,107 @@ function parseDeclaration(tokens, parent, options, errors) { return acc; } let index = -1; + let typ = arena.getType(curr[index]); while (++index < curr.length) { - if (curr[index].typ == EnumToken.IdenTokenType && - equalsIgnoreCase("else", curr[index].val)) { + if (typ == EnumToken.IdenTokenType && + arena.equalsIgnoreCase(curr[index], "val", "else")) { foundElse = true; } - if (curr[index].typ === EnumToken.ColonTokenType) { + if (typ === EnumToken.ColonTokenType) { break; } } if (foundElse && acc.length > 0) { - acc[acc.length - 1] = { - typ: EnumToken.IfElseConditionTokenType, - l: acc[acc.length - 1], - r: { - typ: EnumToken.IfConditionTokenType, - l: trimArray(curr.slice(0, index)), - r: trimArray(curr.at(-1)?.typ === EnumToken.SemiColonTokenType - ? curr.slice(index + 1, -1) - : curr.slice(index + 1)), - }, - }; + const leftChild = arena.allocate(); + arena.setType(leftChild, EnumToken.ListTokenType); + arena.setChildren(leftChild, ...trimArray(curr.slice(0, index), options)); + const rightChild = arena.allocate(); + arena.setType(rightChild, EnumToken.ListTokenType); + arena.setChildren(rightChild, ...trimArray(arena.getType(curr.at(-1)) === EnumToken.SemiColonTokenType + ? curr.slice(index + 1, -1) + : curr.slice(index + 1), options)); + const ifCondition = arena.allocate(); + arena.setType(ifCondition, EnumToken.IfConditionTokenType); + arena.setLeft(ifCondition, leftChild); + arena.setRight(ifCondition, rightChild); + const ifElseLeftCondition = arena.allocate(); + arena.setType(ifElseLeftCondition, EnumToken.IfElseConditionTokenType); + arena.setLeft(ifElseLeftCondition, acc[acc.length - 1]); + arena.setRight(ifElseLeftCondition, ifCondition); + acc[acc.length - 1] = ifElseLeftCondition; + // acc[acc.length - 1] = { + // typ: EnumToken.IfElseConditionTokenType, + // l: acc[acc.length - 1] as IfConditionToken, + // r: { + // typ: EnumToken.IfConditionTokenType, + // l: trimArray(curr.slice(0, index)), + // r: trimArray( + // curr.at(-1)?.typ === EnumToken.SemiColonTokenType + // ? curr.slice(index + 1, -1) + // : curr.slice(index + 1), + // ), + // } as IfConditionToken, + // } as IfElseConditionToken; } else { - acc.push({ - typ: EnumToken.IfConditionTokenType, - l: trimArray(curr.slice(0, index)), - r: trimArray(curr.slice(index + 1)), - }); + const ifCondition = arena.allocate(); + arena.setType(ifCondition, EnumToken.IfConditionTokenType); + const leftChild = arena.allocate(); + arena.setType(leftChild, EnumToken.ListTokenType); + arena.setChildren(leftChild, ...trimArray(curr.slice(0, index), options)); + const rightChild = arena.allocate(); + arena.setType(rightChild, EnumToken.ListTokenType); + arena.setChildren(rightChild, ...trimArray(curr.slice(index + 1), options)); + arena.setLeft(ifCondition, leftChild); + arena.setRight(ifCondition, rightChild); + acc.push(ifCondition); + // acc.push({ + // typ: EnumToken.IfConditionTokenType, + // l: trimArray(curr.slice(0, index)), + // r: trimArray(curr.slice(index + 1)), + // } as IfConditionToken); } return acc; }, []); } - else if (tokens[index].typ === EnumToken.UrlFunctionTokenType) { + else if (arena.getType(tokens[index]) === EnumToken.UrlFunctionTokenType) { let l = -1; - while (++l < tokens[index].chi.length) { - if (tokens[index].chi[l].typ === EnumToken.StringTokenType) { + const children = arena.children(tokens[index]); + while (++l < children.length) { + if (arena.getType(children[l]) === EnumToken.StringTokenType) { break; } } - if (tokens[index].chi[l]?.typ === EnumToken.StringTokenType && - /^[a-zA-Z0-0/_.-]+$/.test(tokens[index].chi[l].val.slice(1, -1))) { - Object.assign(tokens[index].chi[l], { - typ: EnumToken.UrlTokenTokenType, - val: tokens[index].chi[l].val.slice(1, -1), - }); + if (l < children.length && arena.getType(children[l]) === EnumToken.StringTokenType) { + const val = arena.getValue(children[l]).slice(1, -1); + if (/^[a-zA-Z0-0/_.-]+$/.test(val)) { + arena.setValue(children[l], arena.internString(val)); + arena.setType(children[l], EnumToken.UrlTokenTokenType); + // Object.assign((tokens[index] as FunctionToken).chi[l], { + // typ: EnumToken.UrlTokenTokenType, + // val: ((tokens[index] as FunctionToken).chi[l] as StringToken).val.slice(1, -1), + // }); + } } } i = index; - if (tokens[index].typ === EnumToken.MathFunctionTokenType && - equalsIgnoreCase("calc", tokens[index].val)) { - tokens[index].chi = [buildExpression(tokens[index].chi)]; + if (arena.getType(tokens[index]) === EnumToken.MathFunctionTokenType && + arena.equalsIgnoreCase(tokens[index], "val", "calc")) { + arena.setChildren(tokens[index], ...arena.children(tokens[index])); + // (tokens[index] as FunctionToken).chi = [buildExpression((tokens[index] as FunctionToken).chi)]; } - else if (tokens[index].typ === EnumToken.ColorTokenType) { - if (isColor(tokens[index])) { - parseColor(tokens[index]); + else if (arena.getType(tokens[index]) === EnumToken.ColorTokenType) { + if (isColor(tokens[index], arena)) { + parseColor(tokens[index], arena); } else { success = false; - tokens[index].typ = EnumToken.FunctionTokenType; + arena.setType(tokens[index], EnumToken.FunctionTokenType); errors.push({ action: "drop", message: `invalid color`, node: tokens[index], - location: options.source.getSourceLocation(tokens[index][LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(tokens[index])), }); } } @@ -336,31 +421,39 @@ function parseDeclaration(tokens, parent, options, errors) { break; case EnumToken.IdenTokenType: { - const val = token.val.toLowerCase(); - if (val in COLORS_NAMES || val === "currentcolor") { - Object.assign(token, { - val, - typ: EnumToken.ColorTokenType, - kin: ColorType.LIT, - }); + const val = arena.getValue(token).toLowerCase(); + if (COLORS_NAMES[val] != null || val === "currentcolor") { + arena.setType(token, EnumToken.ColorTokenType); + arena.setColorType(token, ColorType.LIT); + // Object.assign(token, { + // val, + // typ: EnumToken.ColorTokenType, + // kin: ColorType.LIT, + // }); } else if (nonStandardColors.has(val)) { - Object.assign(token, { - typ: EnumToken.ColorTokenType, - kin: ColorType.NON_STD, - }); + arena.setType(token, EnumToken.ColorTokenType); + arena.setColorType(token, ColorType.NON_STD); + // Object.assign(token, { + // typ: EnumToken.ColorTokenType, + // kin: ColorType.NON_STD, + // }); } else if (systemColors.has(val)) { - Object.assign(token, { - typ: EnumToken.ColorTokenType, - kin: ColorType.SYS, - }); + arena.setType(token, EnumToken.ColorTokenType); + arena.setColorType(token, ColorType.SYS); + // Object.assign(token, { + // typ: EnumToken.ColorTokenType, + // kin: ColorType.SYS, + // }); } else if (deprecatedSystemColors.has(val)) { - Object.assign(token, { - typ: EnumToken.ColorTokenType, - kin: ColorType.DPSYS, - }); + arena.setType(token, EnumToken.ColorTokenType); + arena.setColorType(token, ColorType.DPSYS); + // Object.assign(token, { + // typ: EnumToken.ColorTokenType, + // kin: ColorType.DPSYS, + // }); } } break; @@ -368,8 +461,9 @@ function parseDeclaration(tokens, parent, options, errors) { hasImportantFlag = true; break; } - if (trimTokenSpace.has(token.typ)) { - if (tokens[i + 1]?.typ === EnumToken.WhitespaceTokenType) { + typ = arena.getType(token); + if (trimTokenSpace.has(typ)) { + if (tokens.length > i + 1 && arena.getType(tokens[i + 1]) === EnumToken.WhitespaceTokenType) { for (let index = i + 1; index < tokens.length - 1; index++) { tokens[index] = tokens[index + 1]; } @@ -377,7 +471,7 @@ function parseDeclaration(tokens, parent, options, errors) { i--; continue; } - if (tokens[i - 1]?.typ == EnumToken.WhitespaceTokenType) { + if (tokens.length >= i && arena.getType(tokens[i - 1]) == EnumToken.WhitespaceTokenType) { for (let index = i - 1; index < tokens.length - 1; index++) { tokens[index] = tokens[index + 1]; } @@ -391,97 +485,156 @@ function parseDeclaration(tokens, parent, options, errors) { action: "drop", message: "unbalanced token", node: stack[stack.length - 1], - location: options.source.getSourceLocation(stack[stack.length - 1][LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(stack[stack.length - 1])), }); - if (tokens[tokens.length - 1][LOCEND] != null) { - name[LOCEND] = tokens[tokens.length - 1][LOCEND]; + if (tokens.length > 0) { + arena.setSpanEnd(name, arena.getSpanEnd(tokens[tokens.length - 1])); + // name[LOCEND] = tokens[tokens.length - 1][LOCEND]; } - name[STATE] = EnumAstNodeStatus.Invalid; - name[ERRORS] = result?.errors ?? []; - //@ts-expect-error - return Object.assign(name, { - typ: EnumToken.DeclarationNodeType, - nam: name.val, - val: tokens, - }); + arena.setName(name, arena.internString(arena.getValue(name))); + arena.set(name, EnumToken.DeclarationNodeType, 0); + arena.setFlags(name, EnumAstNodeStatus.Invalid | EnumAstNodeStatus.HasValues); + arena.setErrors(name, result?.errors ?? []); + arena.setChildren(name, ...tokens); + return name; + // name[STATE] = EnumAstNodeStatus.Invalid; + // name[ERRORS] = result?.errors ?? []; + // return Object.assign(name, { + // typ: EnumToken.DeclarationNodeType, + // nam: name.val, + // val: tokens, + // }) as AstDeclaration; } - for (const { value } of walkValues(tokens)) { - if (value.typ === EnumToken.IdenTokenType && isColor(value)) { - parseColor(value); + for (const value of arena.walkValues(tokens)) { + if (arena.getType(value) === EnumToken.IdenTokenType && isColor(value, arena)) { + parseColor(value, arena); } } - if (name.val === "grid" || - name.val === "grid-template-areas" || - name.val === "grid-template-rows" || - name.val === "grid-template-columns") { + val = arena.getValue(name); + if (val === "grid" || + val === "grid-template-areas" || + val === "grid-template-rows" || + val === "grid-template-columns") { let i = 0; for (; i < tokens.length; i++) { - if (tokens[i].typ === EnumToken.StringTokenType) { - if (/[\s.]/.test(tokens[i].val)) { - tokens[i].val = parseGridTemplate(tokens[i].val); + typ = arena.getType(tokens[i]); + if (typ === EnumToken.StringTokenType) { + val = arena.getValue(tokens[i]); + if (/[\s.]/.test(val)) { + arena.setValue(tokens[i], arena.internString(parseGridTemplate(val))); + // (tokens[i] as StringToken).val = parseGridTemplate(val); } - if (tokens[i + 1]?.typ === EnumToken.WhitespaceTokenType && - tokens[i + 2]?.typ === EnumToken.StringTokenType) { + if (arena.getType(tokens[i + 1]) === EnumToken.WhitespaceTokenType && + arena.getType(tokens[i + 2]) === EnumToken.StringTokenType) { tokens.splice(i + 1, 1); } } } } - if (validate && syntaxRules == null && name.typ === EnumToken.IdenTokenType) { - if (tokens[tokens.length - 1]?.[LOCEND] != null) { - name[LOCEND] = tokens[tokens.length - 1][LOCEND]; + if (validate && syntaxRules == null && arena.getType(name) === EnumToken.IdenTokenType) { + if (tokens.length > 0 != null) { + arena.setSpanEnd(name, arena.getSpanEnd(tokens[tokens.length - 1])); + // name[LOCEND] = tokens[tokens.length - 1][LOCEND]; } - name[STATE] = EnumAstNodeStatus.Unknown; - name[ERRORS] = result?.errors ?? []; - if (hasImportantFlag) { - name[STATE] |= EnumAstNodeStatus.ImportantDeclaration; - } - // @ts-expect-error - return Object.assign(name, { - typ: EnumToken.DeclarationNodeType, - nam: name.val, - val: tokens, - }); + arena.setName(name, arena.internString(arena.getValue(name))); + arena.set(name, EnumToken.DeclarationNodeType, 0); + arena.setFlags(name, hasImportantFlag ? EnumAstNodeStatus.ImportantDeclaration | EnumAstNodeStatus.Unknown : EnumAstNodeStatus.Unknown); + arena.setErrors(name, result?.errors ?? []); + arena.setChildren(name, ...tokens); + return name; + // name[STATE] = EnumAstNodeStatus.Unknown; + // name[ERRORS] = result?.errors ?? []; + // // @ts-expect-error + // return Object.assign(name, { + // typ: EnumToken.DeclarationNodeType, + // nam: name.val, + // val: tokens, + // }) as AstDeclaration; + // if (hasImportantFlag) { + // name[STATE] |= EnumAstNodeStatus.ImportantDeclaration; + // } + // // @ts-expect-error + // return Object.assign(name, { + // typ: EnumToken.DeclarationNodeType, + // nam: name.val, + // val: tokens, + // }) as AstDeclaration; } - if (equalsIgnoreCase("composes", name.val)) { + if (arena.equalsIgnoreCase(name, "val", "composes")) { let index = -1; for (let i = 0; i < tokens.length; i++) { - if (tokens[i].typ === EnumToken.IdenTokenType && equalsIgnoreCase("from", tokens[i].val)) { + if (arena.getType(tokens[i]) === EnumToken.IdenTokenType && + arena.equalsIgnoreCase(tokens[i], "val", "from")) { index = i; break; } } - const left = trimArray(index == -1 ? tokens : tokens.slice(0, index)); - const right = index == -1 ? null : trimArray(tokens.slice(index + 1)); - tokens = [ - { - typ: EnumToken.ComposesSelectorNodeType, - l: left, - r: right?.[0] ?? null, - [LOCSRCID]: tokens[0][LOCSRCID], - [LOCSTA]: tokens[0][LOCSTA], - [LOCEND]: index != -1 ? right[right.length - 1]?.[LOCEND] : left[left.length - 1][LOCEND], - }, - ]; + const left = trimArray(index == -1 ? tokens : tokens.slice(0, index), options); + const right = index == -1 ? null : trimArray(tokens.slice(index + 1), options); + const composeSelector = arena.allocate(); + arena.set(composeSelector, EnumToken.ComposesSelectorNodeType); + arena.setSpan(composeSelector, arena.getSourceId(tokens[0]), arena.getSpanStart(tokens[0]), arena.getSpanEnd(right?.[right.length - 1] ?? left[left.length - 1])); + const leftSide = arena.allocate(); + arena.set(leftSide, EnumToken.ListTokenType); + arena.setChildren(leftSide, ...left); + arena.setLeft(composeSelector, leftSide); + if (right != null) { + const rightSide = arena.allocate(); + arena.set(rightSide, EnumToken.ListTokenType); + arena.setChildren(rightSide, ...right); + arena.setRight(composeSelector, rightSide); + } + tokens.length = 0; + tokens.push(composeSelector); + // tokens = [ + // { + // typ: EnumToken.ComposesSelectorNodeType, + // l: left, + // r: right?.[0] ?? null, + // [LOCSRCID]: tokens[0][LOCSRCID], + // [LOCSTA]: tokens[0][LOCSTA], + // [LOCEND]: index != -1 ? right![right!.length - 1]?.[LOCEND] : left[left.length - 1][LOCEND], + // } as ComposesSelectorToken, + // ]; } - if (tokens[tokens.length - 1]?.[LOCEND] != null) { - name[LOCEND] = tokens[tokens.length - 1][LOCEND]; + if (tokens.length > 0) { + arena.setSpanEnd(name, arena.getSpanEnd(tokens[tokens.length - 1])); + // name[LOCEND] = tokens[tokens.length - 1][LOCEND]; } - name[STATE] = success + let flags = EnumAstNodeStatus.HasValues | (success ? result == null ? EnumAstNodeStatus.Unvalidated : EnumAstNodeStatus.Validated - : EnumAstNodeStatus.ValidationFailed; - name[ERRORS] = result?.errors ?? []; + : EnumAstNodeStatus.ValidationFailed); if (hasImportantFlag) { - name[STATE] |= EnumAstNodeStatus.ImportantDeclaration; + flags |= EnumAstNodeStatus.ImportantDeclaration; } - // @ts-expect-error - return Object.assign(name, { - typ: EnumToken.DeclarationNodeType, - nam: name.val, - val: tokens, - }); + arena.setName(name, arena.internString(arena.getValue(name))); + arena.set(name, EnumToken.DeclarationNodeType, 0); + arena.setFlags(name, flags); + arena.setErrors(name, result?.errors ?? []); + arena.setChildren(name, ...tokens); + return name; + // name[STATE] = success + // ? result == null + // ? EnumAstNodeStatus.Unvalidated + // : EnumAstNodeStatus.Validated + // : EnumAstNodeStatus.ValidationFailed; + // name[ERRORS] = result?.errors ?? []; + // return Object.assign(name, { + // typ: EnumToken.DeclarationNodeType, + // nam: name.val, + // val: tokens, + // }) as AstDeclaration; + // if (hasImportantFlag) { + // name[STATE] |= EnumAstNodeStatus.ImportantDeclaration; + // } + // // @ts-expect-error + // return Object.assign(name, { + // typ: EnumToken.DeclarationNodeType, + // nam: name.val, + // val: tokens, + // }) as AstDeclaration; } export { parseDeclaration }; diff --git a/dist/src/lib/parser/utils/eq.js b/dist/src/lib/parser/utils/eq.js index 75f8b3bb2..277b21d3b 100644 --- a/dist/src/lib/parser/utils/eq.js +++ b/dist/src/lib/parser/utils/eq.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ /** diff --git a/dist/src/lib/parser/utils/hash.js b/dist/src/lib/parser/utils/hash.js index 999fe87dc..1008e8228 100644 --- a/dist/src/lib/parser/utils/hash.js +++ b/dist/src/lib/parser/utils/hash.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ // Alphabet: a-z, A-Z, 0-9, _, - diff --git a/dist/src/lib/parser/utils/selector.js b/dist/src/lib/parser/utils/selector.js index af79f204f..a93d3a307 100644 --- a/dist/src/lib/parser/utils/selector.js +++ b/dist/src/lib/parser/utils/selector.js @@ -2,51 +2,56 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { EnumToken, EnumAstNodeStatus } from '../../ast/types.js'; -import { renderValue } from '../../printer/render.js'; -import { LOCEND, LOCSTA, LOCSRCID, ERRORS, STATE, TOKENS, pseudoElements, combinators, tokensfuncDefMap, PARENT } from '../../syntax/constants.js'; +import { printValue } from '../../printer/print.js'; +import { ERRORS, STATE, TOKENS, LOCEND, LOCSTA, LOCSRCID, pseudoElements, combinators, tokensfuncDefMap } from '../../syntax/constants.js'; import { isHash } from '../../syntax/syntax.js'; import { getParsedSyntax, getSyntaxRule, getSyntaxConfig } from '../../validation/config.js'; import { matchAllSyntaxes, createValidationContext, trimArray, matchSelectorSyntax } from '../../validation/match.js'; import { ValidationSyntaxGroupEnum, ValidationTokenEnum } from '../../validation/parser/typedef.js'; import { splitTokenList } from '../../validation/utils/list.js'; import { trimWhiteSpace } from '../parse.js'; -import { equalsIgnoreCase } from './text.js'; /** * parse selector */ function parseSelector(tokens, context, options, errors) { - if (context?.typ === EnumToken.KeyframesAtRuleNodeType) { - const result = matchAllSyntaxes(getParsedSyntax(ValidationSyntaxGroupEnum.Syntaxes, "keyframe-selectors"), createValidationContext(tokens), options); - const parts = splitTokenList(tokens); + const arena = options.arena; + const contextTyp = arena.getType(context); + let typ; + if (arena.getType(context) === EnumToken.KeyframesAtRuleNodeType) { + const result = matchAllSyntaxes(getParsedSyntax(ValidationSyntaxGroupEnum.Syntaxes, "keyframe-selectors"), createValidationContext(tokens, options), options); + const parts = splitTokenList(tokens, options); for (const part of parts) { - trimArray(part); + trimArray(part, options); if (options.minify) { - const filtered = part.filter((token) => token.typ !== EnumToken.WhitespaceTokenType && token.typ !== EnumToken.CommentTokenType); + const filtered = part.filter((token) => { + typ = arena.getType(token); + }); if (filtered.length === 1) { - if (filtered[0].typ === EnumToken.IdenTokenType && - "from" === filtered[0].val.toLowerCase()) { - filtered[0] = { - typ: EnumToken.PercentageTokenType, - val: 0, - [LOCSRCID]: filtered[0][LOCSRCID], - [LOCSTA]: filtered[0][LOCSTA], - [LOCEND]: filtered[0][LOCEND], - }; + typ = arena.getType(filtered[0]); + if (typ === EnumToken.IdenTokenType && arena.equalsIgnoreCase(filtered[0], "val", "from")) { + arena.set(filtered[0], EnumToken.PercentageTokenType, 0, 0); + // filtered[0] = { + // typ: EnumToken.PercentageTokenType, + // val: 0, + // [LOCSRCID]: filtered[0][LOCSRCID], + // [LOCSTA]: filtered[0][LOCSTA], + // [LOCEND]: filtered[0][LOCEND], + // }; } - else if (filtered[0].typ === EnumToken.PercentageTokenType && - 100 === filtered[0].val) { - filtered[0] = { - typ: EnumToken.IdenTokenType, - val: "to", - [LOCSRCID]: filtered[0][LOCSRCID], - [LOCSTA]: filtered[0][LOCSTA], - [LOCEND]: filtered[0][LOCEND], - }; + else if (typ === EnumToken.PercentageTokenType && 100 === arena.getNumerciValue(filtered[0])) { + arena.set(filtered[0], 0, EnumToken.IdenTokenType, arena.internString("to"), 0); + // filtered[0] = { + // typ: EnumToken.IdenTokenType, + // val: "to", + // [LOCSRCID]: filtered[0][LOCSRCID], + // [LOCSTA]: filtered[0][LOCSTA], + // [LOCEND]: filtered[0][LOCEND], + // }; } part.splice(0, part.length, ...filtered); } @@ -54,18 +59,22 @@ function parseSelector(tokens, context, options, errors) { } tokens.splice(0, tokens.length, ...parts.reduce((acc, curr) => { if (acc.length > 0) { - acc.push({ typ: EnumToken.CommaTokenType }); + const id = arena.allocate(); + arena.set(id, EnumToken.CommaTokenType); + acc.push(id); } for (const c of curr) { acc.push(c); } return acc; }, [])); + const node = arena.allocate(); + arena.set(node, EnumToken.KeyframesRuleNodeType); return { typ: EnumToken.KeyframesRuleNodeType, sel: [ - ...splitTokenList(trimArray(tokens)).reduce((acc, curr) => { - acc.add(curr.reduce((acc, curr) => acc + renderValue(curr, { minify: false }), "")); + ...splitTokenList(trimArray(tokens, options), options).reduce((acc, curr) => { + acc.add(curr.reduce((acc, curr) => acc + printValue(curr, { minify: false }), "")); return acc; }, new Set()), ].join(), @@ -85,10 +94,10 @@ function parseSelector(tokens, context, options, errors) { let parent = context; let nested = false; let val; - if (context?.typ !== EnumToken.StyleSheetNodeType && context?.typ !== EnumToken.RuleNodeType) { + if (contextTyp !== EnumToken.StyleSheetNodeType && contextTyp !== EnumToken.RuleNodeType) { allowed = false; - if (context?.typ === EnumToken.AtRuleNodeType) { - const syntaxRule = getSyntaxRule(ValidationSyntaxGroupEnum.AtRules, "@" + context.nam); + if (contextTyp === EnumToken.AtRuleNodeType) { + const syntaxRule = getSyntaxRule(ValidationSyntaxGroupEnum.AtRules, "@" + arena.getName(context)); allowed = syntaxRule?.acceptAnyRule ?? false; if (!allowed) { const rules = syntaxRule?.getBlockRules?.(); @@ -107,129 +116,201 @@ function parseSelector(tokens, context, options, errors) { } } do { - if (parent?.typ === EnumToken.AtRuleNodeType && "media" === parent.nam) { - parent = parent[PARENT]; + if (contextTyp === EnumToken.AtRuleNodeType && arena.equalsIgnoreCase(parent, "nam", "media")) { + parent = arena.getParent(parent); continue; } - nested = parent?.typ == EnumToken.RuleNodeType; - parent = parent?.[PARENT]; - } while (!nested && parent != null); + nested = arena.getType(parent) == EnumToken.RuleNodeType; + parent = arena.getParent(parent); + } while (!nested && parent != 0); const all = new Array(tokens.length); let write = 0; + let token; + let next; + let newToken; for (let read = 0; read < tokens.length; read++) { - let token = tokens[read]; - if (token.typ == EnumToken.ColonTokenType) { - const next = tokens[read + 1]; - if (next?.typ == EnumToken.IdenTokenType) { - all[write++] = { - ...token, - typ: EnumToken.PseudoElementTokenType, - val: ":" + next.val, - [LOCEND]: next[LOCEND], - }; + token = tokens[read]; + typ = arena.getType(token); + if (typ == EnumToken.ColonTokenType) { + next = tokens[read + 1]; + if (next != null && arena.getType(next) == EnumToken.IdenTokenType) { + newToken = arena.allocate(); + all[write++] = newToken; + arena.set(newToken, EnumToken.PseudoClassTokenType, arena.interString(":" + arena.getValue(next))); + arena.setSpan(newToken, arena.getSourceId(token), arena.getStart(token), arena.getEnd(next)); read++; continue; } - if (next?.typ == EnumToken.FunctionTokenDefType) { - val = ":" + next.val; - all[write++] = { - ...token, - typ: val + "()" in getSyntaxConfig().selectors - ? EnumToken.PseudoClassFunctionTokenDefType - : next.typ, - val, - [LOCEND]: next[LOCEND], - }; + if (next != null && arena.getType(next) == EnumToken.FunctionTokenDefType) { + val = ":" + arena.getValue(next); + // all[write++] = { + // ...token, + // typ: + // val + "()" in getSyntaxConfig().selectors + // ? EnumToken.PseudoClassFunctionTokenDefType + // : next.typ, + // val, + // [LOCEND]: next[LOCEND], + // } as Token; + newToken = arena.allocate(); + all[write++] = newToken; + arena.set(newToken, val + "()" in getSyntaxConfig().selectors + ? EnumToken.PseudoClassFunctionTokenDefType + : arena.getType(next), arena.interString(":" + arena.getValue(next))); + arena.setSpan(newToken, arena.getSourceId(token), arena.getStart(token), arena.getEnd(next)); read++; continue; } } - if (token.typ == EnumToken.DoubleColonTokenType) { + if (arena.getType(token) == EnumToken.DoubleColonTokenType) { const next = tokens[read + 1]; - val = ":" + next.val; - if (next?.typ == EnumToken.IdenTokenType) { - all[write++] = { - ...token, - typ: EnumToken.PseudoClassTokenType, - val: (pseudoElements.includes(val) ? "" : ":") + val, - [LOCEND]: next[LOCEND], - }; + val = ":" + arena.getValue(next); + if (arena.getType(next) == EnumToken.IdenTokenType) { + // all[write++] = { + // ...token, + // typ: EnumToken.PseudoClassTokenType, + // val: (pseudoElements.includes(val) ? "" : ":") + val, + // [LOCEND]: next[LOCEND], + // } as Token; + newToken = arena.allocate(); + all[write++] = newToken; + arena.set(newToken, EnumToken.PseudoClassTokenType, arena.interString((pseudoElements.includes(val) ? "" : ":") + val)); + arena.setSpan(newToken, arena.getSourceId(token), arena.getStart(token), arena.getEnd(next)); read++; continue; } - if (next?.typ == EnumToken.FunctionTokenDefType) { - val = "::" + next.val; - all[write++] = { - ...token, - typ: val + "()" in getSyntaxConfig().selectors - ? EnumToken.PseudoClassFunctionTokenDefType - : EnumToken.FunctionTokenDefType, - val, - [LOCEND]: next[LOCEND], - }; + if (arena.getType(next) == EnumToken.FunctionTokenDefType) { + val = "::" + arena.getValue(next); + // all[write++] = { + // ...token, + // typ: + // val + "()" in getSyntaxConfig().selectors + // ? EnumToken.PseudoClassFunctionTokenDefType + // : EnumToken.FunctionTokenDefType, + // val, + // [LOCEND]: next[LOCEND], + // } as Token; + newToken = arena.allocate(); + all[write++] = newToken; + arena.set(newToken, val + "()" in getSyntaxConfig().selectors + ? EnumToken.PseudoClassFunctionTokenDefType + : EnumToken.FunctionTokenDefType, arena.interString(val)); + arena.setSpan(newToken, arena.getSourceId(token), arena.getStart(token), arena.getEnd(next)); read++; continue; } } - if (token.typ == EnumToken.ColorTokenType) { - if (isHash(token.val)) { - token.typ = EnumToken.HashTokenType; + if (arena.getType(token) == EnumToken.ColorTokenType) { + if (isHash(arena.getValue(token))) { + arena.set(token, EnumToken.HashTokenType); all[write++] = token; continue; } - return { - typ: EnumToken.RuleNodeType, - sel: [ - ...tokens - .reduce((acc, curr, index, array) => { - if (curr.typ == EnumToken.WhitespaceTokenType) { - if (trimWhiteSpace.includes(array[index - 1]?.typ) || - trimWhiteSpace.includes(array[index + 1]?.typ) || - combinators.includes(array[index - 1]?.val) || - combinators.includes(array[index + 1]?.val)) { - return acc; - } - } - let t = renderValue(curr, { minify: false }); - if (t == ",") { - acc.push([]); + const node = arena.allocate(); + node.set(node, EnumToken.RuleNodeType, arena.internString([ + ...all + .reduce((acc, curr, index, array) => { + if (arena.getType(curr) == EnumToken.WhitespaceTokenType) { + if (trimWhiteSpace.includes(arena.getType(array[index - 1])) || + trimWhiteSpace.includes(arena.getType(array[index + 1])) || + combinators.includes(arena.getValue(array[index - 1])) || + combinators.includes(arena.getValue(array[index + 1]))) { + return acc; } - else { - acc[acc.length - 1].push(t); - } - return acc; - }, [[]]) - .reduce((acc, curr) => { - let i = 0; - for (; i < curr.length; i++) { - if (i + 1 < curr.length && curr[i] == "*") { - if (curr[i] == "*") { - let index = curr[i + 1] == " " ? 2 : 1; - if (![">", "~", "+"].includes(curr[index])) { - curr.splice(i, index); - } + } + let t = printValue(curr, { minify: false, arena }); + if (t == ",") { + acc.push([]); + } + else { + acc[acc.length - 1].push(t); + } + return acc; + }, [[]]) + .reduce((acc, curr) => { + let i = 0; + for (; i < curr.length; i++) { + if (i + 1 < curr.length && curr[i] == "*") { + if (curr[i] == "*") { + let index = curr[i + 1] == " " ? 2 : 1; + if (![">", "~", "+"].includes(curr[index])) { + curr.splice(i, index); } } } - acc.set(curr.join(""), curr); - return acc; - }, uniq) - .keys(), - ].join(","), - chi: [], - [LOCSRCID]: tokens[0][LOCSRCID], - [LOCSTA]: tokens[0][LOCSTA], - [LOCEND]: tokens[tokens.length - 1][LOCEND], - [TOKENS]: tokens, - [STATE]: EnumAstNodeStatus.Invalid, - [ERRORS]: [ - { - action: "drop", - node: token, - message: "invalid hash id", - }, - ], - }; + } + acc.set(curr.join(""), curr); + return acc; + }, uniq) + .keys(), + ].join(","))); + arena.setTokens(node, all); + arena.setFlags(node, EnumAstNodeStatus.HasChildren | EnumAstNodeStatus.Invalid); + arena.setErrors(node, [ + { + action: "drop", + node: node, + message: "invalid hash id", + }, + ]); + return node; + // return { + // typ: EnumToken.RuleNodeType, + // sel: [ + // ...tokens + // .reduce( + // (acc: string[][], curr: Token, index: number, array: Token[]) => { + // if (curr.typ == EnumToken.WhitespaceTokenType) { + // if ( + // trimWhiteSpace.includes(array[index - 1]?.typ) || + // trimWhiteSpace.includes(array[index + 1]?.typ) || + // combinators.includes((array[index - 1])?.val) || + // combinators.includes((array[index + 1])?.val) + // ) { + // return acc; + // } + // } + // let t: string = printValue(curr, { minify: false }); + // if (t == ",") { + // acc.push([]); + // } else { + // acc[acc.length - 1].push(t); + // } + // return acc; + // }, + // [[]], + // ) + // .reduce((acc: Map, curr: string[]) => { + // let i: number = 0; + // for (; i < curr.length; i++) { + // if (i + 1 < curr.length && curr[i] == "*") { + // if (curr[i] == "*") { + // let index: number = curr[i + 1] == " " ? 2 : 1; + // if (![">", "~", "+"].includes(curr[index])) { + // curr.splice(i, index); + // } + // } + // } + // } + // acc.set(curr.join(""), curr); + // return acc; + // }, uniq) + // .keys(), + // ].join(","), + // chi: [], + // [LOCSRCID]: tokens[0][LOCSRCID], + // [LOCSTA]: tokens[0][LOCSTA], + // [LOCEND]: tokens[tokens.length - 1][LOCEND], + // [TOKENS]: tokens, + // [STATE]: EnumAstNodeStatus.Invalid, + // [ERRORS]: [ + // { + // action: "drop", + // node: token, + // message: "invalid hash id", + // }, + // ], + // } as AstRule; } all[write++] = token; } @@ -240,194 +321,211 @@ function parseSelector(tokens, context, options, errors) { tokens.length = write; } const result = matchSelectorSyntax(tokens, errors, options, nested); - trimArray(tokens); - if (result.success) { - for (let i = 0; i < tokens.length; i++) { - const token = tokens[i]; - switch (token.typ) { - case EnumToken.AttrStartTokenType: - stack.push(token); - break; - case EnumToken.AttrEndTokenType: - if (stack.at(-1)?.typ == EnumToken.AttrStartTokenType) { - index = tokens.indexOf(stack.at(-1)); - // @ts-expect-error - const { val, ...attr } = stack.at(-1); - attr[LOCSRCID] = stack.at(-1)[LOCSRCID]; - attr[LOCSTA] = stack.at(-1)[LOCSTA]; - attr[LOCEND] = token[LOCEND]; - tokens.splice(i, 1); - Object.assign(attr, { - typ: EnumToken.AttrTokenType, - chi: tokens.splice(index + 1, i - index - 1), - }); - tokens[index] = attr; - i = index; - stack.pop(); + trimArray(tokens, options); + // if (result.success) { + for (let i = 0; i < tokens.length; i++) { + token = tokens[i]; + typ = arena.getType(token); + switch (token) { + case EnumToken.AttrStartTokenType: + stack.push(token); + break; + case EnumToken.AttrEndTokenType: + if (arena.getType(stack.at(-1)) == EnumToken.AttrStartTokenType) { + index = tokens.indexOf(stack.at(-1)); + // const { val, ...attr } = stack.at(-1) as AttrStartToken; + // attr[LOCSRCID] = stack.at(-1)![LOCSRCID]; + // attr[LOCSTA] = stack.at(-1)![LOCSTA]; + // attr[LOCEND] = token[LOCEND]; + arena.setSpanEnd(tokens[index], arena.getSpanEnd(token)); + arena.setChildren(tokens[index], ...tokens.splice(index + 1, i - index - 1)); + tokens.splice(i, 1); + // Object.assign(attr, { + // typ: EnumToken.AttrTokenType, + // chi: tokens.splice(index + 1, i - index - 1), + // }); + // tokens[index] = attr; + i = index; + stack.pop(); + } + break; + case EnumToken.PseudoClassFunctionTokenDefType: + stack.push(token); + break; + case EnumToken.EndParensTokenType: + if (stack.length > 0 && arena.getType(stack.at(-1)) == EnumToken.PseudoClassFunctionTokenDefType) { + const func = stack.at(-1); + const funcType = arena.getType(func); + index = tokens.indexOf(func); + arena.setSpanEnd(tokens[index], arena.getSpanEnd(token)); + // (stack.at(-1) as AttrStartToken)[LOCEND] = token[LOCEND]; + tokens.splice(i, 1); + if (tokensfuncDefMap.has(funcType)) { + arena.setType(func, tokensfuncDefMap.get(funcType)); + arena.setChildren(func, ...trimArray(tokens.splice(index + 1, i - index - 1), options)); } - break; - case EnumToken.PseudoClassFunctionTokenDefType: - stack.push(token); - break; - case EnumToken.EndParensTokenType: - if (stack.at(-1)?.typ == EnumToken.PseudoClassFunctionTokenDefType) { - const func = stack.at(-1); - index = tokens.indexOf(func); - stack.at(-1)[LOCEND] = token[LOCEND]; - tokens.splice(i, 1); - if (tokensfuncDefMap.has(func.typ)) { - // @ts-expect-error - func.typ = tokensfuncDefMap.get(func.typ); - func.chi = trimArray(tokens.splice(index + 1, i - index - 1)); - } - if (result.success && options.minify) { - // parse an+b - // an+b produces an ident such as 'n-0', a literal such as '+2n-0' or a list of tokens such as[2n\s?[+-]\s?3] - if (func.val == ":nth-child" || - func.val == ":nth-last-child" || - func.val == ":nth-of-type" || - func.val == ":nth-last-of-type") { - const list = []; - let index; - for (index = 0; index < func.chi.length; index++) { - if (func.chi[index].typ == EnumToken.CommentTokenType || - func.chi[index].typ == EnumToken.WhitespaceTokenType) { - continue; - } - if (func.chi[index].typ == EnumToken.IdenTokenType && - equalsIgnoreCase("of", func.chi[index].val)) { - index--; - break; - } - list.push(func.chi[index]); + if (result.success && options.minify) { + val = arena.getValue(func); + // parse an+b + // an+b produces an ident such as 'n-0', a literal such as '+2n-0' or a list of tokens such as[2n\s?[+-]\s?3] + if (val == ":nth-child" || + val == ":nth-last-child" || + val == ":nth-of-type" || + val == ":nth-last-of-type") { + const list = []; + const children = arena.children(func); + let index = 0; + let child = 0; + for (index = 0; index < children.length; index++) { + child = children[index]; + typ = arena.getType(child); + if (typ == EnumToken.CommentTokenType || typ == EnumToken.WhitespaceTokenType) { + continue; } - if (list.length == 2) { - if (list[1].typ == EnumToken.NumberTokenType) { - if (list[1].val == 0) { - list.length = 1; - if (list[0].typ == EnumToken.DimensionTokenType && - list[0].val == -2) { - list[0].val = 2; - } - } - else { - const sign = Math.sign(list[1].val); - // @ts-ignore - list[1].val *= sign; - list.splice(1, 0, { - typ: EnumToken.LiteralTokenType, - val: sign > 0 ? "+" : "-", - }); + if (typ == EnumToken.IdenTokenType && arena.equalsIgnoreCase(child, "val", "of")) { + index--; + break; + } + list.push(child); + } + if (list.length == 2) { + if (arena.getType(list[1]) == EnumToken.NumberTokenType) { + if (arena.getNumericValue(list[1]) == 0) { + list.length = 1; + if (arena.getType(list[0]) == EnumToken.DimensionTokenType && + arena.getNumericValue(list[0]) == -2) { + arena.setNumericValue(list[0], 2); } } - if (list.length == 3 && - list[2].typ == EnumToken.NumberTokenType && - list[0].typ == EnumToken.DimensionTokenType && - (list[0].val == 2 || - list[0].val == -2)) { - if (1 == list[2].val) { - list.splice(0, 3, { - typ: EnumToken.IdenTokenType, - val: "odd", - [LOCSRCID]: list[0][LOCSRCID], - [LOCSTA]: list[0][LOCSTA], - [LOCEND]: list[0][LOCEND], - }); + else { + const sign = Math.sign(arena.getNumericValue(list[1])); + // @ts-ignore + list[1].val *= sign; + const literal = arena.allocate(); + arena.setType(literal, EnumToken.LiteralTokenType, arena.internString(sign < 0 ? "-" : "+")); + list.splice(1, 0, literal); + } + } + if (list.length == 3 && + arena.getType(list[2]) == EnumToken.NumberTokenType && + arena.getType(list[0]) == EnumToken.DimensionTokenType) { + const value = arena.getNumericValue(list[0]); + if (value == 2 || value == -2) { + const value0 = arena.getNumericValue(list[2]); + if (1 == value0) { + const literal = arena.allocate(); + arena.setType(literal, EnumToken.IdenTokenType, arena.internString("odd")); + arena.setSpan(literal, arena.getSourceId(list[0]), arena.getSpanStart(list[0]), arena.getSpanEnd(list[0])); + list.splice(0, 3, literal); } - else if (0 == list[2].val) { - list.splice(0, 3, { - typ: EnumToken.IdenTokenType, - val: "even", - [LOCSRCID]: list[0][LOCSRCID], - [LOCSTA]: list[0][LOCSTA], - [LOCEND]: list[0][LOCEND], - }); + else if (0 == value0) { + const literal = arena.allocate(); + arena.setType(literal, EnumToken.IdenTokenType, arena.internString("even")); + arena.setSpan(literal, arena.getSourceId(list[0]), arena.getSpanStart(list[0]), arena.getSpanEnd(list[0])); + list.splice(0, 3, literal); } } - func.chi.splice(0, index, ...list); } - if (list.length == 1) { - if (list[0].typ == EnumToken.IdenTokenType && - equalsIgnoreCase("-n", list[0].val)) { - list[0].val = "n"; - } + arena.setChildren(func, arena.children(func).splice(0, index, ...list)); + } + if (list.length == 1) { + if (arena.getType(list[0]) == EnumToken.IdenTokenType && + arena.equalsIgnoreCase(list[0], "val", "-n")) { + arena.setValue(list[0], arena.interString("n")); } - if (list.length == 3) { - if (list[0].typ == EnumToken.IdenTokenType && - ("n" == list[0].val || - "-n" == list[0].val || - "+n" == list[0].val)) { - if (list[1].typ == EnumToken.NextSiblingCombinatorTokenType) { - if (list[2].typ == EnumToken.NumberTokenType && - 0 == list[2].val) { - list[0].val = "n"; - func.chi.splice(0, index, list[0]); + } + if (list.length == 3) { + if (arena.getType(list[0]) == EnumToken.IdenTokenType) { + val = arena.getValue(list[0]); + if ("n" == val || "-n" == val || "+n" == val) { + if (arena.getType(list[1]) == EnumToken.NextSiblingCombinatorTokenType) { + if (arena.getType(list[2]) == EnumToken.NumberTokenType && + 0 == arena.getNumericValue(list[2])) { + arena.setValue(list[0], "n"); + arena.setChildren(func, arena.children(func).splice(0, index, list[0])); + // func.chi.splice(0, index, list[0]); break; } } } } - const token = func.chi.find((t) => t.typ != EnumToken.WhitespaceTokenType && t.typ != EnumToken.CommentTokenType); - if (token?.typ == EnumToken.IdenTokenType || token?.typ == EnumToken.LiteralTokenType) { - if (token.typ == EnumToken.IdenTokenType && - (token.val == "odd" || token.val == "even")) { - if (token.val == "even") { - Object.assign(token, { - typ: EnumToken.DimensionTokenType, - val: 2, - unit: "n", - }); + } + const token = arena.findChild(func, (t) => { + typ = arena.getType(t); + return typ != EnumToken.WhitespaceTokenType && typ != EnumToken.CommentTokenType; + }); + if (token != null) { + typ = arena.getType(token); + if (typ == EnumToken.IdenTokenType || typ == EnumToken.LiteralTokenType) { + val = arena.getValue(token); + if (typ == EnumToken.IdenTokenType) { + if (val == "even") { + arena.set(token, EnumToken.DimensionTokenType, 0, 2, arena.internString("n")); } } else { - const matches = /^(([+-]?[0-9]*)?n)?([+-]?[0-9]+)?$/.exec(token.val); + const matches = /^(([+-]?[0-9]*)?n)?([+-]?[0-9]+)?$/.exec(val); if (matches != null) { const a1 = matches[2] === "" ? 1 : matches[2] === "-" ? -1 : +matches[2]; const b1 = +matches[3]; if (b1 === 0) { - Object.assign(token, Math.abs(a1) === 1 - ? { - typ: EnumToken.IdenTokenType, - val: "n", - } - : { - typ: EnumToken.DimensionTokenType, - val: a1 * Math.sign(a1), - unit: "n", - }); + if (Math.abs(a1) === 1) { + arena.set(token, EnumToken.IdenTokenType, arena.internString("n")); + } + else { + arena.set(token, EnumToken.DimensionTokenType, 0, a1 * Math.sign(a1), arena.internString("n")); + } + // Object.assign( + // token, + // Math.abs(a1) === 1 + // ? { + // typ: EnumToken.IdenTokenType, + // val: "n", + // } + // : { + // typ: EnumToken.DimensionTokenType, + // val: a1 * Math.sign(a1), + // unit: "n", + // }, + // ); } } } } - else if (token?.typ === EnumToken.DimensionTokenType) { - const index = func.chi.indexOf(token); + else if (typ === EnumToken.DimensionTokenType) { + const children = arena.children(func); + const index = children.indexOf(token); let num = null; let i = index + 1; - for (; i < func.chi.length; i++) { - if (func.chi[i].typ == EnumToken.WhitespaceTokenType || - func.chi[i].typ == EnumToken.CommentTokenType) { + for (; i < children.length; i++) { + typ = arena.getType(children[i]); + if (typ == EnumToken.WhitespaceTokenType || + typ == EnumToken.CommentTokenType) { continue; } - if (func.chi[i].typ == EnumToken.NumberTokenType) { - num = func.chi[i]; + if (typ == EnumToken.NumberTokenType) { + num = children[i]; break; } } if (num != null) { - if (num.val === 0) { - func.chi.splice(index + 1, i - index); - if (token.val < 0) { - token.val *= -1; + if (arena.getNumericValue(num) === 0) { + children.splice(index + 1, i - index); + if (arena.getNumericValue(token) < 0) { + arena.setNumericValue(token, arena.getNumericValue(token) * -1); } + arena.setChildren(func, children); break; } - else if (token.val === 2 && - Math.abs(num.val) === 1) { - Object.assign(token, { - typ: EnumToken.IdenTokenType, - val: "odd", - }); - func.chi.splice(index + 1, i - index); + else if (arena.getNumericValue(token) === 2 && + Math.abs(arena.getNumericValue(num)) === 1) { + arena.set(token, EnumToken.IdenTokenType, arena.internString("n")); + // Object.assign(token, { + // typ: EnumToken.IdenTokenType, + // val: "odd", + // }); + children.splice(index + 1, i - index); + arena.setChildren(func, children); break; } // if (((token as DimensionToken).val as number) === 1) { @@ -452,67 +550,117 @@ function parseSelector(tokens, context, options, errors) { } } } - i = index; - stack.pop(); } - break; - } + i = index; + stack.pop(); + } + break; } } - return { - typ: EnumToken.RuleNodeType, - sel: [ - ...tokens - .reduce((acc, curr, index, array) => { - // if (curr.typ == EnumToken.CommentTokenType) { - // return acc; - // } - if (curr.typ == EnumToken.WhitespaceTokenType) { - if (trimWhiteSpace.includes(array[index - 1]?.typ) || - trimWhiteSpace.includes(array[index + 1]?.typ) || - combinators.includes(array[index - 1]?.val) || - combinators.includes(array[index + 1]?.val)) { - return acc; - } - } - let t = renderValue(curr, { minify: false }); - if (t == ",") { - acc.push([]); - } - else { - acc[acc.length - 1].push(t); + // } + const node = arena.allocate(); + arena.set(node, EnumToken.RuleNodeType, arena.internString([ + ...trimArray(all, options) + .reduce((acc, curr, index, array) => { + // if (curr.typ == EnumToken.CommentTokenType) { + // return acc; + // } + if (arena.getType(curr) == EnumToken.WhitespaceTokenType) { + if (trimWhiteSpace.includes(arena.getType(array[index - 1])) || + trimWhiteSpace.includes(arena.getType(array[index + 1])) || + combinators.includes(arena.getValue(array[index - 1])) || + combinators.includes(arena.getValue(array[index + 1]))) { + return acc; } - return acc; - }, [[]]) - .reduce((acc, curr) => { - let i = 0; - for (; i < curr.length; i++) { - if (i + 1 < curr.length && curr[i] == "*") { - if (curr[i] == "*") { - let index = curr[i + 1] == " " ? 2 : 1; - if (![">", "~", "+"].includes(curr[index])) { - curr.splice(i, index); - } + } + let t = printValue(curr, arena, { minify: false, arena }); + if (t == ",") { + acc.push([]); + } + else { + acc[acc.length - 1].push(t); + } + return acc; + }, [[]]) + .reduce((acc, curr) => { + let i = 0; + for (; i < curr.length; i++) { + if (i + 1 < curr.length && curr[i] == "*") { + if (curr[i] == "*") { + let index = curr[i + 1] == " " ? 2 : 1; + if (![">", "~", "+"].includes(curr[index])) { + curr.splice(i, index); } } } - acc.set(curr.join(""), curr); - return acc; - }, uniq) - .keys(), - ].join(","), - chi: [], - [LOCSRCID]: tokens[0][LOCSRCID], - [LOCSTA]: tokens[0][LOCSTA], - [LOCEND]: tokens[tokens.length - 1][LOCEND], - [TOKENS]: tokens, - [STATE]: result.success && allowed - ? EnumAstNodeStatus.Validated - : allowed - ? EnumAstNodeStatus.Invalid - : EnumAstNodeStatus.Disallowed, - [ERRORS]: result.success ? [] : result.errors, - }; + } + acc.set(curr.join(""), curr); + return acc; + }, uniq) + .keys(), + ].join(","))); + arena.setSpan(node, arena.getSourceId(tokens[0]), arena.getSpanStart(tokens[0]), arena.getSpanEnd(tokens[tokens.length - 1])); + arena.setFlags(node, EnumAstNodeStatus.HasChildren | EnumAstNodeStatus.Validated); + return node; + // return { + // typ: EnumToken.RuleNodeType, + // sel: [ + // ...tokens + // .reduce( + // (acc: string[][], curr: Token, index: number, array: Token[]) => { + // // if (curr.typ == EnumToken.CommentTokenType) { + // // return acc; + // // } + // if (curr.typ == EnumToken.WhitespaceTokenType) { + // if ( + // trimWhiteSpace.includes(array[index - 1]?.typ) || + // trimWhiteSpace.includes(array[index + 1]?.typ) || + // combinators.includes((array[index - 1])?.val) || + // combinators.includes((array[index + 1])?.val) + // ) { + // return acc; + // } + // } + // let t: string = printValue(curr, { minify: false }); + // if (t == ",") { + // acc.push([]); + // } else { + // acc[acc.length - 1].push(t); + // } + // return acc; + // }, + // [[]], + // ) + // .reduce((acc: Map, curr: string[]) => { + // let i: number = 0; + // for (; i < curr.length; i++) { + // if (i + 1 < curr.length && curr[i] == "*") { + // if (curr[i] == "*") { + // let index: number = curr[i + 1] == " " ? 2 : 1; + // if (![">", "~", "+"].includes(curr[index])) { + // curr.splice(i, index); + // } + // } + // } + // } + // acc.set(curr.join(""), curr); + // return acc; + // }, uniq) + // .keys(), + // ].join(","), + // chi: [], + // [LOCSRCID]: tokens[0][LOCSRCID], + // [LOCSTA]: tokens[0][LOCSTA], + // [LOCEND]: tokens[tokens.length - 1][LOCEND], + // [TOKENS]: tokens, + // [STATE]: + // result.success && allowed + // ? EnumAstNodeStatus.Validated + // : allowed + // ? EnumAstNodeStatus.Invalid + // : EnumAstNodeStatus.Disallowed, + // [ERRORS]: result.success ? [] : result.errors, + // } as AstRule; } export { parseSelector }; diff --git a/dist/src/lib/parser/utils/text.js b/dist/src/lib/parser/utils/text.js index 07f8a5ca7..04aa27dbe 100644 --- a/dist/src/lib/parser/utils/text.js +++ b/dist/src/lib/parser/utils/text.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ /** diff --git a/dist/src/lib/parser/utils/token.js b/dist/src/lib/parser/utils/token.js index 3e1882d5d..8c7478d39 100644 --- a/dist/src/lib/parser/utils/token.js +++ b/dist/src/lib/parser/utils/token.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { EnumToken } from '../../ast/types.js'; diff --git a/dist/src/lib/parser/utils/type.js b/dist/src/lib/parser/utils/type.js index bc5816cfe..d2c78cd6f 100644 --- a/dist/src/lib/parser/utils/type.js +++ b/dist/src/lib/parser/utils/type.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { EnumToken } from '../../ast/types.js'; diff --git a/dist/src/lib/printer/render.js b/dist/src/lib/printer/render.js index f36e2a0cb..3cfb26dd6 100644 --- a/dist/src/lib/printer/render.js +++ b/dist/src/lib/printer/render.js @@ -2,16 +2,16 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ -import { convertColor, getAngle } from '../syntax/color/color.js'; +import { getAngle, convertColor } from '../syntax/color/color.js'; import { reduceHexValue } from '../syntax/color/hex.js'; import { EnumToken, ColorType } from '../ast/types.js'; import { expand } from '../ast/expand.js'; import { SourceMap } from './sourcemap/sourcemap.js'; -import { pseudoElements, anglePrecision, urlTokenMatcher, PARENT, tokensfuncSet, LOCSTA, LOCSRCID } from '../syntax/constants.js'; -import { minifyNumber, toPrecisionAngle, reducegradientBackgroundPosition, reduceConicColorStops, reduceColorStops, parseColor, isWhiteSpace, toPrecisionValue } from '../syntax/syntax.js'; +import { PARENT, LOCSTA, LOCSRCID, tokensfuncSet, pseudoElements, anglePrecision, urlTokenMatcher } from '../syntax/constants.js'; +import { isWhiteSpace, minifyNumber, toPrecisionAngle, reducegradientBackgroundPosition, reduceConicColorStops, reduceColorStops, parseColor, toPrecisionValue } from '../syntax/syntax.js'; import { equalsIgnoreCase } from '../parser/utils/text.js'; import { toDegrees } from '../parser/utils/angle.js'; import { LineMap } from '../parser/linesmap.js'; @@ -78,7 +78,7 @@ function doRender(data, options = {}, mapping) { const sourceLocation = { end: 0, }; - const linesMap = options.sourcemap ? new LineMap() : null; + const lineMap = options.sourcemap ? new LineMap() : null; let code = ""; if (mapping != null) { if (mapping.importMapping != null) { @@ -93,7 +93,7 @@ function doRender(data, options = {}, mapping) { } code += `:export${options.indent}{${options.newLine}${Object.entries(mapping.mapping).reduce((acc, [k, v]) => acc + (acc.length > 0 ? options.newLine : "") + `${options.indent}${k}:${options.indent}${v};`, "")}${options.newLine}}${options.newLine}`; if (sourcemap != null) { - move(sourceLocation, linesMap, code); + move(sourceLocation, lineMap, code); } } if (options.output != null) { @@ -106,7 +106,7 @@ function doRender(data, options = {}, mapping) { [EnumToken.StyleSheetNodeType, EnumToken.AtRuleNodeType, EnumToken.RuleNodeType].includes(data.typ) && "chi" in data ? expand(data) - : data, options, sourcemaps, sourceLocation, linesMap, errors, function reducer(acc, curr) { + : data, options, sourcemaps, sourceLocation, lineMap, errors, function reducer(acc, curr) { if (curr.typ == EnumToken.CommentTokenType && options.removeComments) { if (!options.preserveLicense || !curr.val.startsWith("/*!")) { return acc; @@ -141,25 +141,25 @@ function doRender(data, options = {}, mapping) { * @param cache * @param sourcemaps * @param sourceLocation - * @param linesMap + * @param lineMap * @param str * * @internal */ -function updateSourceMap(node, options, cache, sourcemaps, sourceLocation, linesMap, str) { +function updateSourceMap(node, options, cache, sourcemaps, sourceLocation, lineMap, str) { let offset = 0; // eat leanding whitespace while (offset < str.length && isWhiteSpace(str.charCodeAt(offset))) { offset++; } if (offset > 0) { - move(sourceLocation, linesMap, str, 0, offset + 1); + move(sourceLocation, lineMap, str, 0, offset + 1); } if (node[LOCSTA] != null) { const source = options.sourcesMap.get(node[LOCSRCID]); const inputSourceMap = source.getInputSourceMap(); const offsets = source.getOffsets(node[LOCSTA]); - const [newLine, newColumn] = linesMap.getOffsets(sourceLocation.end); + const [newLine, newColumn] = lineMap.getOffsets(sourceLocation.end); let records = null; let srcId = node[LOCSRCID]; let sourceFileName = source.getFileName() || null; @@ -226,17 +226,17 @@ function updateSourceMap(node, options, cache, sourcemaps, sourceLocation, lines sourcemaps.maps.push([newLine, newColumn, srcId, offsets[0], offsets[1]]); } } - move(sourceLocation, linesMap, str, offset); + move(sourceLocation, lineMap, str, offset); } /** * Update position * @param sourceLocation - * @param linesMap + * @param lineMap * @param str * @param start * @param end */ -function move(sourceLocation, linesMap, str, start, end) { +function move(sourceLocation, lineMap, str, start, end) { let i = start ?? 0; let j = end ?? str.length; let codepoint; @@ -255,7 +255,7 @@ function move(sourceLocation, linesMap, str, start, end) { if (codepoint == 0xd && i + 1 < str.length && str.charCodeAt(i + 1) == 0xa) ; else if (codepoint == 0xa && i > 0 && str.charCodeAt(i - 1) == 0xd) ; else { - linesMap.addLineStart(sourceLocation.end); + lineMap.addLineStart(sourceLocation.end); } } } @@ -266,7 +266,7 @@ function move(sourceLocation, linesMap, str, start, end) { * @param options * @param sourcemaps * @param sourceLocation - * @param linesMap + * @param lineMap * @param errors * @param reducer * @param cache @@ -275,7 +275,7 @@ function move(sourceLocation, linesMap, str, start, end) { * * @internal */ -function renderAstNode(data, options, sourcemaps, sourceLocation, linesMap, errors, reducer, cache, level = 0, indents = []) { +function renderAstNode(data, options, sourcemaps, sourceLocation, lineMap, errors, reducer, cache, level = 0, indents = []) { if (indents.length < level + 1) { indents.push(options.indent.repeat(level)); } @@ -303,7 +303,7 @@ function renderAstNode(data, options, sourcemaps, sourceLocation, linesMap, erro : ""; case EnumToken.StyleSheetNodeType: for (const node of data.chi) { - str = renderAstNode(node, options, sourcemaps, sourceLocation, linesMap, errors, reducer, cache, level, indents); + str = renderAstNode(node, options, sourcemaps, sourceLocation, lineMap, errors, reducer, cache, level, indents); if (str === "") { continue; } @@ -312,7 +312,7 @@ function renderAstNode(data, options, sourcemaps, sourceLocation, linesMap, erro } children += str; if (sourcemaps != null && str !== "" && options.newLine) { - move(sourceLocation, linesMap, options.newLine); + move(sourceLocation, lineMap, options.newLine); } } return children; @@ -323,14 +323,14 @@ function renderAstNode(data, options, sourcemaps, sourceLocation, linesMap, erro if ([EnumToken.AtRuleNodeType, EnumToken.KeyframesAtRuleNodeType].includes(data.typ) && !("chi" in data)) { return `${indent}@${data.nam}${data.val === "" ? "" : options.indent || " "}${data.val};`; } - const lineMapLength = linesMap ? linesMap.getLineStarts().length : 0; + const lineMapLength = lineMap ? lineMap.getLineStarts().length : 0; const prelude = (indent.length > 0 ? options.newLine : "") + indent + ([EnumToken.AtRuleNodeType, EnumToken.KeyframesAtRuleNodeType].includes(data.typ) ? `@${data.nam}${data.val === "" ? "" : options.indent || " "}${data.val}${options.indent}{` : data.sel + `${options.indent}{`); if (sourcemaps != null) { - updateSourceMap(data, options, cache, sourcemaps, sourceLocation, linesMap, prelude); + updateSourceMap(data, options, cache, sourcemaps, sourceLocation, lineMap, prelude); } let node; let recordDeclarationSourceMap = data.typ == EnumToken.AtRuleNodeType; @@ -351,7 +351,7 @@ function renderAstNode(data, options, sourcemaps, sourceLocation, linesMap, erro .trimEnd()};`; } else { - str = renderAstNode(node, options, sourcemaps, sourceLocation, linesMap, errors, reducer, cache, level + 1, indents); + str = renderAstNode(node, options, sourcemaps, sourceLocation, lineMap, errors, reducer, cache, level + 1, indents); if (str === "") { continue; } @@ -373,10 +373,10 @@ function renderAstNode(data, options, sourcemaps, sourceLocation, linesMap, erro // } // } // @ts-ignore - updateSourceMap(node, options, cache, sourcemaps, sourceLocation, linesMap, str); + updateSourceMap(node, options, cache, sourcemaps, sourceLocation, lineMap, str); } else { - move(sourceLocation, linesMap, str); + move(sourceLocation, lineMap, str); } } } @@ -387,13 +387,13 @@ function renderAstNode(data, options, sourcemaps, sourceLocation, linesMap, erro if (options.removeEmpty && children === "") { if (sourcemaps != null) { sourceLocation.end -= prelude.length; - linesMap.getLineStarts().length = lineMapLength; + lineMap.getLineStarts().length = lineMapLength; } return ""; } const end = options.newLine + indent + `}`; if (sourcemaps != null) { - move(sourceLocation, linesMap, end); + move(sourceLocation, lineMap, end); } return prelude + children + end; default: @@ -445,7 +445,7 @@ function renderValue(token, options = {}, cache = Object.create(null), reducer, case EnumToken.CustomFunctionTokenDefType: case EnumToken.WildCardFunctionTokenDefType: return token.val + "("; - case EnumToken.ListToken: + case EnumToken.ListTokenType: return token.chi.reduce((acc, curr) => acc + renderValue(curr, options, cache), ""); case EnumToken.BinaryExpressionTokenType: if (EnumToken.Mul === token.op || diff --git a/dist/src/lib/printer/sourcemap/lib/codec.js b/dist/src/lib/printer/sourcemap/lib/codec.js index ba50b9510..daae655d0 100644 --- a/dist/src/lib/printer/sourcemap/lib/codec.js +++ b/dist/src/lib/printer/sourcemap/lib/codec.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ // from https://github.com/Rich-Harris/vlq/tree/master diff --git a/dist/src/lib/printer/sourcemap/sourcemap.js b/dist/src/lib/printer/sourcemap/sourcemap.js index d48fb5b21..a3ef86f9b 100644 --- a/dist/src/lib/printer/sourcemap/sourcemap.js +++ b/dist/src/lib/printer/sourcemap/sourcemap.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { toBase64 } from '../../parser/utils/base64.js'; diff --git a/dist/src/lib/syntax/color/a98rgb.js b/dist/src/lib/syntax/color/a98rgb.js index 73d1c35fc..66804bca4 100644 --- a/dist/src/lib/syntax/color/a98rgb.js +++ b/dist/src/lib/syntax/color/a98rgb.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { xyz2srgb } from './srgb.js'; diff --git a/dist/src/lib/syntax/color/alpha.js b/dist/src/lib/syntax/color/alpha.js index c60b96d96..f0ba32f91 100644 --- a/dist/src/lib/syntax/color/alpha.js +++ b/dist/src/lib/syntax/color/alpha.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { ColorType, EnumToken } from '../../ast/types.js'; diff --git a/dist/src/lib/syntax/color/cmyk.js b/dist/src/lib/syntax/color/cmyk.js index 1c06da289..0ae1e46ab 100644 --- a/dist/src/lib/syntax/color/cmyk.js +++ b/dist/src/lib/syntax/color/cmyk.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { ColorType, EnumToken } from '../../ast/types.js'; diff --git a/dist/src/lib/syntax/color/color-mix.js b/dist/src/lib/syntax/color/color-mix.js index 26db3f12a..1afde0875 100644 --- a/dist/src/lib/syntax/color/color-mix.js +++ b/dist/src/lib/syntax/color/color-mix.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { EnumToken, ColorType, EnumAstNodeStatus } from '../../ast/types.js'; diff --git a/dist/src/lib/syntax/color/color.js b/dist/src/lib/syntax/color/color.js index 4d20f23ce..7a9de6776 100644 --- a/dist/src/lib/syntax/color/color.js +++ b/dist/src/lib/syntax/color/color.js @@ -2,10 +2,10 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ -import { ColorType, EnumToken } from '../../ast/types.js'; +import { EnumToken, ColorType } from '../../ast/types.js'; import { color2RgbToken, lch2RgbToken, lab2RgbToken, oklch2RgbToken, oklab2RgbToken, cmyk2RgbToken, hwb2RgbToken, hsl2RgbToken, hex2RgbToken } from './rgb.js'; import { color2HslToken, lch2HslToken, lab2HslToken, oklch2HslToken, oklab2HslToken, cmyk2HslToken, hwb2HslToken, hex2HslToken, rgb2HslToken } from './hsl.js'; import { color2hwbToken, cmyk2hwbToken, lch2hwbToken, lab2hwbToken, oklch2hwbToken, oklab2hwbToken, hsl2hwbToken, rgb2hwbToken } from './hwb.js'; @@ -567,29 +567,28 @@ function getNumber(token) { * convert angle to turn * @param token */ -function getAngle(token) { - if (token.typ == EnumToken.IdenTokenType) { - if (token.val == "none") { +function getAngle(token, arena) { + const typ = arena.getType(token); + if (typ == EnumToken.IdenTokenType) { + if (arena.equalsIgnoreCase(token, 'val', "none")) { return 0; } } - if (token.typ == EnumToken.AngleTokenType) { - switch (token.unit) { + const val = arena.getNumericValue(token); + if (typ == EnumToken.AngleTokenType) { + switch (arena.getUnit(token)) { case "deg": - // @ts-ignore - return token.val / 360; + return val / 360; case "rad": - // @ts-ignore - return token.val / (2 * Math.PI); + return val / (2 * Math.PI); case "grad": - // @ts-ignore - return token.val / 400; + return val / 400; case "turn": - // @ts-ignore - return +token.val; + // @ts-ign + return +val; } } - return token.val / 360; + return val / 360; } export { cmyk2colorToken, color2colorToken, color2srgbvalues, convertColor, getAngle, getNumber, hex2colorToken, hsl2colorToken, hwb2colorToken, lab2colorToken, lch2colorToken, minmax, oklab2colorToken, oklch2colorToken, rgb2colorToken }; diff --git a/dist/src/lib/syntax/color/hex.js b/dist/src/lib/syntax/color/hex.js index f49aea391..d4c3049d8 100644 --- a/dist/src/lib/syntax/color/hex.js +++ b/dist/src/lib/syntax/color/hex.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { EnumToken, ColorType } from '../../ast/types.js'; diff --git a/dist/src/lib/syntax/color/hsl.js b/dist/src/lib/syntax/color/hsl.js index d7f932fa0..bd06a43c4 100644 --- a/dist/src/lib/syntax/color/hsl.js +++ b/dist/src/lib/syntax/color/hsl.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { color2srgbvalues, getNumber } from './color.js'; diff --git a/dist/src/lib/syntax/color/hwb.js b/dist/src/lib/syntax/color/hwb.js index 2b06f0215..9532bc762 100644 --- a/dist/src/lib/syntax/color/hwb.js +++ b/dist/src/lib/syntax/color/hwb.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { getColorComponents } from './utils/components.js'; diff --git a/dist/src/lib/syntax/color/lab.js b/dist/src/lib/syntax/color/lab.js index ac90a4a3b..361cf0528 100644 --- a/dist/src/lib/syntax/color/lab.js +++ b/dist/src/lib/syntax/color/lab.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { e, k, D50 } from '../constants.js'; diff --git a/dist/src/lib/syntax/color/lch.js b/dist/src/lib/syntax/color/lch.js index bbd4868b9..417a45127 100644 --- a/dist/src/lib/syntax/color/lch.js +++ b/dist/src/lib/syntax/color/lch.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { getColorComponents } from './utils/components.js'; diff --git a/dist/src/lib/syntax/color/oklab.js b/dist/src/lib/syntax/color/oklab.js index 13b993522..f200431b3 100644 --- a/dist/src/lib/syntax/color/oklab.js +++ b/dist/src/lib/syntax/color/oklab.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { getColorComponents } from './utils/components.js'; diff --git a/dist/src/lib/syntax/color/oklch.js b/dist/src/lib/syntax/color/oklch.js index b8f77915b..76c9d09bc 100644 --- a/dist/src/lib/syntax/color/oklch.js +++ b/dist/src/lib/syntax/color/oklch.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { getColorComponents } from './utils/components.js'; diff --git a/dist/src/lib/syntax/color/p3.js b/dist/src/lib/syntax/color/p3.js index 0e1994879..bf9a6a24a 100644 --- a/dist/src/lib/syntax/color/p3.js +++ b/dist/src/lib/syntax/color/p3.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { lsrgb2srgbvalues, xyz2srgb, srgb2lsrgbvalues } from './srgb.js'; diff --git a/dist/src/lib/syntax/color/prophotorgb.js b/dist/src/lib/syntax/color/prophotorgb.js index bca75fb2c..7b5889c95 100644 --- a/dist/src/lib/syntax/color/prophotorgb.js +++ b/dist/src/lib/syntax/color/prophotorgb.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { XYZ_D65_to_D50, xyzd502srgb } from './xyzd50.js'; diff --git a/dist/src/lib/syntax/color/rec2020.js b/dist/src/lib/syntax/color/rec2020.js index 973081169..860124f3c 100644 --- a/dist/src/lib/syntax/color/rec2020.js +++ b/dist/src/lib/syntax/color/rec2020.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { xyz2srgb } from './srgb.js'; diff --git a/dist/src/lib/syntax/color/relative-color.js b/dist/src/lib/syntax/color/relative-color.js index 917043cc6..4816decdc 100644 --- a/dist/src/lib/syntax/color/relative-color.js +++ b/dist/src/lib/syntax/color/relative-color.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { convertColor, getNumber } from './color.js'; diff --git a/dist/src/lib/syntax/color/rgb.js b/dist/src/lib/syntax/color/rgb.js index 09192ff60..d499f4dba 100644 --- a/dist/src/lib/syntax/color/rgb.js +++ b/dist/src/lib/syntax/color/rgb.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { color2srgbvalues, minmax } from './color.js'; diff --git a/dist/src/lib/syntax/color/srgb.js b/dist/src/lib/syntax/color/srgb.js index 8d5445867..022396cf4 100644 --- a/dist/src/lib/syntax/color/srgb.js +++ b/dist/src/lib/syntax/color/srgb.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { getColorComponents } from './utils/components.js'; diff --git a/dist/src/lib/syntax/color/utils/color-type.js b/dist/src/lib/syntax/color/utils/color-type.js index d7b594678..f126fc20d 100644 --- a/dist/src/lib/syntax/color/utils/color-type.js +++ b/dist/src/lib/syntax/color/utils/color-type.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { ColorType } from '../../../ast/types.js'; diff --git a/dist/src/lib/syntax/color/utils/colorspace.js b/dist/src/lib/syntax/color/utils/colorspace.js index ef3a2230d..103133e0d 100644 --- a/dist/src/lib/syntax/color/utils/colorspace.js +++ b/dist/src/lib/syntax/color/utils/colorspace.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { equalsIgnoreCase } from '../../../parser/utils/text.js'; diff --git a/dist/src/lib/syntax/color/utils/components.js b/dist/src/lib/syntax/color/utils/components.js index 89a572b80..6f82a0cd3 100644 --- a/dist/src/lib/syntax/color/utils/components.js +++ b/dist/src/lib/syntax/color/utils/components.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { ColorType, EnumToken } from '../../../ast/types.js'; diff --git a/dist/src/lib/syntax/color/utils/distance.js b/dist/src/lib/syntax/color/utils/distance.js index 3d96f8ea8..b44f17448 100644 --- a/dist/src/lib/syntax/color/utils/distance.js +++ b/dist/src/lib/syntax/color/utils/distance.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { convertColor } from '../color.js'; diff --git a/dist/src/lib/syntax/color/utils/make-color.js b/dist/src/lib/syntax/color/utils/make-color.js index 05ea4e558..39aa27cf1 100644 --- a/dist/src/lib/syntax/color/utils/make-color.js +++ b/dist/src/lib/syntax/color/utils/make-color.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { EnumToken, ColorType } from '../../../ast/types.js'; diff --git a/dist/src/lib/syntax/color/utils/matrix.js b/dist/src/lib/syntax/color/utils/matrix.js index c93af3b97..ff50ef668 100644 --- a/dist/src/lib/syntax/color/utils/matrix.js +++ b/dist/src/lib/syntax/color/utils/matrix.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ // from https://www.w3.org/TR/css-color-4/multiply-matrices.js diff --git a/dist/src/lib/syntax/color/xyz.js b/dist/src/lib/syntax/color/xyz.js index 651cd8f43..2dd33f698 100644 --- a/dist/src/lib/syntax/color/xyz.js +++ b/dist/src/lib/syntax/color/xyz.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { multiplyMatrices } from './utils/matrix.js'; diff --git a/dist/src/lib/syntax/color/xyzd50.js b/dist/src/lib/syntax/color/xyzd50.js index 122f0e476..f68a13e1b 100644 --- a/dist/src/lib/syntax/color/xyzd50.js +++ b/dist/src/lib/syntax/color/xyzd50.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { lsrgb2srgbvalues } from './srgb.js'; diff --git a/dist/src/lib/syntax/constants.js b/dist/src/lib/syntax/constants.js index 27c428abc..e0a8b93b5 100644 --- a/dist/src/lib/syntax/constants.js +++ b/dist/src/lib/syntax/constants.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { EnumToken } from '../ast/types.js'; @@ -20,7 +20,6 @@ const LOCEND = Symbol.for("locEnd"); const LOC = Symbol.for("loc"); const RAW = Symbol.for("raw"); const STATE = Symbol.for("state"); -const ROOT = Symbol.for("root"); const ERRORS = Symbol.for("errors"); const TOKENS = Symbol.for("tokens"); const PARENT = Symbol.for("parent"); @@ -506,4 +505,4 @@ const trimTokenSpace = new Set([ ]); const combinators = ["+", ">", "~", "||", "|"]; -export { COLORS_NAMES, D50, ERRORS, LOC, LOCEND, LOCSRCID, LOCSTA, NAMES_COLORS, OPTIMIZED, PARENT, PROPERTYNAME, RAW, ROOT, STATE, TOKENS, anglePrecision, colorDistancePrecision, colorFuncColorSpace, colorPrecision, colorRange, colorsFunc, combinators, containerFunc, deprecatedSystemColors, e, epsilon, funcLike, gridTemplateFunc, imageFunc, k, mFGT, mFLT, mathFuncs, mediaTypes, nonStandardColors, pageMarginBoxType, pseudoElements, regMatchLinearGradient, regMatchRadialGradient, supportFunc, systemColors, timelineFunc, timingFunc, tokensMap, tokensfuncDefMap, tokensfuncSet, transformFunctions, trimTokenSpace, urlFunc, urlTokenMatcher, whenElseFunc, wildCardFuncs }; +export { COLORS_NAMES, D50, ERRORS, LOC, LOCEND, LOCSRCID, LOCSTA, NAMES_COLORS, OPTIMIZED, PARENT, PROPERTYNAME, RAW, STATE, TOKENS, anglePrecision, colorDistancePrecision, colorFuncColorSpace, colorPrecision, colorRange, colorsFunc, combinators, containerFunc, deprecatedSystemColors, e, epsilon, funcLike, gridTemplateFunc, imageFunc, k, mFGT, mFLT, mathFuncs, mediaTypes, nonStandardColors, pageMarginBoxType, pseudoElements, regMatchLinearGradient, regMatchRadialGradient, supportFunc, systemColors, timelineFunc, timingFunc, tokensMap, tokensfuncDefMap, tokensfuncSet, transformFunctions, trimTokenSpace, urlFunc, urlTokenMatcher, whenElseFunc, wildCardFuncs }; diff --git a/dist/src/lib/syntax/syntax.js b/dist/src/lib/syntax/syntax.js index b9b642ad0..c20438396 100644 --- a/dist/src/lib/syntax/syntax.js +++ b/dist/src/lib/syntax/syntax.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { isOkLabClose } from './color/utils/distance.js'; @@ -15,7 +15,7 @@ import { trimArray } from '../validation/match.js'; import { splitTokenList } from '../validation/utils/list.js'; import { getColorSpace } from './color/utils/colorspace.js'; import { getColorComponents } from './color/utils/components.js'; -import { anglePrecision, nonStandardColors, systemColors, deprecatedSystemColors, COLORS_NAMES, colorsFunc, colorFuncColorSpace, colorPrecision, epsilon } from './constants.js'; +import { anglePrecision, systemColors, deprecatedSystemColors, nonStandardColors, COLORS_NAMES, colorsFunc, colorFuncColorSpace, colorPrecision, epsilon } from './constants.js'; import { getSyntaxConfig } from '../validation/config.js'; // https://www.w3.org/TR/CSS21/syndata.html#syntax @@ -523,7 +523,7 @@ function isFrequency(dimension) { */ // export function isColorspace(token: Token): boolean { // return ( -// token.typ === EnumToken.IdenTokenType && +// typ === EnumToken.IdenTokenType && // [ // "srgb", // "srgb-linear", @@ -729,7 +729,7 @@ function reduceConicColorStops(stops) { * @returns */ function isRectangularOrthogonalColorspace(token) { - return (token.typ === EnumToken.IdenTokenType && + return (typ === EnumToken.IdenTokenType && colorFuncColorSpace.some((t) => equalsIgnoreCase(t, token.val))); } /** @@ -738,7 +738,7 @@ function isRectangularOrthogonalColorspace(token) { * @returns */ function isPolarColorspace(token) { - return (token.typ === EnumToken.IdenTokenType && + return (typ === EnumToken.IdenTokenType && ["hsl", "hwb", "lch", "oklch"].some((t) => equalsIgnoreCase(t, token.val))); } /** @@ -748,7 +748,7 @@ function isPolarColorspace(token) { */ // export function isHueInterpolationMethod(token: Token | Token[]): boolean { // if (!Array.isArray(token)) { -// return token.typ == EnumToken.IdenTokenType && "hue" === (token as IdentToken).val?.toLowerCase?.(); +// return typ == EnumToken.IdenTokenType && "hue" === (token as IdentToken).val?.toLowerCase?.(); // } // // if (token.length != 2 || token[0].typ != EnumToken.IdenTokenType || token[1].typ != EnumToken.IdenTokenType) { @@ -767,26 +767,27 @@ function isPolarColorspace(token) { * @returns */ function isIdentColor(token) { - return (token.typ == EnumToken.ColorTokenType && + return (typ == EnumToken.ColorTokenType && [ColorType.SYS, ColorType.DPSYS, ColorType.LIT].includes(token.kin) && isIdent(token.val)); } // export function isPercentageToken(token: Token): boolean { // return ( -// token.typ == EnumToken.PercentageTokenType || -// (token.typ == EnumToken.NumberTokenType && (token as NumberToken).val == 0) +// typ == EnumToken.PercentageTokenType || +// (typ == EnumToken.NumberTokenType && (token as NumberToken).val == 0) // ); // } -function isColor(token, errors) { - if (token.typ == EnumToken.WildCardFunctionTokenType) { +function isColor(token, arena) { + const typ = arena.getType(token); + if (typ == EnumToken.WildCardFunctionTokenType) { return true; } - if (token.typ == EnumToken.ColorTokenType) { + if (typ == EnumToken.ColorTokenType) { if ("kin" in token && !("chi" in token)) { return true; } } - if (token.typ == EnumToken.IdenTokenType) { + if (typ == EnumToken.IdenTokenType) { const val = token.val.toLowerCase(); if (systemColors.has(val) || deprecatedSystemColors.has(val) || nonStandardColors.has(val)) { return true; @@ -794,7 +795,7 @@ function isColor(token, errors) { // named color return val in COLORS_NAMES || "currentcolor" === val || "transparent" === val; } - if (token.typ === EnumToken.FunctionTokenType || token.typ === EnumToken.ColorTokenType) { + if (typ === EnumToken.FunctionTokenType || typ === EnumToken.ColorTokenType) { // if (!colorsFunc.includes((token as FunctionToken).val.toLowerCase())) { // return false; // } @@ -1074,8 +1075,9 @@ function isColor(token, errors) { } return false; } -function parseColor(token) { - if (token.typ === EnumToken.IdenTokenType) { +function parseColor(token, arena) { + const typ = arena.getType(token); + if (typ === EnumToken.IdenTokenType) { const val = token.val.toLowerCase(); if (nonStandardColors.has(val)) { Object.assign(token, { @@ -1107,7 +1109,7 @@ function parseColor(token) { return token; } } - if (token.typ === EnumToken.ColorTokenType) { + if (typ === EnumToken.ColorTokenType) { if (!("kin" in token) && "val" in token) { // @ts-expect-error token.kin = ColorType[token.val.replaceAll("-", "_").toUpperCase()]; @@ -1453,24 +1455,24 @@ function isWhiteSpace(codepoint) { // } // // return ( -// token.typ === EnumToken.IdenTokenType || -// token.typ === EnumToken.DimensionTokenType || -// token.typ === EnumToken.LengthTokenType || -// token.typ === EnumToken.AngleTokenType || -// token.typ === EnumToken.FlexTokenType || -// token.typ === EnumToken.TimeTokenType || -// token.typ === EnumToken.ResolutionTokenType || -// token.typ === EnumToken.FrequencyTokenType || -// token.typ === EnumToken.NumberTokenType || -// token.typ === EnumToken.ColorTokenType || -// token.typ === EnumToken.FunctionTokenType || -// token.typ === EnumToken.UrlFunctionTokenType || -// token.typ === EnumToken.GridTemplateFuncTokenType || -// token.typ === EnumToken.ImageFunctionTokenType || -// token.typ === EnumToken.TimelineFunctionTokenType || -// token.typ === EnumToken.TimingFunctionTokenType || -// token.typ === EnumToken.MathFunctionTokenType || -// token.typ === EnumToken.TransformFunctionTokenType +// typ === EnumToken.IdenTokenType || +// typ === EnumToken.DimensionTokenType || +// typ === EnumToken.LengthTokenType || +// typ === EnumToken.AngleTokenType || +// typ === EnumToken.FlexTokenType || +// typ === EnumToken.TimeTokenType || +// typ === EnumToken.ResolutionTokenType || +// typ === EnumToken.FrequencyTokenType || +// typ === EnumToken.NumberTokenType || +// typ === EnumToken.ColorTokenType || +// typ === EnumToken.FunctionTokenType || +// typ === EnumToken.UrlFunctionTokenType || +// typ === EnumToken.GridTemplateFuncTokenType || +// typ === EnumToken.ImageFunctionTokenType || +// typ === EnumToken.TimelineFunctionTokenType || +// typ === EnumToken.TimingFunctionTokenType || +// typ === EnumToken.MathFunctionTokenType || +// typ === EnumToken.TransformFunctionTokenType // ); // } // https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Values_and_units#absolute_length_units diff --git a/dist/src/lib/validation/config.js b/dist/src/lib/validation/config.js index 652f5d329..9f9ddcd9a 100644 --- a/dist/src/lib/validation/config.js +++ b/dist/src/lib/validation/config.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import config from '../../data/validation.json.js'; diff --git a/dist/src/lib/validation/match.js b/dist/src/lib/validation/match.js index 128ec27a6..c6804f3ca 100644 --- a/dist/src/lib/validation/match.js +++ b/dist/src/lib/validation/match.js @@ -2,13 +2,13 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { getParsedSyntax, getSyntaxConfig } from './config.js'; import { EnumToken } from '../ast/types.js'; import { ValidationSyntaxGroupEnum, ValidationTokenEnum, MediaFeatureType } from './parser/typedef.js'; -import { LOCSTA, tokensfuncDefMap, tokensfuncSet, funcLike, mFLT, mFGT } from '../syntax/constants.js'; +import { tokensfuncDefMap, LOCSTA, tokensfuncSet, funcLike, mFLT, mFGT } from '../syntax/constants.js'; import { isColor } from '../syntax/syntax.js'; import { equalsIgnoreCase } from '../parser/utils/text.js'; import { cloneNode } from '../ast/clone.js'; @@ -41,11 +41,12 @@ funcTypes.push(EnumToken.FunctionTokenType, EnumToken.PseudoClassFuncTokenType); * @param tokens * @returns */ -function trimArray(tokens) { - while (tokens[0]?.typ === EnumToken.WhitespaceTokenType) { +function trimArray(tokens, options) { + const arena = options.arena; + while (arena.getType(tokens[0]) === EnumToken.WhitespaceTokenType) { tokens.shift(); } - while (tokens[tokens.length - 1]?.typ === EnumToken.WhitespaceTokenType) { + while (arena.getType(tokens[tokens.length - 1]) === EnumToken.WhitespaceTokenType) { tokens.pop(); } return tokens; @@ -133,8 +134,7 @@ function isMFValue(featureName, tokens, isMFRange) { case MediaFeatureType.RatioType: return { valid: true, - success: (tokens.length == 1 && - tokens[0].typ == EnumToken.NumberTokenType) || + success: (tokens.length == 1 && tokens[0].typ == EnumToken.NumberTokenType) || (tokens.length === 3 && tokens[0].typ == EnumToken.NumberTokenType && typeof tokens[0].val === "number" && @@ -156,9 +156,10 @@ function isMFValue(featureName, tokens, isMFRange) { * @param tokens * @returns */ -function createValidationContext(tokens) { - tokens = trimArray(tokens.filter((t) => t.typ !== EnumToken.CommentTokenType)); - if (tokens.at(-1)?.typ === EnumToken.ImportantTokenType) { +function createValidationContext(tokens, options) { + const arena = options.arena; + tokens = trimArray(tokens.filter((t) => arena.getType(t) !== EnumToken.CommentTokenType)); + if (arena.getType(tokens.at(-1)) === EnumToken.ImportantTokenType) { tokens.pop(); trimArray(tokens); } @@ -313,15 +314,22 @@ function createValidationContext(tokens) { function matchSelectorSyntax(stream, errors, options, nested = true) { const stack = []; const tokens = []; + const arena = options.arena; let token; let i = 0; let success = true; - while (i < stream.length && - (stream[i].typ === EnumToken.WhitespaceTokenType || stream[i].typ === EnumToken.CommentTokenType)) { + let typ; + let val; + while (i < stream.length) { + typ = arena.getType(stream[i]); + if (typ !== EnumToken.WhitespaceTokenType && typ !== EnumToken.CommentTokenType) { + break; + } i++; } if (i < stream.length) { - switch (stream[i].typ) { + typ = arena.getType(stream[i]); + switch (typ) { case EnumToken.Plus: case EnumToken.Tilda: case EnumToken.GtTokenType: @@ -335,10 +343,10 @@ function matchSelectorSyntax(stream, errors, options, nested = true) { errors: [ { action: "drop", - message: `Unexpected token ${EnumToken[stream[i].typ]}`, + message: `Unexpected token ${EnumToken[typ]}`, node: stream[i], // @ts-expect-error - location: options.source.getSourceLocation(stream[i][LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(stream[i])), }, ], }; @@ -347,29 +355,31 @@ function matchSelectorSyntax(stream, errors, options, nested = true) { } for (; i < stream.length; i++) { token = stream[i]; - if (token.typ === EnumToken.Star) { - token.typ = EnumToken.UniversalSelectorTokenType; + typ = arena.getType(token); + if (typ === EnumToken.Star) { + typ = EnumToken.UniversalSelectorTokenType; } tokens.push(token); - if (tokensfuncDefMap.has(token.typ)) { - if (stack.length > 0 && selectorNodeTypes.includes(stack.at(-1).typ)) { + if (tokensfuncDefMap.has(typ)) { + if (stack.length > 0 && selectorNodeTypes.includes(arena.getType(stack.at(-1)))) { stack.pop(); } stack.push(token); continue; } if (stack.length > 0 && - selectorNodeTypes.includes(stack.at(-1).typ) && - !selectorNodeTypes.includes(token.typ) && - token.typ !== EnumToken.WhitespaceTokenType && - token.typ !== EnumToken.CommentTokenType && - token.typ !== EnumToken.CDOCOMMTokenType) { + selectorNodeTypes.includes(arena.getType(stack.at(-1))) && + !selectorNodeTypes.includes(typ) && + typ !== EnumToken.WhitespaceTokenType && + typ !== EnumToken.CommentTokenType && + typ !== EnumToken.CDOCOMMTokenType) { stack.pop(); } - switch (token.typ) { + switch (typ) { case EnumToken.PseudoClassFuncTokenType: { - const result = matchAllSyntaxes(getParsedSyntax(ValidationSyntaxGroupEnum.Selectors, token.val + "()")?.[0]?.chi ?? [], createValidationContext(token.chi), options); + val = arena.getValue(token); + const result = matchAllSyntaxes(getParsedSyntax(ValidationSyntaxGroupEnum.Selectors, val + "()")?.[0]?.chi ?? [], createValidationContext(arena.children(token), options), options); if (!result.success) { success = false; if (result.errors.length > 0) { @@ -394,41 +404,43 @@ function matchSelectorSyntax(stream, errors, options, nested = true) { message: `Nesting selector is not allowed`, node: token, // @ts-expect-error - location: options.source.getSourceLocation(token[LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(token)), }, ], }; } break; case EnumToken.Plus: - Object.assign(token, { typ: EnumToken.NextSiblingCombinatorTokenType }); + arena.set(token, EnumToken.SubsequentSiblingCombinatorTokenType, 0); + // Object.assign(token, { typ: EnumToken.NextSiblingCombinatorTokenType }); stack.push(token); break; case EnumToken.Tilda: case EnumToken.GtTokenType: - Object.assign(token, { typ: selectorEnumMap.get(token.typ) }); + arena.set(token, selectorEnumMap.get(typ)); + // Object.assign(token, { typ: selectorEnumMap.get(token.typ)! }); case EnumToken.ColumnCombinatorTokenType: case EnumToken.ChildCombinatorTokenType: case EnumToken.UniversalSelectorTokenType: case EnumToken.DescendantCombinatorTokenType: case EnumToken.NextSiblingCombinatorTokenType: case EnumToken.SubsequentSiblingCombinatorTokenType: - if (stream[i + 1]?.typ === EnumToken.WhitespaceTokenType) { + if (arena.getType(stream[i + 1]) === EnumToken.WhitespaceTokenType) { i++; } - if (stack.length > 0 && stack.at(-1)?.typ === EnumToken.UniversalSelectorTokenType) { + if (stack.length > 0 && arena.getType(stack.at(-1)) === EnumToken.UniversalSelectorTokenType) { stack.pop(); } - if (stack.length > 0 && selectorNodeTypes.includes(stack.at(-1)?.typ)) { + if (stack.length > 0 && selectorNodeTypes.includes(arena.getType(stack.at(-1)))) { return { success: false, errors: [ { action: "drop", - message: `Unexpected combinator ${EnumToken[token.typ]}`, + message: `Unexpected combinator ${EnumToken[typ]}`, node: token, // @ts-expect-error - location: options.source.getSourceLocation(token[LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(token)), }, ], }; @@ -436,10 +448,20 @@ function matchSelectorSyntax(stream, errors, options, nested = true) { stack.push(token); break; case EnumToken.CommaTokenType: - if (stack.length > 0 && stack.at(-1).typ === EnumToken.UniversalSelectorTokenType) { + if (stack.length > 0 && arena.getType(stack.at(-1)) === EnumToken.UniversalSelectorTokenType) { stack.pop(); } stack.push(token); + if (!nested) { + let k = i + 1; + while (k < stream.length) { + typ = arena.getType(stream[k]); + if (typ !== EnumToken.WhitespaceTokenType && typ !== EnumToken.CommentTokenType) { + break; + } + k++; + } + } break; case EnumToken.Pipe: case EnumToken.DelimTokenType: @@ -460,19 +482,19 @@ function matchSelectorSyntax(stream, errors, options, nested = true) { stack.push(token); break; case EnumToken.AttrEndTokenType: - if (stack.length > 0 && stack.at(-1).typ === EnumToken.UniversalSelectorTokenType) { + if (stack.length > 0 && arena.getType(stack.at(-1)) === EnumToken.UniversalSelectorTokenType) { stack.pop(); } - if (stack.at(-1)?.typ !== EnumToken.AttrStartTokenType) { + if (arena.getType(stack.at(-1)) !== EnumToken.AttrStartTokenType) { return { success: false, errors: [ { action: "drop", - message: `Unexpected token ${EnumToken[token.typ]}`, + message: `Unexpected token ${EnumToken[typ]}`, node: token, // @ts-expect-error - location: options.source.getSourceLocation(token[LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(token)), }, ], }; @@ -482,17 +504,18 @@ function matchSelectorSyntax(stream, errors, options, nested = true) { let index = tokens.indexOf(stack.at(-1)); const slice = []; for (let n = index + 1; n < k; n++) { - if (tokens[n].typ === EnumToken.WhitespaceTokenType || - tokens[n].typ === EnumToken.CommentTokenType || - tokens[n].typ === EnumToken.CDOCOMMTokenType) { + typ = arena.getType(tokens[n]); + if (typ === EnumToken.WhitespaceTokenType || + typ === EnumToken.CommentTokenType || + typ === EnumToken.CDOCOMMTokenType) { continue; } slice.push(tokens[n]); } - if (slice[1]?.typ === EnumToken.Pipe) { + if (arena.getType(slice[1]) === EnumToken.Pipe) { slice.shift(); } - if (slice[0].typ === EnumToken.Pipe) { + if (arena.getType(slice[0]) === EnumToken.Pipe) { slice.shift(); } if (slice.length === 1) { @@ -501,29 +524,29 @@ function matchSelectorSyntax(stream, errors, options, nested = true) { } slice.shift(); slice.shift(); - if (slice[0]?.typ === EnumToken.StringTokenType && - /^[a-zA-Z0-9_-]+$/.test(slice[0].val.slice(1, -1))) { - Object.assign(slice[0], { - typ: EnumToken.IdenTokenType, - val: slice[0].val.slice(1, -1), - }); + if (arena.getType(slice[0]) === EnumToken.StringTokenType) { + val = arena.getValue(slice[0]); + if (/^[a-zA-Z0-9_-]+$/.test(val.slice(1, -1))) { + arena.set(slice[0], EnumToken.IdenTokenType, val.slice(1, -1)); + } } slice.shift(); if (slice.length === 0) { stack.pop(); break; } - if (slice[0].typ !== EnumToken.IdenTokenType || - (slice[0].val != "i" && slice[0].val != "s")) { + val = arena.getValue(slice[0]); + if (arena.getType(slice[0]) !== EnumToken.IdenTokenType || + (val != "i" && val != "s")) { return { success: false, errors: [ { action: "drop", - message: `Unexpected token ${EnumToken[slice[0].typ]}`, + message: `Unexpected token ${EnumToken[arena.getType(slice[0])]}`, node: slice[0], // @ts-expect-error - location: options.source.getSourceLocation(slice[0][LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(slice[0])), }, ], }; @@ -538,19 +561,19 @@ function matchSelectorSyntax(stream, errors, options, nested = true) { errors: [ { action: "drop", - message: `Unexpected token ${EnumToken[token.typ]}`, + message: `Unexpected token ${EnumToken[typ]}`, node: token, // @ts-expect-error - location: options.source.getSourceLocation(token[LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(token)), }, ], }; case EnumToken.EndParensTokenType: - if (stack.length > 0 && stack.at(-1)?.typ === EnumToken.UniversalSelectorTokenType) { + if (stack.length > 0 && arena.getType(stack.at(-1)) === EnumToken.UniversalSelectorTokenType) { stack.pop(); } - if (stack.at(-1)?.typ === EnumToken.PseudoClassFunctionTokenDefType || - stack.at(-1)?.typ === EnumToken.PseudoElementTokenType) { + if (arena.getType(stack.at(-1)) === EnumToken.PseudoClassFunctionTokenDefType || + arena.getType(stack.at(-1)) === EnumToken.PseudoElementTokenType) { const token = stack.at(-1); const index = tokens.indexOf(token); const result = matchAllSyntaxes(getParsedSyntax(ValidationSyntaxGroupEnum.Selectors, token.val + "()")?.[0]?.chi ?? [], createValidationContext(tokens.slice(index + 1, tokens.length - 1)), options); @@ -570,10 +593,10 @@ function matchSelectorSyntax(stream, errors, options, nested = true) { errors: [ { action: "drop", - message: `Unexpected token ${EnumToken[token.typ]}`, + message: `Unexpected token ${EnumToken[typ]}`, node: token, // @ts-expect-error - location: options.source.getSourceLocation(token[LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(token)), }, ], }; @@ -583,7 +606,7 @@ function matchSelectorSyntax(stream, errors, options, nested = true) { // if (stack.at(-1)?.typ === EnumToken.CommaTokenType) { // stack.pop(); // } - if (stack.at(-1)?.typ === EnumToken.PseudoClassFunctionTokenDefType) { + if (arena.getType(stack.at(-1)) === EnumToken.PseudoClassFunctionTokenDefType) { break; } default: @@ -592,10 +615,10 @@ function matchSelectorSyntax(stream, errors, options, nested = true) { errors: [ { action: "drop", - message: `Unsupported selector token ${EnumToken[token.typ]}`, + message: `Unsupported selector token ${EnumToken[typ]}`, node: token, // @ts-expect-error - location: options.source.getSourceLocation(token[LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(token)), }, ], }; @@ -609,7 +632,7 @@ function matchSelectorSyntax(stream, errors, options, nested = true) { tokens.splice(tokens.length - 2, 1); } } - if (stack.length > 0 && stack.at(-1).typ === EnumToken.UniversalSelectorTokenType) { + if (stack.length > 0 && arena.getType(stack.at(-1)) === EnumToken.UniversalSelectorTokenType) { stack.pop(); } if (stack.length > 0) { @@ -618,10 +641,10 @@ function matchSelectorSyntax(stream, errors, options, nested = true) { errors: [ { action: "drop", - message: `Unmatched token ${EnumToken[stack.at(-1).typ]}`, + message: `Unmatched token ${EnumToken[arena.getType(stack.at(-1))]}`, node: stack.at(-1), // @ts-expect-error - location: options.source.getSourceLocation(stack.at(-1)[LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(stack.at(-1))), }, ], }; @@ -1702,17 +1725,15 @@ function matchProperty(property, context, options) { case "display-internal": case "display-outside": case "display-legacy": - case "content-position": - { - let values = propertyKeywordValues.get(property.val); - if (values == null) { - values = new Set(config.syntaxes[property.val].syntax.split(/[\s|]+/)); - propertyKeywordValues.set(property.val, values); - } - success = - token?.typ == EnumToken.IdenTokenType && values.has(token.val.toLowerCase()); - break; + case "content-position": { + let values = propertyKeywordValues.get(property.val); + if (values == null) { + values = new Set(config.syntaxes[property.val].syntax.split(/[\s|]+/)); + propertyKeywordValues.set(property.val, values); } + success = token?.typ == EnumToken.IdenTokenType && values.has(token.val.toLowerCase()); + break; + } case "mf-name": { const token = context.peek(); @@ -1732,8 +1753,7 @@ function matchProperty(property, context, options) { case "custom-ident": case "counter-name": case "counter-style-name": - success = - token?.typ == EnumToken.IdenTokenType || token?.typ == EnumToken.DashedIdenTokenType; + success = token?.typ == EnumToken.IdenTokenType || token?.typ == EnumToken.DashedIdenTokenType; if (success && token?.typ === EnumToken.IdenTokenType) { const val = token.val.toLowerCase(); success = "none" !== val && !allValues.includes(val); diff --git a/dist/src/lib/validation/parser/parse.js b/dist/src/lib/validation/parser/parse.js index 92af8c910..b13602fdc 100644 --- a/dist/src/lib/validation/parser/parse.js +++ b/dist/src/lib/validation/parser/parse.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { EnumToken } from '../../ast/types.js'; diff --git a/dist/src/lib/validation/parser/typedef.js b/dist/src/lib/validation/parser/typedef.js index 2e34c3ce9..84a353764 100644 --- a/dist/src/lib/validation/parser/typedef.js +++ b/dist/src/lib/validation/parser/typedef.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ var ValidationTokenEnum; diff --git a/dist/src/lib/validation/utils/list.js b/dist/src/lib/validation/utils/list.js index 8d252ab8b..8d3b3bb0f 100644 --- a/dist/src/lib/validation/utils/list.js +++ b/dist/src/lib/validation/utils/list.js @@ -2,24 +2,28 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { EnumToken } from '../../ast/types.js'; -function stripCommaToken(tokenList) { +function stripCommaToken(tokenList, options) { let result = []; + let typ; + const arena = options.arena; for (let i = 0; i < tokenList.length; i++) { - if (tokenList[i].typ == EnumToken.CommentTokenType || tokenList[i].typ == EnumToken.CommaTokenType) { + typ = arena.getType(tokenList[i]); + if (typ == EnumToken.CommentTokenType || typ == EnumToken.CommaTokenType) { continue; } result.push(tokenList[i]); } return result; } -function splitTokenList(tokenList, split = [EnumToken.CommaTokenType], includeSplitToken = false) { +function splitTokenList(tokenList, options, split = [EnumToken.CommaTokenType], includeSplitToken = false) { + const arena = options.arena; return tokenList.reduce((acc, curr) => { - if (split.includes(curr.typ)) { + if (split.includes(arena.getType(curr))) { if (includeSplitToken && Array.isArray(acc[acc.length - 1])) { acc[acc.length - 1].push(curr); } diff --git a/dist/src/node.js b/dist/src/node.js index 19e87891d..016b234b0 100644 --- a/dist/src/node.js +++ b/dist/src/node.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { deprecate } from 'node:util'; @@ -12,9 +12,8 @@ import { lstat, readFile } from 'node:fs/promises'; import { doParse, doParseSync } from './lib/parser/parse.js'; export { parseDeclarations, parseString } from './lib/parser/parse.js'; import { doRender } from './lib/printer/render.js'; -export { renderValue as renderToken } from './lib/printer/render.js'; import { ModuleScopeEnumOptions } from './lib/ast/types.js'; -export { ColorType, EnumAstNodeStatus, EnumToken, ModuleCaseTransformEnum, ValidationLevel } from './lib/ast/types.js'; +export { ColorType, EnumAstNodeStatus, EnumToken, ModuleCaseTransformEnum } from './lib/ast/types.js'; import { Tokenizer } from './lib/parser/tokenize.js'; import { dirname, resolve, matchUrl } from './lib/fs/resolve.js'; import { ResponseType } from './types.js'; @@ -31,6 +30,7 @@ export { isOkLabClose, okLabDistance } from './lib/syntax/color/utils/distance.j export { find, findAll, findByValue, findLast } from './lib/ast/find.js'; export { cloneNode } from './lib/ast/clone.js'; export { replaceNodeOrValue } from './lib/parser/utils/token.js'; +export { printValue } from './lib/printer/print.js'; export { SourceMap } from './lib/printer/sourcemap/sourcemap.js'; export { FeatureWalkMode } from './lib/ast/features/type.js'; export { getNodeProperty, setNodeProperty } from './lib/ast/node.js'; @@ -179,7 +179,7 @@ function parseSync(...args) { options ??= {}; options.src ??= ""; options.sourcesMap ??= new Map(); - options.arena ??= new Arena(); + options.arena ??= new Arena(2 << Math.ceil(2 + Math.log2(stream.length))); Object.assign(options, { resolve, dirname, @@ -336,6 +336,7 @@ async function parse(...args) { options ??= {}; options.src ??= ""; options.sourcesMap ??= new Map(); + options.arena ??= new Arena(typeof stream == "string" ? 2 << Math.ceil(2 + Math.log2(stream.length)) : 4096); Object.assign(options, { load, resolve, diff --git a/dist/src/types.js b/dist/src/types.js index 7e5156e45..47a66a090 100644 --- a/dist/src/types.js +++ b/dist/src/types.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ /** diff --git a/dist/src/utils/sync.js b/dist/src/utils/sync.js index ae6ab6b61..a20a2061e 100644 --- a/dist/src/utils/sync.js +++ b/dist/src/utils/sync.js @@ -2,7 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { EnumToken } from '../lib/ast/types.js'; diff --git a/dist/src/web.js b/dist/src/web.js index 62e2b7d9a..f7061c8f0 100644 --- a/dist/src/web.js +++ b/dist/src/web.js @@ -2,20 +2,20 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela - Version: 1.6.7 + Version: 2.0.0-alpha.1 License: MIT */ import { doParse, doParseSync } from './lib/parser/parse.js'; export { parseDeclarations, parseString } from './lib/parser/parse.js'; import { doRender } from './lib/printer/render.js'; -export { renderValue as renderToken } from './lib/printer/render.js'; import { ModuleScopeEnumOptions } from './lib/ast/types.js'; -export { ColorType, EnumAstNodeStatus, EnumToken, ModuleCaseTransformEnum, ValidationLevel } from './lib/ast/types.js'; +export { ColorType, EnumAstNodeStatus, EnumToken, ModuleCaseTransformEnum } from './lib/ast/types.js'; import { Tokenizer } from './lib/parser/tokenize.js'; import { matchUrl, resolve, dirname } from './lib/fs/resolve.js'; import { ResponseType } from './types.js'; import { SourceFile } from './lib/parser/source.js'; import { parseResult, validateSyncArguments } from './utils/sync.js'; +import { Arena } from './lib/parser/arena.js'; export { minify } from './lib/ast/minify.js'; export { expand } from './lib/ast/expand.js'; export { WalkerEvent, WalkerOptionEnum, walk, walkValues } from './lib/ast/walk.js'; @@ -24,7 +24,7 @@ export { isOkLabClose, okLabDistance } from './lib/syntax/color/utils/distance.j export { find, findAll, findByValue, findLast } from './lib/ast/find.js'; export { cloneNode } from './lib/ast/clone.js'; export { replaceNodeOrValue } from './lib/parser/utils/token.js'; -export { PropertyList } from './lib/parser/declaration/list.js'; +export { printValue } from './lib/printer/print.js'; export { SourceMap } from './lib/printer/sourcemap/sourcemap.js'; export { FeatureWalkMode } from './lib/ast/features/type.js'; export { getNodeProperty, setNodeProperty } from './lib/ast/node.js'; @@ -173,6 +173,7 @@ function parseSync(...args) { options ??= {}; options.src ??= ""; options.sourcesMap ??= new Map(); + options.arena ??= new Arena(2 << Math.ceil(2 + Math.log2(stream.length))); Object.assign(options, { resolve, dirname, @@ -181,6 +182,7 @@ function parseSync(...args) { : dirname(self.location.pathname), }); options.src = resolve(options.src, options.cwd).relative; + options.arena ??= new Arena(typeof stream == "string" ? 2 << Math.ceil(2 + Math.log2(stream.length)) : 4096); if (options.source == null) { const source = new SourceFile(typeof stream == "string" ? stream : "", [], options.src); options.sourcesMap.set(source.id, source); diff --git a/src/@types/index.d.ts b/src/@types/index.d.ts index 67175951a..515762641 100644 --- a/src/@types/index.d.ts +++ b/src/@types/index.d.ts @@ -7,7 +7,7 @@ import type { import type { AstAtRule, AstDeclaration, AstNode, AstRule, AstStyleSheet } from "./ast.d.ts"; import { SourceMap } from "../lib/printer/sourcemap/sourcemap.ts"; import type { PropertyListOptions } from "./parse.d.ts"; -import { EnumToken, ModuleCaseTransformEnum, ModuleScopeEnumOptions, ValidationLevel } from "../lib/ast/types.ts"; +import { EnumToken, ModuleCaseTransformEnum, ModuleScopeEnumOptions } from "../lib/ast/types.ts"; import type { CssVariableToken, Token } from "./token.d.ts"; import { FeatureWalkMode } from "../lib/ast/features/type.ts"; import { ValidationToken } from "../lib/validation/parser/types"; @@ -75,11 +75,10 @@ export interface ValidationOptions { nestedRule?: boolean; /** - * Enable CSS validation. Using ValidationLevel as value is deprecated and will be removed + * Enable CSS validation * - * see {@link ValidationLevel} */ - validation?: boolean | ValidationLevel; + validation?: boolean ; /** * Lenient validation. retain nodes that failed validation diff --git a/src/lib/parser/arena.ts b/src/lib/parser/arena.ts index 0e5f3c227..daacd55a2 100644 --- a/src/lib/parser/arena.ts +++ b/src/lib/parser/arena.ts @@ -1,12 +1,5 @@ import { - AstNode, - AstRuleList, ErrorDescription, - - FunctionToken, - ParensToken, - Token, - WalkAttributesResult, WalkerEvent, WalkerFilter, WalkerOption, @@ -15,7 +8,7 @@ import { WalkerValueFilter, } from "../../node.ts"; import { EnumAstNodeStatus, EnumToken } from "../ast/types.ts"; -import { PARENT, TOKENS } from "../syntax/constants.ts"; +import { PARENT } from "../syntax/constants.ts"; import { equalsIgnoreCase } from "./utils/text.ts"; export class Arena { @@ -37,10 +30,6 @@ export class Arena { public numericValue: Float64Array; public unit: Uint32Array; public flags: Uint32Array; - // public left: Uint32Array; - // public right: Uint32Array; - // public op: Uint32Array; - // public op2: Uint32Array; public colorType: Uint8Array; /** @@ -48,6 +37,9 @@ export class Arena { */ public spans: BigUint64Array; + /** + * error cache + */ public errors: Map = new Map(); /** * rendered tokens cache @@ -176,7 +168,6 @@ export class Arena { } getValue(id: number): string { - return this.strings[this.stringValue[id]] ?? null; } @@ -374,7 +365,7 @@ export class Arena { } getLeft(id: number): number { - return this.getChildAt(id, 0 ) as number; + return this.getChildAt(id, 0) as number; } setRight(id: number, rightId: number): void { @@ -802,7 +793,7 @@ export class Arena { this.parents[children[0]] = id; - for (let i = 1; i < children.length - 1; i++) { + for (let i = 1; i < children.length; i++) { this.detach(children[i]); this.previousSibling[children[i]] = children[i - 1]; this.nextSibling[children[i - 1]] = children[i]; diff --git a/src/lib/parser/utils/declaration.ts b/src/lib/parser/utils/declaration.ts index 26b6432ab..350f74d33 100644 --- a/src/lib/parser/utils/declaration.ts +++ b/src/lib/parser/utils/declaration.ts @@ -396,7 +396,7 @@ export function parseDeclaration( ); arena.setChildren(tokens[index], ...trimArray(tokens.splice(index + 1, i - index - 1), options)); - + // Object.assign(tokens[index], { // typ: // stack.at(-1)?.typ === EnumToken.StartParensTokenType diff --git a/test/specs/code/color-a98-rgb.js b/test/specs/code/color-a98-rgb.js index d77a10a52..9014b3e3d 100644 --- a/test/specs/code/color-a98-rgb.js +++ b/test/specs/code/color-a98-rgb.js @@ -1,604 +1,715 @@ -import {ColorType, EnumToken} from "../../../dist/src/lib/ast/types.js"; +import { ColorType, EnumToken } from "../../../dist/src/lib/ast/types.js"; import { parseString } from "../../../dist/src/lib/parser/parse.js"; -import {isOkLabClose} from "../../../dist/src/lib/syntax/color/utils/distance.js"; -import {convertColor} from "../../../dist/src/lib/syntax/color/color.js"; +import { isOkLabClose } from "../../../dist/src/lib/syntax/color/utils/distance.js"; +import { convertColor } from "../../../dist/src/lib/syntax/color/color.js"; export function run(describe, expect, it, transform, parse, render) { - - describe('convert to a98-rgb', function () { - - it('rgb(a) to a98-rgb #1', function () { + describe("convert to a98-rgb", function () { + it("rgb(a) to a98-rgb #1", function () { return transform(`.hsl { color: rgb(179 34 34/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "a98-rgb" - }, - { - typ: EnumToken.NumberTokenType, - val: ".6014734178208441" - }, { - typ: EnumToken.NumberTokenType, - val: ".15252996199964947" - }, { - typ: EnumToken.NumberTokenType, - val: ".15252996199964927" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "a98-rgb", + }, + { + typ: EnumToken.NumberTokenType, + val: ".6014734178208441", + }, + { + typ: EnumToken.NumberTokenType, + val: ".15252996199964947", + }, + { + typ: EnumToken.NumberTokenType, + val: ".15252996199964927", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('hsl(a) to a98-rgb #2', function () { + it("hsl(a) to a98-rgb #2", function () { return transform(`.hsl { color: hsl(0 68.07511737089203% 41.764705882352935%/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "a98-rgb" - }, - { - typ: EnumToken.NumberTokenType, - val: ".6014734178208441" - }, { - typ: EnumToken.NumberTokenType, - val: ".15252996199964947" - }, { - typ: EnumToken.NumberTokenType, - val: ".15252996199964927" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "a98-rgb", + }, + { + typ: EnumToken.NumberTokenType, + val: ".6014734178208441", + }, + { + typ: EnumToken.NumberTokenType, + val: ".15252996199964947", + }, + { + typ: EnumToken.NumberTokenType, + val: ".15252996199964927", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('device-cmyk() to a98-rgb #3', function () { - return transform(`.hsl { color: device-cmyk(0 81.00558659217877% 81.00558659217877% 29.803921568627455%/50%); }`, { - beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "a98-rgb" - }, - { - typ: EnumToken.NumberTokenType, - val: ".6014734178208441" - }, { - typ: EnumToken.NumberTokenType, - val: ".15252996199964947" - }, { - typ: EnumToken.NumberTokenType, - val: ".15252996199964927" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + it("device-cmyk() to a98-rgb #3", function () { + return transform( + `.hsl { color: device-cmyk(0 81.00558659217877% 81.00558659217877% 29.803921568627455%/50%); }`, + { + beautify: true, + convertColor: ColorType.SRGB_LINEAR, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "a98-rgb", + }, + { + typ: EnumToken.NumberTokenType, + val: ".6014734178208441", + }, + { + typ: EnumToken.NumberTokenType, + val: ".15252996199964947", + }, + { + typ: EnumToken.NumberTokenType, + val: ".15252996199964927", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('hwb() to a98-rgb #4', function () { + it("hwb() to a98-rgb #4", function () { return transform(`.hsl { color: hwb(0 13.333333333333334% 30%/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "a98-rgb" - }, - { - typ: EnumToken.NumberTokenType, - val: .599797 - }, { - typ: EnumToken.NumberTokenType, - val: .15253 - }, { - typ: EnumToken.NumberTokenType, - val: .15253 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "a98-rgb", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.599797, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.15253, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.15253, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('hwb() to a98-rgb #5', function () { + it("hwb() to a98-rgb #5", function () { return transform(`.hsl { color: #b3222280; }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "a98-rgb" - }, - { - typ: EnumToken.NumberTokenType, - val: ".6014734178208441" - }, { - typ: EnumToken.NumberTokenType, - val: ".15252996199964947" - }, { - typ: EnumToken.NumberTokenType, - val: ".15252996199964927" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "a98-rgb", + }, + { + typ: EnumToken.NumberTokenType, + val: ".6014734178208441", + }, + { + typ: EnumToken.NumberTokenType, + val: ".15252996199964947", + }, + { + typ: EnumToken.NumberTokenType, + val: ".15252996199964927", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('srgb-linear to a98-rgb #6', function () { - return transform(`.hsl { color: color(srgb-linear .45078578283822346 .01599629336550963 .01599629336550963/50%); }`, { - beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "a98-rgb" - }, - { - typ: EnumToken.NumberTokenType, - val: ".6014734178208441" - }, { - typ: EnumToken.NumberTokenType, - val: ".15252996199964947" - }, { - typ: EnumToken.NumberTokenType, - val: ".15252996199964927" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + it("srgb-linear to a98-rgb #6", function () { + return transform( + `.hsl { color: color(srgb-linear .45078578283822346 .01599629336550963 .01599629336550963/50%); }`, + { + beautify: true, + convertColor: ColorType.SRGB_LINEAR, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "a98-rgb", + }, + { + typ: EnumToken.NumberTokenType, + val: ".6014734178208441", + }, + { + typ: EnumToken.NumberTokenType, + val: ".15252996199964947", + }, + { + typ: EnumToken.NumberTokenType, + val: ".15252996199964927", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('display-p3 to a98-rgb #7', function () { - return transform(`.hsl { color: color(display-p3 0.644980276448 0.191199800941 0.165770885403 / 0.501960784314); }`, { - beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "a98-rgb" - }, - { - typ: EnumToken.NumberTokenType, - val: ".6014734178208441" - }, { - typ: EnumToken.NumberTokenType, - val: ".15252996199964947" - }, { - typ: EnumToken.NumberTokenType, - val: ".15252996199964927" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + it("display-p3 to a98-rgb #7", function () { + return transform( + `.hsl { color: color(display-p3 0.644980276448 0.191199800941 0.165770885403 / 0.501960784314); }`, + { + beautify: true, + convertColor: ColorType.SRGB_LINEAR, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "a98-rgb", + }, + { + typ: EnumToken.NumberTokenType, + val: ".6014734178208441", + }, + { + typ: EnumToken.NumberTokenType, + val: ".15252996199964947", + }, + { + typ: EnumToken.NumberTokenType, + val: ".15252996199964927", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('srgb to a98-rgb #8', function () { - return transform(`.hsl { color: color(srgb .7019607843137254 .13333333333333333 .13333333333333333/50%); }`, { - beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "a98-rgb" - }, - { - typ: EnumToken.NumberTokenType, - val: ".6014734178208441" - }, { - typ: EnumToken.NumberTokenType, - val: ".15252996199964947" - }, { - typ: EnumToken.NumberTokenType, - val: ".15252996199964927" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + it("srgb to a98-rgb #8", function () { + return transform( + `.hsl { color: color(srgb .7019607843137254 .13333333333333333 .13333333333333333/50%); }`, + { + beautify: true, + convertColor: ColorType.SRGB_LINEAR, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "a98-rgb", + }, + { + typ: EnumToken.NumberTokenType, + val: ".6014734178208441", + }, + { + typ: EnumToken.NumberTokenType, + val: ".15252996199964947", + }, + { + typ: EnumToken.NumberTokenType, + val: ".15252996199964927", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('photo-rgb to a98-rgb #9', function () { + it("photo-rgb to a98-rgb #9", function () { return transform(`.hsl { color: color(prophoto-rgb 0.4589 0.2071 0.124 / 0.50); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "a98-rgb" - }, - { - typ: EnumToken.NumberTokenType, - val: .601435 - }, { - typ: EnumToken.NumberTokenType, - val:.152578 - }, { - typ: EnumToken.NumberTokenType, - val: .152566 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "a98-rgb", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.601435, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.152578, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.152566, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('xyz to a98-rgb #11', function () { + it("xyz to a98-rgb #11", function () { return transform(`.hsl { color: color(xyz-d65 0.1945 0.1084 0.0258 / 0.50); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "a98-rgb" - }, - { - typ: EnumToken.NumberTokenType, - val:.601492 - }, { - typ: EnumToken.NumberTokenType, - val: .152442 - }, { - typ: EnumToken.NumberTokenType, - val: .152151 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - }, - .001 - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose( + result.ast.chi[0].chi[0].val[0], + { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "a98-rgb", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.601492, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.152442, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.152151, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }, + 0.001, + ), + ).equals(true), + ); }); - it('xyz-d50 to a98-rgb #12', function () { + it("xyz-d50 to a98-rgb #12", function () { return transform(`.hsl { color: color(xyz-d50 0.205 0.1127 0.0193 / 0.50); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "a98-rgb" - }, - { - typ: EnumToken.NumberTokenType, - val: .601441 - }, { - typ: EnumToken.NumberTokenType, - val: .152825 - }, { - typ: EnumToken.NumberTokenType, - val:.152348 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - }, - .001 - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose( + result.ast.chi[0].chi[0].val[0], + { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "a98-rgb", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.601441, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.152825, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.152348, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }, + 0.001, + ), + ).equals(true), + ); }); - it('xyz-d65 to a98-rgb #13', function () { + it("xyz-d65 to a98-rgb #13", function () { return transform(`.hsl { color: color(xyz-d65 0.1945 0.1084 0.0258 / 0.50); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "a98-rgb" - }, - { - typ: EnumToken.NumberTokenType, - val: .601492 - }, { - typ: EnumToken.NumberTokenType, - val: .152442 - }, { - typ: EnumToken.NumberTokenType, - val: .152151 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - }, - .001 - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose( + result.ast.chi[0].chi[0].val[0], + { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "a98-rgb", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.601492, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.152442, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.152151, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }, + 0.001, + ), + ).equals(true), + ); }); - it('oklab to a98-rgb #14', function () { + it("oklab to a98-rgb #14", function () { return transform(`.hsl { color: oklab(.49863253097209403 .16117650019451524 .08155524287358212/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "a98-rgb" - }, - { - typ: EnumToken.NumberTokenType, - val: .601473 - }, { - typ: EnumToken.NumberTokenType, - val: .15253 - }, { - typ: EnumToken.NumberTokenType, - val: .15253 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "a98-rgb", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.601473, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.15253, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.15253, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('oklch to a98-rgb #15', function () { + it("oklch to a98-rgb #15", function () { return transform(`.hsl { color: oklch(.49863253097209403 .1806353283694016 26.839391611662446/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "a98-rgb" - }, - { - typ: EnumToken.NumberTokenType, - val: ".6014734178208441" - }, { - typ: EnumToken.NumberTokenType, - val: ".15252996199964947" - }, { - typ: EnumToken.NumberTokenType, - val: ".15252996199964927" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "a98-rgb", + }, + { + typ: EnumToken.NumberTokenType, + val: ".6014734178208441", + }, + { + typ: EnumToken.NumberTokenType, + val: ".15252996199964947", + }, + { + typ: EnumToken.NumberTokenType, + val: ".15252996199964927", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('lab to a98-rgb #16', function () { + it("lab to a98-rgb #16", function () { return transform(`.hsl { color: lab(40.03718216996103 56.88417629340889 39.4648992397212/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "a98-rgb" - }, - { - typ: EnumToken.NumberTokenType, - val: ".6014734178208441" - }, { - typ: EnumToken.NumberTokenType, - val: ".15252996199964947" - }, { - typ: EnumToken.NumberTokenType, - val: ".15252996199964927" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "a98-rgb", + }, + { + typ: EnumToken.NumberTokenType, + val: ".6014734178208441", + }, + { + typ: EnumToken.NumberTokenType, + val: ".15252996199964947", + }, + { + typ: EnumToken.NumberTokenType, + val: ".15252996199964927", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('lch to a98-rgb #17', function () { + it("lch to a98-rgb #17", function () { return transform(`.hsl { color: lch(40.03718216996103 69.23357411387173 34.75200047889167/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "a98-rgb" - }, - { - typ: EnumToken.NumberTokenType, - val: ".6014734178208441" - }, { - typ: EnumToken.NumberTokenType, - val: ".15252996199964947" - }, { - typ: EnumToken.NumberTokenType, - val: ".15252996199964927" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "a98-rgb", + }, + { + typ: EnumToken.NumberTokenType, + val: ".6014734178208441", + }, + { + typ: EnumToken.NumberTokenType, + val: ".15252996199964947", + }, + { + typ: EnumToken.NumberTokenType, + val: ".15252996199964927", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - - it('lch to a98-rgb #18', function () { - return expect(isOkLabClose(parseString('lch(40.03718216996103 69.23357411387173 34.75200047889167/50%)')[0], parseString('color(a98-rgb .601473 .15253 .15253/50%)')[0] - )).equals(true); + + it("lch to a98-rgb #18", function () { + return expect( + isOkLabClose( + parseString("lch(40.03718216996103 69.23357411387173 34.75200047889167/50%)")[0], + parseString("color(a98-rgb .601473 .15253 .15253/50%)")[0], + ), + ).equals(true); }); - - it('srgb to a98-rgb #19', function () { - return expect(isOkLabClose(parseString('color(srgb .701961 .133333 .133333/50%)')[0], parseString('color(a98-rgb .601473 .15253 .15253/50%)')[0] - )).equals(true); + + it("srgb to a98-rgb #19", function () { + return expect( + isOkLabClose( + parseString("color(srgb .701961 .133333 .133333/50%)")[0], + parseString("color(a98-rgb .601473 .15253 .15253/50%)")[0], + ), + ).equals(true); }); - - it('srgb to a98-rgb #20', function () { - return expect(isOkLabClose(convertColor(parseString('color(srgb .701961 .133333 .133333/50%)')[0], ColorType.A98_RGB), parseString('color(a98-rgb .601473 .15253 .15253/50%)')[0] - )).equals(true); + + it("srgb to a98-rgb #20", function () { + return expect( + isOkLabClose( + convertColor(parseString("color(srgb .701961 .133333 .133333/50%)")[0], ColorType.A98_RGB), + parseString("color(a98-rgb .601473 .15253 .15253/50%)")[0], + ), + ).equals(true); }); }); } diff --git a/test/specs/code/color-cmyk.js b/test/specs/code/color-cmyk.js index 68c8078ae..b8c97e117 100644 --- a/test/specs/code/color-cmyk.js +++ b/test/specs/code/color-cmyk.js @@ -1,331 +1,418 @@ -import {ColorType, EnumToken} from "../../../dist/src/lib/ast/types.js"; -import {isOkLabClose} from "../../../dist/src/lib/syntax/color/utils/distance.js"; +import { ColorType, EnumToken } from "../../../dist/src/lib/ast/types.js"; +import { isOkLabClose } from "../../../dist/src/lib/syntax/color/utils/distance.js"; export function run(describe, expect, it, transform, parse, render) { - - describe('convert to cmyk', function () { - - it('hex to cmyk #1', function () { + describe("convert to cmyk", function () { + it("hex to cmyk #1", function () { return transform(`.hsl { color: #b3222280; }`, { beautify: true, - convertColor: ColorType.DEVICE_CMYK - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'device-cmyk', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: 81.005587 }, - {typ: EnumToken.PercentageTokenType, val: 81.005587}, - {typ: EnumToken.PercentageTokenType, val: 29.803922}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.DEVICE_CMYK - })).equals(true)); + convertColor: ColorType.DEVICE_CMYK, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "device-cmyk", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: 81.005587 }, + { typ: EnumToken.PercentageTokenType, val: 81.005587 }, + { typ: EnumToken.PercentageTokenType, val: 29.803922 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.DEVICE_CMYK, + }), + ).equals(true), + ); }); - it('hsl(a) to cmyk #2', function () { + it("hsl(a) to cmyk #2", function () { return transform(`.hsl { color: hsl(0 68.07511737089203% 41.764705882352935%/50%); }`, { beautify: true, - convertColor: ColorType.DEVICE_CMYK - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'device-cmyk', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: 81.005587 }, - {typ: EnumToken.PercentageTokenType, val: 81.005587}, - {typ: EnumToken.PercentageTokenType, val: 29.803922}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.DEVICE_CMYK - })).equals(true)); + convertColor: ColorType.DEVICE_CMYK, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "device-cmyk", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: 81.005587 }, + { typ: EnumToken.PercentageTokenType, val: 81.005587 }, + { typ: EnumToken.PercentageTokenType, val: 29.803922 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.DEVICE_CMYK, + }), + ).equals(true), + ); }); - it('rgb to cmyk #3', function () { + it("rgb to cmyk #3", function () { return transform(`.hsl { color: rgb(179 34 34/50%); }`, { beautify: true, - convertColor: ColorType.DEVICE_CMYK - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'device-cmyk', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: 81.005587 }, - {typ: EnumToken.PercentageTokenType, val: 81.005587}, - {typ: EnumToken.PercentageTokenType, val: 29.803922}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.DEVICE_CMYK - })).equals(true)); + convertColor: ColorType.DEVICE_CMYK, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "device-cmyk", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: 81.005587 }, + { typ: EnumToken.PercentageTokenType, val: 81.005587 }, + { typ: EnumToken.PercentageTokenType, val: 29.803922 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.DEVICE_CMYK, + }), + ).equals(true), + ); }); - it('hwb() to cmyk #4', function () { + it("hwb() to cmyk #4", function () { return transform(`.hsl { color: hwb(0 13.333333333333334% 30%/50%); }`, { beautify: true, - convertColor: ColorType.DEVICE_CMYK - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'device-cmyk', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: 80.952381 }, - {typ: EnumToken.PercentageTokenType, val: 80.952381}, - {typ: EnumToken.PercentageTokenType, val: 30}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.DEVICE_CMYK - })).equals(true)); + convertColor: ColorType.DEVICE_CMYK, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "device-cmyk", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: 80.952381 }, + { typ: EnumToken.PercentageTokenType, val: 80.952381 }, + { typ: EnumToken.PercentageTokenType, val: 30 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.DEVICE_CMYK, + }), + ).equals(true), + ); }); - it('srgb to cmyk #5', function () { - return transform(`.hsl { color: color(srgb .7019607843137254 .13333333333333333 .13333333333333333/50%); }`, { - beautify: true, - convertColor: ColorType.DEVICE_CMYK - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'device-cmyk', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: 81.005587 }, - {typ: EnumToken.PercentageTokenType, val: 81.005587}, - {typ: EnumToken.PercentageTokenType, val: 29.803922}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.DEVICE_CMYK - })).equals(true)); + it("srgb to cmyk #5", function () { + return transform( + `.hsl { color: color(srgb .7019607843137254 .13333333333333333 .13333333333333333/50%); }`, + { + beautify: true, + convertColor: ColorType.DEVICE_CMYK, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "device-cmyk", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: 81.005587 }, + { typ: EnumToken.PercentageTokenType, val: 81.005587 }, + { typ: EnumToken.PercentageTokenType, val: 29.803922 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.DEVICE_CMYK, + }), + ).equals(true), + ); }); - it('srgb-linear to cmyk #6', function () { - return transform(`.hsl { color: color(srgb-linear .45078578283822346 .01599629336550963 .01599629336550963/50%); }`, { - beautify: true, - convertColor: ColorType.DEVICE_CMYK - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'device-cmyk', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: 81.005587 }, - {typ: EnumToken.PercentageTokenType, val: 81.005587}, - {typ: EnumToken.PercentageTokenType, val: 29.803922}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.DEVICE_CMYK - })).equals(true)); + it("srgb-linear to cmyk #6", function () { + return transform( + `.hsl { color: color(srgb-linear .45078578283822346 .01599629336550963 .01599629336550963/50%); }`, + { + beautify: true, + convertColor: ColorType.DEVICE_CMYK, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "device-cmyk", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: 81.005587 }, + { typ: EnumToken.PercentageTokenType, val: 81.005587 }, + { typ: EnumToken.PercentageTokenType, val: 29.803922 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.DEVICE_CMYK, + }), + ).equals(true), + ); }); - it('display-p3 to cmyk #7', function () { - return transform(`.hsl { color: color(display-p3 0.644980276448 0.191199800941 0.165770885403 / 0.501960784314); }`, { - beautify: true, - convertColor: ColorType.DEVICE_CMYK - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'device-cmyk', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: 81.005587 }, - {typ: EnumToken.PercentageTokenType, val: 81.005587}, - {typ: EnumToken.PercentageTokenType, val: 29.803922}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.DEVICE_CMYK - })).equals(true)); + it("display-p3 to cmyk #7", function () { + return transform( + `.hsl { color: color(display-p3 0.644980276448 0.191199800941 0.165770885403 / 0.501960784314); }`, + { + beautify: true, + convertColor: ColorType.DEVICE_CMYK, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "device-cmyk", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: 81.005587 }, + { typ: EnumToken.PercentageTokenType, val: 81.005587 }, + { typ: EnumToken.PercentageTokenType, val: 29.803922 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.DEVICE_CMYK, + }), + ).equals(true), + ); }); - it('a98-rgb to cmyk #8', function () { + it("a98-rgb to cmyk #8", function () { return transform(`.hsl { color: color(a98-rgb 0.6015 0.1525 0.1525 / 0.50); }`, { beautify: true, - convertColor: ColorType.DEVICE_CMYK - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'device-cmyk', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: 81.011309 }, - {typ: EnumToken.PercentageTokenType, val: 81.011309}, - {typ: EnumToken.PercentageTokenType, val: 29.80062}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.DEVICE_CMYK - })).equals(true)); + convertColor: ColorType.DEVICE_CMYK, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "device-cmyk", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: 81.011309 }, + { typ: EnumToken.PercentageTokenType, val: 81.011309 }, + { typ: EnumToken.PercentageTokenType, val: 29.80062 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.DEVICE_CMYK, + }), + ).equals(true), + ); }); - it('photo-rgb to cmyk #9', function () { + it("photo-rgb to cmyk #9", function () { return transform(`.hsl { color: color(prophoto-rgb 0.4589 0.2071 0.124 / 0.50); }`, { beautify: true, - convertColor: ColorType.DEVICE_CMYK - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'device-cmyk', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: 80.998477 }, - {typ: EnumToken.PercentageTokenType, val: 80.996439}, - {typ: EnumToken.PercentageTokenType, val: 29.808619}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.DEVICE_CMYK - })).equals(true)); + convertColor: ColorType.DEVICE_CMYK, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "device-cmyk", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: 80.998477 }, + { typ: EnumToken.PercentageTokenType, val: 80.996439 }, + { typ: EnumToken.PercentageTokenType, val: 29.808619 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.DEVICE_CMYK, + }), + ).equals(true), + ); }); - it('rec2020 to cmyk #10', function () { - return transform(`.hsl { color: color(rec2020 .5293019037237446 .1758230143656851 .10251000214438549/50%); }`, { - beautify: true, - convertColor: ColorType.DEVICE_CMYK - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'device-cmyk', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: '81.00558659217877'}, - {typ: EnumToken.PercentageTokenType, val: '81.00558659217877'}, - {typ: EnumToken.PercentageTokenType, val: '29.803921568627455'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.DEVICE_CMYK - })).equals(true)); + it("rec2020 to cmyk #10", function () { + return transform( + `.hsl { color: color(rec2020 .5293019037237446 .1758230143656851 .10251000214438549/50%); }`, + { + beautify: true, + convertColor: ColorType.DEVICE_CMYK, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "device-cmyk", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: "81.00558659217877" }, + { typ: EnumToken.PercentageTokenType, val: "81.00558659217877" }, + { typ: EnumToken.PercentageTokenType, val: "29.803921568627455" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.DEVICE_CMYK, + }), + ).equals(true), + ); }); - it('xyz to cmyk #11', function () { - return transform(`.hsl { color: color(xyz .1945069569161968 .10844949817036895 .025825724073578825/50%); }`, { - beautify: true, - convertColor: ColorType.DEVICE_CMYK - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'device-cmyk', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: '81.00558659217877'}, - {typ: EnumToken.PercentageTokenType, val: '81.00558659217877'}, - {typ: EnumToken.PercentageTokenType, val: '29.803921568627455'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.DEVICE_CMYK - })).equals(true)); + it("xyz to cmyk #11", function () { + return transform( + `.hsl { color: color(xyz .1945069569161968 .10844949817036895 .025825724073578825/50%); }`, + { + beautify: true, + convertColor: ColorType.DEVICE_CMYK, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "device-cmyk", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: "81.00558659217877" }, + { typ: EnumToken.PercentageTokenType, val: "81.00558659217877" }, + { typ: EnumToken.PercentageTokenType, val: "29.803921568627455" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.DEVICE_CMYK, + }), + ).equals(true), + ); }); - it('xyz-d50 to cmyk #12', function () { - return transform(`.hsl { color: color(xyz-d50 .20502195999126108 .11273398825257433 .019252589983419107/50%); }`, { - beautify: true, - convertColor: ColorType.DEVICE_CMYK - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'device-cmyk', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: '81.00558659217877'}, - {typ: EnumToken.PercentageTokenType, val: '81.00558659217877'}, - {typ: EnumToken.PercentageTokenType, val: '29.803921568627455'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.DEVICE_CMYK - })).equals(true)); + it("xyz-d50 to cmyk #12", function () { + return transform( + `.hsl { color: color(xyz-d50 .20502195999126108 .11273398825257433 .019252589983419107/50%); }`, + { + beautify: true, + convertColor: ColorType.DEVICE_CMYK, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "device-cmyk", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: "81.00558659217877" }, + { typ: EnumToken.PercentageTokenType, val: "81.00558659217877" }, + { typ: EnumToken.PercentageTokenType, val: "29.803921568627455" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.DEVICE_CMYK, + }), + ).equals(true), + ); }); - it('xyz-d65 to cmyk #13', function () { - return transform(`.hsl { color: color(xyz .1945069569161968 .10844949817036895 .025825724073578825/50%); }`, { - beautify: true, - convertColor: ColorType.DEVICE_CMYK - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'device-cmyk', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: '81.00558659217877'}, - {typ: EnumToken.PercentageTokenType, val: '81.00558659217877'}, - {typ: EnumToken.PercentageTokenType, val: '29.803921568627455'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.DEVICE_CMYK - })).equals(true)); + it("xyz-d65 to cmyk #13", function () { + return transform( + `.hsl { color: color(xyz .1945069569161968 .10844949817036895 .025825724073578825/50%); }`, + { + beautify: true, + convertColor: ColorType.DEVICE_CMYK, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "device-cmyk", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: "81.00558659217877" }, + { typ: EnumToken.PercentageTokenType, val: "81.00558659217877" }, + { typ: EnumToken.PercentageTokenType, val: "29.803921568627455" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.DEVICE_CMYK, + }), + ).equals(true), + ); }); - it('oklab to cmyk #14', function () { + it("oklab to cmyk #14", function () { return transform(`.hsl { color: oklab(.49863253097209403 .16117650019451524 .08155524287358212/50%); }`, { beautify: true, - convertColor: ColorType.DEVICE_CMYK - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'device-cmyk', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: '81.00558659217877'}, - {typ: EnumToken.PercentageTokenType, val: '81.00558659217877'}, - {typ: EnumToken.PercentageTokenType, val: '29.803921568627455'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.DEVICE_CMYK - })).equals(true)); + convertColor: ColorType.DEVICE_CMYK, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "device-cmyk", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: "81.00558659217877" }, + { typ: EnumToken.PercentageTokenType, val: "81.00558659217877" }, + { typ: EnumToken.PercentageTokenType, val: "29.803921568627455" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.DEVICE_CMYK, + }), + ).equals(true), + ); }); - it('oklch to cmyk #15', function () { + it("oklch to cmyk #15", function () { return transform(`.hsl { color: oklch(.49863253097209403 .1806353283694016 26.839391611662446/50%); }`, { beautify: true, - convertColor: ColorType.DEVICE_CMYK - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'device-cmyk', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: '81.00558659217877'}, - {typ: EnumToken.PercentageTokenType, val: '81.00558659217877'}, - {typ: EnumToken.PercentageTokenType, val: '29.803921568627455'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.DEVICE_CMYK - })).equals(true)); + convertColor: ColorType.DEVICE_CMYK, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "device-cmyk", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: "81.00558659217877" }, + { typ: EnumToken.PercentageTokenType, val: "81.00558659217877" }, + { typ: EnumToken.PercentageTokenType, val: "29.803921568627455" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.DEVICE_CMYK, + }), + ).equals(true), + ); }); - it('lab to cmyk #16', function () { + it("lab to cmyk #16", function () { return transform(`.hsl { color: lab(40.03718216996103 56.88417629340889 39.4648992397212/50%); }`, { beautify: true, - convertColor: ColorType.DEVICE_CMYK - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'device-cmyk', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: '81.00558659217877'}, - {typ: EnumToken.PercentageTokenType, val: '81.00558659217877'}, - {typ: EnumToken.PercentageTokenType, val: '29.803921568627455'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.DEVICE_CMYK - })).equals(true)); + convertColor: ColorType.DEVICE_CMYK, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "device-cmyk", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: "81.00558659217877" }, + { typ: EnumToken.PercentageTokenType, val: "81.00558659217877" }, + { typ: EnumToken.PercentageTokenType, val: "29.803921568627455" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.DEVICE_CMYK, + }), + ).equals(true), + ); }); - it('lch to cmyk #17', function () { + it("lch to cmyk #17", function () { return transform(`.hsl { color: lch(40.03718216996103 69.23357411387173 34.75200047889167/50%); }`, { beautify: true, - convertColor: ColorType.DEVICE_CMYK - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'device-cmyk', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: '81.00558659217877'}, - {typ: EnumToken.PercentageTokenType, val: '81.00558659217877'}, - {typ: EnumToken.PercentageTokenType, val: '29.803921568627455'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.DEVICE_CMYK - })).equals(true)); + convertColor: ColorType.DEVICE_CMYK, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "device-cmyk", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: "81.00558659217877" }, + { typ: EnumToken.PercentageTokenType, val: "81.00558659217877" }, + { typ: EnumToken.PercentageTokenType, val: "29.803921568627455" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.DEVICE_CMYK, + }), + ).equals(true), + ); }); }); } diff --git a/test/specs/code/color-display-p3.js b/test/specs/code/color-display-p3.js index a7a161346..0bcf4b342 100644 --- a/test/specs/code/color-display-p3.js +++ b/test/specs/code/color-display-p3.js @@ -1,604 +1,725 @@ -import {ColorType, EnumToken} from "../../../dist/src/lib/ast/types.js"; -import {isOkLabClose} from "../../../dist/src/lib/syntax/color/utils/distance.js"; +import { ColorType, EnumToken } from "../../../dist/src/lib/ast/types.js"; +import { isOkLabClose } from "../../../dist/src/lib/syntax/color/utils/distance.js"; import { parseString } from "../../../dist/src/lib/parser/parse.js"; -export function run(describe, expect, transform, parse, render, dirname, readFile, resolve, ColorType, EnumToken, ModuleCaseTransformEnum, ModuleScopeEnumOptions, transformSync, parseSync) { - - describe('convert to display-p3', function () { - - it('rgb(a) to display-p3 #1', function () { +export function run( + describe, + expect, + transform, + parse, + render, + dirname, + readFile, + resolve, + ColorType, + EnumToken, + ModuleCaseTransformEnum, + ModuleScopeEnumOptions, + transformSync, + parseSync, +) { + describe("convert to display-p3", function () { + it("rgb(a) to display-p3 #1", function () { return transform(`.hsl { color: rgb(179 34 34/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "display-p3" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.6449802764484531" - }, { - typ: EnumToken.NumberTokenType, - val: "0.19119980094061145" - }, { - typ: EnumToken.NumberTokenType, - val: "0.16577088540310694" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "display-p3", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.6449802764484531", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.19119980094061145", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.16577088540310694", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('hsl(a) to display-p3 #2', function () { + it("hsl(a) to display-p3 #2", function () { return transform(`.hsl { color: hsl(0 68.07511737089203% 41.764705882352935%/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "display-p3" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.6449802764484531" - }, { - typ: EnumToken.NumberTokenType, - val: "0.19119980094061145" - }, { - typ: EnumToken.NumberTokenType, - val: "0.16577088540310694" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "display-p3", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.6449802764484531", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.19119980094061145", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.16577088540310694", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('device-cmyk() to display-p3 #3', function () { - return transform(`.hsl { color: device-cmyk(0 81.00558659217877% 81.00558659217877% 29.803921568627455%/50%); }`, { - beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "display-p3" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.6449802764484531" - }, { - typ: EnumToken.NumberTokenType, - val: "0.19119980094061145" - }, { - typ: EnumToken.NumberTokenType, - val: "0.16577088540310694" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + it("device-cmyk() to display-p3 #3", function () { + return transform( + `.hsl { color: device-cmyk(0 81.00558659217877% 81.00558659217877% 29.803921568627455%/50%); }`, + { + beautify: true, + convertColor: ColorType.SRGB_LINEAR, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "display-p3", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.6449802764484531", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.19119980094061145", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.16577088540310694", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('hwb() to display-p3 #4', function () { + it("hwb() to display-p3 #4", function () { return transform(`.hsl { color: hwb(0 13.333333333333334% 30%/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "display-p3" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.6449802764484531" - }, { - typ: EnumToken.NumberTokenType, - val: "0.19119980094061145" - }, { - typ: EnumToken.NumberTokenType, - val: "0.16577088540310694" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "display-p3", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.6449802764484531", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.19119980094061145", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.16577088540310694", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('srgb-linear to display-p3 #6', function () { - return transform(`.hsl { color: color(srgb-linear .45078578283822346 .01599629336550963 .01599629336550963/50%); }`, { - beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "display-p3" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.6449802764484531" - }, { - typ: EnumToken.NumberTokenType, - val: "0.19119980094061145" - }, { - typ: EnumToken.NumberTokenType, - val: "0.16577088540310694" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + it("srgb-linear to display-p3 #6", function () { + return transform( + `.hsl { color: color(srgb-linear .45078578283822346 .01599629336550963 .01599629336550963/50%); }`, + { + beautify: true, + convertColor: ColorType.SRGB_LINEAR, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "display-p3", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.6449802764484531", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.19119980094061145", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.16577088540310694", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('display-p3 to display-p3 #7', function () { - return transform(`.hsl { color: color(display-p3 0.644980276448 0.191199800941 0.165770885403 / 0.501960784314); }`, { - beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "display-p3" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.6449802764484531" - }, { - typ: EnumToken.NumberTokenType, - val: "0.19119980094061145" - }, { - typ: EnumToken.NumberTokenType, - val: "0.16577088540310694" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + it("display-p3 to display-p3 #7", function () { + return transform( + `.hsl { color: color(display-p3 0.644980276448 0.191199800941 0.165770885403 / 0.501960784314); }`, + { + beautify: true, + convertColor: ColorType.SRGB_LINEAR, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "display-p3", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.6449802764484531", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.19119980094061145", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.16577088540310694", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('a98-rgb to display-p3 #8', function () { + it("a98-rgb to display-p3 #8", function () { return transform(`.hsl { color: color(a98-rgb 0.6015 0.1525 0.1525 / 0.50); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "display-p3" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.6449802764484531" - }, { - typ: EnumToken.NumberTokenType, - val: "0.19119980094061145" - }, { - typ: EnumToken.NumberTokenType, - val: "0.16577088540310694" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "display-p3", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.6449802764484531", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.19119980094061145", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.16577088540310694", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('photo-rgb to display-p3 #9', function () { + it("photo-rgb to display-p3 #9", function () { return transform(`.hsl { color: color(prophoto-rgb 0.4589 0.2071 0.124 / 0.50); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "display-p3" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.6449802764484531" - }, { - typ: EnumToken.NumberTokenType, - val: "0.19119980094061145" - }, { - typ: EnumToken.NumberTokenType, - val: "0.16577088540310694" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "display-p3", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.6449802764484531", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.19119980094061145", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.16577088540310694", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('xyz to display-p3 #11', function () { + it("xyz to display-p3 #11", function () { return transform(`.hsl { color: color(xyz-d65 0.1945 0.1084 0.0258 / 0.50); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "display-p3" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.6449802764484531" - }, { - typ: EnumToken.NumberTokenType, - val: "0.19119980094061145" - }, { - typ: EnumToken.NumberTokenType, - val: "0.16577088540310694" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "display-p3", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.6449802764484531", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.19119980094061145", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.16577088540310694", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('xyz-d50 to display-p3 #12', function () { + it("xyz-d50 to display-p3 #12", function () { return transform(`.hsl { color: color(xyz-d50 0.205 0.1127 0.0193 / 0.50); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "display-p3" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.6449802764484531" - }, { - typ: EnumToken.NumberTokenType, - val: "0.19119980094061145" - }, { - typ: EnumToken.NumberTokenType, - val: "0.16577088540310694" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "display-p3", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.6449802764484531", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.19119980094061145", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.16577088540310694", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('xyz-d65 to display-p3 #13', function () { + it("xyz-d65 to display-p3 #13", function () { return transform(`.hsl { color: color(xyz-d65 0.1945 0.1084 0.0258 / 0.50); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "display-p3" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.6449802764484531" - }, { - typ: EnumToken.NumberTokenType, - val: "0.19119980094061145" - }, { - typ: EnumToken.NumberTokenType, - val: "0.16577088540310694" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "display-p3", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.6449802764484531", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.19119980094061145", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.16577088540310694", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('oklab to display-p3 #14', function () { + it("oklab to display-p3 #14", function () { return transform(`.hsl { color: oklab(.49863253097209403 .16117650019451524 .08155524287358212/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "display-p3" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.6449802764484531" - }, { - typ: EnumToken.NumberTokenType, - val: "0.19119980094061145" - }, { - typ: EnumToken.NumberTokenType, - val: "0.16577088540310694" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "display-p3", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.6449802764484531", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.19119980094061145", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.16577088540310694", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('oklch to display-p3 #15', function () { + it("oklch to display-p3 #15", function () { return transform(`.hsl { color: oklch(.49863253097209403 .1806353283694016 26.839391611662446/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "display-p3" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.6449802764484531" - }, { - typ: EnumToken.NumberTokenType, - val: "0.19119980094061145" - }, { - typ: EnumToken.NumberTokenType, - val: "0.16577088540310694" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "display-p3", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.6449802764484531", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.19119980094061145", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.16577088540310694", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('lab to display-p3 #16', function () { + it("lab to display-p3 #16", function () { return transform(`.hsl { color: lab(40.03718216996103 56.88417629340889 39.4648992397212/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "display-p3" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.6449802764484531" - }, { - typ: EnumToken.NumberTokenType, - val: "0.19119980094061145" - }, { - typ: EnumToken.NumberTokenType, - val: "0.16577088540310694" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "display-p3", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.6449802764484531", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.19119980094061145", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.16577088540310694", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('lch to display-p3 #17', function () { + it("lch to display-p3 #17", function () { return transform(`.hsl { color: lch(40.03718216996103 69.23357411387173 34.75200047889167/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "display-p3" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.6449802764484531" - }, { - typ: EnumToken.NumberTokenType, - val: "0.19119980094061145" - }, { - typ: EnumToken.NumberTokenType, - val: "0.16577088540310694" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "display-p3", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.6449802764484531", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.19119980094061145", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.16577088540310694", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('display-p3-linear and display-p3 #18', function () { - return expect(isOkLabClose(parseString('color(display-p3 0.644980276448 0.191199800941 0.165770885403 / 0.501960784314)')[0], parseString('color(display-p3-linear .373594 .030429 .023424/50%)')[0] - )).equals(true); + it("display-p3-linear and display-p3 #18", function () { + return expect( + isOkLabClose( + parseString("color(display-p3 0.644980276448 0.191199800941 0.165770885403 / 0.501960784314)")[0], + parseString("color(display-p3-linear .373594 .030429 .023424/50%)")[0], + ), + ).equals(true); }); - it('display-p3-linear to display-p3 #19', function () { - return transform(` + it("display-p3-linear to display-p3 #19", function () { + return transform( + ` .hsl { color: color-mix(in display-p3-linear, plum 60%, #123456 50%); - }`, { - beautify: true, - }).then(result => expect(result.code).equals(`.hsl { + }`, + { + beautify: true, + }, + ).then((result) => + expect(result.code).equals(`.hsl { color: #a97eb0 -}`)); +}`), + ); }); - it('display-p3 #20', function () { - return transform(` + it("display-p3 #20", function () { + return transform( + ` .hsl { color: color-mix(in display-p3-linear,white,black); - }`, { - beautify: true, - }).then(result => expect(result.code).equals(`.hsl { + }`, + { + beautify: true, + }, + ).then((result) => + expect(result.code).equals(`.hsl { color: #bcbcbc -}`)); +}`), + ); }); - it('display-p3 #21', function () { - return transform(` + it("display-p3 #21", function () { + return transform( + ` .hsl { color: #bcbcbc -}`, { - beautify: true, - convertColor: ColorType.DISPLAY_P3_LINEAR, - }).then(result => expect(result.code).equals(`.hsl { +}`, + { + beautify: true, + convertColor: ColorType.DISPLAY_P3_LINEAR, + }, + ).then((result) => + expect(result.code).equals(`.hsl { color: color(display-p3-linear .502886 .502886 .502886) -}`)); +}`), + ); }); - it('display-p3 #22', function () { - return transform(` + it("display-p3 #22", function () { + return transform( + ` .hsl { color: color(display-p3-linear .502886 .502886 .502886) -}`, { - beautify: true, - convertColor: ColorType.DISPLAY_P3, - }).then(result => expect(result.code).equals(`.hsl { +}`, + { + beautify: true, + convertColor: ColorType.DISPLAY_P3, + }, + ).then((result) => + expect(result.code).equals(`.hsl { color: color(display-p3 .737255 .737255 .737255) -}`)); +}`), + ); }); }); } diff --git a/test/specs/code/color-hex.js b/test/specs/code/color-hex.js index bc6aa42f7..a934449c2 100644 --- a/test/specs/code/color-hex.js +++ b/test/specs/code/color-hex.js @@ -1,196 +1,265 @@ -import {ColorType, EnumToken} from "../../../dist/src/lib/ast/types.js"; -import {isOkLabClose} from "../../../dist/src/lib/syntax/color/utils/distance.js"; +import { ColorType, EnumToken } from "../../../dist/src/lib/ast/types.js"; +import { isOkLabClose } from "../../../dist/src/lib/syntax/color/utils/distance.js"; export function run(describe, expect, it, transform, parse, render) { - - describe('convert to hex', function () { - - it('rgb(a) to hex #1', function () { + describe("convert to hex", function () { + it("rgb(a) to hex #1", function () { return transform(`.hsl { color: rgb(179 34 34/50%); }`, { beautify: true, - convertColor: ColorType.HEX - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: '#b3222280', - kin: ColorType.HEX - })).equals(true)); + convertColor: ColorType.HEX, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "#b3222280", + kin: ColorType.HEX, + }), + ).equals(true), + ); }); - it('hsl(a) to hex #2', function () { + it("hsl(a) to hex #2", function () { return transform(`.hsl { color: hsl(0 68.0751% 41.7647%/50%); }`, { beautify: true, - convertColor: ColorType.HEX - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: '#b3222280', - kin: ColorType.HEX - })).equals(true)); + convertColor: ColorType.HEX, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "#b3222280", + kin: ColorType.HEX, + }), + ).equals(true), + ); }); - it('device-cmyk() to hex #3', function () { + it("device-cmyk() to hex #3", function () { return transform(`.hsl { color: device-cmyk(0 81.005587% 81.005587% 29.803922%/50%); }`, { beautify: true, - convertColor: ColorType.HEX - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: '#b3222280', - kin: ColorType.HEX - })).equals(true)); + convertColor: ColorType.HEX, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "#b3222280", + kin: ColorType.HEX, + }), + ).equals(true), + ); }); - it('hwb() to hex #4', function () { + it("hwb() to hex #4", function () { return transform(`.hsl { color: hwb(0 13.333333% 30%/50%); }`, { beautify: true, - convertColor: ColorType.HEX - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: '#b3222280', - kin: ColorType.HEX - }, - .1)).equals(true)); + convertColor: ColorType.HEX, + }).then((result) => + expect( + isOkLabClose( + result.ast.chi[0].chi[0].val[0], + { + typ: EnumToken.ColorTokenType, + val: "#b3222280", + kin: ColorType.HEX, + }, + 0.1, + ), + ).equals(true), + ); }); - it('srgb to hex #5', function () { + it("srgb to hex #5", function () { return transform(`.hsl { color: color(srgb .701961 .133333 .133333/50%); }`, { beautify: true, - convertColor: ColorType.HEX - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: '#b3222280', - kin: ColorType.HEX - })).equals(true)); + convertColor: ColorType.HEX, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "#b3222280", + kin: ColorType.HEX, + }), + ).equals(true), + ); }); - it('srgb-linear to hex #6', function () { + it("srgb-linear to hex #6", function () { return transform(`.hsl { color: color(srgb-linear .450786 .015996 .015996/50%); }`, { beautify: true, - convertColor: ColorType.HEX - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: '#b3222280', - kin: ColorType.HEX - })).equals(true)); + convertColor: ColorType.HEX, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "#b3222280", + kin: ColorType.HEX, + }), + ).equals(true), + ); }); - it('display-p3 to hex #7', function () { + it("display-p3 to hex #7", function () { return transform(`.hsl { color: color(display-p3 .64498 .1912 .165771 / 0.501960784314); }`, { beautify: true, - convertColor: ColorType.HEX - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: '#b3222280', - kin: ColorType.HEX - })).equals(true)); + convertColor: ColorType.HEX, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "#b3222280", + kin: ColorType.HEX, + }), + ).equals(true), + ); }); - it('a98-rgb to hex #8', function () { + it("a98-rgb to hex #8", function () { return transform(`.hsl { color: color(a98-rgb .601473 .15253 .15253 / 0.50); }`, { beautify: true, - convertColor: ColorType.HEX - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: '#b3222280', - kin: ColorType.HEX - })).equals(true)); + convertColor: ColorType.HEX, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "#b3222280", + kin: ColorType.HEX, + }), + ).equals(true), + ); }); - it('photo-rgb to hex #9', function () { + it("photo-rgb to hex #9", function () { return transform(`.hsl { color: color(prophoto-rgb .458932 .207098 .123972 / 0.50); }`, { beautify: true, - convertColor: ColorType.HEX - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: '#b3222280', - kin: ColorType.HEX - })).equals(true)); + convertColor: ColorType.HEX, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "#b3222280", + kin: ColorType.HEX, + }), + ).equals(true), + ); }); - it('rec2020 to hex #10', function () { + it("rec2020 to hex #10", function () { return transform(`.hsl { color: color(prophoto-rgb .458932 .207098 .123972/50%); }`, { beautify: true, - convertColor: ColorType.HEX - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: '#b3222280', - kin: ColorType.HEX - })).equals(true)); + convertColor: ColorType.HEX, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "#b3222280", + kin: ColorType.HEX, + }), + ).equals(true), + ); }); - it('xyz to hex #11', function () { + it("xyz to hex #11", function () { return transform(`.hsl { color: color(xyz-d65 .194507 .108449 .025826/50%); }`, { beautify: true, - convertColor: ColorType.HEX - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: '#b3222280', - kin: ColorType.HEX - })).equals(true)); + convertColor: ColorType.HEX, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "#b3222280", + kin: ColorType.HEX, + }), + ).equals(true), + ); }); - it('xyz-d50 to hex #12', function () { + it("xyz-d50 to hex #12", function () { return transform(`.hsl { color: color(xyz-d50 .205022 .112734 .019253/50%); }`, { beautify: true, - convertColor: ColorType.HEX - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: '#b3222280', - kin: ColorType.HEX - })).equals(true)); + convertColor: ColorType.HEX, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "#b3222280", + kin: ColorType.HEX, + }), + ).equals(true), + ); }); - it('xyz-d65 to hex #13', function () { + it("xyz-d65 to hex #13", function () { return transform(`.hsl { color: color(xyz .194507 .108449 .025826/50%); }`, { beautify: true, - convertColor: ColorType.HEX - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: '#b3222280', - kin: ColorType.HEX - })).equals(true)); + convertColor: ColorType.HEX, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "#b3222280", + kin: ColorType.HEX, + }), + ).equals(true), + ); }); - it('oklab to hex #14', function () { + it("oklab to hex #14", function () { return transform(`.hsl { color: oklab(.49863253097209403 .16117650019451524 .08155524287358212/50%); }`, { beautify: true, - convertColor: ColorType.HEX - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: '#b3222280', - kin: ColorType.HEX - })).equals(true)); + convertColor: ColorType.HEX, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "#b3222280", + kin: ColorType.HEX, + }), + ).equals(true), + ); }); - it('oklch to hex #15', function () { + it("oklch to hex #15", function () { return transform(`.hsl { color: oklch(.49863253097209403 .1806353283694016 26.839391611662446/50%); }`, { beautify: true, - convertColor: ColorType.HEX - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: '#b3222280', - kin: ColorType.HEX - })).equals(true)); + convertColor: ColorType.HEX, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "#b3222280", + kin: ColorType.HEX, + }), + ).equals(true), + ); }); - it('lab to hex #16', function () { + it("lab to hex #16", function () { return transform(`.hsl { color: lab(40.03718216996103 56.88417629340889 39.4648992397212/50%); }`, { beautify: true, - convertColor: ColorType.HEX - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: '#b3222280', - kin: ColorType.HEX - })).equals(true)); + convertColor: ColorType.HEX, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "#b3222280", + kin: ColorType.HEX, + }), + ).equals(true), + ); }); - it('lch to hex #17', function () { + it("lch to hex #17", function () { return transform(`.hsl { color: lch(40.03718216996103 69.23357411387173 34.75200047889167/50%); }`, { beautify: true, - convertColor: ColorType.HEX - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: '#b3222280', - kin: ColorType.HEX - })).equals(true)); + convertColor: ColorType.HEX, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "#b3222280", + kin: ColorType.HEX, + }), + ).equals(true), + ); }); }); } diff --git a/test/specs/code/color-hsl.js b/test/specs/code/color-hsl.js index 4481948cc..092ee19bc 100644 --- a/test/specs/code/color-hsl.js +++ b/test/specs/code/color-hsl.js @@ -1,332 +1,408 @@ -import {ColorType, EnumToken} from "../../../dist/src/lib/ast/types.js"; -import {isOkLabClose} from "../../../dist/src/lib/syntax/color/utils/distance.js"; +import { ColorType, EnumToken } from "../../../dist/src/lib/ast/types.js"; +import { isOkLabClose } from "../../../dist/src/lib/syntax/color/utils/distance.js"; export function run(describe, expect, it, transform, parse, render) { - - describe('convert to hsl', function () { - - it('hex to hsl #1', function () { + describe("convert to hsl", function () { + it("hex to hsl #1", function () { return transform(`.hsl { color: #b3222280; }`, { beautify: true, - convertColor: ColorType.HSL - }).then(result => - expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'hsl', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: '68.07511737089203'}, - {typ: EnumToken.PercentageTokenType, val: '41.764705882352935'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.HSL - })).equals(true) + convertColor: ColorType.HSL, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "hsl", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: "68.07511737089203" }, + { typ: EnumToken.PercentageTokenType, val: "41.764705882352935" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.HSL, + }), + ).equals(true), ); }); - it('rgb to hsl #2', function () { + it("rgb to hsl #2", function () { return transform(`.hsl { color: rgb(179 34 34/50%); }`, { beautify: true, - convertColor: ColorType.HSL - }).then(result => - expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'hsl', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: '68.07511737089203'}, - {typ: EnumToken.PercentageTokenType, val: '41.764705882352935'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.HSL - })).equals(true)); + convertColor: ColorType.HSL, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "hsl", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: "68.07511737089203" }, + { typ: EnumToken.PercentageTokenType, val: "41.764705882352935" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.HSL, + }), + ).equals(true), + ); }); - it('device-cmyk() to hsl #3', function () { + it("device-cmyk() to hsl #3", function () { return transform(`.hsl { color: device-cmyk(0 81% 81% 30%/50%); }`, { beautify: true, - convertColor: ColorType.HSL - }).then(result => - expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'hsl', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: 68.0751}, - {typ: EnumToken.PercentageTokenType, val: 41.7647}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.HSL - }, .1)).equals(true)); + convertColor: ColorType.HSL, + }).then((result) => + expect( + isOkLabClose( + result.ast.chi[0].chi[0].val[0], + { + typ: EnumToken.ColorTokenType, + val: "hsl", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: 68.0751 }, + { typ: EnumToken.PercentageTokenType, val: 41.7647 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.HSL, + }, + 0.1, + ), + ).equals(true), + ); }); - it('hwb() to hsl #4', function () { + it("hwb() to hsl #4", function () { return transform(`.hsl { color: hwb(0 13.33333333333332% 29.803921568627455%/50%); }`, { beautify: true, - convertColor: ColorType.HSL - }).then(result => - expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'hsl', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: '68.07511737089203'}, - {typ: EnumToken.PercentageTokenType, val: '41.764705882352935'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.HSL - })).equals(true)); + convertColor: ColorType.HSL, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "hsl", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: "68.07511737089203" }, + { typ: EnumToken.PercentageTokenType, val: "41.764705882352935" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.HSL, + }), + ).equals(true), + ); }); - it('srgb to hsl #5', function () { - return transform(`.hsl { color: color(srgb .7019607843137254 .13333333333333333 .13333333333333333/50%); }`, { - beautify: true, - convertColor: ColorType.HSL - }).then(result => - expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'hsl', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: '68.07511737089203'}, - {typ: EnumToken.PercentageTokenType, val: '41.764705882352935'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.HSL - })).equals(true)); + it("srgb to hsl #5", function () { + return transform( + `.hsl { color: color(srgb .7019607843137254 .13333333333333333 .13333333333333333/50%); }`, + { + beautify: true, + convertColor: ColorType.HSL, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "hsl", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: "68.07511737089203" }, + { typ: EnumToken.PercentageTokenType, val: "41.764705882352935" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.HSL, + }), + ).equals(true), + ); }); - it('srgb-linear to hsl #6', function () { - return transform(`.hsl { color: color(srgb-linear .45078578283822346 .01599629336550963 .01599629336550963/50%); }`, { - beautify: true, - convertColor: ColorType.HSL - }).then(result => - expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'hsl', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: '68.07511737089203'}, - {typ: EnumToken.PercentageTokenType, val: '41.764705882352935'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.HSL - })).equals(true)); + it("srgb-linear to hsl #6", function () { + return transform( + `.hsl { color: color(srgb-linear .45078578283822346 .01599629336550963 .01599629336550963/50%); }`, + { + beautify: true, + convertColor: ColorType.HSL, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "hsl", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: "68.07511737089203" }, + { typ: EnumToken.PercentageTokenType, val: "41.764705882352935" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.HSL, + }), + ).equals(true), + ); }); - it('display-p3 to hsl #7', function () { + it("display-p3 to hsl #7", function () { return transform(`.hsl { color: color(display-p3 0.644980276448 0.191199800941 0.165770885403 / 0.50); }`, { beautify: true, - convertColor: ColorType.HSL - }).then(result => - expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'hsl', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: '68.07511737089203'}, - {typ: EnumToken.PercentageTokenType, val: '41.764705882352935'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.HSL - })).equals(true)); + convertColor: ColorType.HSL, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "hsl", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: "68.07511737089203" }, + { typ: EnumToken.PercentageTokenType, val: "41.764705882352935" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.HSL, + }), + ).equals(true), + ); }); - it('a98-rgb to hsl #8', function () { - return transform(`.hsl { color: color(a98-rgb 0.601473417821 0.152529962 0.152529962 / 0.501960784314); }`, { - beautify: true, - convertColor: ColorType.HSL - }).then(result => - expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'hsl', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: '68.07511737089203'}, - {typ: EnumToken.PercentageTokenType, val: '41.764705882352935'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.HSL - })).equals(true)); + it("a98-rgb to hsl #8", function () { + return transform( + `.hsl { color: color(a98-rgb 0.601473417821 0.152529962 0.152529962 / 0.501960784314); }`, + { + beautify: true, + convertColor: ColorType.HSL, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "hsl", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: "68.07511737089203" }, + { typ: EnumToken.PercentageTokenType, val: "41.764705882352935" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.HSL, + }), + ).equals(true), + ); }); - it('photo-rgb to hsl #9', function () { - return transform(`.hsl { color: color(prophoto-rgb 0.458932310317 0.207098041055 0.12397162868 / 0.501960784314); }`, { - beautify: true, - convertColor: ColorType.HSL - }).then(result => - expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'hsl', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: '68.07511737089203'}, - {typ: EnumToken.PercentageTokenType, val: '41.764705882352935'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.HSL - })).equals(true)); + it("photo-rgb to hsl #9", function () { + return transform( + `.hsl { color: color(prophoto-rgb 0.458932310317 0.207098041055 0.12397162868 / 0.501960784314); }`, + { + beautify: true, + convertColor: ColorType.HSL, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "hsl", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: "68.07511737089203" }, + { typ: EnumToken.PercentageTokenType, val: "41.764705882352935" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.HSL, + }), + ).equals(true), + ); }); - it('rec2020 to hsl #10', function () { - return transform(`.hsl { color: color(rec2020 0.529301903724 0.175823014366 0.102510002144 / 0.501960784314); }`, { - beautify: true, - convertColor: ColorType.HSL - }).then(result => - expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'hsl', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: '68.07511737089203'}, - {typ: EnumToken.PercentageTokenType, val: '41.764705882352935'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.HSL - })).equals(true)); + it("rec2020 to hsl #10", function () { + return transform( + `.hsl { color: color(rec2020 0.529301903724 0.175823014366 0.102510002144 / 0.501960784314); }`, + { + beautify: true, + convertColor: ColorType.HSL, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "hsl", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: "68.07511737089203" }, + { typ: EnumToken.PercentageTokenType, val: "41.764705882352935" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.HSL, + }), + ).equals(true), + ); }); - it('xyz to hsl #11', function () { - return transform(`.hsl { color: color(xyz-d65 0.194506956916 0.10844949817 0.025825724074 / 0.501960784314); }`, { - beautify: true, - convertColor: ColorType.HSL - }).then(result => - expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'hsl', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: '68.07511737089203'}, - {typ: EnumToken.PercentageTokenType, val: '41.764705882352935'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.HSL - })).equals(true)); + it("xyz to hsl #11", function () { + return transform( + `.hsl { color: color(xyz-d65 0.194506956916 0.10844949817 0.025825724074 / 0.501960784314); }`, + { + beautify: true, + convertColor: ColorType.HSL, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "hsl", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: "68.07511737089203" }, + { typ: EnumToken.PercentageTokenType, val: "41.764705882352935" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.HSL, + }), + ).equals(true), + ); }); - it('xyz-d50 to hsl #12', function () { - return transform(`.hsl { color: color(xyz-d50 0.205021959991 0.112733988253 0.019252589983 / 0.501960784314); }`, { - beautify: true, - convertColor: ColorType.HSL - }).then(result => - expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'hsl', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: '68.07511737089203'}, - {typ: EnumToken.PercentageTokenType, val: '41.764705882352935'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.HSL - })).equals(true)); + it("xyz-d50 to hsl #12", function () { + return transform( + `.hsl { color: color(xyz-d50 0.205021959991 0.112733988253 0.019252589983 / 0.501960784314); }`, + { + beautify: true, + convertColor: ColorType.HSL, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "hsl", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: "68.07511737089203" }, + { typ: EnumToken.PercentageTokenType, val: "41.764705882352935" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.HSL, + }), + ).equals(true), + ); }); - it('xyz-d65 to hsl #13', function () { - return transform(`.hsl { color: color(xyz-d65 0.194506956916 0.10844949817 0.025825724074 / 0.501960784314); }`, { - beautify: true, - convertColor: ColorType.HSL - }).then(result => - expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'hsl', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: '68.07511737089203'}, - {typ: EnumToken.PercentageTokenType, val: '41.764705882352935'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.HSL - })).equals(true)); + it("xyz-d65 to hsl #13", function () { + return transform( + `.hsl { color: color(xyz-d65 0.194506956916 0.10844949817 0.025825724074 / 0.501960784314); }`, + { + beautify: true, + convertColor: ColorType.HSL, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "hsl", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: "68.07511737089203" }, + { typ: EnumToken.PercentageTokenType, val: "41.764705882352935" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.HSL, + }), + ).equals(true), + ); }); - it('oklab to hsl #14', function () { + it("oklab to hsl #14", function () { return transform(`.hsl { color: oklab(49.8632533734% 0.161176505417 0.081555225588 / 0.501960784314); }`, { beautify: true, - convertColor: ColorType.HSL - }).then(result => - expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'hsl', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: '68.07511737089203'}, - {typ: EnumToken.PercentageTokenType, val: '41.764705882352935'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.HSL - })).equals(true)); + convertColor: ColorType.HSL, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "hsl", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: "68.07511737089203" }, + { typ: EnumToken.PercentageTokenType, val: "41.764705882352935" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.HSL, + }), + ).equals(true), + ); }); - it('oklch to hsl #15', function () { + it("oklch to hsl #15", function () { return transform(`.hsl { color: oklch(49.8632533734% 0.180635325225 26.8393859717 / 0.501960784314); }`, { beautify: true, - convertColor: ColorType.HSL - }).then(result => - expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'hsl', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: '68.07511737089203'}, - {typ: EnumToken.PercentageTokenType, val: '41.764705882352935'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.HSL - })).equals(true)); + convertColor: ColorType.HSL, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "hsl", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: "68.07511737089203" }, + { typ: EnumToken.PercentageTokenType, val: "41.764705882352935" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.HSL, + }), + ).equals(true), + ); }); - it('lab to hsl #16', function () { + it("lab to hsl #16", function () { return transform(`.hsl { color: lab(40.03718216996103 56.88417629340889 39.4648992397212/50%); }`, { beautify: true, - convertColor: ColorType.HSL - }).then(result => - expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'hsl', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: '68.07511737089203'}, - {typ: EnumToken.PercentageTokenType, val: '41.764705882352935'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.HSL - })).equals(true)); + convertColor: ColorType.HSL, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "hsl", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: "68.07511737089203" }, + { typ: EnumToken.PercentageTokenType, val: "41.764705882352935" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.HSL, + }), + ).equals(true), + ); }); - it('lch to hsl #17', function () { + it("lch to hsl #17", function () { return transform(`.hsl { color: lch(40.03718216996103 69.23357411387173 34.75200047889167/50%); }`, { beautify: true, - convertColor: ColorType.HSL - }).then(result => - expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'hsl', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: '68.07511737089203'}, - {typ: EnumToken.PercentageTokenType, val: '41.764705882352935'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.HSL - })).equals(true)); + convertColor: ColorType.HSL, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "hsl", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: "68.07511737089203" }, + { typ: EnumToken.PercentageTokenType, val: "41.764705882352935" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.HSL, + }), + ).equals(true), + ); }); }); } diff --git a/test/specs/code/color-hwb.js b/test/specs/code/color-hwb.js index f312c37ec..a47260683 100644 --- a/test/specs/code/color-hwb.js +++ b/test/specs/code/color-hwb.js @@ -1,331 +1,461 @@ -import {ColorType, EnumToken} from "../../../dist/src/lib/ast/types.js"; -import {isOkLabClose} from "../../../dist/src/lib/syntax/color/utils/distance.js"; +import { ColorType, EnumToken } from "../../../dist/src/lib/ast/types.js"; +import { isOkLabClose } from "../../../dist/src/lib/syntax/color/utils/distance.js"; export function run(describe, expect, it, transform, parse, render) { - - describe('convert to hwb', function () { - - it('hex to hwb #1', function () { + describe("convert to hwb", function () { + it("hex to hwb #1", function () { return transform(`.hsl { color: #b3222280; }`, { beautify: true, - convertColor: ColorType.HWB - }).then(result => - expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'hwb', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: 13.333333}, - {typ: EnumToken.PercentageTokenType, val: 30}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.HWB - }, - .1)).equals(true) + convertColor: ColorType.HWB, + }).then((result) => + expect( + isOkLabClose( + result.ast.chi[0].chi[0].val[0], + { + typ: EnumToken.ColorTokenType, + val: "hwb", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: 13.333333 }, + { typ: EnumToken.PercentageTokenType, val: 30 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.HWB, + }, + 0.1, + ), + ).equals(true), ); }); - it('hsl(a) to hwb #2', function () { + it("hsl(a) to hwb #2", function () { return transform(`.hsl { color: hsl(0 68.07511737089203% 41.764705882352935%/50%); }`, { beautify: true, - convertColor: ColorType.HWB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'hwb', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: 13.333333}, - {typ: EnumToken.PercentageTokenType, val: 29.803922}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.HWB - })).equals(true)); + convertColor: ColorType.HWB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "hwb", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: 13.333333 }, + { typ: EnumToken.PercentageTokenType, val: 29.803922 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.HWB, + }), + ).equals(true), + ); }); - it('device-cmyk() to hwb #3', function () { + it("device-cmyk() to hwb #3", function () { return transform(`.hsl { color: device-cmyk(0 81% 81% 30%/50%); }`, { beautify: true, - convertColor: ColorType.HWB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'hwb', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: 13.3}, - {typ: EnumToken.PercentageTokenType, val: 30}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.HWB - })).equals(true)); + convertColor: ColorType.HWB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "hwb", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: 13.3 }, + { typ: EnumToken.PercentageTokenType, val: 30 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.HWB, + }), + ).equals(true), + ); }); - it('rgb to hwb #4', function () { + it("rgb to hwb #4", function () { return transform(`.hsl { color: rgb(179 34 34/50%); }`, { beautify: true, - convertColor: ColorType.HWB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'hwb', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: 13.333333}, - {typ: EnumToken.PercentageTokenType, val: 30}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.HWB - }, - .1)).equals(true)); + convertColor: ColorType.HWB, + }).then((result) => + expect( + isOkLabClose( + result.ast.chi[0].chi[0].val[0], + { + typ: EnumToken.ColorTokenType, + val: "hwb", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: 13.333333 }, + { typ: EnumToken.PercentageTokenType, val: 30 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.HWB, + }, + 0.1, + ), + ).equals(true), + ); }); - it('srgb to hwb #5', function () { - return transform(`.hsl { color: color(srgb .7019607843137254 .13333333333333333 .13333333333333333/50%); }`, { - beautify: true, - convertColor: ColorType.HWB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'hwb', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: 13.333333}, - {typ: EnumToken.PercentageTokenType, val: 30}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.HWB - }, - .1)).equals(true)); + it("srgb to hwb #5", function () { + return transform( + `.hsl { color: color(srgb .7019607843137254 .13333333333333333 .13333333333333333/50%); }`, + { + beautify: true, + convertColor: ColorType.HWB, + }, + ).then((result) => + expect( + isOkLabClose( + result.ast.chi[0].chi[0].val[0], + { + typ: EnumToken.ColorTokenType, + val: "hwb", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: 13.333333 }, + { typ: EnumToken.PercentageTokenType, val: 30 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.HWB, + }, + 0.1, + ), + ).equals(true), + ); }); - it('srgb-linear to hwb #6', function () { - return transform(`.hsl { color: color(srgb-linear .45078578283822346 .01599629336550963 .01599629336550963/50%); }`, { - beautify: true, - convertColor: ColorType.HWB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'hwb', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: 13.333333}, - {typ: EnumToken.PercentageTokenType, val: 30}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.HWB - }, - .1)).equals(true)); + it("srgb-linear to hwb #6", function () { + return transform( + `.hsl { color: color(srgb-linear .45078578283822346 .01599629336550963 .01599629336550963/50%); }`, + { + beautify: true, + convertColor: ColorType.HWB, + }, + ).then((result) => + expect( + isOkLabClose( + result.ast.chi[0].chi[0].val[0], + { + typ: EnumToken.ColorTokenType, + val: "hwb", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: 13.333333 }, + { typ: EnumToken.PercentageTokenType, val: 30 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.HWB, + }, + 0.1, + ), + ).equals(true), + ); }); - it('display-p3 to hwb #7', function () { - return transform(`.hsl { color: color(display-p3 0.644980276448 0.191199800941 0.165770885403 / 0.501960784314); }`, { - beautify: true, - convertColor: ColorType.HWB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'hwb', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: 13.333333}, - {typ: EnumToken.PercentageTokenType, val: 30}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.HWB - }, - .1)).equals(true)); + it("display-p3 to hwb #7", function () { + return transform( + `.hsl { color: color(display-p3 0.644980276448 0.191199800941 0.165770885403 / 0.501960784314); }`, + { + beautify: true, + convertColor: ColorType.HWB, + }, + ).then((result) => + expect( + isOkLabClose( + result.ast.chi[0].chi[0].val[0], + { + typ: EnumToken.ColorTokenType, + val: "hwb", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: 13.333333 }, + { typ: EnumToken.PercentageTokenType, val: 30 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.HWB, + }, + 0.1, + ), + ).equals(true), + ); }); - it('a98-rgb to hwb #8', function () { + it("a98-rgb to hwb #8", function () { return transform(`.hsl { color: color(a98-rgb 0.6015 0.1525 0.1525 / 0.50); }`, { beautify: true, - convertColor: ColorType.HWB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'hwb', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: 13.329943}, - {typ: EnumToken.PercentageTokenType, val: 30}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.HWB - }, - .1)).equals(true)); + convertColor: ColorType.HWB, + }).then((result) => + expect( + isOkLabClose( + result.ast.chi[0].chi[0].val[0], + { + typ: EnumToken.ColorTokenType, + val: "hwb", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: 13.329943 }, + { typ: EnumToken.PercentageTokenType, val: 30 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.HWB, + }, + 0.1, + ), + ).equals(true), + ); }); - it('photo-rgb to hwb #9', function () { - return transform(`.hsl { color: color(prophoto-rgb 0.458932310318 0.207098041055 0.12397162868 / 0.501960784314); }`, { - beautify: true, - convertColor: ColorType.HWB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'hwb', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: 13.333329}, - {typ: EnumToken.PercentageTokenType, val: 30}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.HWB - }, - .1)).equals(true)); + it("photo-rgb to hwb #9", function () { + return transform( + `.hsl { color: color(prophoto-rgb 0.458932310318 0.207098041055 0.12397162868 / 0.501960784314); }`, + { + beautify: true, + convertColor: ColorType.HWB, + }, + ).then((result) => + expect( + isOkLabClose( + result.ast.chi[0].chi[0].val[0], + { + typ: EnumToken.ColorTokenType, + val: "hwb", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: 13.333329 }, + { typ: EnumToken.PercentageTokenType, val: 30 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.HWB, + }, + 0.1, + ), + ).equals(true), + ); }); - it('rec2020 to hwb #10', function () { - return transform(`.hsl { color: color(prophoto-rgb 0.458932310318 0.207098041055 0.12397162868 / 0.501960784314); }`, { - beautify: true, - convertColor: ColorType.HWB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'hwb', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: 13.333329}, - {typ: EnumToken.PercentageTokenType, val: 30}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.HWB - }, - .1)).equals(true)); + it("rec2020 to hwb #10", function () { + return transform( + `.hsl { color: color(prophoto-rgb 0.458932310318 0.207098041055 0.12397162868 / 0.501960784314); }`, + { + beautify: true, + convertColor: ColorType.HWB, + }, + ).then((result) => + expect( + isOkLabClose( + result.ast.chi[0].chi[0].val[0], + { + typ: EnumToken.ColorTokenType, + val: "hwb", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: 13.333329 }, + { typ: EnumToken.PercentageTokenType, val: 30 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.HWB, + }, + 0.1, + ), + ).equals(true), + ); }); - it('xyz to hwb #11', function () { + it("xyz to hwb #11", function () { return transform(`.hsl { color: color(xyz .1945069569161969 .108449498170369 .025825724073578846/50%); }`, { beautify: true, - convertColor: ColorType.HWB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'hwb', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: 13.333333}, - {typ: EnumToken.PercentageTokenType, val: 30}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.HWB - }, - .1)).equals(true)); + convertColor: ColorType.HWB, + }).then((result) => + expect( + isOkLabClose( + result.ast.chi[0].chi[0].val[0], + { + typ: EnumToken.ColorTokenType, + val: "hwb", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: 13.333333 }, + { typ: EnumToken.PercentageTokenType, val: 30 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.HWB, + }, + 0.1, + ), + ).equals(true), + ); }); - it('xyz-d50 to hwb #12', function () { - return transform(`.hsl { color: color(xyz-d50 .2050219599912612 .11273398825257439 .019252589983419124/50%); }`, { - beautify: true, - convertColor: ColorType.HWB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'hwb', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: 13.333329}, - {typ: EnumToken.PercentageTokenType, val: 30}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.HWB - }, - .1)).equals(true)); + it("xyz-d50 to hwb #12", function () { + return transform( + `.hsl { color: color(xyz-d50 .2050219599912612 .11273398825257439 .019252589983419124/50%); }`, + { + beautify: true, + convertColor: ColorType.HWB, + }, + ).then((result) => + expect( + isOkLabClose( + result.ast.chi[0].chi[0].val[0], + { + typ: EnumToken.ColorTokenType, + val: "hwb", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: 13.333329 }, + { typ: EnumToken.PercentageTokenType, val: 30 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.HWB, + }, + 0.1, + ), + ).equals(true), + ); }); - it('xyz-d65 to hwb #13', function () { - return transform(`.hsl { color: color(xyz-d65 .1945069569161969 .108449498170369 .025825724073578846/50%); }`, { - beautify: true, - convertColor: ColorType.HWB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'hwb', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: 13.333333}, - {typ: EnumToken.PercentageTokenType, val: 30}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.HWB - }, - .1)).equals(true)); + it("xyz-d65 to hwb #13", function () { + return transform( + `.hsl { color: color(xyz-d65 .1945069569161969 .108449498170369 .025825724073578846/50%); }`, + { + beautify: true, + convertColor: ColorType.HWB, + }, + ).then((result) => + expect( + isOkLabClose( + result.ast.chi[0].chi[0].val[0], + { + typ: EnumToken.ColorTokenType, + val: "hwb", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: 13.333333 }, + { typ: EnumToken.PercentageTokenType, val: 30 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.HWB, + }, + 0.1, + ), + ).equals(true), + ); }); - it('oklab to hwb #14', function () { + it("oklab to hwb #14", function () { return transform(`.hsl { color: oklab(.49863253097209403 .16117650019451524 .08155524287358212/50%); }`, { beautify: true, - convertColor: ColorType.HWB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'hwb', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: 13.333333}, - {typ: EnumToken.PercentageTokenType, val: 30}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.HWB - }, - .1)).equals(true)); + convertColor: ColorType.HWB, + }).then((result) => + expect( + isOkLabClose( + result.ast.chi[0].chi[0].val[0], + { + typ: EnumToken.ColorTokenType, + val: "hwb", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: 13.333333 }, + { typ: EnumToken.PercentageTokenType, val: 30 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.HWB, + }, + 0.1, + ), + ).equals(true), + ); }); - it('oklch to hwb #15', function () { + it("oklch to hwb #15", function () { return transform(`.hsl { color: oklch(.49863253097209403 .1806353283694016 26.8394/50%); }`, { beautify: true, - convertColor: ColorType.HWB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'hwb', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: 13.333323}, - {typ: EnumToken.PercentageTokenType, val: 30}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.HWB - }, - .1)).equals(true)); + convertColor: ColorType.HWB, + }).then((result) => + expect( + isOkLabClose( + result.ast.chi[0].chi[0].val[0], + { + typ: EnumToken.ColorTokenType, + val: "hwb", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: 13.333323 }, + { typ: EnumToken.PercentageTokenType, val: 30 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.HWB, + }, + 0.1, + ), + ).equals(true), + ); }); - it('lab to hwb #16', function () { + it("lab to hwb #16", function () { return transform(`.hsl { color: lab(40.03718216996103 56.88417629340889 39.4648992397212/50%); }`, { beautify: true, - convertColor: ColorType.HWB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'hwb', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: 13.333322}, - {typ: EnumToken.PercentageTokenType, val: 30}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.HWB - }, - .1)).equals(true)); + convertColor: ColorType.HWB, + }).then((result) => + expect( + isOkLabClose( + result.ast.chi[0].chi[0].val[0], + { + typ: EnumToken.ColorTokenType, + val: "hwb", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: 13.333322 }, + { typ: EnumToken.PercentageTokenType, val: 30 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.HWB, + }, + 0.1, + ), + ).equals(true), + ); }); - it('lch to hwb #17', function () { + it("lch to hwb #17", function () { return transform(`.hsl { color: lch(40.03718101914915 69.23357317984528 34.752/50%); }`, { beautify: true, - convertColor: ColorType.HWB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'hwb', - chi: [ - {typ: EnumToken.NumberTokenType, val: 0}, - {typ: EnumToken.PercentageTokenType, val: 13.333322}, - {typ: EnumToken.PercentageTokenType, val: 30}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.HWB - }, - .1)).equals(true)); + convertColor: ColorType.HWB, + }).then((result) => + expect( + isOkLabClose( + result.ast.chi[0].chi[0].val[0], + { + typ: EnumToken.ColorTokenType, + val: "hwb", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0 }, + { typ: EnumToken.PercentageTokenType, val: 13.333322 }, + { typ: EnumToken.PercentageTokenType, val: 30 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.HWB, + }, + 0.1, + ), + ).equals(true), + ); }); }); } diff --git a/test/specs/code/color-lab.js b/test/specs/code/color-lab.js index a3405a196..2a9b3fbbe 100644 --- a/test/specs/code/color-lab.js +++ b/test/specs/code/color-lab.js @@ -1,332 +1,404 @@ -import {ColorType, EnumToken} from "../../../dist/src/lib/ast/types.js"; -import {isOkLabClose} from "../../../dist/src/lib/syntax/color/utils/distance.js"; +import { ColorType, EnumToken } from "../../../dist/src/lib/ast/types.js"; +import { isOkLabClose } from "../../../dist/src/lib/syntax/color/utils/distance.js"; export function run(describe, expect, it, transform, parse, render) { - - describe('convert to lab', function () { - - it('hex to lab #1', function () { + describe("convert to lab", function () { + it("hex to lab #1", function () { return transform(`.hsl { color: #b3222280; }`, { beautify: true, - convertColor: ColorType.LAB - }).then(result => - expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'lab', - chi: [ - {typ: EnumToken.NumberTokenType, val: '40.03718101914915'}, - {typ: EnumToken.NumberTokenType, val: '56.88418218837618'}, - {typ: EnumToken.NumberTokenType, val: '39.46488910422299'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.LAB - })).equals(true) + convertColor: ColorType.LAB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "lab", + chi: [ + { typ: EnumToken.NumberTokenType, val: "40.03718101914915" }, + { typ: EnumToken.NumberTokenType, val: "56.88418218837618" }, + { typ: EnumToken.NumberTokenType, val: "39.46488910422299" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.LAB, + }), + ).equals(true), ); }); - it('hsl(a) to lab #2', function () { + it("hsl(a) to lab #2", function () { return transform(`.hsl { color: hsl(0 68.07511737089203% 41.764705882352935%/50%); }`, { beautify: true, - convertColor: ColorType.LAB - }).then(result => - expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'lab', - chi: [ - {typ: EnumToken.NumberTokenType, val: '40.03718101914915'}, - {typ: EnumToken.NumberTokenType, val: '56.88418218837618'}, - {typ: EnumToken.NumberTokenType, val: '39.46488910422299'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.LAB - })).equals(true)); + convertColor: ColorType.LAB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "lab", + chi: [ + { typ: EnumToken.NumberTokenType, val: "40.03718101914915" }, + { typ: EnumToken.NumberTokenType, val: "56.88418218837618" }, + { typ: EnumToken.NumberTokenType, val: "39.46488910422299" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.LAB, + }), + ).equals(true), + ); }); - it('device-cmyk() to lab #3', function () { - return transform(`.hsl { color: device-cmyk(0 81.00558659217877% 81.00558659217877% 29.803921568627455%/50%); }`, { - beautify: true, - convertColor: ColorType.LAB - }).then(result => - expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'lab', - chi: [ - {typ: EnumToken.NumberTokenType, val: '40.03718101914915'}, - {typ: EnumToken.NumberTokenType, val: '56.88418218837618'}, - {typ: EnumToken.NumberTokenType, val: '39.46488910422299'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.LAB - })).equals(true)); + it("device-cmyk() to lab #3", function () { + return transform( + `.hsl { color: device-cmyk(0 81.00558659217877% 81.00558659217877% 29.803921568627455%/50%); }`, + { + beautify: true, + convertColor: ColorType.LAB, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "lab", + chi: [ + { typ: EnumToken.NumberTokenType, val: "40.03718101914915" }, + { typ: EnumToken.NumberTokenType, val: "56.88418218837618" }, + { typ: EnumToken.NumberTokenType, val: "39.46488910422299" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.LAB, + }), + ).equals(true), + ); }); - it('rgb to lab #4', function () { + it("rgb to lab #4", function () { return transform(`.hsl { color: rgb(179 34 34/50%); }`, { beautify: true, - convertColor: ColorType.LAB - }).then(result => - expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'lab', - chi: [ - {typ: EnumToken.NumberTokenType, val: '40.03718101914915'}, - {typ: EnumToken.NumberTokenType, val: '56.88418218837618'}, - {typ: EnumToken.NumberTokenType, val: '39.46488910422299'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.LAB - })).equals(true)); + convertColor: ColorType.LAB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "lab", + chi: [ + { typ: EnumToken.NumberTokenType, val: "40.03718101914915" }, + { typ: EnumToken.NumberTokenType, val: "56.88418218837618" }, + { typ: EnumToken.NumberTokenType, val: "39.46488910422299" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.LAB, + }), + ).equals(true), + ); }); - it('srgb to lab #5', function () { - return transform(`.hsl { color: color(srgb-linear .45078578283822346 .01599629336550963 .01599629336550963/50%); }`, { - beautify: true, - convertColor: ColorType.LAB - }).then(result => - expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'lab', - chi: [ - {typ: EnumToken.NumberTokenType, val: '40.03718101914915'}, - {typ: EnumToken.NumberTokenType, val: '56.88418218837618'}, - {typ: EnumToken.NumberTokenType, val: '39.46488910422299'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.LAB - })).equals(true)); + it("srgb to lab #5", function () { + return transform( + `.hsl { color: color(srgb-linear .45078578283822346 .01599629336550963 .01599629336550963/50%); }`, + { + beautify: true, + convertColor: ColorType.LAB, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "lab", + chi: [ + { typ: EnumToken.NumberTokenType, val: "40.03718101914915" }, + { typ: EnumToken.NumberTokenType, val: "56.88418218837618" }, + { typ: EnumToken.NumberTokenType, val: "39.46488910422299" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.LAB, + }), + ).equals(true), + ); }); - it('srgb-linear to lab #6', function () { - return transform(`.hsl { color: color(srgb-linear .45078578283822346 .01599629336550963 .01599629336550963/50%); }`, { - beautify: true, - convertColor: ColorType.LAB - }).then(result => - expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'lab', - chi: [ - {typ: EnumToken.NumberTokenType, val: '40.03718101914915'}, - {typ: EnumToken.NumberTokenType, val: '56.88418218837618'}, - {typ: EnumToken.NumberTokenType, val: '39.46488910422299'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.LAB - })).equals(true)); + it("srgb-linear to lab #6", function () { + return transform( + `.hsl { color: color(srgb-linear .45078578283822346 .01599629336550963 .01599629336550963/50%); }`, + { + beautify: true, + convertColor: ColorType.LAB, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "lab", + chi: [ + { typ: EnumToken.NumberTokenType, val: "40.03718101914915" }, + { typ: EnumToken.NumberTokenType, val: "56.88418218837618" }, + { typ: EnumToken.NumberTokenType, val: "39.46488910422299" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.LAB, + }), + ).equals(true), + ); }); - it('display-p3 to lab #7', function () { - return transform(`.hsl { color: color(display-p3 .6449802764484531 .19119980094061145 .16577088540310694/50%); }`, { - beautify: true, - convertColor: ColorType.LAB - }).then(result => - expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'lab', - chi: [ - {typ: EnumToken.NumberTokenType, val: '40.03718101914915'}, - {typ: EnumToken.NumberTokenType, val: '56.88418218837618'}, - {typ: EnumToken.NumberTokenType, val: '39.46488910422299'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.LAB - })).equals(true)); + it("display-p3 to lab #7", function () { + return transform( + `.hsl { color: color(display-p3 .6449802764484531 .19119980094061145 .16577088540310694/50%); }`, + { + beautify: true, + convertColor: ColorType.LAB, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "lab", + chi: [ + { typ: EnumToken.NumberTokenType, val: "40.03718101914915" }, + { typ: EnumToken.NumberTokenType, val: "56.88418218837618" }, + { typ: EnumToken.NumberTokenType, val: "39.46488910422299" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.LAB, + }), + ).equals(true), + ); }); - it('a98-rgb to lab #8', function () { - return transform(`.hsl { color: color(a98-rgb .6014734178208441 .15252996199964947 .15252996199964927/50%); }`, { - beautify: true, - convertColor: ColorType.LAB - }).then(result => - expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'lab', - chi: [ - {typ: EnumToken.NumberTokenType, val: '40.03718101914915'}, - {typ: EnumToken.NumberTokenType, val: '56.88418218837618'}, - {typ: EnumToken.NumberTokenType, val: '39.46488910422299'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.LAB - })).equals(true)); + it("a98-rgb to lab #8", function () { + return transform( + `.hsl { color: color(a98-rgb .6014734178208441 .15252996199964947 .15252996199964927/50%); }`, + { + beautify: true, + convertColor: ColorType.LAB, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "lab", + chi: [ + { typ: EnumToken.NumberTokenType, val: "40.03718101914915" }, + { typ: EnumToken.NumberTokenType, val: "56.88418218837618" }, + { typ: EnumToken.NumberTokenType, val: "39.46488910422299" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.LAB, + }), + ).equals(true), + ); }); - it('photo-rgb to lab #9', function () { - return transform(`.hsl { color: color(prophoto-rgb .458932310317104 .20709804105465876 .12397162868002198/50%); }`, { - beautify: true, - convertColor: ColorType.LAB - }).then(result => - expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'lab', - chi: [ - {typ: EnumToken.NumberTokenType, val: '40.03718101914915'}, - {typ: EnumToken.NumberTokenType, val: '56.88418218837618'}, - {typ: EnumToken.NumberTokenType, val: '39.46488910422299'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.LAB - })).equals(true)); + it("photo-rgb to lab #9", function () { + return transform( + `.hsl { color: color(prophoto-rgb .458932310317104 .20709804105465876 .12397162868002198/50%); }`, + { + beautify: true, + convertColor: ColorType.LAB, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "lab", + chi: [ + { typ: EnumToken.NumberTokenType, val: "40.03718101914915" }, + { typ: EnumToken.NumberTokenType, val: "56.88418218837618" }, + { typ: EnumToken.NumberTokenType, val: "39.46488910422299" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.LAB, + }), + ).equals(true), + ); }); - it('rec2020 to lab #10', function () { - return transform(`.hsl { color: color(rec2020 .5293019037237446 .1758230143656851 .10251000214438549/50%); }`, { - beautify: true, - convertColor: ColorType.LAB - }).then(result => - expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'lab', - chi: [ - {typ: EnumToken.NumberTokenType, val: '40.03718101914915'}, - {typ: EnumToken.NumberTokenType, val: '56.88418218837618'}, - {typ: EnumToken.NumberTokenType, val: '39.46488910422299'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.LAB - })).equals(true)); + it("rec2020 to lab #10", function () { + return transform( + `.hsl { color: color(rec2020 .5293019037237446 .1758230143656851 .10251000214438549/50%); }`, + { + beautify: true, + convertColor: ColorType.LAB, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "lab", + chi: [ + { typ: EnumToken.NumberTokenType, val: "40.03718101914915" }, + { typ: EnumToken.NumberTokenType, val: "56.88418218837618" }, + { typ: EnumToken.NumberTokenType, val: "39.46488910422299" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.LAB, + }), + ).equals(true), + ); }); - it('xyz to lab #11', function () { + it("xyz to lab #11", function () { return transform(`.hsl { color: color(xyz .1945069569161969 .108449498170369 .025825724073578846/50%); }`, { beautify: true, - convertColor: ColorType.LAB - }).then(result => - expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'lab', - chi: [ - {typ: EnumToken.NumberTokenType, val: '40.03718101914915'}, - {typ: EnumToken.NumberTokenType, val: '56.88418218837618'}, - {typ: EnumToken.NumberTokenType, val: '39.46488910422299'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.LAB - })).equals(true)); + convertColor: ColorType.LAB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "lab", + chi: [ + { typ: EnumToken.NumberTokenType, val: "40.03718101914915" }, + { typ: EnumToken.NumberTokenType, val: "56.88418218837618" }, + { typ: EnumToken.NumberTokenType, val: "39.46488910422299" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.LAB, + }), + ).equals(true), + ); }); - it('xyz-d50 to lab #12', function () { - return transform(`.hsl { color: color(xyz-d50 .2050219599912612 .11273398825257439 .019252589983419124/50%); }`, { - beautify: true, - convertColor: ColorType.LAB - }).then(result => - expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'lab', - chi: [ - {typ: EnumToken.NumberTokenType, val: '40.03718101914915'}, - {typ: EnumToken.NumberTokenType, val: '56.88418218837618'}, - {typ: EnumToken.NumberTokenType, val: '39.46488910422299'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.LAB - })).equals(true)); + it("xyz-d50 to lab #12", function () { + return transform( + `.hsl { color: color(xyz-d50 .2050219599912612 .11273398825257439 .019252589983419124/50%); }`, + { + beautify: true, + convertColor: ColorType.LAB, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "lab", + chi: [ + { typ: EnumToken.NumberTokenType, val: "40.03718101914915" }, + { typ: EnumToken.NumberTokenType, val: "56.88418218837618" }, + { typ: EnumToken.NumberTokenType, val: "39.46488910422299" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.LAB, + }), + ).equals(true), + ); }); - it('xyz-d65 to lab #13', function () { + it("xyz-d65 to lab #13", function () { return transform(`.hsl { color: color(xyz .1945069569161969 .108449498170369 .025825724073578846/50%); }`, { beautify: true, - convertColor: ColorType.LAB - }).then(result => - expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'lab', - chi: [ - {typ: EnumToken.NumberTokenType, val: '40.03718101914915'}, - {typ: EnumToken.NumberTokenType, val: '56.88418218837618'}, - {typ: EnumToken.NumberTokenType, val: '39.46488910422299'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.LAB - })).equals(true)); + convertColor: ColorType.LAB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "lab", + chi: [ + { typ: EnumToken.NumberTokenType, val: "40.03718101914915" }, + { typ: EnumToken.NumberTokenType, val: "56.88418218837618" }, + { typ: EnumToken.NumberTokenType, val: "39.46488910422299" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.LAB, + }), + ).equals(true), + ); }); - it('oklab to lab #14', function () { + it("oklab to lab #14", function () { return transform(`.hsl { color: oklab(.49863253097209403 .16117650019451524 .08155524287358212/50%); }`, { beautify: true, - convertColor: ColorType.LAB - }).then(result => - expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'lab', - chi: [ - {typ: EnumToken.NumberTokenType, val: '40.03718101914915'}, - {typ: EnumToken.NumberTokenType, val: '56.88418218837618'}, - {typ: EnumToken.NumberTokenType, val: '39.46488910422299'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.LAB - })).equals(true)); + convertColor: ColorType.LAB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "lab", + chi: [ + { typ: EnumToken.NumberTokenType, val: "40.03718101914915" }, + { typ: EnumToken.NumberTokenType, val: "56.88418218837618" }, + { typ: EnumToken.NumberTokenType, val: "39.46488910422299" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.LAB, + }), + ).equals(true), + ); }); - it('oklch to lab #15', function () { + it("oklch to lab #15", function () { return transform(`.hsl { color: oklch(.49863253097209403 .1806353283694016 26.8394/50%); }`, { beautify: true, - convertColor: ColorType.LAB - }).then(result => - expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'lab', - chi: [ - {typ: EnumToken.NumberTokenType, val: '40.03718101914915'}, - {typ: EnumToken.NumberTokenType, val: '56.88418218837618'}, - {typ: EnumToken.NumberTokenType, val: '39.46488910422299'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.LAB - })).equals(true)); + convertColor: ColorType.LAB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "lab", + chi: [ + { typ: EnumToken.NumberTokenType, val: "40.03718101914915" }, + { typ: EnumToken.NumberTokenType, val: "56.88418218837618" }, + { typ: EnumToken.NumberTokenType, val: "39.46488910422299" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.LAB, + }), + ).equals(true), + ); }); - it('lch to lab #16', function () { + it("lch to lab #16", function () { return transform(`.hsl { color: lch(40.0371810192% 69.2335731799 34.7519908063 / 0.501960784314); }`, { beautify: true, - convertColor: ColorType.LAB - }).then(result => - expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'lab', - chi: [ - {typ: EnumToken.NumberTokenType, val: 40.037181 }, - {typ: EnumToken.NumberTokenType, val: 56.884182}, - {typ: EnumToken.NumberTokenType, val: 39.464889}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.LAB - })).equals(true)); + convertColor: ColorType.LAB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "lab", + chi: [ + { typ: EnumToken.NumberTokenType, val: 40.037181 }, + { typ: EnumToken.NumberTokenType, val: 56.884182 }, + { typ: EnumToken.NumberTokenType, val: 39.464889 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.LAB, + }), + ).equals(true), + ); }); - it('hwb to lab #17', function () { + it("hwb to lab #17", function () { return transform(`.hsl { color: hwb(0 13.333333333333334% 30%/50%); }`, { beautify: true, - convertColor: ColorType.LAB - }).then(result => - expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'lab', - chi: [ - {typ: EnumToken.NumberTokenType, val: 39.933865 }, - {typ: EnumToken.NumberTokenType, val: 56.736479}, - {typ: EnumToken.NumberTokenType, val: 39.325325}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.LAB - })).equals(true)); + convertColor: ColorType.LAB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "lab", + chi: [ + { typ: EnumToken.NumberTokenType, val: 39.933865 }, + { typ: EnumToken.NumberTokenType, val: 56.736479 }, + { typ: EnumToken.NumberTokenType, val: 39.325325 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.LAB, + }), + ).equals(true), + ); }); }); } diff --git a/test/specs/code/color-lch.js b/test/specs/code/color-lch.js index 34b42a45f..52b711d8b 100644 --- a/test/specs/code/color-lch.js +++ b/test/specs/code/color-lch.js @@ -1,315 +1,404 @@ -import {ColorType, EnumToken} from "../../../dist/src/lib/ast/types.js"; -import {isOkLabClose} from "../../../dist/src/lib/syntax/color/utils/distance.js"; +import { ColorType, EnumToken } from "../../../dist/src/lib/ast/types.js"; +import { isOkLabClose } from "../../../dist/src/lib/syntax/color/utils/distance.js"; export function run(describe, expect, it, transform, parse, render) { - - describe('convert to lab', function () { - - it('hex to lab #1', function () { + describe("convert to lab", function () { + it("hex to lab #1", function () { return transform(`.hsl { color: #b3222280; }`, { beautify: true, - convertColor: ColorType.LCH - }).then(result => - expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'lch', - chi: [ - {typ: EnumToken.NumberTokenType, val: 40.037181 }, - {typ: EnumToken.NumberTokenType, val: 69.233573}, - {typ: EnumToken.NumberTokenType, val: 34.751991}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.LCH - })).equals(true)); + convertColor: ColorType.LCH, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "lch", + chi: [ + { typ: EnumToken.NumberTokenType, val: 40.037181 }, + { typ: EnumToken.NumberTokenType, val: 69.233573 }, + { typ: EnumToken.NumberTokenType, val: 34.751991 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.LCH, + }), + ).equals(true), + ); }); - it('hsl(a) to lab #2', function () { + it("hsl(a) to lab #2", function () { return transform(`.hsl { color: hsl(0 68.07511737089203% 41.764705882352935%/50%); }`, { beautify: true, - convertColor: ColorType.LCH - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'lch', - chi: [ - {typ: EnumToken.NumberTokenType, val: 40.037181 }, - {typ: EnumToken.NumberTokenType, val: 69.233573}, - {typ: EnumToken.NumberTokenType, val: 34.751991}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.LCH - })).equals(true)); + convertColor: ColorType.LCH, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "lch", + chi: [ + { typ: EnumToken.NumberTokenType, val: 40.037181 }, + { typ: EnumToken.NumberTokenType, val: 69.233573 }, + { typ: EnumToken.NumberTokenType, val: 34.751991 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.LCH, + }), + ).equals(true), + ); }); - it('device-cmyk() to lab #3', function () { - return transform(`.hsl { color: device-cmyk(0 81.00558659217877% 81.00558659217877% 29.803921568627455%/50%); }`, { - beautify: true, - convertColor: ColorType.LCH - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'lch', - chi: [ - {typ: EnumToken.NumberTokenType, val: 40.037181 }, - {typ: EnumToken.NumberTokenType, val: 69.233573}, - {typ: EnumToken.NumberTokenType, val: 34.751991}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.LCH - })).equals(true)); + it("device-cmyk() to lab #3", function () { + return transform( + `.hsl { color: device-cmyk(0 81.00558659217877% 81.00558659217877% 29.803921568627455%/50%); }`, + { + beautify: true, + convertColor: ColorType.LCH, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "lch", + chi: [ + { typ: EnumToken.NumberTokenType, val: 40.037181 }, + { typ: EnumToken.NumberTokenType, val: 69.233573 }, + { typ: EnumToken.NumberTokenType, val: 34.751991 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.LCH, + }), + ).equals(true), + ); }); - it('rgb to lab #4', function () { + it("rgb to lab #4", function () { return transform(`.hsl { color: rgb(179 34 34/50%); }`, { beautify: true, - convertColor: ColorType.LCH - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'lch', - chi: [ - {typ: EnumToken.NumberTokenType, val: 40.037181 }, - {typ: EnumToken.NumberTokenType, val: 69.233573}, - {typ: EnumToken.NumberTokenType, val: 34.751991}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.LCH - })).equals(true)); + convertColor: ColorType.LCH, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "lch", + chi: [ + { typ: EnumToken.NumberTokenType, val: 40.037181 }, + { typ: EnumToken.NumberTokenType, val: 69.233573 }, + { typ: EnumToken.NumberTokenType, val: 34.751991 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.LCH, + }), + ).equals(true), + ); }); - it('srgb to lab #5', function () { - return transform(`.hsl { color: color(srgb-linear .45078578283822346 .01599629336550963 .01599629336550963/50%); }`, { - beautify: true, - convertColor: ColorType.LCH - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'lch', - chi: [ - {typ: EnumToken.NumberTokenType, val: 40.037181 }, - {typ: EnumToken.NumberTokenType, val: 69.233573}, - {typ: EnumToken.NumberTokenType, val: 34.751991}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.LCH - })).equals(true)); + it("srgb to lab #5", function () { + return transform( + `.hsl { color: color(srgb-linear .45078578283822346 .01599629336550963 .01599629336550963/50%); }`, + { + beautify: true, + convertColor: ColorType.LCH, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "lch", + chi: [ + { typ: EnumToken.NumberTokenType, val: 40.037181 }, + { typ: EnumToken.NumberTokenType, val: 69.233573 }, + { typ: EnumToken.NumberTokenType, val: 34.751991 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.LCH, + }), + ).equals(true), + ); }); - it('srgb-linear to lab #6', function () { - return transform(`.hsl { color: color(srgb-linear .45078578283822346 .01599629336550963 .01599629336550963/50%); }`, { - beautify: true, - convertColor: ColorType.LCH - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'lch', - chi: [ - {typ: EnumToken.NumberTokenType, val: 40.037181 }, - {typ: EnumToken.NumberTokenType, val: 69.233573}, - {typ: EnumToken.NumberTokenType, val: 34.751991}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.LCH - })).equals(true)); + it("srgb-linear to lab #6", function () { + return transform( + `.hsl { color: color(srgb-linear .45078578283822346 .01599629336550963 .01599629336550963/50%); }`, + { + beautify: true, + convertColor: ColorType.LCH, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "lch", + chi: [ + { typ: EnumToken.NumberTokenType, val: 40.037181 }, + { typ: EnumToken.NumberTokenType, val: 69.233573 }, + { typ: EnumToken.NumberTokenType, val: 34.751991 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.LCH, + }), + ).equals(true), + ); }); - it('display-p3 to lab #7', function () { - return transform(`.hsl { color: color(display-p3 .6449802764484531 .19119980094061145 .16577088540310694/50%); }`, { - beautify: true, - convertColor: ColorType.LCH - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'lch', - chi: [ - {typ: EnumToken.NumberTokenType, val: 40.037181 }, - {typ: EnumToken.NumberTokenType, val: 69.233573}, - {typ: EnumToken.NumberTokenType, val: 34.751991}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.LCH - })).equals(true)); + it("display-p3 to lab #7", function () { + return transform( + `.hsl { color: color(display-p3 .6449802764484531 .19119980094061145 .16577088540310694/50%); }`, + { + beautify: true, + convertColor: ColorType.LCH, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "lch", + chi: [ + { typ: EnumToken.NumberTokenType, val: 40.037181 }, + { typ: EnumToken.NumberTokenType, val: 69.233573 }, + { typ: EnumToken.NumberTokenType, val: 34.751991 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.LCH, + }), + ).equals(true), + ); }); - it('a98-rgb to lab #8', function () { - return transform(`.hsl { color: color(a98-rgb .6014734178208441 .15252996199964947 .15252996199964927/50%); }`, { - beautify: true, - convertColor: ColorType.LCH - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'lch', - chi: [ - {typ: EnumToken.NumberTokenType, val: 40.037181 }, - {typ: EnumToken.NumberTokenType, val: 69.233573}, - {typ: EnumToken.NumberTokenType, val: 34.751991}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.LCH - })).equals(true)); + it("a98-rgb to lab #8", function () { + return transform( + `.hsl { color: color(a98-rgb .6014734178208441 .15252996199964947 .15252996199964927/50%); }`, + { + beautify: true, + convertColor: ColorType.LCH, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "lch", + chi: [ + { typ: EnumToken.NumberTokenType, val: 40.037181 }, + { typ: EnumToken.NumberTokenType, val: 69.233573 }, + { typ: EnumToken.NumberTokenType, val: 34.751991 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.LCH, + }), + ).equals(true), + ); }); - it('photo-rgb to lab #9', function () { - return transform(`.hsl { color: color(prophoto-rgb .458932310317104 .20709804105465876 .12397162868002198/50%); }`, { - beautify: true, - convertColor: ColorType.LCH - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'lch', - chi: [ - {typ: EnumToken.NumberTokenType, val: 40.037182 }, - {typ: EnumToken.NumberTokenType, val: 69.233573}, - {typ: EnumToken.NumberTokenType, val: 34.751998}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.LCH - })).equals(true)); + it("photo-rgb to lab #9", function () { + return transform( + `.hsl { color: color(prophoto-rgb .458932310317104 .20709804105465876 .12397162868002198/50%); }`, + { + beautify: true, + convertColor: ColorType.LCH, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "lch", + chi: [ + { typ: EnumToken.NumberTokenType, val: 40.037182 }, + { typ: EnumToken.NumberTokenType, val: 69.233573 }, + { typ: EnumToken.NumberTokenType, val: 34.751998 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.LCH, + }), + ).equals(true), + ); }); - it('rec2020 to lab #10', function () { - return transform(`.hsl { color: color(rec2020 .5293019037237446 .1758230143656851 .10251000214438549/50%); }`, { - beautify: true, - convertColor: ColorType.LCH - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'lch', - chi: [ - {typ: EnumToken.NumberTokenType, val: 40.037181 }, - {typ: EnumToken.NumberTokenType, val: 69.233573}, - {typ: EnumToken.NumberTokenType, val: 34.751991}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.LCH - })).equals(true)); + it("rec2020 to lab #10", function () { + return transform( + `.hsl { color: color(rec2020 .5293019037237446 .1758230143656851 .10251000214438549/50%); }`, + { + beautify: true, + convertColor: ColorType.LCH, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "lch", + chi: [ + { typ: EnumToken.NumberTokenType, val: 40.037181 }, + { typ: EnumToken.NumberTokenType, val: 69.233573 }, + { typ: EnumToken.NumberTokenType, val: 34.751991 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.LCH, + }), + ).equals(true), + ); }); - it('xyz to lab #11', function () { + it("xyz to lab #11", function () { return transform(`.hsl { color: color(xyz .1945069569161969 .108449498170369 .025825724073578846/50%); }`, { beautify: true, - convertColor: ColorType.LCH - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'lch', - chi: [ - {typ: EnumToken.NumberTokenType, val: 40.037181 }, - {typ: EnumToken.NumberTokenType, val: 69.233573}, - {typ: EnumToken.NumberTokenType, val: 34.751991}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.LCH - })).equals(true)); + convertColor: ColorType.LCH, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "lch", + chi: [ + { typ: EnumToken.NumberTokenType, val: 40.037181 }, + { typ: EnumToken.NumberTokenType, val: 69.233573 }, + { typ: EnumToken.NumberTokenType, val: 34.751991 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.LCH, + }), + ).equals(true), + ); }); - it('xyz-d50 to lab #12', function () { - return transform(`.hsl { color: color(xyz-d50 .2050219599912612 .11273398825257439 .019252589983419124/50%); }`, { - beautify: true, - convertColor: ColorType.LCH - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'lch', - chi: [ - {typ: EnumToken.NumberTokenType, val: 40.037182 }, - {typ: EnumToken.NumberTokenType, val: 69.233573}, - {typ: EnumToken.NumberTokenType, val: 34.751998}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.LCH - })).equals(true)); + it("xyz-d50 to lab #12", function () { + return transform( + `.hsl { color: color(xyz-d50 .2050219599912612 .11273398825257439 .019252589983419124/50%); }`, + { + beautify: true, + convertColor: ColorType.LCH, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "lch", + chi: [ + { typ: EnumToken.NumberTokenType, val: 40.037182 }, + { typ: EnumToken.NumberTokenType, val: 69.233573 }, + { typ: EnumToken.NumberTokenType, val: 34.751998 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.LCH, + }), + ).equals(true), + ); }); - it('xyz-d65 to lab #13', function () { + it("xyz-d65 to lab #13", function () { return transform(`.hsl { color: color(xyz .1945069569161969 .108449498170369 .025825724073578846/50%); }`, { beautify: true, - convertColor: ColorType.LCH - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'lch', - chi: [ - {typ: EnumToken.NumberTokenType, val: 40.037181 }, - {typ: EnumToken.NumberTokenType, val: 69.233573}, - {typ: EnumToken.NumberTokenType, val: 34.751991}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.LCH - })).equals(true)); + convertColor: ColorType.LCH, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "lch", + chi: [ + { typ: EnumToken.NumberTokenType, val: 40.037181 }, + { typ: EnumToken.NumberTokenType, val: 69.233573 }, + { typ: EnumToken.NumberTokenType, val: 34.751991 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.LCH, + }), + ).equals(true), + ); }); - it('oklab to lab #14', function () { + it("oklab to lab #14", function () { return transform(`.hsl { color: oklab(.49863253097209403 .16117650019451524 .08155524287358212/50%); }`, { beautify: true, - convertColor: ColorType.LCH - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'lch', - chi: [ - {typ: EnumToken.NumberTokenType, val: 40.037181 }, - {typ: EnumToken.NumberTokenType, val: 69.23358}, - {typ: EnumToken.NumberTokenType, val: 34.751999}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.LCH - })).equals(true)); + convertColor: ColorType.LCH, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "lch", + chi: [ + { typ: EnumToken.NumberTokenType, val: 40.037181 }, + { typ: EnumToken.NumberTokenType, val: 69.23358 }, + { typ: EnumToken.NumberTokenType, val: 34.751999 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.LCH, + }), + ).equals(true), + ); }); - it('oklch to lab #15', function () { + it("oklch to lab #15", function () { return transform(`.hsl { color: oklch(.49863253097209403 .1806353283694016 26.8394/50%); }`, { beautify: true, - convertColor: ColorType.LCH - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'lch', - chi: [ - {typ: EnumToken.NumberTokenType, val: 40.037181 }, - {typ: EnumToken.NumberTokenType, val: 69.23358}, - {typ: EnumToken.NumberTokenType, val: 34.752002}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.LCH - })).equals(true)); + convertColor: ColorType.LCH, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "lch", + chi: [ + { typ: EnumToken.NumberTokenType, val: 40.037181 }, + { typ: EnumToken.NumberTokenType, val: 69.23358 }, + { typ: EnumToken.NumberTokenType, val: 34.752002 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.LCH, + }), + ).equals(true), + ); }); - it('lab to lch #16', function () { + it("lab to lch #16", function () { return transform(`.hsl { color: lab(40.03718101914915 56.88418218837618 39.46488910422299/50%); }`, { beautify: true, - convertColor: ColorType.LCH - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'lch', - chi: [ - {typ: EnumToken.NumberTokenType, val: 40.037181 }, - {typ: EnumToken.NumberTokenType, val: 69.233573}, - {typ: EnumToken.NumberTokenType, val: 34.751991}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.LCH - })).equals(true)); + convertColor: ColorType.LCH, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "lch", + chi: [ + { typ: EnumToken.NumberTokenType, val: 40.037181 }, + { typ: EnumToken.NumberTokenType, val: 69.233573 }, + { typ: EnumToken.NumberTokenType, val: 34.751991 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.LCH, + }), + ).equals(true), + ); }); - it('hwb to lab #17', function () { + it("hwb to lab #17", function () { return transform(`.hsl { color: hwb(0 13.333333333333334% 30%/50%); }`, { beautify: true, - convertColor: ColorType.LCH - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'lch', - chi: [ - {typ: EnumToken.NumberTokenType, val: 39.933865}, - {typ: EnumToken.NumberTokenType, val: 69.032668}, - {typ: EnumToken.NumberTokenType, val: 34.726697}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.LCH - })).equals(true)); + convertColor: ColorType.LCH, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "lch", + chi: [ + { typ: EnumToken.NumberTokenType, val: 39.933865 }, + { typ: EnumToken.NumberTokenType, val: 69.032668 }, + { typ: EnumToken.NumberTokenType, val: 34.726697 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.LCH, + }), + ).equals(true), + ); }); }); } diff --git a/test/specs/code/color-linear-srgb.js b/test/specs/code/color-linear-srgb.js index 6aea531e3..c338fca81 100644 --- a/test/specs/code/color-linear-srgb.js +++ b/test/specs/code/color-linear-srgb.js @@ -1,620 +1,715 @@ -import {ColorType, EnumToken} from "../../../dist/src/lib/ast/types.js"; -import {isOkLabClose} from "../../../dist/src/lib/syntax/color/utils/distance.js"; +import { ColorType, EnumToken } from "../../../dist/src/lib/ast/types.js"; +import { isOkLabClose } from "../../../dist/src/lib/syntax/color/utils/distance.js"; export function run(describe, expect, it, transform, parse, render) { - - describe('convert to srgb-linear', function () { - - it('rgb(a) to srgb-linear #1', function () { + describe("convert to srgb-linear", function () { + it("rgb(a) to srgb-linear #1", function () { return transform(`.hsl { color: rgb(179 34 34/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "srgb-linear" - }, - { - typ: EnumToken.NumberTokenType, - val: .450786 - }, { - typ: EnumToken.NumberTokenType, - val: .015996 - }, { - typ: EnumToken.NumberTokenType, - val: .015996 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "srgb-linear", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.450786, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.015996, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.015996, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('hsl(a) to srgb-linear #2', function () { + it("hsl(a) to srgb-linear #2", function () { return transform(`.hsl { color: hsl(0 68.07511737089203% 41.764705882352935%/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "srgb-linear" - }, - { - typ: EnumToken.NumberTokenType, - val: .450786 - }, { - typ: EnumToken.NumberTokenType, - val: .015996 - }, { - typ: EnumToken.NumberTokenType, - val: .015996 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "srgb-linear", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.450786, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.015996, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.015996, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('device-cmyk() to srgb-linear #3', function () { - return transform(`.hsl { color: device-cmyk(0 81.00558659217877% 81.00558659217877% 29.803921568627455%/50%); }`, { - beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "srgb-linear" - }, - { - typ: EnumToken.NumberTokenType, - val: .450786 - }, { - typ: EnumToken.NumberTokenType, - val: .015996 - }, { - typ: EnumToken.NumberTokenType, - val: .015996 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + it("device-cmyk() to srgb-linear #3", function () { + return transform( + `.hsl { color: device-cmyk(0 81.00558659217877% 81.00558659217877% 29.803921568627455%/50%); }`, + { + beautify: true, + convertColor: ColorType.SRGB_LINEAR, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "srgb-linear", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.450786, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.015996, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.015996, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('hwb() to srgb-linear #4', function () { + it("hwb() to srgb-linear #4", function () { return transform(`.hsl { color: hwb(0 13.333333333333334% 30%/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "srgb-linear" - }, - { - typ: EnumToken.NumberTokenType, - val: .447988 - }, { - typ: EnumToken.NumberTokenType, - val: .015996 - }, { - typ: EnumToken.NumberTokenType, - val:.015996 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "srgb-linear", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.447988, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.015996, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.015996, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('srgb to srgb-linear #5', function () { - return transform(`.hsl { color: color(srgb .7019607843137254 .13333333333333333 .13333333333333333/50%); }`, { - beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "srgb-linear" - }, - { - typ: EnumToken.NumberTokenType, - val: .450786 - }, { - typ: EnumToken.NumberTokenType, - val: .015996 - }, { - typ: EnumToken.NumberTokenType, - val: .015996 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + it("srgb to srgb-linear #5", function () { + return transform( + `.hsl { color: color(srgb .7019607843137254 .13333333333333333 .13333333333333333/50%); }`, + { + beautify: true, + convertColor: ColorType.SRGB_LINEAR, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "srgb-linear", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.450786, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.015996, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.015996, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('srgb to srgb-linear #6', function () { - return transform(`.hsl { color: color(srgb .7019607843137254 .13333333333333333 .13333333333333333/50%); }`, { - beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "srgb-linear" - }, - { - typ: EnumToken.NumberTokenType, - val: .450786 - }, { - typ: EnumToken.NumberTokenType, - val: .015996 - }, { - typ: EnumToken.NumberTokenType, - val: .015996 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + it("srgb to srgb-linear #6", function () { + return transform( + `.hsl { color: color(srgb .7019607843137254 .13333333333333333 .13333333333333333/50%); }`, + { + beautify: true, + convertColor: ColorType.SRGB_LINEAR, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "srgb-linear", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.450786, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.015996, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.015996, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('display-p3 to srgb-linear #7', function () { - return transform(`.hsl { color: color(display-p3 0.644980276448 0.191199800941 0.165770885403 / 0.501960784314); }`, { - beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "srgb-linear" - }, - { - typ: EnumToken.NumberTokenType, - val: .450786 - }, { - typ: EnumToken.NumberTokenType, - val: .015996 - }, { - typ: EnumToken.NumberTokenType, - val: .015996 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + it("display-p3 to srgb-linear #7", function () { + return transform( + `.hsl { color: color(display-p3 0.644980276448 0.191199800941 0.165770885403 / 0.501960784314); }`, + { + beautify: true, + convertColor: ColorType.SRGB_LINEAR, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "srgb-linear", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.450786, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.015996, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.015996, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('a98-rgb to srgb-linear #8', function () { + it("a98-rgb to srgb-linear #8", function () { return transform(`.hsl { color: color(a98-rgb 0.6015 0.1525 0.1525 / 0.50); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "srgb-linear" - }, - { - typ: EnumToken.NumberTokenType, - val: .450833 - }, { - typ: EnumToken.NumberTokenType, - val: .015989 - }, { - typ: EnumToken.NumberTokenType, - val: .015989 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "srgb-linear", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.450833, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.015989, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.015989, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('photo-rgb to srgb-linear #9', function () { + it("photo-rgb to srgb-linear #9", function () { return transform(`.hsl { color: color(prophoto-rgb 0.4589 0.2071 0.124 / 0.50); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "srgb-linear" - }, - { - typ: EnumToken.NumberTokenType, - val: .450719 - }, { - typ: EnumToken.NumberTokenType, - val: .016005 - }, { - typ: EnumToken.NumberTokenType, - val: .016008 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "srgb-linear", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.450719, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.016005, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.016008, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('rec2020 to srgb-linear #10', function () { + it("rec2020 to srgb-linear #10", function () { return transform(`.hsl { color: color(prophoto-rgb 0.4589 0.2071 0.124 / 0.50); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "srgb-linear" - }, - { - typ: EnumToken.NumberTokenType, - val: .450719 - }, { - typ: EnumToken.NumberTokenType, - val: .016005 - }, { - typ: EnumToken.NumberTokenType, - val: .016008 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "srgb-linear", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.450719, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.016005, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.016008, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('xyz to srgb-linear #11', function () { + it("xyz to srgb-linear #11", function () { return transform(`.hsl { color: color(xyz-d65 0.1945 0.1084 0.0258 / 0.50); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "srgb-linear" - }, - { - typ: EnumToken.NumberTokenType, - val: .450852 - }, { - typ: EnumToken.NumberTokenType, - val: .015909 - }, { - typ: EnumToken.NumberTokenType, - val: .015979 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "srgb-linear", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.450852, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.015909, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.015979, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('xyz-d50 to srgb-linear #12', function () { + it("xyz-d50 to srgb-linear #12", function () { return transform(`.hsl { color: color(xyz-d50 0.205 0.1127 0.0193 / 0.50); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "srgb-linear" - }, - { - typ: EnumToken.NumberTokenType, - val: .450749 - }, { - typ: EnumToken.NumberTokenType, - val: .015954 - }, { - typ: EnumToken.NumberTokenType, - val: .016069 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "srgb-linear", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.450749, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.015954, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.016069, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('xyz-d65 to srgb-linear #13', function () { + it("xyz-d65 to srgb-linear #13", function () { return transform(`.hsl { color: color(xyz-d65 0.1945 0.1084 0.0258 / 0.50); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "srgb-linear" - }, - { - typ: EnumToken.NumberTokenType, - val: .450852 - }, { - typ: EnumToken.NumberTokenType, - val: .015909 - }, { - typ: EnumToken.NumberTokenType, - val:.015979 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "srgb-linear", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.450852, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.015909, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.015979, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('oklab to srgb-linear #14', function () { + it("oklab to srgb-linear #14", function () { return transform(`.hsl { color: oklab(.49863253097209403 .16117650019451524 .08155524287358212/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "srgb-linear" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.45078578283822346" - }, { - typ: EnumToken.NumberTokenType, - val: "0.01599629336550963" - }, { - typ: EnumToken.NumberTokenType, - val: "0.01599629336550963" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "srgb-linear", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.45078578283822346", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.01599629336550963", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.01599629336550963", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('oklch to srgb-linear #15', function () { + it("oklch to srgb-linear #15", function () { return transform(`.hsl { color: oklch(.49863253097209403 .1806353283694016 26.839391611662446/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "srgb-linear" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.45078578283822346" - }, { - typ: EnumToken.NumberTokenType, - val: "0.01599629336550963" - }, { - typ: EnumToken.NumberTokenType, - val: "0.01599629336550963" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "srgb-linear", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.45078578283822346", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.01599629336550963", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.01599629336550963", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('lab to srgb-linear #16', function () { + it("lab to srgb-linear #16", function () { return transform(`.hsl { color: lab(40.03718216996103 56.88417629340889 39.4648992397212/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "srgb-linear" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.45078578283822346" - }, { - typ: EnumToken.NumberTokenType, - val: "0.01599629336550963" - }, { - typ: EnumToken.NumberTokenType, - val: "0.01599629336550963" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "srgb-linear", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.45078578283822346", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.01599629336550963", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.01599629336550963", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('lch to srgb-linear #17', function () { + it("lch to srgb-linear #17", function () { return transform(`.hsl { color: lch(40.03718216996103 69.23357411387173 34.75200047889167/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "srgb-linear" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.45078578283822346" - }, { - typ: EnumToken.NumberTokenType, - val: "0.01599629336550963" - }, { - typ: EnumToken.NumberTokenType, - val: "0.01599629336550963" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "srgb-linear", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.45078578283822346", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.01599629336550963", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.01599629336550963", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); }); } diff --git a/test/specs/code/color-oklab.js b/test/specs/code/color-oklab.js index 631e45629..7fc6268e3 100644 --- a/test/specs/code/color-oklab.js +++ b/test/specs/code/color-oklab.js @@ -1,315 +1,404 @@ -import {ColorType, EnumToken} from "../../../dist/src/lib/ast/types.js"; -import {isOkLabClose} from "../../../dist/src/lib/syntax/color/utils/distance.js"; +import { ColorType, EnumToken } from "../../../dist/src/lib/ast/types.js"; +import { isOkLabClose } from "../../../dist/src/lib/syntax/color/utils/distance.js"; export function run(describe, expect, it, transform, parse, render) { - - describe('convert to oklab', function () { - - it('hex to oklab #1', function () { + describe("convert to oklab", function () { + it("hex to oklab #1", function () { return transform(`.hsl { color: #b3222280; }`, { beautify: true, - convertColor: ColorType.OKLAB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'oklab', - chi: [ - {typ: EnumToken.NumberTokenType, val: '.49863253097209403'}, - {typ: EnumToken.NumberTokenType, val: '.16117650019451524'}, - {typ: EnumToken.NumberTokenType, val: '.08155524287358212'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: '50'} - ], - kin: ColorType.OKLAB - })).equals(true) + convertColor: ColorType.OKLAB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "oklab", + chi: [ + { typ: EnumToken.NumberTokenType, val: ".49863253097209403" }, + { typ: EnumToken.NumberTokenType, val: ".16117650019451524" }, + { typ: EnumToken.NumberTokenType, val: ".08155524287358212" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: "50" }, + ], + kin: ColorType.OKLAB, + }), + ).equals(true), ); }); - it('hsl(a) to oklab #2', function () { + it("hsl(a) to oklab #2", function () { return transform(`.hsl { color: hsl(0 68.07511737089203% 41.764705882352935%/50%); }`, { beautify: true, - convertColor: ColorType.OKLAB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'oklab', - chi: [ - {typ: EnumToken.NumberTokenType, val: '.49863253097209403'}, - {typ: EnumToken.NumberTokenType, val: '.16117650019451524'}, - {typ: EnumToken.NumberTokenType, val: '.08155524287358212'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: '50'} - ], - kin: ColorType.OKLAB - })).equals(true)); + convertColor: ColorType.OKLAB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "oklab", + chi: [ + { typ: EnumToken.NumberTokenType, val: ".49863253097209403" }, + { typ: EnumToken.NumberTokenType, val: ".16117650019451524" }, + { typ: EnumToken.NumberTokenType, val: ".08155524287358212" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: "50" }, + ], + kin: ColorType.OKLAB, + }), + ).equals(true), + ); }); - it('device-cmyk() to oklab #3', function () { - return transform(`.hsl { color: device-cmyk(0 81.00558659217877% 81.00558659217877% 29.803921568627455%/50%); }`, { - beautify: true, - convertColor: ColorType.OKLAB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'oklab', - chi: [ - {typ: EnumToken.NumberTokenType, val: '.49863253097209403'}, - {typ: EnumToken.NumberTokenType, val: '.16117650019451524'}, - {typ: EnumToken.NumberTokenType, val: '.08155524287358212'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: '50'} - ], - kin: ColorType.OKLAB - })).equals(true)); + it("device-cmyk() to oklab #3", function () { + return transform( + `.hsl { color: device-cmyk(0 81.00558659217877% 81.00558659217877% 29.803921568627455%/50%); }`, + { + beautify: true, + convertColor: ColorType.OKLAB, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "oklab", + chi: [ + { typ: EnumToken.NumberTokenType, val: ".49863253097209403" }, + { typ: EnumToken.NumberTokenType, val: ".16117650019451524" }, + { typ: EnumToken.NumberTokenType, val: ".08155524287358212" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: "50" }, + ], + kin: ColorType.OKLAB, + }), + ).equals(true), + ); }); - it('rgb to oklab #4', function () { + it("rgb to oklab #4", function () { return transform(`.hsl { color: rgb(179 34 34/50%); }`, { beautify: true, - convertColor: ColorType.OKLAB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'oklab', - chi: [ - {typ: EnumToken.NumberTokenType, val: '.49863253097209403'}, - {typ: EnumToken.NumberTokenType, val: '.16117650019451524'}, - {typ: EnumToken.NumberTokenType, val: '.08155524287358212'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: '50'} - ], - kin: ColorType.OKLAB - })).equals(true)); + convertColor: ColorType.OKLAB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "oklab", + chi: [ + { typ: EnumToken.NumberTokenType, val: ".49863253097209403" }, + { typ: EnumToken.NumberTokenType, val: ".16117650019451524" }, + { typ: EnumToken.NumberTokenType, val: ".08155524287358212" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: "50" }, + ], + kin: ColorType.OKLAB, + }), + ).equals(true), + ); }); - it('srgb to oklab #5', function () { - return transform(`.hsl { color: color(srgb-linear .45078578283822346 .01599629336550963 .01599629336550963/50%); }`, { - beautify: true, - convertColor: ColorType.OKLAB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'oklab', - chi: [ - {typ: EnumToken.NumberTokenType, val: '.49863253097209403'}, - {typ: EnumToken.NumberTokenType, val: '.16117650019451524'}, - {typ: EnumToken.NumberTokenType, val: '.08155524287358212'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: '50'} - ], - kin: ColorType.OKLAB - })).equals(true)); + it("srgb to oklab #5", function () { + return transform( + `.hsl { color: color(srgb-linear .45078578283822346 .01599629336550963 .01599629336550963/50%); }`, + { + beautify: true, + convertColor: ColorType.OKLAB, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "oklab", + chi: [ + { typ: EnumToken.NumberTokenType, val: ".49863253097209403" }, + { typ: EnumToken.NumberTokenType, val: ".16117650019451524" }, + { typ: EnumToken.NumberTokenType, val: ".08155524287358212" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: "50" }, + ], + kin: ColorType.OKLAB, + }), + ).equals(true), + ); }); - it('srgb-linear to oklab #6', function () { - return transform(`.hsl { color: color(srgb-linear .45078578283822346 .01599629336550963 .01599629336550963/50%); }`, { - beautify: true, - convertColor: ColorType.OKLAB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'oklab', - chi: [ - {typ: EnumToken.NumberTokenType, val: '.49863253097209403'}, - {typ: EnumToken.NumberTokenType, val: '.16117650019451524'}, - {typ: EnumToken.NumberTokenType, val: '.08155524287358212'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: '50'} - ], - kin: ColorType.OKLAB - })).equals(true)); + it("srgb-linear to oklab #6", function () { + return transform( + `.hsl { color: color(srgb-linear .45078578283822346 .01599629336550963 .01599629336550963/50%); }`, + { + beautify: true, + convertColor: ColorType.OKLAB, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "oklab", + chi: [ + { typ: EnumToken.NumberTokenType, val: ".49863253097209403" }, + { typ: EnumToken.NumberTokenType, val: ".16117650019451524" }, + { typ: EnumToken.NumberTokenType, val: ".08155524287358212" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: "50" }, + ], + kin: ColorType.OKLAB, + }), + ).equals(true), + ); }); - it('display-p3 to oklab #7', function () { - return transform(`.hsl { color: color(display-p3 .6449802764484531 .19119980094061145 .16577088540310694/50%); }`, { - beautify: true, - convertColor: ColorType.OKLAB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'oklab', - chi: [ - {typ: EnumToken.NumberTokenType, val: '.49863253097209403'}, - {typ: EnumToken.NumberTokenType, val: '.16117650019451524'}, - {typ: EnumToken.NumberTokenType, val: '.08155524287358212'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: '50'} - ], - kin: ColorType.OKLAB - })).equals(true)); + it("display-p3 to oklab #7", function () { + return transform( + `.hsl { color: color(display-p3 .6449802764484531 .19119980094061145 .16577088540310694/50%); }`, + { + beautify: true, + convertColor: ColorType.OKLAB, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "oklab", + chi: [ + { typ: EnumToken.NumberTokenType, val: ".49863253097209403" }, + { typ: EnumToken.NumberTokenType, val: ".16117650019451524" }, + { typ: EnumToken.NumberTokenType, val: ".08155524287358212" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: "50" }, + ], + kin: ColorType.OKLAB, + }), + ).equals(true), + ); }); - it('a98-rgb to oklab #8', function () { - return transform(`.hsl { color: color(a98-rgb .6014734178208441 .15252996199964947 .15252996199964927/50%); }`, { - beautify: true, - convertColor: ColorType.OKLAB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'oklab', - chi: [ - {typ: EnumToken.NumberTokenType, val: '.49863253097209403'}, - {typ: EnumToken.NumberTokenType, val: '.16117650019451524'}, - {typ: EnumToken.NumberTokenType, val: '.08155524287358212'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: '50'} - ], - kin: ColorType.OKLAB - })).equals(true)); + it("a98-rgb to oklab #8", function () { + return transform( + `.hsl { color: color(a98-rgb .6014734178208441 .15252996199964947 .15252996199964927/50%); }`, + { + beautify: true, + convertColor: ColorType.OKLAB, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "oklab", + chi: [ + { typ: EnumToken.NumberTokenType, val: ".49863253097209403" }, + { typ: EnumToken.NumberTokenType, val: ".16117650019451524" }, + { typ: EnumToken.NumberTokenType, val: ".08155524287358212" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: "50" }, + ], + kin: ColorType.OKLAB, + }), + ).equals(true), + ); }); - it('photo-rgb to oklab #9', function () { - return transform(`.hsl { color: color(prophoto-rgb .458932310317104 .20709804105465876 .12397162868002198/50%); }`, { - beautify: true, - convertColor: ColorType.OKLAB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'oklab', - chi: [ - {typ: EnumToken.NumberTokenType, val: '.49863253097209403'}, - {typ: EnumToken.NumberTokenType, val: '.16117650019451524'}, - {typ: EnumToken.NumberTokenType, val: '.08155524287358212'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: '50'} - ], - kin: ColorType.OKLAB - })).equals(true)); + it("photo-rgb to oklab #9", function () { + return transform( + `.hsl { color: color(prophoto-rgb .458932310317104 .20709804105465876 .12397162868002198/50%); }`, + { + beautify: true, + convertColor: ColorType.OKLAB, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "oklab", + chi: [ + { typ: EnumToken.NumberTokenType, val: ".49863253097209403" }, + { typ: EnumToken.NumberTokenType, val: ".16117650019451524" }, + { typ: EnumToken.NumberTokenType, val: ".08155524287358212" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: "50" }, + ], + kin: ColorType.OKLAB, + }), + ).equals(true), + ); }); - it('rec2020 to oklab #10', function () { - return transform(`.hsl { color: color(rec2020 .5293019037237446 .1758230143656851 .10251000214438549/50%); }`, { - beautify: true, - convertColor: ColorType.OKLAB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'oklab', - chi: [ - {typ: EnumToken.NumberTokenType, val: '.49863253097209403'}, - {typ: EnumToken.NumberTokenType, val: '.16117650019451524'}, - {typ: EnumToken.NumberTokenType, val: '.08155524287358212'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: '50'} - ], - kin: ColorType.OKLAB - })).equals(true)); + it("rec2020 to oklab #10", function () { + return transform( + `.hsl { color: color(rec2020 .5293019037237446 .1758230143656851 .10251000214438549/50%); }`, + { + beautify: true, + convertColor: ColorType.OKLAB, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "oklab", + chi: [ + { typ: EnumToken.NumberTokenType, val: ".49863253097209403" }, + { typ: EnumToken.NumberTokenType, val: ".16117650019451524" }, + { typ: EnumToken.NumberTokenType, val: ".08155524287358212" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: "50" }, + ], + kin: ColorType.OKLAB, + }), + ).equals(true), + ); }); - it('xyz to oklab #11', function () { + it("xyz to oklab #11", function () { return transform(`.hsl { color: color(xyz .1945069569161969 .108449498170369 .025825724073578846/50%); }`, { beautify: true, - convertColor: ColorType.OKLAB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'oklab', - chi: [ - {typ: EnumToken.NumberTokenType, val: '.49863253097209403'}, - {typ: EnumToken.NumberTokenType, val: '.16117650019451524'}, - {typ: EnumToken.NumberTokenType, val: '.08155524287358212'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: '50'} - ], - kin: ColorType.OKLAB - })).equals(true)); + convertColor: ColorType.OKLAB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "oklab", + chi: [ + { typ: EnumToken.NumberTokenType, val: ".49863253097209403" }, + { typ: EnumToken.NumberTokenType, val: ".16117650019451524" }, + { typ: EnumToken.NumberTokenType, val: ".08155524287358212" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: "50" }, + ], + kin: ColorType.OKLAB, + }), + ).equals(true), + ); }); - it('xyz-d50 to oklab #12', function () { - return transform(`.hsl { color: color(xyz-d50 .2050219599912612 .11273398825257439 .019252589983419124/50%); }`, { - beautify: true, - convertColor: ColorType.OKLAB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'oklab', - chi: [ - {typ: EnumToken.NumberTokenType, val: '.49863253097209403'}, - {typ: EnumToken.NumberTokenType, val: '.16117650019451524'}, - {typ: EnumToken.NumberTokenType, val: '.08155524287358212'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: '50'} - ], - kin: ColorType.OKLAB - })).equals(true)); + it("xyz-d50 to oklab #12", function () { + return transform( + `.hsl { color: color(xyz-d50 .2050219599912612 .11273398825257439 .019252589983419124/50%); }`, + { + beautify: true, + convertColor: ColorType.OKLAB, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "oklab", + chi: [ + { typ: EnumToken.NumberTokenType, val: ".49863253097209403" }, + { typ: EnumToken.NumberTokenType, val: ".16117650019451524" }, + { typ: EnumToken.NumberTokenType, val: ".08155524287358212" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: "50" }, + ], + kin: ColorType.OKLAB, + }), + ).equals(true), + ); }); - it('xyz-d65 to oklab #13', function () { + it("xyz-d65 to oklab #13", function () { return transform(`.hsl { color: color(xyz .1945069569161969 .108449498170369 .025825724073578846/50%); }`, { beautify: true, - convertColor: ColorType.OKLAB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'oklab', - chi: [ - {typ: EnumToken.NumberTokenType, val: '.49863253097209403'}, - {typ: EnumToken.NumberTokenType, val: '.16117650019451524'}, - {typ: EnumToken.NumberTokenType, val: '.08155524287358212'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: '50'} - ], - kin: ColorType.OKLAB - })).equals(true)); + convertColor: ColorType.OKLAB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "oklab", + chi: [ + { typ: EnumToken.NumberTokenType, val: ".49863253097209403" }, + { typ: EnumToken.NumberTokenType, val: ".16117650019451524" }, + { typ: EnumToken.NumberTokenType, val: ".08155524287358212" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: "50" }, + ], + kin: ColorType.OKLAB, + }), + ).equals(true), + ); }); - it('lab to oklab #14', function () { + it("lab to oklab #14", function () { return transform(`.hsl { color: lab(40.03718101914915 56.88418218837618 39.46488910422299/50%); }`, { beautify: true, - convertColor: ColorType.OKLAB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'oklab', - chi: [ - {typ: EnumToken.NumberTokenType, val: '.49863253097209403'}, - {typ: EnumToken.NumberTokenType, val: '.16117650019451524'}, - {typ: EnumToken.NumberTokenType, val: '.08155524287358212'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: '50'} - ], - kin: ColorType.OKLAB - })).equals(true)); + convertColor: ColorType.OKLAB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "oklab", + chi: [ + { typ: EnumToken.NumberTokenType, val: ".49863253097209403" }, + { typ: EnumToken.NumberTokenType, val: ".16117650019451524" }, + { typ: EnumToken.NumberTokenType, val: ".08155524287358212" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: "50" }, + ], + kin: ColorType.OKLAB, + }), + ).equals(true), + ); }); - it('oklch to oklab #15', function () { + it("oklch to oklab #15", function () { return transform(`.hsl { color: oklch(.49863253097209403 .1806353283694016 26.8394/50%); }`, { beautify: true, - convertColor: ColorType.OKLAB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'oklab', - chi: [ - {typ: EnumToken.NumberTokenType, val: '.49863253097209403'}, - {typ: EnumToken.NumberTokenType, val: '.16117650019451524'}, - {typ: EnumToken.NumberTokenType, val: '.08155524287358212'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: '50'} - ], - kin: ColorType.OKLAB - })).equals(true)); + convertColor: ColorType.OKLAB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "oklab", + chi: [ + { typ: EnumToken.NumberTokenType, val: ".49863253097209403" }, + { typ: EnumToken.NumberTokenType, val: ".16117650019451524" }, + { typ: EnumToken.NumberTokenType, val: ".08155524287358212" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: "50" }, + ], + kin: ColorType.OKLAB, + }), + ).equals(true), + ); }); - it('lch to oklab #16', function () { + it("lch to oklab #16", function () { return transform(`.hsl { color: lch(40.0371810192% 69.2335731799 34.7519908063 / 0.501960784314); }`, { beautify: true, - convertColor: ColorType.OKLAB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'oklab', - chi: [ - {typ: EnumToken.NumberTokenType, val: '.49863253097209403'}, - {typ: EnumToken.NumberTokenType, val: '.16117650019451524'}, - {typ: EnumToken.NumberTokenType, val: '.08155524287358212'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: '50'} - ], - kin: ColorType.OKLAB - })).equals(true)); + convertColor: ColorType.OKLAB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "oklab", + chi: [ + { typ: EnumToken.NumberTokenType, val: ".49863253097209403" }, + { typ: EnumToken.NumberTokenType, val: ".16117650019451524" }, + { typ: EnumToken.NumberTokenType, val: ".08155524287358212" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: "50" }, + ], + kin: ColorType.OKLAB, + }), + ).equals(true), + ); }); - it('hwb to oklab #17', function () { + it("hwb to oklab #17", function () { return transform(`.hsl { color: hwb(0 13.333333333333334% 30%/50%); }`, { beautify: true, - convertColor: ColorType.OKLAB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'oklch', - chi: [ - {typ: EnumToken.NumberTokenType, val: .497702 }, - {typ: EnumToken.NumberTokenType, val: .180163}, - {typ: EnumToken.NumberTokenType, val: 26.827138}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.OKLCH - })).equals(true)); + convertColor: ColorType.OKLAB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "oklch", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0.497702 }, + { typ: EnumToken.NumberTokenType, val: 0.180163 }, + { typ: EnumToken.NumberTokenType, val: 26.827138 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.OKLCH, + }), + ).equals(true), + ); }); }); } diff --git a/test/specs/code/color-oklch.js b/test/specs/code/color-oklch.js index 56a261dc7..baa7da9e0 100644 --- a/test/specs/code/color-oklch.js +++ b/test/specs/code/color-oklch.js @@ -1,314 +1,404 @@ -import {ColorType, EnumToken} from "../../../dist/src/lib/ast/types.js"; -import {isOkLabClose} from "../../../dist/src/lib/syntax/color/utils/distance.js"; +import { ColorType, EnumToken } from "../../../dist/src/lib/ast/types.js"; +import { isOkLabClose } from "../../../dist/src/lib/syntax/color/utils/distance.js"; export function run(describe, expect, it, transform, parse, render) { - - describe('convert to oklch', function () { - - it('hex to oklch #1', function () { - return transform(`.hsl { color: #b3222280; }`,{ + describe("convert to oklch", function () { + it("hex to oklch #1", function () { + return transform(`.hsl { color: #b3222280; }`, { beautify: true, - convertColor: ColorType.OKLCH - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'oklch', - chi: [ - {typ: EnumToken.NumberTokenType, val: .49863253097209403}, - {typ: EnumToken.NumberTokenType, val: .1806353283694016}, - {typ: EnumToken.NumberTokenType, val: '26.8394'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: '50'} - ], - kin: ColorType.OKLCH - })).equals(true)); + convertColor: ColorType.OKLCH, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "oklch", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0.49863253097209403 }, + { typ: EnumToken.NumberTokenType, val: 0.1806353283694016 }, + { typ: EnumToken.NumberTokenType, val: "26.8394" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: "50" }, + ], + kin: ColorType.OKLCH, + }), + ).equals(true), + ); }); - it('hsl(a) to oklch #2', function () { - return transform(`.hsl { color: hsl(0 68.07511737089203% 41.764705882352935%/50%); }`,{ + it("hsl(a) to oklch #2", function () { + return transform(`.hsl { color: hsl(0 68.07511737089203% 41.764705882352935%/50%); }`, { beautify: true, - convertColor: ColorType.OKLCH - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'oklch', - chi: [ - {typ: EnumToken.NumberTokenType, val: .49863253097209403}, - {typ: EnumToken.NumberTokenType, val: .1806353283694016}, - {typ: EnumToken.NumberTokenType, val: '26.8394'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: '50'} - ], - kin: ColorType.OKLCH - })).equals(true)); + convertColor: ColorType.OKLCH, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "oklch", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0.49863253097209403 }, + { typ: EnumToken.NumberTokenType, val: 0.1806353283694016 }, + { typ: EnumToken.NumberTokenType, val: "26.8394" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: "50" }, + ], + kin: ColorType.OKLCH, + }), + ).equals(true), + ); }); - it('device-cmyk() to oklch #3', function () { - return transform(`.hsl { color: device-cmyk(0 81.00558659217877% 81.00558659217877% 29.803921568627455%/50%); }`,{ - beautify: true, - convertColor: ColorType.OKLCH - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'oklch', - chi: [ - {typ: EnumToken.NumberTokenType, val: .49863253097209403}, - {typ: EnumToken.NumberTokenType, val: .1806353283694016}, - {typ: EnumToken.NumberTokenType, val: '26.8394'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: '50'} - ], - kin: ColorType.OKLCH - })).equals(true)); + it("device-cmyk() to oklch #3", function () { + return transform( + `.hsl { color: device-cmyk(0 81.00558659217877% 81.00558659217877% 29.803921568627455%/50%); }`, + { + beautify: true, + convertColor: ColorType.OKLCH, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "oklch", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0.49863253097209403 }, + { typ: EnumToken.NumberTokenType, val: 0.1806353283694016 }, + { typ: EnumToken.NumberTokenType, val: "26.8394" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: "50" }, + ], + kin: ColorType.OKLCH, + }), + ).equals(true), + ); }); - it('rgb to oklch #4', function () { - return transform(`.hsl { color: rgb(179 34 34/50%); }`,{ + it("rgb to oklch #4", function () { + return transform(`.hsl { color: rgb(179 34 34/50%); }`, { beautify: true, - convertColor: ColorType.OKLCH - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'oklch', - chi: [ - {typ: EnumToken.NumberTokenType, val: .49863253097209403}, - {typ: EnumToken.NumberTokenType, val: .1806353283694016}, - {typ: EnumToken.NumberTokenType, val: '26.8394'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: '50'} - ], - kin: ColorType.OKLCH - })).equals(true)); + convertColor: ColorType.OKLCH, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "oklch", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0.49863253097209403 }, + { typ: EnumToken.NumberTokenType, val: 0.1806353283694016 }, + { typ: EnumToken.NumberTokenType, val: "26.8394" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: "50" }, + ], + kin: ColorType.OKLCH, + }), + ).equals(true), + ); }); - it('srgb to oklch #5', function () { - return transform(`.hsl { color: color(srgb-linear .45078578283822346 .01599629336550963 .01599629336550963/50%); }`,{ - beautify: true, - convertColor: ColorType.OKLCH - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'oklch', - chi: [ - {typ: EnumToken.NumberTokenType, val: .49863253097209403}, - {typ: EnumToken.NumberTokenType, val: .1806353283694016}, - {typ: EnumToken.NumberTokenType, val: '26.8394'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: '50'} - ], - kin: ColorType.OKLCH - })).equals(true)); + it("srgb to oklch #5", function () { + return transform( + `.hsl { color: color(srgb-linear .45078578283822346 .01599629336550963 .01599629336550963/50%); }`, + { + beautify: true, + convertColor: ColorType.OKLCH, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "oklch", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0.49863253097209403 }, + { typ: EnumToken.NumberTokenType, val: 0.1806353283694016 }, + { typ: EnumToken.NumberTokenType, val: "26.8394" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: "50" }, + ], + kin: ColorType.OKLCH, + }), + ).equals(true), + ); }); - it('srgb-linear to oklch #6', function () { - return transform(`.hsl { color: color(srgb-linear .45078578283822346 .01599629336550963 .01599629336550963/50%); }`,{ - beautify: true, - convertColor: ColorType.OKLCH - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'oklch', - chi: [ - {typ: EnumToken.NumberTokenType, val: .49863253097209403}, - {typ: EnumToken.NumberTokenType, val: .1806353283694016}, - {typ: EnumToken.NumberTokenType, val: '26.8394'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: '50'} - ], - kin: ColorType.OKLCH - })).equals(true)); + it("srgb-linear to oklch #6", function () { + return transform( + `.hsl { color: color(srgb-linear .45078578283822346 .01599629336550963 .01599629336550963/50%); }`, + { + beautify: true, + convertColor: ColorType.OKLCH, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "oklch", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0.49863253097209403 }, + { typ: EnumToken.NumberTokenType, val: 0.1806353283694016 }, + { typ: EnumToken.NumberTokenType, val: "26.8394" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: "50" }, + ], + kin: ColorType.OKLCH, + }), + ).equals(true), + ); }); - it('display-p3 to oklch #7', function () { - return transform(`.hsl { color: color(display-p3 .6449802764484531 .19119980094061145 .16577088540310694/50%); }`,{ - beautify: true, - convertColor: ColorType.OKLCH - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'oklch', - chi: [ - {typ: EnumToken.NumberTokenType, val: .49863253097209403}, - {typ: EnumToken.NumberTokenType, val: .1806353283694016}, - {typ: EnumToken.NumberTokenType, val: '26.8394'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: '50'} - ], - kin: ColorType.OKLCH - })).equals(true)); + it("display-p3 to oklch #7", function () { + return transform( + `.hsl { color: color(display-p3 .6449802764484531 .19119980094061145 .16577088540310694/50%); }`, + { + beautify: true, + convertColor: ColorType.OKLCH, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "oklch", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0.49863253097209403 }, + { typ: EnumToken.NumberTokenType, val: 0.1806353283694016 }, + { typ: EnumToken.NumberTokenType, val: "26.8394" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: "50" }, + ], + kin: ColorType.OKLCH, + }), + ).equals(true), + ); }); - it('a98-rgb to oklch #8', function () { - return transform(`.hsl { color: color(a98-rgb .6014734178208441 .15252996199964947 .15252996199964927/50%); }`,{ - beautify: true, - convertColor: ColorType.OKLCH - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'oklch', - chi: [ - {typ: EnumToken.NumberTokenType, val: .49863253097209403}, - {typ: EnumToken.NumberTokenType, val: .1806353283694016}, - {typ: EnumToken.NumberTokenType, val: '26.8394'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: '50'} - ], - kin: ColorType.OKLCH - })).equals(true)); + it("a98-rgb to oklch #8", function () { + return transform( + `.hsl { color: color(a98-rgb .6014734178208441 .15252996199964947 .15252996199964927/50%); }`, + { + beautify: true, + convertColor: ColorType.OKLCH, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "oklch", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0.49863253097209403 }, + { typ: EnumToken.NumberTokenType, val: 0.1806353283694016 }, + { typ: EnumToken.NumberTokenType, val: "26.8394" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: "50" }, + ], + kin: ColorType.OKLCH, + }), + ).equals(true), + ); }); - it('photo-rgb to oklch #9', function () { - return transform(`.hsl { color: color(prophoto-rgb .458932310317104 .20709804105465876 .12397162868002198/50%); }`,{ - beautify: true, - convertColor: ColorType.OKLCH - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'oklch', - chi: [ - {typ: EnumToken.NumberTokenType, val: .49863253097209403}, - {typ: EnumToken.NumberTokenType, val: .1806353283694016}, - {typ: EnumToken.NumberTokenType, val: '26.8394'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: '50'} - ], - kin: ColorType.OKLCH - })).equals(true)); + it("photo-rgb to oklch #9", function () { + return transform( + `.hsl { color: color(prophoto-rgb .458932310317104 .20709804105465876 .12397162868002198/50%); }`, + { + beautify: true, + convertColor: ColorType.OKLCH, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "oklch", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0.49863253097209403 }, + { typ: EnumToken.NumberTokenType, val: 0.1806353283694016 }, + { typ: EnumToken.NumberTokenType, val: "26.8394" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: "50" }, + ], + kin: ColorType.OKLCH, + }), + ).equals(true), + ); }); - it('rec2020 to oklch #10', function () { - return transform(`.hsl { color: color(rec2020 .5293019037237446 .1758230143656851 .10251000214438549/50%); }`,{ - beautify: true, - convertColor: ColorType.OKLCH - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'oklch', - chi: [ - {typ: EnumToken.NumberTokenType, val: .49863253097209403}, - {typ: EnumToken.NumberTokenType, val: .1806353283694016}, - {typ: EnumToken.NumberTokenType, val: '26.8394'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: '50'} - ], - kin: ColorType.OKLCH - })).equals(true)); + it("rec2020 to oklch #10", function () { + return transform( + `.hsl { color: color(rec2020 .5293019037237446 .1758230143656851 .10251000214438549/50%); }`, + { + beautify: true, + convertColor: ColorType.OKLCH, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "oklch", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0.49863253097209403 }, + { typ: EnumToken.NumberTokenType, val: 0.1806353283694016 }, + { typ: EnumToken.NumberTokenType, val: "26.8394" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: "50" }, + ], + kin: ColorType.OKLCH, + }), + ).equals(true), + ); }); - it('xyz to oklch #11', function () { - return transform(`.hsl { color: color(xyz .1945069569161969 .108449498170369 .025825724073578846/50%); }`,{ + it("xyz to oklch #11", function () { + return transform(`.hsl { color: color(xyz .1945069569161969 .108449498170369 .025825724073578846/50%); }`, { beautify: true, - convertColor: ColorType.OKLCH - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'oklch', - chi: [ - {typ: EnumToken.NumberTokenType, val: .49863253097209403}, - {typ: EnumToken.NumberTokenType, val: .1806353283694016}, - {typ: EnumToken.NumberTokenType, val: '26.8394'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: '50'} - ], - kin: ColorType.OKLCH - })).equals(true)); + convertColor: ColorType.OKLCH, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "oklch", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0.49863253097209403 }, + { typ: EnumToken.NumberTokenType, val: 0.1806353283694016 }, + { typ: EnumToken.NumberTokenType, val: "26.8394" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: "50" }, + ], + kin: ColorType.OKLCH, + }), + ).equals(true), + ); }); - it('xyz-d50 to oklch #12', function () { - return transform(`.hsl { color: color(xyz-d50 .2050219599912612 .11273398825257439 .019252589983419124/50%); }`,{ - beautify: true, - convertColor: ColorType.OKLCH - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'oklch', - chi: [ - {typ: EnumToken.NumberTokenType, val: .49863253097209403}, - {typ: EnumToken.NumberTokenType, val: .1806353283694016}, - {typ: EnumToken.NumberTokenType, val: '26.8394'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: '50'} - ], - kin: ColorType.OKLCH - })).equals(true)); + it("xyz-d50 to oklch #12", function () { + return transform( + `.hsl { color: color(xyz-d50 .2050219599912612 .11273398825257439 .019252589983419124/50%); }`, + { + beautify: true, + convertColor: ColorType.OKLCH, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "oklch", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0.49863253097209403 }, + { typ: EnumToken.NumberTokenType, val: 0.1806353283694016 }, + { typ: EnumToken.NumberTokenType, val: "26.8394" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: "50" }, + ], + kin: ColorType.OKLCH, + }), + ).equals(true), + ); }); - it('xyz-d65 to oklch #13', function () { - return transform(`.hsl { color: color(xyz .1945069569161969 .108449498170369 .025825724073578846/50%); }`,{ + it("xyz-d65 to oklch #13", function () { + return transform(`.hsl { color: color(xyz .1945069569161969 .108449498170369 .025825724073578846/50%); }`, { beautify: true, - convertColor: ColorType.OKLCH - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'oklch', - chi: [ - {typ: EnumToken.NumberTokenType, val: .49863253097209403}, - {typ: EnumToken.NumberTokenType, val: .1806353283694016}, - {typ: EnumToken.NumberTokenType, val: '26.8394'}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: '50'} - ], - kin: ColorType.OKLCH - })).equals(true)); + convertColor: ColorType.OKLCH, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "oklch", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0.49863253097209403 }, + { typ: EnumToken.NumberTokenType, val: 0.1806353283694016 }, + { typ: EnumToken.NumberTokenType, val: "26.8394" }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: "50" }, + ], + kin: ColorType.OKLCH, + }), + ).equals(true), + ); }); - it('oklab to oklch #14', function () { - return transform(`.hsl { color: oklab(.49863253097209403 .16117650019451524 .08155524287358212/50%); }`,{ + it("oklab to oklch #14", function () { + return transform(`.hsl { color: oklab(.49863253097209403 .16117650019451524 .08155524287358212/50%); }`, { beautify: true, - convertColor: ColorType.OKLCH - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'oklch', - chi: [ - {typ: EnumToken.NumberTokenType, val: .49863253097209403}, - {typ: EnumToken.NumberTokenType, val: .1806353283694016}, - {typ: EnumToken.NumberTokenType, val: 26.8394}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.OKLCH - })).equals(true)); + convertColor: ColorType.OKLCH, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "oklch", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0.49863253097209403 }, + { typ: EnumToken.NumberTokenType, val: 0.1806353283694016 }, + { typ: EnumToken.NumberTokenType, val: 26.8394 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.OKLCH, + }), + ).equals(true), + ); }); - it('lch to oklch #15', function () { - return transform(`.hsl { color: lch(40.03718101914915 69.23357317984528 34.752/50%); }`,{ + it("lch to oklch #15", function () { + return transform(`.hsl { color: lch(40.03718101914915 69.23357317984528 34.752/50%); }`, { beautify: true, - convertColor: ColorType.OKLCH - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'oklch', - chi: [ - {typ: EnumToken.NumberTokenType, val: .49863253097209403}, - {typ: EnumToken.NumberTokenType, val: .1806353283694016}, - {typ: EnumToken.NumberTokenType, val: 26.8394}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.OKLCH - })).equals(true)); + convertColor: ColorType.OKLCH, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "oklch", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0.49863253097209403 }, + { typ: EnumToken.NumberTokenType, val: 0.1806353283694016 }, + { typ: EnumToken.NumberTokenType, val: 26.8394 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.OKLCH, + }), + ).equals(true), + ); }); - it('lab to lch #16', function () { - return transform(`.hsl { color: lab(40.03718101914915 56.88418218837618 39.46488910422299/50%); }`,{ + it("lab to lch #16", function () { + return transform(`.hsl { color: lab(40.03718101914915 56.88418218837618 39.46488910422299/50%); }`, { beautify: true, - convertColor: ColorType.OKLCH - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'oklch', - chi: [ - {typ: EnumToken.NumberTokenType, val: .49863253097209403}, - {typ: EnumToken.NumberTokenType, val: .1806353283694016}, - {typ: EnumToken.NumberTokenType, val: 26.8394}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.OKLCH - })).equals(true)); + convertColor: ColorType.OKLCH, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "oklch", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0.49863253097209403 }, + { typ: EnumToken.NumberTokenType, val: 0.1806353283694016 }, + { typ: EnumToken.NumberTokenType, val: 26.8394 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.OKLCH, + }), + ).equals(true), + ); }); - it('hwb to oklch #17', function () { - return transform(`.hsl { color: hwb(0 13.333333333333334% 30%/50%); }`,{ + it("hwb to oklch #17", function () { + return transform(`.hsl { color: hwb(0 13.333333333333334% 30%/50%); }`, { beautify: true, - convertColor: ColorType.OKLCH - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'oklch', - chi: [ - {typ: EnumToken.NumberTokenType, val: .497702 }, - {typ: EnumToken.NumberTokenType, val: .180163}, - {typ: EnumToken.NumberTokenType, val: 26.827138}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.OKLCH - })).equals(true)); + convertColor: ColorType.OKLCH, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "oklch", + chi: [ + { typ: EnumToken.NumberTokenType, val: 0.497702 }, + { typ: EnumToken.NumberTokenType, val: 0.180163 }, + { typ: EnumToken.NumberTokenType, val: 26.827138 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.OKLCH, + }), + ).equals(true), + ); }); }); } diff --git a/test/specs/code/color-prophoto-rgb.js b/test/specs/code/color-prophoto-rgb.js index 8ba92f62d..5deba7b51 100644 --- a/test/specs/code/color-prophoto-rgb.js +++ b/test/specs/code/color-prophoto-rgb.js @@ -1,593 +1,685 @@ -import {ColorType, EnumToken} from "../../../dist/src/lib/ast/types.js"; +import { ColorType, EnumToken } from "../../../dist/src/lib/ast/types.js"; -import {isOkLabClose} from "../../../dist/src/lib/syntax/color/utils/distance.js"; -import {convertColor} from "../../../dist/src/lib/syntax/color/color.js"; -import {parseString} from "../../../dist/src/lib/parser/parse.js"; +import { isOkLabClose } from "../../../dist/src/lib/syntax/color/utils/distance.js"; +import { convertColor } from "../../../dist/src/lib/syntax/color/color.js"; +import { parseString } from "../../../dist/src/lib/parser/parse.js"; export function run(describe, expect, it, transform, parse, render) { - - describe('convert to prophoto-rgb', function () { - - it('rgb(a) to prophoto-rgb #1', function () { + describe("convert to prophoto-rgb", function () { + it("rgb(a) to prophoto-rgb #1", function () { return transform(`.hsl { color: rgb(179 34 34/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "prophoto-rgb" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.458932310317104" - }, { - typ: EnumToken.NumberTokenType, - val: "0.20709804105465876" - }, { - typ: EnumToken.NumberTokenType, - val: "0.12397162868002198" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "prophoto-rgb", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.458932310317104", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.20709804105465876", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.12397162868002198", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('hsl(a) to prophoto-rgb #2', function () { + it("hsl(a) to prophoto-rgb #2", function () { return transform(`.hsl { color: hsl(0 68.07511737089203% 41.764705882352935%/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "prophoto-rgb" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.458932310317104" - }, { - typ: EnumToken.NumberTokenType, - val: "0.20709804105465876" - }, { - typ: EnumToken.NumberTokenType, - val: "0.12397162868002198" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "prophoto-rgb", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.458932310317104", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.20709804105465876", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.12397162868002198", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('device-cmyk() to prophoto-rgb #3', function () { - return transform(`.hsl { color: device-cmyk(0 81.00558659217877% 81.00558659217877% 29.803921568627455%/50%); }`, { - beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "prophoto-rgb" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.458932310317104" - }, { - typ: EnumToken.NumberTokenType, - val: "0.20709804105465876" - }, { - typ: EnumToken.NumberTokenType, - val: "0.12397162868002198" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + it("device-cmyk() to prophoto-rgb #3", function () { + return transform( + `.hsl { color: device-cmyk(0 81.00558659217877% 81.00558659217877% 29.803921568627455%/50%); }`, + { + beautify: true, + convertColor: ColorType.SRGB_LINEAR, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "prophoto-rgb", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.458932310317104", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.20709804105465876", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.12397162868002198", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('hwb() to prophoto-rgb #4', function () { + it("hwb() to prophoto-rgb #4", function () { return transform(`.hsl { color: hwb(0 13.333333333333334% 30%/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "prophoto-rgb" - }, - { - typ: EnumToken.NumberTokenType, - val: .457396 - }, { - typ: EnumToken.NumberTokenType, - val: .206559 - }, { - typ: EnumToken.NumberTokenType, - val: .123832 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "prophoto-rgb", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.457396, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.206559, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.123832, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('hex to prophoto-rgb #5', function () { + it("hex to prophoto-rgb #5", function () { return transform(`.hsl { color: #b3222280; }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "prophoto-rgb" - }, - { - typ: EnumToken.NumberTokenType, - val: .458932 - }, { - typ: EnumToken.NumberTokenType, - val: .207098 - }, { - typ: EnumToken.NumberTokenType, - val: .123972 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "prophoto-rgb", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.458932, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.207098, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.123972, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('srgb-linear to prophoto-rgb #6', function () { - return transform(`.hsl { color: color(srgb-linear .45078578283822346 .01599629336550963 .01599629336550963/50%); }`, { - beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "prophoto-rgb" - }, - { - typ: EnumToken.NumberTokenType, - val: .458932 - }, { - typ: EnumToken.NumberTokenType, - val: .207098 - }, { - typ: EnumToken.NumberTokenType, - val: .123972 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + it("srgb-linear to prophoto-rgb #6", function () { + return transform( + `.hsl { color: color(srgb-linear .45078578283822346 .01599629336550963 .01599629336550963/50%); }`, + { + beautify: true, + convertColor: ColorType.SRGB_LINEAR, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "prophoto-rgb", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.458932, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.207098, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.123972, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('display-p3 to prophoto-rgb #7', function () { - return transform(`.hsl { color: color(display-p3 0.644980276448 0.191199800941 0.165770885403 / 0.501960784314); }`, { - beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "prophoto-rgb" - }, - { - typ: EnumToken.NumberTokenType, - val: .458932 - }, { - typ: EnumToken.NumberTokenType, - val: .207098 - }, { - typ: EnumToken.NumberTokenType, - val: .123972 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + it("display-p3 to prophoto-rgb #7", function () { + return transform( + `.hsl { color: color(display-p3 0.644980276448 0.191199800941 0.165770885403 / 0.501960784314); }`, + { + beautify: true, + convertColor: ColorType.SRGB_LINEAR, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "prophoto-rgb", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.458932, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.207098, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.123972, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('a98-rgb to prophoto-rgb #8', function () { + it("a98-rgb to prophoto-rgb #8", function () { return transform(`.hsl { color: color(a98-rgb 0.6015 0.1525 0.1525 / 0.50); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "prophoto-rgb" - }, - { - typ: EnumToken.NumberTokenType, - val: .458955 - }, { - typ: EnumToken.NumberTokenType, - val:.207095 - }, { - typ: EnumToken.NumberTokenType, - val: .123954 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "prophoto-rgb", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.458955, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.207095, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.123954, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('srgb to prophoto-rgb #9', function () { - return transform(`.hsl { color: color(srgb .7019607843137254 .13333333333333333 .13333333333333333/50%); }`, { - beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "prophoto-rgb" - }, - { - typ: EnumToken.NumberTokenType, - val: .458932 - }, { - typ: EnumToken.NumberTokenType, - val: .207098 - }, { - typ: EnumToken.NumberTokenType, - val: .123972 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + it("srgb to prophoto-rgb #9", function () { + return transform( + `.hsl { color: color(srgb .7019607843137254 .13333333333333333 .13333333333333333/50%); }`, + { + beautify: true, + convertColor: ColorType.SRGB_LINEAR, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "prophoto-rgb", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.458932, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.207098, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.123972, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('xyz to prophoto-rgb #11', function () { + it("xyz to prophoto-rgb #11", function () { return transform(`.hsl { color: color(xyz-d65 0.1945 0.1084 0.0258 / 0.50); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "prophoto-rgb" - }, - { - typ: EnumToken.NumberTokenType, - val: .458936 - }, { - typ: EnumToken.NumberTokenType, - val: .206961 - }, { - typ: EnumToken.NumberTokenType, - val: .1239 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "prophoto-rgb", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.458936, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.206961, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.1239, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('xyz-d50 to prophoto-rgb #12', function () { + it("xyz-d50 to prophoto-rgb #12", function () { return transform(`.hsl { color: color(xyz-d50 0.205 0.1127 0.0193 / 0.50); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "prophoto-rgb" - }, - { - typ: EnumToken.NumberTokenType, - val: .458908 - }, { - typ: EnumToken.NumberTokenType, - val: .207023 - }, { - typ: EnumToken.NumberTokenType, - val: .124141 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "prophoto-rgb", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.458908, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.207023, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.124141, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('xyz-d65 to prophoto-rgb #13', function () { + it("xyz-d65 to prophoto-rgb #13", function () { return transform(`.hsl { color: color(xyz-d65 0.1945 0.1084 0.0258 / 0.50); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "prophoto-rgb" - }, - { - typ: EnumToken.NumberTokenType, - val: .458936 - }, { - typ: EnumToken.NumberTokenType, - val: .206961 - }, { - typ: EnumToken.NumberTokenType, - val: .1239 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "prophoto-rgb", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.458936, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.206961, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.1239, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('oklab to prophoto-rgb #14', function () { + it("oklab to prophoto-rgb #14", function () { return transform(`.hsl { color: oklab(.49863253097209403 .16117650019451524 .08155524287358212/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "prophoto-rgb" - }, - { - typ: EnumToken.NumberTokenType, - val: .458932 - }, { - typ: EnumToken.NumberTokenType, - val: .207098 - }, { - typ: EnumToken.NumberTokenType, - val: .123972 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "prophoto-rgb", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.458932, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.207098, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.123972, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('oklch to prophoto-rgb #15', function () { + it("oklch to prophoto-rgb #15", function () { return transform(`.hsl { color: oklch(.49863253097209403 .1806353283694016 26.839391611662446/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "prophoto-rgb" - }, - { - typ: EnumToken.NumberTokenType, - val: .458932 - }, { - typ: EnumToken.NumberTokenType, - val: .207098 - }, { - typ: EnumToken.NumberTokenType, - val: .123972 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "prophoto-rgb", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.458932, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.207098, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.123972, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('lab to prophoto-rgb #16', function () { + it("lab to prophoto-rgb #16", function () { return transform(`.hsl { color: lab(40.03718216996103 56.88417629340889 39.4648992397212/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "prophoto-rgb" - }, - { - typ: EnumToken.NumberTokenType, - val: .458932 - }, { - typ: EnumToken.NumberTokenType, - val: .207098 - }, { - typ: EnumToken.NumberTokenType, - val: .123972 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "prophoto-rgb", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.458932, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.207098, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.123972, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('lch to prophoto-rgb #17', function () { + it("lch to prophoto-rgb #17", function () { return transform(`.hsl { color: lch(40.03718216996103 69.23357411387173 34.75200047889167/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "prophoto-rgb" - }, - { - typ: EnumToken.NumberTokenType, - val: .458932 - }, { - typ: EnumToken.NumberTokenType, - val: .207098 - }, { - typ: EnumToken.NumberTokenType, - val: .123972 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "prophoto-rgb", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.458932, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.207098, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.123972, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - - - it('srgb to photo-rgb #20', function () { - return expect(isOkLabClose(convertColor(parseString('color(srgb .701961 .133333 .133333/50%)')[0], ColorType.PROPHOTO_RGB), parseString('color(a98-rgb .601473 .15253 .15253/50%)')[0] - )).equals(true); + it("srgb to photo-rgb #20", function () { + return expect( + isOkLabClose( + convertColor(parseString("color(srgb .701961 .133333 .133333/50%)")[0], ColorType.PROPHOTO_RGB), + parseString("color(a98-rgb .601473 .15253 .15253/50%)")[0], + ), + ).equals(true); }); }); } diff --git a/test/specs/code/color-rec2020.js b/test/specs/code/color-rec2020.js index f6080e95d..e8c8e603d 100644 --- a/test/specs/code/color-rec2020.js +++ b/test/specs/code/color-rec2020.js @@ -1,584 +1,668 @@ -import {ColorType, EnumToken} from "../../../dist/src/lib/ast/types.js"; -import {isOkLabClose} from "../../../dist/src/lib/syntax/color/utils/distance.js"; +import { ColorType, EnumToken } from "../../../dist/src/lib/ast/types.js"; +import { isOkLabClose } from "../../../dist/src/lib/syntax/color/utils/distance.js"; export function run(describe, expect, it, transform, parse, render) { - - describe('convert to rec2020', function () { - - it('rgb(a) to rec2020 #1', function () { + describe("convert to rec2020", function () { + it("rgb(a) to rec2020 #1", function () { return transform(`.hsl { color: rgb(179 34 34/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "rec2020" - }, - { - typ: EnumToken.NumberTokenType, - val: .529302 - }, { - typ: EnumToken.NumberTokenType, - val: .175823 - }, { - typ: EnumToken.NumberTokenType, - val: .10251 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "rec2020", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.529302, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.175823, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.10251, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('hsl(a) to rec2020 #2', function () { + it("hsl(a) to rec2020 #2", function () { return transform(`.hsl { color: hsl(0 68.07511737089203% 41.764705882352935%/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "rec2020" - }, - { - typ: EnumToken.NumberTokenType, - val: .529302 - }, { - typ: EnumToken.NumberTokenType, - val: .175823 - }, { - typ: EnumToken.NumberTokenType, - val: .10251 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "rec2020", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.529302, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.175823, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.10251, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('device-cmyk() to rec2020 #3', function () { - return transform(`.hsl { color: device-cmyk(0 81.00558659217877% 81.00558659217877% 29.803921568627455%/50%); }`, { - beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "rec2020" - }, - { - typ: EnumToken.NumberTokenType, - val: .529302 - }, { - typ: EnumToken.NumberTokenType, - val: .175823 - }, { - typ: EnumToken.NumberTokenType, - val: .10251 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + it("device-cmyk() to rec2020 #3", function () { + return transform( + `.hsl { color: device-cmyk(0 81.00558659217877% 81.00558659217877% 29.803921568627455%/50%); }`, + { + beautify: true, + convertColor: ColorType.SRGB_LINEAR, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "rec2020", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.529302, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.175823, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.10251, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('hwb() to rec2020 #4', function () { + it("hwb() to rec2020 #4", function () { return transform(`.hsl { color: hwb(0 13.333333333333334% 30%/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "rec2020" - }, - { - typ: EnumToken.NumberTokenType, - val: .52758 - }, { - typ: EnumToken.NumberTokenType, - val: .175303 - }, { - typ: EnumToken.NumberTokenType, - val:.10233 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "rec2020", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.52758, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.175303, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.10233, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('hex to rec2020 #5', function () { + it("hex to rec2020 #5", function () { return transform(`.hsl { color: #b3222280; }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "rec2020" - }, - { - typ: EnumToken.NumberTokenType, - val: .529302 - }, { - typ: EnumToken.NumberTokenType, - val: .175823 - }, { - typ: EnumToken.NumberTokenType, - val: .10251 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "rec2020", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.529302, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.175823, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.10251, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('srgb-linear to rec2020 #6', function () { - return transform(`.hsl { color: color(srgb-linear .45078578283822346 .01599629336550963 .01599629336550963/50%); }`, { - beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "rec2020" - }, - { - typ: EnumToken.NumberTokenType, - val: .529302 - }, { - typ: EnumToken.NumberTokenType, - val: .175823 - }, { - typ: EnumToken.NumberTokenType, - val: .10251 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + it("srgb-linear to rec2020 #6", function () { + return transform( + `.hsl { color: color(srgb-linear .45078578283822346 .01599629336550963 .01599629336550963/50%); }`, + { + beautify: true, + convertColor: ColorType.SRGB_LINEAR, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "rec2020", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.529302, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.175823, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.10251, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('display-p3 to rec2020 #7', function () { + it("display-p3 to rec2020 #7", function () { return transform(`.hsl { color: color(display-p3 0.644980276448 0.191199800941 0.165770885403 / 0.5); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "rec2020" - }, - { - typ: EnumToken.NumberTokenType, - val: .529302 - }, { - typ: EnumToken.NumberTokenType, - val: .175823 - }, { - typ: EnumToken.NumberTokenType, - val:.10251 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "rec2020", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.529302, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.175823, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.10251, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('a98-rgb to rec2020 #8', function () { + it("a98-rgb to rec2020 #8", function () { return transform(`.hsl { color: color(a98-rgb 0.6015 0.1525 0.1525 / 0.50); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "rec2020" - }, - { - typ: EnumToken.NumberTokenType, - val: .529328 - }, { - typ: EnumToken.NumberTokenType, - val: .175814 - }, { - typ: EnumToken.NumberTokenType, - val: .102486 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "rec2020", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.529328, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.175814, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.102486, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('photo-rgb to rec2020 #9', function () { + it("photo-rgb to rec2020 #9", function () { return transform(`.hsl { color: color(prophoto-rgb 0.4589 0.2071 0.124 / 0.50); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "rec2020" - }, - { - typ: EnumToken.NumberTokenType, - val: .529264 - }, { - typ: EnumToken.NumberTokenType, - val:.175832 - }, { - typ: EnumToken.NumberTokenType, - val: .102548 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "rec2020", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.529264, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.175832, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.102548, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('xyz to rec2020 #11', function () { + it("xyz to rec2020 #11", function () { return transform(`.hsl { color: color(xyz-d65 0.1945 0.1084 0.0258 / 0.50); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "rec2020" - }, - { - typ: EnumToken.NumberTokenType, - val: .529314 - }, { - typ: EnumToken.NumberTokenType, - val: .175619 - }, { - typ: EnumToken.NumberTokenType, - val: .102423 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "rec2020", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.529314, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.175619, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.102423, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('xyz-d50 to rec2020 #12', function () { + it("xyz-d50 to rec2020 #12", function () { return transform(`.hsl { color: color(xyz-d50 0.205 0.1127 0.0193 / 0.50); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "rec2020" - }, - { - typ: EnumToken.NumberTokenType, - val: .529269 - }, { - typ: EnumToken.NumberTokenType, - val: .175714 - }, { - typ: EnumToken.NumberTokenType, - val: .102749 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "rec2020", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.529269, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.175714, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.102749, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('xyz-d65 to rec2020 #13', function () { + it("xyz-d65 to rec2020 #13", function () { return transform(`.hsl { color: color(xyz-d65 0.1945 0.1084 0.0258 / 0.50); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "rec2020" - }, - { - typ: EnumToken.NumberTokenType, - val: .529314 - }, { - typ: EnumToken.NumberTokenType, - val: .175619 - }, { - typ: EnumToken.NumberTokenType, - val: .102423 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "rec2020", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.529314, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.175619, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.102423, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('oklab to rec2020 #14', function () { + it("oklab to rec2020 #14", function () { return transform(`.hsl { color: oklab(.49863253097209403 .16117650019451524 .08155524287358212/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "rec2020" - }, - { - typ: EnumToken.NumberTokenType, - val: .529302 - }, { - typ: EnumToken.NumberTokenType, - val: .175823 - }, { - typ: EnumToken.NumberTokenType, - val: .10251 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "rec2020", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.529302, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.175823, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.10251, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('oklch to rec2020 #15', function () { + it("oklch to rec2020 #15", function () { return transform(`.hsl { color: oklch(.49863253097209403 .1806353283694016 26.839391611662446/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "rec2020" - }, - { - typ: EnumToken.NumberTokenType, - val:.529302 - }, { - typ: EnumToken.NumberTokenType, - val: .175823 - }, { - typ: EnumToken.NumberTokenType, - val:.10251 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "rec2020", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.529302, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.175823, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.10251, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('lab to rec2020 #16', function () { + it("lab to rec2020 #16", function () { return transform(`.hsl { color: lab(40.03718216996103 56.88417629340889 39.4648992397212/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "rec2020" - }, - { - typ: EnumToken.NumberTokenType, - val: .529302 - }, { - typ: EnumToken.NumberTokenType, - val: .175823 - }, { - typ: EnumToken.NumberTokenType, - val: .10251 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "rec2020", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.529302, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.175823, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.10251, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('lch to rec2020 #17', function () { + it("lch to rec2020 #17", function () { return transform(`.hsl { color: lch(40.03718216996103 69.23357411387173 34.75200047889167/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "rec2020" - }, - { - typ: EnumToken.NumberTokenType, - val: .529302 - }, { - typ: EnumToken.NumberTokenType, - val: .175823 - }, { - typ: EnumToken.NumberTokenType, - val: .10251 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "rec2020", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.529302, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.175823, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.10251, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); }); } diff --git a/test/specs/code/color-rgb.js b/test/specs/code/color-rgb.js index 713075e0c..221fa0448 100644 --- a/test/specs/code/color-rgb.js +++ b/test/specs/code/color-rgb.js @@ -1,315 +1,397 @@ -import {ColorType, EnumToken} from "../../../dist/src/lib/ast/types.js"; -import {isOkLabClose} from "../../../dist/src/lib/syntax/color/utils/distance.js"; +import { ColorType, EnumToken } from "../../../dist/src/lib/ast/types.js"; +import { isOkLabClose } from "../../../dist/src/lib/syntax/color/utils/distance.js"; export function run(describe, expect, it, transform, parse, render) { - - describe('convert to rgb', function () { - - it('hex to rgb #1', function () { + describe("convert to rgb", function () { + it("hex to rgb #1", function () { return transform(`.hsl { color: #b3222280; }`, { beautify: true, - convertColor: ColorType.RGB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'rgb', - chi: [ - {typ: EnumToken.NumberTokenType, val: 179}, - {typ: EnumToken.NumberTokenType, val: 34}, - {typ: EnumToken.NumberTokenType, val: 34}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.RGB - })).equals(true)); + convertColor: ColorType.RGB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "rgb", + chi: [ + { typ: EnumToken.NumberTokenType, val: 179 }, + { typ: EnumToken.NumberTokenType, val: 34 }, + { typ: EnumToken.NumberTokenType, val: 34 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.RGB, + }), + ).equals(true), + ); }); - it('hsl(a) to rgb #2', function () { + it("hsl(a) to rgb #2", function () { return transform(`.hsl { color: hsl(0 68.07511737089203% 41.764705882352935%/50%); }`, { beautify: true, - convertColor: ColorType.RGB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'rgb', - chi: [ - {typ: EnumToken.NumberTokenType, val: 179}, - {typ: EnumToken.NumberTokenType, val: 34}, - {typ: EnumToken.NumberTokenType, val: 34}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.RGB - })).equals(true)); + convertColor: ColorType.RGB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "rgb", + chi: [ + { typ: EnumToken.NumberTokenType, val: 179 }, + { typ: EnumToken.NumberTokenType, val: 34 }, + { typ: EnumToken.NumberTokenType, val: 34 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.RGB, + }), + ).equals(true), + ); }); - it('device-cmyk() to rgb #3', function () { + it("device-cmyk() to rgb #3", function () { return transform(`.hsl { color: device-cmyk(0 81.005587% 81.005587% 29.803922%/50%); }`, { beautify: true, - convertColor: ColorType.RGB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'rgb', - chi: [ - {typ: EnumToken.NumberTokenType, val: 179}, - {typ: EnumToken.NumberTokenType, val: 34}, - {typ: EnumToken.NumberTokenType, val: 34}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.RGB - })).equals(true)); + convertColor: ColorType.RGB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "rgb", + chi: [ + { typ: EnumToken.NumberTokenType, val: 179 }, + { typ: EnumToken.NumberTokenType, val: 34 }, + { typ: EnumToken.NumberTokenType, val: 34 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.RGB, + }), + ).equals(true), + ); }); - it('hwb() to rgb #4', function () { + it("hwb() to rgb #4", function () { return transform(`.hsl { color: hwb(0 13.333333% 32%/50%); }`, { beautify: true, - convertColor: ColorType.RGB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'rgb', - chi: [ - {typ: EnumToken.NumberTokenType, val: 179}, - {typ: EnumToken.NumberTokenType, val: 34}, - {typ: EnumToken.NumberTokenType, val: 34}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.RGB - }, - .1)).equals(true)); + convertColor: ColorType.RGB, + }).then((result) => + expect( + isOkLabClose( + result.ast.chi[0].chi[0].val[0], + { + typ: EnumToken.ColorTokenType, + val: "rgb", + chi: [ + { typ: EnumToken.NumberTokenType, val: 179 }, + { typ: EnumToken.NumberTokenType, val: 34 }, + { typ: EnumToken.NumberTokenType, val: 34 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.RGB, + }, + 0.1, + ), + ).equals(true), + ); }); - it('srgb to rgb #5', function () { - return transform(`.hsl { color: color(srgb .7019607843137254 .13333333333333333 .13333333333333333/50%); }`, { - beautify: true, - convertColor: ColorType.RGB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'rgb', - chi: [ - {typ: EnumToken.NumberTokenType, val: 179}, - {typ: EnumToken.NumberTokenType, val: 34}, - {typ: EnumToken.NumberTokenType, val: 34}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.RGB - }, 0.1)).equals(true)); + it("srgb to rgb #5", function () { + return transform( + `.hsl { color: color(srgb .7019607843137254 .13333333333333333 .13333333333333333/50%); }`, + { + beautify: true, + convertColor: ColorType.RGB, + }, + ).then((result) => + expect( + isOkLabClose( + result.ast.chi[0].chi[0].val[0], + { + typ: EnumToken.ColorTokenType, + val: "rgb", + chi: [ + { typ: EnumToken.NumberTokenType, val: 179 }, + { typ: EnumToken.NumberTokenType, val: 34 }, + { typ: EnumToken.NumberTokenType, val: 34 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.RGB, + }, + 0.1, + ), + ).equals(true), + ); }); - it('srgb-linear to rgb #6', function () { - return transform(`.hsl { color: color(srgb-linear .45078578283822346 .01599629336550963 .01599629336550963/50%); }`, { - beautify: true, - convertColor: ColorType.RGB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'rgb', - chi: [ - {typ: EnumToken.NumberTokenType, val: 179}, - {typ: EnumToken.NumberTokenType, val: 34}, - {typ: EnumToken.NumberTokenType, val: 34}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.RGB - })).equals(true)); + it("srgb-linear to rgb #6", function () { + return transform( + `.hsl { color: color(srgb-linear .45078578283822346 .01599629336550963 .01599629336550963/50%); }`, + { + beautify: true, + convertColor: ColorType.RGB, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "rgb", + chi: [ + { typ: EnumToken.NumberTokenType, val: 179 }, + { typ: EnumToken.NumberTokenType, val: 34 }, + { typ: EnumToken.NumberTokenType, val: 34 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.RGB, + }), + ).equals(true), + ); }); - it('display-p3 to rgb #7', function () { - return transform(`.hsl { color: color(display-p3 0.644980276448 0.191199800941 0.165770885403 / 0.501960784314); }`, { - beautify: true, - convertColor: ColorType.RGB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'rgb', - chi: [ - {typ: EnumToken.NumberTokenType, val: 179}, - {typ: EnumToken.NumberTokenType, val: 34}, - {typ: EnumToken.NumberTokenType, val: 34}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.RGB - })).equals(true)); + it("display-p3 to rgb #7", function () { + return transform( + `.hsl { color: color(display-p3 0.644980276448 0.191199800941 0.165770885403 / 0.501960784314); }`, + { + beautify: true, + convertColor: ColorType.RGB, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "rgb", + chi: [ + { typ: EnumToken.NumberTokenType, val: 179 }, + { typ: EnumToken.NumberTokenType, val: 34 }, + { typ: EnumToken.NumberTokenType, val: 34 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.RGB, + }), + ).equals(true), + ); }); - it('a98-rgb to rgb #8', function () { + it("a98-rgb to rgb #8", function () { return transform(`.hsl { color: color(a98-rgb .601473 .15253 .15253/50%); }`, { beautify: true, - convertColor: ColorType.RGB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'rgb', - chi: [ - {typ: EnumToken.NumberTokenType, val: 179}, - {typ: EnumToken.NumberTokenType, val: 34}, - {typ: EnumToken.NumberTokenType, val: 34}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.RGB - })).equals(true)); + convertColor: ColorType.RGB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "rgb", + chi: [ + { typ: EnumToken.NumberTokenType, val: 179 }, + { typ: EnumToken.NumberTokenType, val: 34 }, + { typ: EnumToken.NumberTokenType, val: 34 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.RGB, + }), + ).equals(true), + ); }); - it('photo-rgb to rgb #9', function () { + it("photo-rgb to rgb #9", function () { return transform(`.hsl { color: color(prophoto-rgb .458932 .207098 .123972/50%); }`, { beautify: true, - convertColor: ColorType.RGB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'rgb', - chi: [ - {typ: EnumToken.NumberTokenType, val: 179}, - {typ: EnumToken.NumberTokenType, val: 34}, - {typ: EnumToken.NumberTokenType, val: 34}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.RGB - })).equals(true)); + convertColor: ColorType.RGB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "rgb", + chi: [ + { typ: EnumToken.NumberTokenType, val: 179 }, + { typ: EnumToken.NumberTokenType, val: 34 }, + { typ: EnumToken.NumberTokenType, val: 34 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.RGB, + }), + ).equals(true), + ); }); - it('rec2020 to rgb #10', function () { + it("rec2020 to rgb #10", function () { return transform(`.hsl { color: color(prophoto-rgb .458932 .207098 .123972); }`, { beautify: true, - convertColor: ColorType.RGB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'rgb', - chi: [ - {typ: EnumToken.NumberTokenType, val: 179}, - {typ: EnumToken.NumberTokenType, val: 34}, - {typ: EnumToken.NumberTokenType, val: 34}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.RGB - })).equals(true)); + convertColor: ColorType.RGB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "rgb", + chi: [ + { typ: EnumToken.NumberTokenType, val: 179 }, + { typ: EnumToken.NumberTokenType, val: 34 }, + { typ: EnumToken.NumberTokenType, val: 34 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.RGB, + }), + ).equals(true), + ); }); - it('xyz to rgb #11', function () { + it("xyz to rgb #11", function () { return transform(`.hsl { color: color(xyz .194507 .108449 .025826/50%); }`, { beautify: true, - convertColor: ColorType.RGB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'rgb', - chi: [ - {typ: EnumToken.NumberTokenType, val: 179}, - {typ: EnumToken.NumberTokenType, val: 34}, - {typ: EnumToken.NumberTokenType, val: 34}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.RGB - })).equals(true)); + convertColor: ColorType.RGB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "rgb", + chi: [ + { typ: EnumToken.NumberTokenType, val: 179 }, + { typ: EnumToken.NumberTokenType, val: 34 }, + { typ: EnumToken.NumberTokenType, val: 34 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.RGB, + }), + ).equals(true), + ); }); - it('xyz-d50 to rgb #12', function () { + it("xyz-d50 to rgb #12", function () { return transform(`.hsl { color: color(xyz-d50 .205022 .112734 .019253/50%); }`, { beautify: true, - convertColor: ColorType.RGB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'rgb', - chi: [ - {typ: EnumToken.NumberTokenType, val: 179}, - {typ: EnumToken.NumberTokenType, val: 34}, - {typ: EnumToken.NumberTokenType, val: 34}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.RGB - })).equals(true)); + convertColor: ColorType.RGB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "rgb", + chi: [ + { typ: EnumToken.NumberTokenType, val: 179 }, + { typ: EnumToken.NumberTokenType, val: 34 }, + { typ: EnumToken.NumberTokenType, val: 34 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.RGB, + }), + ).equals(true), + ); }); - it('xyz-d65 to rgb #13', function () { + it("xyz-d65 to rgb #13", function () { return transform(`.hsl { color: color(xyz-d65 .194507 .108449 .025826/50%); }`, { beautify: true, - convertColor: ColorType.RGB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'rgb', - chi: [ - {typ: EnumToken.NumberTokenType, val: 179}, - {typ: EnumToken.NumberTokenType, val: 34}, - {typ: EnumToken.NumberTokenType, val: 34}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.RGB - })).equals(true)); + convertColor: ColorType.RGB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "rgb", + chi: [ + { typ: EnumToken.NumberTokenType, val: 179 }, + { typ: EnumToken.NumberTokenType, val: 34 }, + { typ: EnumToken.NumberTokenType, val: 34 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.RGB, + }), + ).equals(true), + ); }); - it('oklab to rgb #14', function () { + it("oklab to rgb #14", function () { return transform(`.hsl { color: oklab(.498633 .161177 .081555/50%); }`, { beautify: true, - convertColor: ColorType.RGB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'rgb', - chi: [ - {typ: EnumToken.NumberTokenType, val: 179}, - {typ: EnumToken.NumberTokenType, val: 34}, - {typ: EnumToken.NumberTokenType, val: 34}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.RGB - })).equals(true)); + convertColor: ColorType.RGB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "rgb", + chi: [ + { typ: EnumToken.NumberTokenType, val: 179 }, + { typ: EnumToken.NumberTokenType, val: 34 }, + { typ: EnumToken.NumberTokenType, val: 34 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.RGB, + }), + ).equals(true), + ); }); - it('oklch to rgb #15', function () { + it("oklch to rgb #15", function () { return transform(`.hsl { color: oklch(.498633 .180635 26.839392/50%); }`, { beautify: true, - convertColor: ColorType.RGB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'rgb', - chi: [ - {typ: EnumToken.NumberTokenType, val: 179}, - {typ: EnumToken.NumberTokenType, val: 34}, - {typ: EnumToken.NumberTokenType, val: 34}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.RGB - })).equals(true)); + convertColor: ColorType.RGB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "rgb", + chi: [ + { typ: EnumToken.NumberTokenType, val: 179 }, + { typ: EnumToken.NumberTokenType, val: 34 }, + { typ: EnumToken.NumberTokenType, val: 34 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.RGB, + }), + ).equals(true), + ); }); - it('lab to rgb #16', function () { + it("lab to rgb #16", function () { return transform(`.hsl { color: lab(40.03718216996103 56.88417629340889 39.4648992397212/50%); }`, { beautify: true, - convertColor: ColorType.RGB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'rgb', - chi: [ - {typ: EnumToken.NumberTokenType, val: 179}, - {typ: EnumToken.NumberTokenType, val: 34}, - {typ: EnumToken.NumberTokenType, val: 34}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.RGB - })).equals(true)); + convertColor: ColorType.RGB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "rgb", + chi: [ + { typ: EnumToken.NumberTokenType, val: 179 }, + { typ: EnumToken.NumberTokenType, val: 34 }, + { typ: EnumToken.NumberTokenType, val: 34 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.RGB, + }), + ).equals(true), + ); }); - it('lch to rgb #17', function () { + it("lch to rgb #17", function () { return transform(`.hsl { color: lch(40.03718216996103 69.23357411387173 34.75200047889167/50%); }`, { beautify: true, - convertColor: ColorType.RGB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: 'rgb', - chi: [ - {typ: EnumToken.NumberTokenType, val: 179}, - {typ: EnumToken.NumberTokenType, val: 34}, - {typ: EnumToken.NumberTokenType, val: 34}, - {typ: EnumToken.LiteralTokenType, val: '/'}, - {typ: EnumToken.PercentageTokenType, val: 50} - ], - kin: ColorType.RGB - })).equals(true)); + convertColor: ColorType.RGB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "rgb", + chi: [ + { typ: EnumToken.NumberTokenType, val: 179 }, + { typ: EnumToken.NumberTokenType, val: 34 }, + { typ: EnumToken.NumberTokenType, val: 34 }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { typ: EnumToken.PercentageTokenType, val: 50 }, + ], + kin: ColorType.RGB, + }), + ).equals(true), + ); }); }); } diff --git a/test/specs/code/color-srgb.js b/test/specs/code/color-srgb.js index 8f0b0acd8..ba41c6476 100644 --- a/test/specs/code/color-srgb.js +++ b/test/specs/code/color-srgb.js @@ -1,584 +1,649 @@ -import {ColorType, EnumToken} from "../../../dist/src/lib/ast/types.js"; -import {isOkLabClose} from "../../../dist/src/lib/syntax/color/utils/distance.js"; +import { ColorType, EnumToken } from "../../../dist/src/lib/ast/types.js"; +import { isOkLabClose } from "../../../dist/src/lib/syntax/color/utils/distance.js"; export function run(describe, expect, it, transform, parse, render) { - - describe('convert to srgb', function () { - - it('rgb(a) to srgb #1', function () { + describe("convert to srgb", function () { + it("rgb(a) to srgb #1", function () { return transform(`.hsl { color: rgb(179 34 34/50%); }`, { beautify: true, - convertColor: ColorType.SRGB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - "typ": EnumToken.ColorTokenType, - "val": "color", - "chi": [{"typ": 7, "val": "display-p3"}, {"typ": 12, "val": "0.6449802764484531"}, { - "typ": 12, - "val": "0.19119980094061145" - }, {"typ": 12, "val": "0.16577088540310694"}, {"typ": 6, "val": "/"}, {"typ": 14, "val": "50"}], - "kin": ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { typ: 7, val: "display-p3" }, + { typ: 12, val: "0.6449802764484531" }, + { + typ: 12, + val: "0.19119980094061145", + }, + { typ: 12, val: "0.16577088540310694" }, + { typ: 6, val: "/" }, + { typ: 14, val: "50" }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('hsl(a) to srgb #2', function () { + it("hsl(a) to srgb #2", function () { return transform(`.hsl { color: hsl(0 68.07511737089203% 41.764705882352935%/50%); }`, { beautify: true, - convertColor: ColorType.SRGB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "srgb" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.7019607843137254" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.13333333333333333" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.13333333333333333" - }, - {typ: EnumToken.LiteralTokenType, val: "/"}, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "srgb", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.7019607843137254", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.13333333333333333", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.13333333333333333", + }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('device-cmyk() to srgb #3', function () { - return transform(`.hsl { color: device-cmyk(0 81.00558659217877% 81.00558659217877% 29.803921568627455%/50%); }`, { - beautify: true, - convertColor: ColorType.SRGB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "srgb" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.7019607843137254" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.13333333333333333" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.13333333333333333" - }, - {typ: EnumToken.LiteralTokenType, val: "/"}, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + it("device-cmyk() to srgb #3", function () { + return transform( + `.hsl { color: device-cmyk(0 81.00558659217877% 81.00558659217877% 29.803921568627455%/50%); }`, + { + beautify: true, + convertColor: ColorType.SRGB, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "srgb", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.7019607843137254", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.13333333333333333", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.13333333333333333", + }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('hwb() to srgb #4', function () { + it("hwb() to srgb #4", function () { return transform(`.hsl { color: hwb(0 13.333333333333334% 30%/50%); }`, { beautify: true, - convertColor: ColorType.SRGB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "srgb" - }, - { - typ: EnumToken.NumberTokenType, - val: .7 - }, - { - typ: EnumToken.NumberTokenType, - val: .133333 - }, - { - typ: EnumToken.NumberTokenType, - val: .133333 - }, - {typ: EnumToken.LiteralTokenType, val: "/"}, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "srgb", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.7, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.133333, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.133333, + }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('hex to srgb #5', function () { + it("hex to srgb #5", function () { return transform(`.hsl { color: #b3222280; }`, { beautify: true, - convertColor: ColorType.SRGB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "srgb" - }, - { - typ: EnumToken.NumberTokenType, - val: .701961 - }, - { - typ: EnumToken.NumberTokenType, - val: .133333 - }, - { - typ: EnumToken.NumberTokenType, - val: .133333 - }, - {typ: EnumToken.LiteralTokenType, val: "/"}, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "srgb", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.701961, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.133333, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.133333, + }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('srgb-linear to srgb #6', function () { - return transform(`.hsl { color: color(srgb-linear .45078578283822346 .01599629336550963 .01599629336550963/50%); }`, { - beautify: true, - convertColor: ColorType.SRGB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "srgb" - }, - { - typ: EnumToken.NumberTokenType, - val: .701961 - }, - { - typ: EnumToken.NumberTokenType, - val: .133333 - }, - { - typ: EnumToken.NumberTokenType, - val: .133333 - }, - {typ: EnumToken.LiteralTokenType, val: "/"}, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + it("srgb-linear to srgb #6", function () { + return transform( + `.hsl { color: color(srgb-linear .45078578283822346 .01599629336550963 .01599629336550963/50%); }`, + { + beautify: true, + convertColor: ColorType.SRGB, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "srgb", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.701961, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.133333, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.133333, + }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('display-p3 to srgb #7', function () { - return transform(`.hsl { color: color(display-p3 0.644980276448 0.191199800941 0.165770885403 / 0.501960784314); }`, { - beautify: true, - convertColor: ColorType.SRGB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "srgb" - }, - { - typ: EnumToken.NumberTokenType, - val: .701961 - }, - { - typ: EnumToken.NumberTokenType, - val: .133333 - }, - { - typ: EnumToken.NumberTokenType, - val: .133333 - }, - {typ: EnumToken.LiteralTokenType, val: "/"}, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + it("display-p3 to srgb #7", function () { + return transform( + `.hsl { color: color(display-p3 0.644980276448 0.191199800941 0.165770885403 / 0.501960784314); }`, + { + beautify: true, + convertColor: ColorType.SRGB, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "srgb", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.701961, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.133333, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.133333, + }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('a98-rgb to srgb #8', function () { + it("a98-rgb to srgb #8", function () { return transform(`.hsl { color: color(a98-rgb 0.6015 0.1525 0.1525 / 0.50); }`, { beautify: true, - convertColor: ColorType.SRGB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "srgb" - }, - { - typ: EnumToken.NumberTokenType, - val:.701994 - }, - { - typ: EnumToken.NumberTokenType, - val: .133299 - }, - { - typ: EnumToken.NumberTokenType, - val: .133299 - }, - {typ: EnumToken.LiteralTokenType, val: "/"}, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "srgb", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.701994, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.133299, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.133299, + }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('photo-rgb to srgb #9', function () { + it("photo-rgb to srgb #9", function () { return transform(`.hsl { color: color(prophoto-rgb 0.4589 0.2071 0.124 / 0.50); }`, { beautify: true, - convertColor: ColorType.SRGB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "srgb" - }, - { - typ: EnumToken.NumberTokenType, - val: .701914 - }, - { - typ: EnumToken.NumberTokenType, - val: .133374 - }, - { - typ: EnumToken.NumberTokenType, - val:.133389 - }, - {typ: EnumToken.LiteralTokenType, val: "/"}, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "srgb", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.701914, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.133374, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.133389, + }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('rec2020 to srgb #10', function () { + it("rec2020 to srgb #10", function () { return transform(`.hsl { color: color(prophoto-rgb 0.4589 0.2071 0.124 / 0.50); }`, { beautify: true, - convertColor: ColorType.SRGB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "srgb" - }, - { - typ: EnumToken.NumberTokenType, - val: .701914 - }, - { - typ: EnumToken.NumberTokenType, - val: .133374 - }, - { - typ: EnumToken.NumberTokenType, - val: .133389 - }, - {typ: EnumToken.LiteralTokenType, val: "/"}, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "srgb", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.701914, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.133374, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.133389, + }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('xyz to srgb #11', function () { + it("xyz to srgb #11", function () { return transform(`.hsl { color: color(xyz-d65 0.1945 0.1084 0.0258 / 0.50); }`, { beautify: true, - convertColor: ColorType.SRGB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "srgb" - }, - { - typ: EnumToken.NumberTokenType, - val: .702007 - }, - { - typ: EnumToken.NumberTokenType, - val: .132905 - }, - { - typ: EnumToken.NumberTokenType, - val: .133248 - }, - {typ: EnumToken.LiteralTokenType, val: "/"}, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "srgb", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.702007, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.132905, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.133248, + }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('xyz-d50 to srgb #12', function () { + it("xyz-d50 to srgb #12", function () { return transform(`.hsl { color: color(xyz-d50 0.205 0.1127 0.0193 / 0.50); }`, { beautify: true, - convertColor: ColorType.SRGB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "srgb" - }, - { - typ: EnumToken.NumberTokenType, - val: .701935 - }, - { - typ: EnumToken.NumberTokenType, - val: .133127 - }, - { - typ: EnumToken.NumberTokenType, - val: .13369 - }, - {typ: EnumToken.LiteralTokenType, val: "/"}, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "srgb", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.701935, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.133127, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.13369, + }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('xyz-d65 to srgb #13', function () { + it("xyz-d65 to srgb #13", function () { return transform(`.hsl { color: color(xyz-d65 0.1945 0.1084 0.0258 / 0.50); }`, { beautify: true, - convertColor: ColorType.SRGB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "srgb" - }, - { - typ: EnumToken.NumberTokenType, - val: .702007 - }, - { - typ: EnumToken.NumberTokenType, - val: .132905 - }, - { - typ: EnumToken.NumberTokenType, - val: .133248 - }, - {typ: EnumToken.LiteralTokenType, val: "/"}, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "srgb", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.702007, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.132905, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.133248, + }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('oklab to srgb #14', function () { + it("oklab to srgb #14", function () { return transform(`.hsl { color: oklab(.49863253097209403 .16117650019451524 .08155524287358212/50%); }`, { beautify: true, - convertColor: ColorType.SRGB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "srgb" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.7019607843137254" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.13333333333333333" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.13333333333333333" - }, - {typ: EnumToken.LiteralTokenType, val: "/"}, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "srgb", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.7019607843137254", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.13333333333333333", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.13333333333333333", + }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('oklch to srgb #15', function () { + it("oklch to srgb #15", function () { return transform(`.hsl { color: oklch(.49863253097209403 .1806353283694016 26.839391611662446/50%); }`, { beautify: true, - convertColor: ColorType.SRGB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "srgb" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.7019607843137254" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.13333333333333333" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.13333333333333333" - }, - {typ: EnumToken.LiteralTokenType, val: "/"}, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "srgb", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.7019607843137254", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.13333333333333333", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.13333333333333333", + }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('lab to srgb #16', function () { + it("lab to srgb #16", function () { return transform(`.hsl { color: lab(40.03718216996103 56.88417629340889 39.4648992397212/50%); }`, { beautify: true, - convertColor: ColorType.SRGB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "srgb" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.7019607843137254" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.13333333333333333" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.13333333333333333" - }, - {typ: EnumToken.LiteralTokenType, val: "/"}, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "srgb", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.7019607843137254", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.13333333333333333", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.13333333333333333", + }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('lch to srgb #17', function () { + it("lch to srgb #17", function () { return transform(`.hsl { color: lch(40.03718216996103 69.23357411387173 34.75200047889167/50%); }`, { beautify: true, - convertColor: ColorType.SRGB - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "srgb" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.7019607843137254" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.13333333333333333" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.13333333333333333" - }, - {typ: EnumToken.LiteralTokenType, val: "/"}, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "srgb", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.7019607843137254", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.13333333333333333", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.13333333333333333", + }, + { typ: EnumToken.LiteralTokenType, val: "/" }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); }); } diff --git a/test/specs/code/color-xyz-d50.js b/test/specs/code/color-xyz-d50.js index a5fea68d0..b7554e411 100644 --- a/test/specs/code/color-xyz-d50.js +++ b/test/specs/code/color-xyz-d50.js @@ -1,584 +1,674 @@ -import {ColorType, EnumToken} from "../../../dist/src/lib/ast/types.js"; -import {isOkLabClose} from "../../../dist/src/lib/syntax/color/utils/distance.js"; +import { ColorType, EnumToken } from "../../../dist/src/lib/ast/types.js"; +import { isOkLabClose } from "../../../dist/src/lib/syntax/color/utils/distance.js"; export function run(describe, expect, it, transform, parse, render) { - - describe('convert to xyz-d50', function () { - - it('rgb(a) to xyz-d50 #1', function () { + describe("convert to xyz-d50", function () { + it("rgb(a) to xyz-d50 #1", function () { return transform(`.hsl { color: rgb(179 34 34/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "xyz-d50" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.2050219599912612" - }, { - typ: EnumToken.NumberTokenType, - val: "0.11273398825257439" - }, { - typ: EnumToken.NumberTokenType, - val: ".019252589983419124" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "xyz-d50", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.2050219599912612", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.11273398825257439", + }, + { + typ: EnumToken.NumberTokenType, + val: ".019252589983419124", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('hsl(a) to xyz-d50 #2', function () { + it("hsl(a) to xyz-d50 #2", function () { return transform(`.hsl { color: hsl(0 68.07511737089203% 41.764705882352935%/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "xyz-d50" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.2050219599912612" - }, { - typ: EnumToken.NumberTokenType, - val: "0.11273398825257439" - }, { - typ: EnumToken.NumberTokenType, - val: ".019252589983419124" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "xyz-d50", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.2050219599912612", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.11273398825257439", + }, + { + typ: EnumToken.NumberTokenType, + val: ".019252589983419124", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('device-cmyk() to xyz-d50 #3', function () { - return transform(`.hsl { color: device-cmyk(0 81.00558659217877% 81.00558659217877% 29.803921568627455%/50%); }`, { - beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "xyz-d50" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.2050219599912612" - }, { - typ: EnumToken.NumberTokenType, - val: "0.11273398825257439" - }, { - typ: EnumToken.NumberTokenType, - val: ".019252589983419124" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + it("device-cmyk() to xyz-d50 #3", function () { + return transform( + `.hsl { color: device-cmyk(0 81.00558659217877% 81.00558659217877% 29.803921568627455%/50%); }`, + { + beautify: true, + convertColor: ColorType.SRGB_LINEAR, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "xyz-d50", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.2050219599912612", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.11273398825257439", + }, + { + typ: EnumToken.NumberTokenType, + val: ".019252589983419124", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('hwb() to xyz-d50 #4', function () { + it("hwb() to xyz-d50 #4", function () { return transform(`.hsl { color: hwb(0 13.333333333333334% 30%/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "xyz-d50" - }, - { - typ: EnumToken.NumberTokenType, - val: .203802 - }, { - typ: EnumToken.NumberTokenType, - val: .112112 - }, { - typ: EnumToken.NumberTokenType, - val: .019214 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "xyz-d50", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.203802, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.112112, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.019214, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('hex to xyz-d50 #5', function () { + it("hex to xyz-d50 #5", function () { return transform(`.hsl { color: #b3222280; }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "xyz-d50" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.2050219599912612" - }, { - typ: EnumToken.NumberTokenType, - val: "0.11273398825257439" - }, { - typ: EnumToken.NumberTokenType, - val: ".019252589983419124" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "xyz-d50", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.2050219599912612", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.11273398825257439", + }, + { + typ: EnumToken.NumberTokenType, + val: ".019252589983419124", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('srgb-linear to xyz-d50 #6', function () { - return transform(`.hsl { color: color(srgb-linear .45078578283822346 .01599629336550963 .01599629336550963/50%); }`, { - beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "xyz-d50" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.2050219599912612" - }, { - typ: EnumToken.NumberTokenType, - val: "0.11273398825257439" - }, { - typ: EnumToken.NumberTokenType, - val: ".019252589983419124" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + it("srgb-linear to xyz-d50 #6", function () { + return transform( + `.hsl { color: color(srgb-linear .45078578283822346 .01599629336550963 .01599629336550963/50%); }`, + { + beautify: true, + convertColor: ColorType.SRGB_LINEAR, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "xyz-d50", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.2050219599912612", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.11273398825257439", + }, + { + typ: EnumToken.NumberTokenType, + val: ".019252589983419124", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('display-p3 to xyz-d50 #7', function () { - return transform(`.hsl { color: color(display-p3 0.644980276448 0.191199800941 0.165770885403 / 0.501960784314); }`, { - beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "xyz-d50" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.2050219599912612" - }, { - typ: EnumToken.NumberTokenType, - val: "0.11273398825257439" - }, { - typ: EnumToken.NumberTokenType, - val: ".019252589983419124" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + it("display-p3 to xyz-d50 #7", function () { + return transform( + `.hsl { color: color(display-p3 0.644980276448 0.191199800941 0.165770885403 / 0.501960784314); }`, + { + beautify: true, + convertColor: ColorType.SRGB_LINEAR, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "xyz-d50", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.2050219599912612", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.11273398825257439", + }, + { + typ: EnumToken.NumberTokenType, + val: ".019252589983419124", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('a98-rgb to xyz-d50 #8', function () { + it("a98-rgb to xyz-d50 #8", function () { return transform(`.hsl { color: color(a98-rgb 0.6015 0.1525 0.1525 / 0.50); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "xyz-d50" - }, - { - typ: EnumToken.NumberTokenType, - val: .205039 - }, { - typ: EnumToken.NumberTokenType, - val: .112739 - }, { - typ: EnumToken.NumberTokenType, - val: .019248 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "xyz-d50", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.205039, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.112739, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.019248, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('photo-rgb to xyz-d50 #9', function () { + it("photo-rgb to xyz-d50 #9", function () { return transform(`.hsl { color: color(prophoto-rgb 0.4589 0.2071 0.124 / 0.50); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "xyz-d50" - }, - { - typ: EnumToken.NumberTokenType, - val: 0.204998 - }, { - typ: EnumToken.NumberTokenType, - val: 0.112726 - }, { - typ: EnumToken.NumberTokenType, - val: 0.019261 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "xyz-d50", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.204998, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.112726, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.019261, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('xyz to xyz-d50 #11', function () { + it("xyz to xyz-d50 #11", function () { return transform(`.hsl { color: color(xyz-d65 0.1945 0.1084 0.0258 / 0.50); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "xyz-d50" - }, - { - typ: EnumToken.NumberTokenType, - val: .205015 - }, { - typ: EnumToken.NumberTokenType, - val: .112685 - }, { - typ: EnumToken.NumberTokenType, - val: .019233 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "xyz-d50", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.205015, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.112685, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.019233, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('srgb to xyz-d50 #12', function () { - return transform(`.hsl { color: color(srgb .7019607843137254 .13333333333333333 .13333333333333333/50%); }`, { - beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "xyz-d50" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.2050219599912612" - }, { - typ: EnumToken.NumberTokenType, - val: "0.11273398825257439" - }, { - typ: EnumToken.NumberTokenType, - val: ".019252589983419124" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + it("srgb to xyz-d50 #12", function () { + return transform( + `.hsl { color: color(srgb .7019607843137254 .13333333333333333 .13333333333333333/50%); }`, + { + beautify: true, + convertColor: ColorType.SRGB_LINEAR, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "xyz-d50", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.2050219599912612", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.11273398825257439", + }, + { + typ: EnumToken.NumberTokenType, + val: ".019252589983419124", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('xyz-d65 to xyz-d50 #13', function () { + it("xyz-d65 to xyz-d50 #13", function () { return transform(`.hsl { color: color(xyz-d65 0.1945 0.1084 0.0258 / 0.50); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "xyz-d50" - }, - { - typ: EnumToken.NumberTokenType, - val: .205015 - }, { - typ: EnumToken.NumberTokenType, - val: .112685 - }, { - typ: EnumToken.NumberTokenType, - val: .019233 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "xyz-d50", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.205015, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.112685, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.019233, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('oklab to xyz-d50 #14', function () { + it("oklab to xyz-d50 #14", function () { return transform(`.hsl { color: oklab(.49863253097209403 .16117650019451524 .08155524287358212/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "xyz-d50" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.2050219599912612" - }, { - typ: EnumToken.NumberTokenType, - val: "0.11273398825257439" - }, { - typ: EnumToken.NumberTokenType, - val: ".019252589983419124" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "xyz-d50", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.2050219599912612", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.11273398825257439", + }, + { + typ: EnumToken.NumberTokenType, + val: ".019252589983419124", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('oklch to xyz-d50 #15', function () { + it("oklch to xyz-d50 #15", function () { return transform(`.hsl { color: oklch(.49863253097209403 .1806353283694016 26.839391611662446/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "xyz-d50" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.2050219599912612" - }, { - typ: EnumToken.NumberTokenType, - val: "0.11273398825257439" - }, { - typ: EnumToken.NumberTokenType, - val: ".019252589983419124" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "xyz-d50", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.2050219599912612", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.11273398825257439", + }, + { + typ: EnumToken.NumberTokenType, + val: ".019252589983419124", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('lab to xyz-d50 #16', function () { + it("lab to xyz-d50 #16", function () { return transform(`.hsl { color: lab(40.03718216996103 56.88417629340889 39.4648992397212/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "xyz-d50" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.2050219599912612" - }, { - typ: EnumToken.NumberTokenType, - val: "0.11273398825257439" - }, { - typ: EnumToken.NumberTokenType, - val: ".019252589983419124" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "xyz-d50", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.2050219599912612", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.11273398825257439", + }, + { + typ: EnumToken.NumberTokenType, + val: ".019252589983419124", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('lch to xyz-d50 #17', function () { + it("lch to xyz-d50 #17", function () { return transform(`.hsl { color: lch(40.03718216996103 69.23357411387173 34.75200047889167/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "xyz-d50" - }, - { - typ: EnumToken.NumberTokenType, - val: "0.2050219599912612" - }, { - typ: EnumToken.NumberTokenType, - val: "0.11273398825257439" - }, { - typ: EnumToken.NumberTokenType, - val: ".019252589983419124" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "xyz-d50", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.2050219599912612", + }, + { + typ: EnumToken.NumberTokenType, + val: "0.11273398825257439", + }, + { + typ: EnumToken.NumberTokenType, + val: ".019252589983419124", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); }); } diff --git a/test/specs/code/color-xyz.js b/test/specs/code/color-xyz.js index f134b4ecd..36415a6d2 100644 --- a/test/specs/code/color-xyz.js +++ b/test/specs/code/color-xyz.js @@ -1,584 +1,674 @@ -import {ColorType, EnumToken} from "../../../dist/src/lib/ast/types.js"; -import {isOkLabClose} from "../../../dist/src/lib/syntax/color/utils/distance.js"; +import { ColorType, EnumToken } from "../../../dist/src/lib/ast/types.js"; +import { isOkLabClose } from "../../../dist/src/lib/syntax/color/utils/distance.js"; export function run(describe, expect, it, transform, parse, render) { - - describe('convert to xyz', function () { - - it('rgb(a) to xyz #1', function () { + describe("convert to xyz", function () { + it("rgb(a) to xyz #1", function () { return transform(`.hsl { color: rgb(179 34 34/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "xyz" - }, - { - typ: EnumToken.NumberTokenType, - val: ".1945069569161969" - }, { - typ: EnumToken.NumberTokenType, - val: ".108449498170369" - }, { - typ: EnumToken.NumberTokenType, - val: ".025825724073578846" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "xyz", + }, + { + typ: EnumToken.NumberTokenType, + val: ".1945069569161969", + }, + { + typ: EnumToken.NumberTokenType, + val: ".108449498170369", + }, + { + typ: EnumToken.NumberTokenType, + val: ".025825724073578846", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('hsl(a) to xyz #2', function () { + it("hsl(a) to xyz #2", function () { return transform(`.hsl { color: hsl(0 68.07511737089203% 41.764705882352935%/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "xyz" - }, - { - typ: EnumToken.NumberTokenType, - val: ".1945069569161969" - }, { - typ: EnumToken.NumberTokenType, - val: ".108449498170369" - }, { - typ: EnumToken.NumberTokenType, - val: ".025825724073578846" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "xyz", + }, + { + typ: EnumToken.NumberTokenType, + val: ".1945069569161969", + }, + { + typ: EnumToken.NumberTokenType, + val: ".108449498170369", + }, + { + typ: EnumToken.NumberTokenType, + val: ".025825724073578846", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('device-cmyk() to xyz #3', function () { - return transform(`.hsl { color: device-cmyk(0 81.00558659217877% 81.00558659217877% 29.803921568627455%/50%); }`, { - beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "xyz" - }, - { - typ: EnumToken.NumberTokenType, - val: ".1945069569161969" - }, { - typ: EnumToken.NumberTokenType, - val: ".108449498170369" - }, { - typ: EnumToken.NumberTokenType, - val: ".025825724073578846" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + it("device-cmyk() to xyz #3", function () { + return transform( + `.hsl { color: device-cmyk(0 81.00558659217877% 81.00558659217877% 29.803921568627455%/50%); }`, + { + beautify: true, + convertColor: ColorType.SRGB_LINEAR, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "xyz", + }, + { + typ: EnumToken.NumberTokenType, + val: ".1945069569161969", + }, + { + typ: EnumToken.NumberTokenType, + val: ".108449498170369", + }, + { + typ: EnumToken.NumberTokenType, + val: ".025825724073578846", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('hwb() to xyz #4', function () { + it("hwb() to xyz #4", function () { return transform(`.hsl { color: hwb(0 13.333333333333334% 30%/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "xyz" - }, - { - typ: EnumToken.NumberTokenType, - val:.193353 - }, { - typ: EnumToken.NumberTokenType, - val: .107855 - }, { - typ: EnumToken.NumberTokenType, - val:.025772 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "xyz", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.193353, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.107855, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.025772, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('hex to xyz #5', function () { + it("hex to xyz #5", function () { return transform(`.hsl { color: #b3222280; }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "xyz" - }, - { - typ: EnumToken.NumberTokenType, - val: ".1945069569161969" - }, { - typ: EnumToken.NumberTokenType, - val: ".108449498170369" - }, { - typ: EnumToken.NumberTokenType, - val: ".025825724073578846" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "xyz", + }, + { + typ: EnumToken.NumberTokenType, + val: ".1945069569161969", + }, + { + typ: EnumToken.NumberTokenType, + val: ".108449498170369", + }, + { + typ: EnumToken.NumberTokenType, + val: ".025825724073578846", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('srgb-linear to xyz #6', function () { - return transform(`.hsl { color: color(srgb-linear .45078578283822346 .01599629336550963 .01599629336550963/50%); }`, { - beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "xyz" - }, - { - typ: EnumToken.NumberTokenType, - val: ".1945069569161969" - }, { - typ: EnumToken.NumberTokenType, - val: ".108449498170369" - }, { - typ: EnumToken.NumberTokenType, - val: ".025825724073578846" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + it("srgb-linear to xyz #6", function () { + return transform( + `.hsl { color: color(srgb-linear .45078578283822346 .01599629336550963 .01599629336550963/50%); }`, + { + beautify: true, + convertColor: ColorType.SRGB_LINEAR, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "xyz", + }, + { + typ: EnumToken.NumberTokenType, + val: ".1945069569161969", + }, + { + typ: EnumToken.NumberTokenType, + val: ".108449498170369", + }, + { + typ: EnumToken.NumberTokenType, + val: ".025825724073578846", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('display-p3 to xyz #7', function () { - return transform(`.hsl { color: color(display-p3 0.644980276448 0.191199800941 0.165770885403 / 0.501960784314); }`, { - beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "xyz" - }, - { - typ: EnumToken.NumberTokenType, - val: ".1945069569161969" - }, { - typ: EnumToken.NumberTokenType, - val: ".108449498170369" - }, { - typ: EnumToken.NumberTokenType, - val: ".025825724073578846" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + it("display-p3 to xyz #7", function () { + return transform( + `.hsl { color: color(display-p3 0.644980276448 0.191199800941 0.165770885403 / 0.501960784314); }`, + { + beautify: true, + convertColor: ColorType.SRGB_LINEAR, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "xyz", + }, + { + typ: EnumToken.NumberTokenType, + val: ".1945069569161969", + }, + { + typ: EnumToken.NumberTokenType, + val: ".108449498170369", + }, + { + typ: EnumToken.NumberTokenType, + val: ".025825724073578846", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('a98-rgb to xyz #8', function () { + it("a98-rgb to xyz #8", function () { return transform(`.hsl { color: color(a98-rgb 0.6015 0.1525 0.1525 / 0.50); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "xyz" - }, - { - typ: EnumToken.NumberTokenType, - val: .194523 - }, { - typ: EnumToken.NumberTokenType, - val: .108454 - }, { - typ: EnumToken.NumberTokenType, - val: .025819 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "xyz", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.194523, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.108454, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.025819, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('photo-rgb to xyz #9', function () { + it("photo-rgb to xyz #9", function () { return transform(`.hsl { color: color(prophoto-rgb 0.4589 0.2071 0.124 / 0.50); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "xyz" - }, - { - typ: EnumToken.NumberTokenType, - val: .194484 - }, { - typ: EnumToken.NumberTokenType, - val: .108442 - }, { - typ: EnumToken.NumberTokenType, - val: .025836 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "xyz", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.194484, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.108442, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.025836, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('srgb to xyz #11', function () { - return transform(`.hsl { color: color(srgb .7019607843137254 .13333333333333333 .13333333333333333/50%); }`, { - beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "xyz" - }, - { - typ: EnumToken.NumberTokenType, - val: ".1945069569161969" - }, { - typ: EnumToken.NumberTokenType, - val: ".108449498170369" - }, { - typ: EnumToken.NumberTokenType, - val: ".025825724073578846" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + it("srgb to xyz #11", function () { + return transform( + `.hsl { color: color(srgb .7019607843137254 .13333333333333333 .13333333333333333/50%); }`, + { + beautify: true, + convertColor: ColorType.SRGB_LINEAR, + }, + ).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "xyz", + }, + { + typ: EnumToken.NumberTokenType, + val: ".1945069569161969", + }, + { + typ: EnumToken.NumberTokenType, + val: ".108449498170369", + }, + { + typ: EnumToken.NumberTokenType, + val: ".025825724073578846", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('xyz-d50 to xyz #12', function () { + it("xyz-d50 to xyz #12", function () { return transform(`.hsl { color: color(xyz-d50 0.205 0.1127 0.0193 / 0.50); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "xyz" - }, - { - typ: EnumToken.NumberTokenType, - val: .19449 - }, { - typ: EnumToken.NumberTokenType, - val:.108417 - }, { - typ: EnumToken.NumberTokenType, - val: .025889 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "xyz", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.19449, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.108417, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.025889, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('xyz-d65 to xyz #13', function () { + it("xyz-d65 to xyz #13", function () { return transform(`.hsl { color: color(xyz-d65 0.1945 0.1084 0.0258 / 0.50); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "xyz" - }, - { - typ: EnumToken.NumberTokenType, - val: .1945 - }, { - typ: EnumToken.NumberTokenType, - val: .1084 - }, { - typ: EnumToken.NumberTokenType, - val: .0258 - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: 50 - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "xyz", + }, + { + typ: EnumToken.NumberTokenType, + val: 0.1945, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.1084, + }, + { + typ: EnumToken.NumberTokenType, + val: 0.0258, + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: 50, + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('oklab to xyz #14', function () { + it("oklab to xyz #14", function () { return transform(`.hsl { color: oklab(.49863253097209403 .16117650019451524 .08155524287358212/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "xyz" - }, - { - typ: EnumToken.NumberTokenType, - val: ".1945069569161969" - }, { - typ: EnumToken.NumberTokenType, - val: ".108449498170369" - }, { - typ: EnumToken.NumberTokenType, - val: ".025825724073578846" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "xyz", + }, + { + typ: EnumToken.NumberTokenType, + val: ".1945069569161969", + }, + { + typ: EnumToken.NumberTokenType, + val: ".108449498170369", + }, + { + typ: EnumToken.NumberTokenType, + val: ".025825724073578846", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('oklch to xyz #15', function () { + it("oklch to xyz #15", function () { return transform(`.hsl { color: oklch(.49863253097209403 .1806353283694016 26.839391611662446/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "xyz" - }, - { - typ: EnumToken.NumberTokenType, - val: ".1945069569161969" - }, { - typ: EnumToken.NumberTokenType, - val: ".108449498170369" - }, { - typ: EnumToken.NumberTokenType, - val: ".025825724073578846" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "xyz", + }, + { + typ: EnumToken.NumberTokenType, + val: ".1945069569161969", + }, + { + typ: EnumToken.NumberTokenType, + val: ".108449498170369", + }, + { + typ: EnumToken.NumberTokenType, + val: ".025825724073578846", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('lab to xyz #16', function () { + it("lab to xyz #16", function () { return transform(`.hsl { color: lab(40.03718216996103 56.88417629340889 39.4648992397212/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "xyz" - }, - { - typ: EnumToken.NumberTokenType, - val: ".1945069569161969" - }, { - typ: EnumToken.NumberTokenType, - val: ".108449498170369" - }, { - typ: EnumToken.NumberTokenType, - val: ".025825724073578846" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "xyz", + }, + { + typ: EnumToken.NumberTokenType, + val: ".1945069569161969", + }, + { + typ: EnumToken.NumberTokenType, + val: ".108449498170369", + }, + { + typ: EnumToken.NumberTokenType, + val: ".025825724073578846", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); - it('lch to xyz #17', function () { + it("lch to xyz #17", function () { return transform(`.hsl { color: lch(40.03718216996103 69.23357411387173 34.75200047889167/50%); }`, { beautify: true, - convertColor: ColorType.SRGB_LINEAR - }).then(result => expect(isOkLabClose(result.ast.chi[0].chi[0].val[0], { - typ: EnumToken.ColorTokenType, - val: "color", - chi: [ - { - typ: EnumToken.IdenTokenType, - val: "xyz" - }, - { - typ: EnumToken.NumberTokenType, - val: ".1945069569161969" - }, { - typ: EnumToken.NumberTokenType, - val: ".108449498170369" - }, { - typ: EnumToken.NumberTokenType, - val: ".025825724073578846" - }, - { - typ: EnumToken.LiteralTokenType, - val: "/" - }, - { - typ: EnumToken.PercentageTokenType, - val: "50" - } - ], - kin: ColorType.COLOR - } - )).equals(true)); + convertColor: ColorType.SRGB_LINEAR, + }).then((result) => + expect( + isOkLabClose(result.ast.chi[0].chi[0].val[0], { + typ: EnumToken.ColorTokenType, + val: "color", + chi: [ + { + typ: EnumToken.IdenTokenType, + val: "xyz", + }, + { + typ: EnumToken.NumberTokenType, + val: ".1945069569161969", + }, + { + typ: EnumToken.NumberTokenType, + val: ".108449498170369", + }, + { + typ: EnumToken.NumberTokenType, + val: ".025825724073578846", + }, + { + typ: EnumToken.LiteralTokenType, + val: "/", + }, + { + typ: EnumToken.PercentageTokenType, + val: "50", + }, + ], + kin: ColorType.COLOR, + }), + ).equals(true), + ); }); }); } diff --git a/test/specs/code/modules.js b/test/specs/code/modules.js index 5d1869146..6a178b1bf 100644 --- a/test/specs/code/modules.js +++ b/test/specs/code/modules.js @@ -1,5 +1,3 @@ -import { ColorType, EnumToken, ModuleCaseTransformEnum, ModuleScopeEnumOptions } from "../../../dist/src/lib/ast/types.js"; - export function run( describe, expect, diff --git a/test/specs/code/sourcemaps.js b/test/specs/code/sourcemaps.js index 5165f371f..385c9f499 100644 --- a/test/specs/code/sourcemaps.js +++ b/test/specs/code/sourcemaps.js @@ -1,4 +1,3 @@ -import { ColorType, EnumToken, ModuleCaseTransformEnum, ModuleScopeEnumOptions } from "../../../dist/src/lib/ast/types.js"; export function run( describe, diff --git a/test/specs/code/visitors.js b/test/specs/code/visitors.js index 0f2a484e5..d6d96435b 100644 --- a/test/specs/code/visitors.js +++ b/test/specs/code/visitors.js @@ -1,4 +1,3 @@ -import { ColorType, EnumToken } from "../../../dist/src/lib/ast/types.js"; import { WalkerEvent } from "../../../dist/src/lib/ast/walk.js"; export function run( @@ -465,7 +464,6 @@ html,body { Rule: { type: WalkerEvent.Enter, handler(node) { - node.sel = ".ruled"; // node.val = "slide-in-out"; // return node; @@ -474,7 +472,6 @@ html,body { Declaration: { type: WalkerEvent.Leave, handler(node) { - // node.sel = ".ruled"; // node.val = "slide-in-out"; // return node; @@ -573,8 +570,6 @@ html,body { ); }); - - it("visitor #10", function () { const css = ` @@ -614,25 +609,25 @@ html,body { removePrefix: true, beautify: true, visitor: [ - { - Rule: { - type: WalkerEvent.Enter, - handler(node) { - node.sel = ".ruled"; - // node.val = "slide-in-out"; - // return node; - }, - }, - }, - { - KeyframesAtRule: { - slideIn(node) { - node.val = "slide-in-out"; - return node; - }, - }, - }, - ], + { + Rule: { + type: WalkerEvent.Enter, + handler(node) { + node.sel = ".ruled"; + // node.val = "slide-in-out"; + // return node; + }, + }, + }, + { + KeyframesAtRule: { + slideIn(node) { + node.val = "slide-in-out"; + return node; + }, + }, + }, + ], }; const result = transformSync(css, options); @@ -675,14 +670,14 @@ html,body { } `; const options = { - removePrefix: true, - beautify: true, - visitor: function Rule(node) { + removePrefix: true, + beautify: true, + visitor: function Rule(node) { node.sel = ".ruled"; // node.val = "slide-in-out"; // return node; }, -}; + }; const result = transformSync(css, options); diff --git a/test/specs/code/walk.js b/test/specs/code/walk.js index 0f69b520e..a5084c279 100644 --- a/test/specs/code/walk.js +++ b/test/specs/code/walk.js @@ -48,7 +48,6 @@ export function run(describe, expect, it, transform, parse, render, dirname, rea ]; return parse(css, { minify: false }).then((r) => { - let i = 0; for (const s of walk(r.ast)) { expect(s.node.typ).equals(values[i++]); diff --git a/test/specs/node.spec.js b/test/specs/node.spec.js index fb97b19b3..46c335bd7 100644 --- a/test/specs/node.spec.js +++ b/test/specs/node.spec.js @@ -1,16 +1,31 @@ -import {parse, render, resolve, transform, transformSync, parseSync} from '../../dist/src/node.js'; -import {ColorType, EnumToken, ModuleCaseTransformEnum, ModuleScopeEnumOptions} from '../../dist/src/lib/ast/types.js' -import * as tests from './code/index.js'; -import {expect, use} from "chai"; -import {readFile} from "node:fs/promises"; -import {dirname} from 'node:path'; -import chaiAsPromised from 'chai-as-promised'; +import { parse, render, resolve, transform, transformSync, parseSync } from "../../dist/src/node.js"; +import { ColorType, EnumToken, ModuleCaseTransformEnum, ModuleScopeEnumOptions } from "../../dist/src/lib/ast/types.js"; +import * as tests from "./code/index.js"; +import { expect, use } from "chai"; +import { readFile } from "node:fs/promises"; +import { dirname } from "node:path"; +import chaiAsPromised from "chai-as-promised"; use(chaiAsPromised); // // run(describe, expect, transform, parse, render); for (const test of Object.values(tests)) { - - test.run(describe, expect, it, transform, parse, render, dirname, async (path) => readFile(path, { encoding: 'utf-8' }), resolve, ColorType, EnumToken, ModuleCaseTransformEnum, ModuleScopeEnumOptions, transformSync, parseSync); -} \ No newline at end of file + test.run( + describe, + expect, + it, + transform, + parse, + render, + dirname, + async (path) => readFile(path, { encoding: "utf-8" }), + resolve, + ColorType, + EnumToken, + ModuleCaseTransformEnum, + ModuleScopeEnumOptions, + transformSync, + parseSync, + ); +} diff --git a/test/specs/web.spec.js b/test/specs/web.spec.js index 901546bd2..b65e8173b 100644 --- a/test/specs/web.spec.js +++ b/test/specs/web.spec.js @@ -1,24 +1,37 @@ -import {dirname, parse, render, resolve, transform, transformSync, parseSync} from '../../dist/src/web.js'; -import {ColorType, EnumToken, ModuleCaseTransformEnum, ModuleScopeEnumOptions} from '../../dist/src/lib/ast/types.js'; -import {expect, use} from "chai"; -import * as tests from './code/index.js'; -import chaiAsPromised from 'chai-as-promised'; +import { dirname, parse, render, resolve, transform, transformSync, parseSync } from "../../dist/src/web.js"; +import { ColorType, EnumToken, ModuleCaseTransformEnum, ModuleScopeEnumOptions } from "../../dist/src/lib/ast/types.js"; +import { expect, use } from "chai"; +import * as tests from "./code/index.js"; +import chaiAsPromised from "chai-as-promised"; use(chaiAsPromised); - async function readFile(path) { - - return fetch(path).then(response => response.text()) + return fetch(path).then((response) => response.text()); } // // run(describe, expect, transform, parse, render); -for (const [name,test] of Object.entries(tests)) { - - if (name === 'sourceMap') { +for (const [name, test] of Object.entries(tests)) { + if (name === "sourceMap") { continue; } - test.run(describe, expect, it, transform, parse, render, dirname, readFile, resolve, ColorType, EnumToken, ModuleCaseTransformEnum, ModuleScopeEnumOptions, transformSync, parseSync); -} \ No newline at end of file + test.run( + describe, + expect, + it, + transform, + parse, + render, + dirname, + readFile, + resolve, + ColorType, + EnumToken, + ModuleCaseTransformEnum, + ModuleScopeEnumOptions, + transformSync, + parseSync, + ); +} From cb8a25c02be0814cd0ebd49225f6b32e84f0d151 Mon Sep 17 00:00:00 2001 From: Thierry Bela Nanga Date: Sat, 17 Oct 2026 20:48:49 -0400 Subject: [PATCH 06/24] work in progress --- src/lib/parser/arena.ts | 71 ++++++- src/lib/parser/parse.ts | 263 +++++++++++++++--------- src/lib/parser/utils/at-rule-generic.ts | 48 +++-- 3 files changed, 259 insertions(+), 123 deletions(-) diff --git a/src/lib/parser/arena.ts b/src/lib/parser/arena.ts index daacd55a2..ee083b87c 100644 --- a/src/lib/parser/arena.ts +++ b/src/lib/parser/arena.ts @@ -293,6 +293,10 @@ export class Arena { return this.flags[id]; } + // addFlags(id: number, flags: number): void { + // this.flags[id] |= flags; + // } + hasFlags(id: number, flag: number): boolean { return (this.flags[id] & flag) != 0; } @@ -318,6 +322,10 @@ export class Arena { this.previousSibling[this.nextSibling[this.firstChild[id]]] = child; } + if (this.lastChild[id] == this.firstChild[id]) { + this.lastChild[id] = child; + } + this.firstChild[id] = child; this.parents[child] = id; } @@ -352,6 +360,10 @@ export class Arena { this.parents[currentChild] = 0; } + if(currentChild == this.lastChild[id]){ + this.lastChild[id] = child; + } + this.parents[child] = id; } @@ -360,20 +372,65 @@ export class Arena { * @param id * @param child */ - setLeft(id: number, child: number): void { + setLeft(id: number, ...children: number[]): void { + + let child = this.firstChild[id]; + + if(child !=0 &&this.dataTypes[child]!= EnumToken.ListTokenType){ + + this.detach(child); + child = this.allocate(); + this.dataTypes[child] = EnumToken.ListTokenType; + } + + this.setChildren(child, ...children); this.setChildAt(id, 0, child); } - getLeft(id: number): number { - return this.getChildAt(id, 0) as number; + + getLeft(id: number): number[] | null { + + const child = this.getChildAt(id, 0); + + if(child == null){ + return null; + } + + return this.children(child); } - setRight(id: number, rightId: number): void { - this.setChildAt(id, 1, rightId); + setRight(id: number, rightId: null): void; + + setRight(id: number, ...rightId: number[] ): void; + + setRight(id: number, ...rightId: number[] | [null]): void { + + let child = this.getChildAt(id, 1); + + if (child == null || this.dataTypes[child] != EnumToken.ListTokenType) { + + if (child != null) { + this.detach(child); + } + + child = this.allocate(); + this.dataTypes[child] = EnumToken.ListTokenType; + this.setChildAt(id, 1, child); + } + + this.setChildren(child, ...(rightId == null || rightId[0] == null ? [] : rightId as number[])); + this.setChildAt(id, 1, child); } - getRight(id: number): number { - return this.getChildAt(id, 1) as number; + getRight(id: number): number[] | null { + + const child = this.getChildAt(id, 1); + + if(child == null){ + return null; + } + + return this.children(child); } setOp(id: number, opId: number): void { diff --git a/src/lib/parser/parse.ts b/src/lib/parser/parse.ts index ea36656ec..fa9fc46bd 100644 --- a/src/lib/parser/parse.ts +++ b/src/lib/parser/parse.ts @@ -3401,8 +3401,8 @@ function parseNode( } if ( - node[STATE] & - (EnumAstNodeStatus.Invalid | + arena.hasFlags(node, + EnumAstNodeStatus.Invalid | EnumAstNodeStatus.Disallowed | EnumAstNodeStatus.Unknown | EnumAstNodeStatus.Unparsed | @@ -3448,12 +3448,12 @@ function parseNode( return node; } else { - const node = parseDeclaration(tokens, context, options, errors) as AstDeclaration; + const node = parseDeclaration(tokens, context, options, errors); // node[PARENT] = context; // node[ROOT] = context[ROOT]; - if (contextType === EnumToken.StyleSheetNodeType && node.typ === EnumToken.DeclarationNodeType) { - node[STATE] = EnumAstNodeStatus.Invalid; + if (contextType === EnumToken.StyleSheetNodeType && arena.getType(node) === EnumToken.DeclarationNodeType) { + arena.setFlags(node, EnumAstNodeStatus.Invalid) ; errors.push({ message: " not allowed in ", @@ -3461,20 +3461,19 @@ function parseNode( node, location: options.source!.getSourceLocation(arena.getSpanStart(node)), }); - } else if (options.lenient || node.typ === EnumToken.DeclarationNodeType) { + } else if (options.lenient || arena.getType(node) === EnumToken.DeclarationNodeType) { // context.chi!.push(node); arena.appendChildren(context, node); } if ( - node[STATE] & - (EnumAstNodeStatus.Invalid | + arena.hasFlags(node, EnumAstNodeStatus.Invalid | EnumAstNodeStatus.Disallowed | EnumAstNodeStatus.Unknown | EnumAstNodeStatus.Unparsed | EnumAstNodeStatus.Malformed) ) { - invalidNodes.push(node as AstDeclaration); + invalidNodes.push(node); } } } @@ -3491,21 +3490,24 @@ function parseNode( */ export function parseAtRule( - stream: Token[], - context: AstRule | AstAtRule | AstStyleSheet, + stream: number[], + context: number, options: ParserOptions, errors: ErrorDescription[], parseAsBlock: boolean | null = null, -): AstAtRule | CssVariableImportTokenType | CssVariableToken | null { +): number| null { + + const arena = options.arena as Arena; let success: boolean = true; - let atRuleName = (stream[0] as AtRuleToken).nam; + let atRuleName = arena.getName(stream[0]); if (atRuleName.startsWith("-")) { atRuleName = atRuleName.replace(/^-[a-z]+-/, "").toLowerCase(); } - const atRule = stream.shift() as AtRuleToken; - const syntaxRules = getSyntaxRule(ValidationSyntaxGroupEnum.AtRules, "@" + atRule.nam); + const atRule = stream.shift() as number; + const name = arena.getName(atRule); + const syntaxRules = getSyntaxRule(ValidationSyntaxGroupEnum.AtRules, "@" + name); const syntax: ValidationToken[] = syntaxRules?.getPreludeRules()?.slice?.(1) as ValidationToken[]; const blockAllowed: boolean = syntaxRules?.getBlockRules() != null; @@ -3514,7 +3516,7 @@ export function parseAtRule( errors.push({ action: "drop", node: atRule, - location: options.source!.getSourceLocation(atRule[LOCSTA]!), + location: options.source!.getSourceLocation(atRule), message: "unknown at-rule", }); @@ -4525,58 +4527,75 @@ export function parseString( * @private */ export function parseTokens( - tokens: Token[], - options?: { parseColor?: boolean; source?: SourceFile } | null, + tokens: number[], + options?: { parseColor?: boolean; source?: SourceFile, arena?: Arena } | null, errors?: ErrorDescription[], ): Token[] { - const stack: Token[] = []; + const stack: number[] = []; + const arena = options?.arena as Arena; let i: number = 0; let index: number; - let t: Token; + let t: number; + let typ: EnumToken; + let typ2: EnumToken; options ??= { parseColor: true }; for (; i < tokens.length; i++) { t = tokens[i]; + typ = arena.getType(t); + + if (typ === EnumToken.FunctionTokenDefType) { - if (t.typ === EnumToken.FunctionTokenDefType) { + typ2 = arena.getType(tokens[i - 1] as number); if ( - tokens[i - 1]?.typ === EnumToken.ColonTokenType || - tokens[i - 1]?.typ === EnumToken.DoubleColonTokenType + typ2 === EnumToken.ColonTokenType || + typ2 === EnumToken.DoubleColonTokenType ) { - t = Object.assign(tokens[i - 1], { - typ: EnumToken.PseudoClassFunctionTokenDefType, - val: - (tokens[i - 1].typ === EnumToken.ColonTokenType ? ":" : "::") + - (tokens[i] as FunctionToken).val, - }); - t[LOCEND] = tokens[i][LOCEND]; + + arena.set(tokens[i - 1], EnumToken.PseudoClassFunctionTokenDefType, arena.internString((typ2 === EnumToken.ColonTokenType ? ":" : "::") + + arena.getValue(tokens[i])), + ); + + // t = Object.assign(tokens[i - 1], { + // typ: EnumToken.PseudoClassFunctionTokenDefType, + // val: + // (tokens[i - 1].typ === EnumToken.ColonTokenType ? ":" : "::") + + // (tokens[i] as FunctionToken).val, + // }); + + arena.setSpanEnd(tokens[i - 1], arena.getSpanEnd(tokens[i])); + // t[LOCEND] = tokens[i][LOCEND]; + t = tokens[i - 1]; tokens.splice(i--, 1); + typ = EnumToken.PseudoClassFunctionTokenDefType; } } if ( - t.typ === EnumToken.AttrStartTokenType || - t.typ === EnumToken.StartParensTokenType || - tokensfuncDefMap.has(t.typ) + typ === EnumToken.AttrStartTokenType || + typ === EnumToken.StartParensTokenType || + tokensfuncDefMap.has(typ) ) { stack.push(t); continue; } - if (t.typ === EnumToken.EndParensTokenType) { + if (typ === EnumToken.EndParensTokenType) { + + typ2 = arena.getType(stack.at(-1) as number); if ( stack.length === 0 || - (stack.at(-1)?.typ !== EnumToken.StartParensTokenType && !tokensfuncDefMap.has(stack.at(-1)?.typ)) + (typ2 !== EnumToken.StartParensTokenType && !tokensfuncDefMap.has(typ2)) ) { // unbalanced parens - const node: Token = stack.at(-1) as Token; + const node: number = stack.at(-1) as number; errors?.push?.({ action: "drop", message: `Unbalanced token ')'`, node, - location: options.source!.getSourceLocation(node[LOCSTA]!), + location: options.source!.getSourceLocation(node), }); // return []; @@ -4586,47 +4605,63 @@ export function parseTokens( tokens.splice(i, 1); index = tokens.indexOf(stack.at(-1)!); - Object.assign(tokens[index], { - typ: - tokens[index].typ === EnumToken.StartParensTokenType + typ2 = arena.getType(tokens[index]); + typ2 = typ2 === EnumToken.StartParensTokenType ? EnumToken.ParensTokenType - : (tokensfuncDefMap.get(tokens[index].typ) as EnumToken), - chi: trimArray(tokens.splice(index + 1, i - index - 1)), - }); + : tokensfuncDefMap.get(typ2) as EnumToken; + + arena.setType(tokens[index], typ2); + + arena.setChildren(tokens[index], ...trimArray(tokens.splice(index + 1, i - index - 1), options)); + + // Object.assign(tokens[index], { + // typ: + // tokens[index].typ === EnumToken.StartParensTokenType + // ? EnumToken.ParensTokenType + // : (tokensfuncDefMap.get(tokens[index].typ) as EnumToken), + // chi: trimArray(tokens.splice(index + 1, i - index - 1)), + // }); + i = index; - if (tokens[index].typ === EnumToken.ColorTokenType && options?.parseColor) { - parseColor(tokens[index]); + if (typ2 === EnumToken.ColorTokenType && options?.parseColor) { + parseColor(tokens[index], arena); } stack.pop(); continue; } - if (t.typ === EnumToken.AttrEndTokenType) { - if (stack.at(-1)?.typ !== EnumToken.AttrStartTokenType) { + if (typ === EnumToken.AttrEndTokenType) { + if (arena.getType(stack.at(-1)!) !== EnumToken.AttrStartTokenType) { // unbalanced - const node: Token = stack.at(-1) as Token; + const node: number = stack.at(-1) as number; errors?.push?.({ action: "drop", message: `Unbalanced token ']'`, node, - location: options.source!.getSourceLocation(node[LOCSTA]!), + location: options.source!.getSourceLocation(node), }); continue; } index = tokens.indexOf(stack.at(-1)!); - const attr = stack.at(-1) as AttrStartToken; + const attr = stack.at(-1) as number; - attr[LOCEND] = t[LOCEND]; + arena.setSpanEnd(attr, arena.getSpanEnd(t)); + + // attr[LOCEND] = t[LOCEND]; tokens.splice(i, 1); - Object.assign(attr, { - typ: EnumToken.AttrTokenType, - chi: tokens.splice(index + 1, i - index - 1), - }); + arena.setType(attr, EnumToken.AttrTokenType); + arena.setChildren(attr, ...trimArray(tokens.splice(index + 1, i - index - 1), options)); + + + // Object.assign(attr, { + // typ: EnumToken.AttrTokenType, + // chi: tokens.splice(index + 1, i - index - 1), + // }); tokens[index] = attr; i = index; @@ -4634,28 +4669,30 @@ export function parseTokens( continue; } - if (t.typ == EnumToken.IdenTokenType) { - if ((t as IdentToken).val == "from" && i > 0) { - const left: Token[] = []; - const right: Token[] = []; + if (typ == EnumToken.IdenTokenType) { + if (arena.equalsIgnoreCase(t, 'val', "from") && i > 0) { + const left: number[] = []; + const right: number[] = []; let foundLeft: number = 0; let foundRight: number = 0; let k: number = i; let l: number = i; + typ2 = arena.getType(tokens[k - 1]); + while (k > 0) { if ( - tokens[k - 1].typ == EnumToken.CommentTokenType || - tokens[k - 1].typ == EnumToken.WhitespaceTokenType + typ2 == EnumToken.CommentTokenType || + typ2 == EnumToken.WhitespaceTokenType ) { left.push(tokens[--k]); continue; } if ( - tokens[k - 1].typ == EnumToken.IdenTokenType || - tokens[k - 1].typ == EnumToken.DashedIdenTokenType + typ2 == EnumToken.IdenTokenType || + typ2 == EnumToken.DashedIdenTokenType ) { foundLeft++; left.push(tokens[--k]); @@ -4666,12 +4703,14 @@ export function parseTokens( } while (++l < tokens.length) { - if (tokens[l].typ == EnumToken.CommentTokenType || tokens[l].typ == EnumToken.WhitespaceTokenType) { + + typ2 = arena.getType(tokens[l]); + if (typ2 == EnumToken.CommentTokenType || typ2 == EnumToken.WhitespaceTokenType) { right.push(tokens[l]); continue; } - if (tokens[l].typ == EnumToken.IdenTokenType || tokens[l].typ == EnumToken.StringTokenType) { + if (typ2 == EnumToken.IdenTokenType || typ2 == EnumToken.StringTokenType) { foundRight++; right.push(tokens[l]); continue; @@ -4681,62 +4720,96 @@ export function parseTokens( } if (foundLeft > 0 && foundRight == 1) { - while (left?.[0].typ == EnumToken.WhitespaceTokenType) { - left.shift(); - } - while (left.at(-1)?.typ == EnumToken.WhitespaceTokenType) { - left.pop(); - } + trimArray(left, options); + // while (left?.[0].typ == EnumToken.WhitespaceTokenType) { + // left.shift(); + // } + + // while (left.at(-1)?.typ == EnumToken.WhitespaceTokenType) { + // left.pop(); + // } - tokens.splice(k, l - k + 1, { - typ: EnumToken.ComposesSelectorNodeType, - l: left, - r: right.reduce((a: Token | null, b: Token) => { + arena.setLeft(t, ...left); + // @ts-ignore + arena.setRight(t, right.reduce((a: number | null, b: number) => { + + typ = arena.getType(b); return a == null ? b - : b.typ == EnumToken.IdenTokenType || b.typ == EnumToken.StringTokenType + : typ == EnumToken.IdenTokenType || typ == EnumToken.StringTokenType ? b : a; - }, null), - }); + }, null)); + + const composeSelector = arena.allocate(); + arena.setType(composeSelector, EnumToken.ComposesSelectorNodeType); + // arena.setChildren(composeSelector, t, ...right); + arena.setLeft(t, ...left); + // @ts-ignore + arena.setRight(composeSelector, right.reduce((a: number | null, b: number) => { + + typ = arena.getType(b); + return a == null + ? b + : typ == EnumToken.IdenTokenType || typ == EnumToken.StringTokenType + ? b + : a; + }, null)); + + tokens.splice(k, l - k + 1, composeSelector); + + // tokens.splice(k, l - k + 1, { + // typ: EnumToken.ComposesSelectorNodeType, + // l: left, + // r: right.reduce((a: Token | null, b: Token) => { + // return a == null + // ? b + // : b.typ == EnumToken.IdenTokenType || b.typ == EnumToken.StringTokenType + // ? b + // : a; + // }, null), + // }); i = k; continue; } - } else if (options?.parseColor && isColor(t)) { - parseColor(t); + } else if (options?.parseColor && isColor(t, arena)) { + parseColor(t, arena); } } + typ = arena.getType(t); + typ2 = arena.getType(tokens[i + 1]); + if ( - t.typ == EnumToken.WhitespaceTokenType && + typ == EnumToken.WhitespaceTokenType && (i == 0 || i + 1 == tokens.length || - [ - EnumToken.CommaTokenType, - EnumToken.GteTokenType, - EnumToken.LteTokenType, - EnumToken.ColumnCombinatorTokenType, - ].includes(tokens[i + 1].typ) || - (i > 0 && trimWhiteSpace.includes(tokens[i - 1].typ))) + ( + EnumToken.CommaTokenType == typ2 || + EnumToken.GteTokenType == typ2 || + EnumToken.LteTokenType == typ2 || + EnumToken.ColumnCombinatorTokenType == typ2) + ) || + (i > 0 && trimWhiteSpace.includes(arena.getType(tokens[i - 1]))) ) { tokens.splice(i--, 1); continue; } - if (t.typ == EnumToken.ColonTokenType) { - const typ: EnumToken = tokens[i + 1]?.typ; + if (typ == EnumToken.ColonTokenType) { + // const typ2: EnumToken = tokens[i + 1]?.typ; - if (typ != null) { - if (typ == EnumToken.FunctionTokenType) { - tokens[i + 1].typ = EnumToken.PseudoClassFuncTokenType; - } else if (typ == EnumToken.IdenTokenType) { + if (typ2 != null) { + if (typ2 == EnumToken.FunctionTokenType) { + typ2 = EnumToken.PseudoClassFuncTokenType; + } else if (typ2 == EnumToken.IdenTokenType) { (tokens[i + 1] as PseudoClassToken).val = ":" + (tokens[i + 1] as PseudoClassToken).val; tokens[i + 1].typ = EnumToken.PseudoClassTokenType; } - if (typ == EnumToken.FunctionTokenType || typ == EnumToken.IdenTokenType) { + if (typ2 == EnumToken.FunctionTokenType || typ2 == EnumToken.IdenTokenType) { tokens.splice(i, 1); i--; } diff --git a/src/lib/parser/utils/at-rule-generic.ts b/src/lib/parser/utils/at-rule-generic.ts index 8173c8d26..268a0613c 100644 --- a/src/lib/parser/utils/at-rule-generic.ts +++ b/src/lib/parser/utils/at-rule-generic.ts @@ -1,30 +1,35 @@ import type { ErrorDescription, IdentToken, ParserOptions, Token } from "../../../@types/index.d.ts"; import { EnumToken } from "../../ast/types.ts"; import { LOCSTA, tokensfuncDefMap } from "../../syntax/constants.ts"; +import { Arena } from "../arena.ts"; import { equalsIgnoreCase } from "./text.ts"; export function matchGenericSyntax( - stream: Token[], + stream: number[], options: ParserOptions, ): { success: boolean; errors: ErrorDescription[]; } { - const stack: Token[] = []; + const arena = options.arena as Arena; + const stack: number[] = []; let i: number = 0; let success: boolean = true; let expectAndOr: boolean = false; let expectComma: boolean = false; + let typ: EnumToken; const errors: ErrorDescription[] = []; const scopes: Array> = [new Set()]; for (; i < stream.length; i++) { const token = stream[i]; - if (token.typ === EnumToken.WhitespaceTokenType || token.typ === EnumToken.CommentTokenType) { + typ = arena.getType(token); + + if (typ === EnumToken.WhitespaceTokenType || typ === EnumToken.CommentTokenType) { continue; } - if (token.typ === EnumToken.StartParensTokenType || tokensfuncDefMap.has(token.typ)) { + if (typ === EnumToken.StartParensTokenType || tokensfuncDefMap.has(typ)) { stack.push(token); scopes.push(new Set()); @@ -32,16 +37,16 @@ export function matchGenericSyntax( continue; } - if (token.typ === EnumToken.EndParensTokenType) { + if (typ === EnumToken.EndParensTokenType) { if ( stack.length === 0 || - (stack.at(-1)?.typ !== EnumToken.StartParensTokenType && !tokensfuncDefMap.has(stack.at(-1)?.typ)) + (arena.getType(stack.at(-1)!) !== EnumToken.StartParensTokenType && !tokensfuncDefMap.has(arena.getType(stack.at(-1)!))) ) { errors.push({ action: "drop", - message: `unexpected token ${EnumToken[token.typ]}`, + message: `unexpected token ${EnumToken[typ]}`, node: token, - location: options.source!.getSourceLocation(token[LOCSTA]!), + location: options.source!.getSourceLocation(token), }); success = false; break; @@ -53,32 +58,33 @@ export function matchGenericSyntax( continue; } - if (token.typ === EnumToken.IdenTokenType && equalsIgnoreCase("and", (token as IdentToken).val)) { + if (typ === EnumToken.IdenTokenType && arena.equalsIgnoreCase(token, 'val', "and")) { if (!expectAndOr || scopes.at(-1)?.has(EnumToken.OrTokenType)) { errors.push({ action: "drop", - message: `unexpected token ${EnumToken[token.typ]}`, + message: `unexpected token ${EnumToken[typ]}`, node: token, - location: options.source!.getSourceLocation(token[LOCSTA]!), + location: options.source!.getSourceLocation(token), }); success = false; break; } - Object.assign(token, { typ: EnumToken.AndTokenType }); + typ = EnumToken.AndTokenType; + arena.setType(token, typ); scopes.at(-1)!.add(EnumToken.AndTokenType); expectComma = false; continue; } - if (token.typ === EnumToken.IdenTokenType && equalsIgnoreCase("or", (token as IdentToken).val)) { + if (typ === EnumToken.IdenTokenType && arena.equalsIgnoreCase(token, 'val', "or")) { if (!expectAndOr || scopes.at(-1)?.has(EnumToken.AndTokenType)) { errors.push({ action: "drop", - message: `unexpected token ${EnumToken[token.typ]}`, + message: `unexpected token ${EnumToken[typ]}`, node: token, - location: options.source!.getSourceLocation(token[LOCSTA]!), + location: options.source!.getSourceLocation(token), }); success = false; break; @@ -91,13 +97,13 @@ export function matchGenericSyntax( continue; } - if (token.typ === EnumToken.CommaTokenType) { + if (typ === EnumToken.CommaTokenType) { if (!expectComma) { errors.push({ action: "drop", - message: `unexpected token ${EnumToken[token.typ]}`, + message: `unexpected token ${EnumToken[typ]}`, node: token, - location: options.source!.getSourceLocation(token[LOCSTA]!), + location: options.source!.getSourceLocation(token), }); success = false; break; @@ -111,7 +117,7 @@ export function matchGenericSyntax( expectAndOr = true; expectComma = true; - if (stack.length > 0 && stack.at(-1)?.typ === EnumToken.CommaTokenType) { + if (stack.length > 0 && arena.getType(stack.at(-1)!) === EnumToken.CommaTokenType) { stack.pop(); } } @@ -119,9 +125,9 @@ export function matchGenericSyntax( if (stack.length > 0) { errors.push({ action: "drop", - message: `unexpected token ${EnumToken[stack.at(-1)?.typ]}`, + message: `unexpected token ${EnumToken[arena.getType(stack.at(-1)!)]}`, node: stack.at(-1), - location: options.source!.getSourceLocation(stack.at(-1)?.[LOCSTA]!), + location: options.source!.getSourceLocation(stack.at(-1)!), }); success = false; } From c8ec883409bcfbd117cc70d7ecd04bc1af60ab02 Mon Sep 17 00:00:00 2001 From: Thierry Bela Nanga Date: Sun, 18 Oct 2026 20:21:45 -0400 Subject: [PATCH 07/24] work in progress --- src/lib/parser/parse.ts | 27 ++++++++------- src/lib/parser/utils/at-rule-container.ts | 40 ++++++++++++++--------- src/lib/validation/match.ts | 20 +++++++----- 3 files changed, 51 insertions(+), 36 deletions(-) diff --git a/src/lib/parser/parse.ts b/src/lib/parser/parse.ts index fa9fc46bd..114d27d4b 100644 --- a/src/lib/parser/parse.ts +++ b/src/lib/parser/parse.ts @@ -4496,7 +4496,7 @@ export function parseString( mapped.push(token); } - const result: Token[] = parseTokens(mapped, options, errors); + const result: number[] = parseTokens(mapped, options, errors); // remove EOF token result.splice(result.length - (result[result.length - 2]?.typ === EnumToken.WhitespaceTokenType ? 2 : 1), 2); @@ -4530,7 +4530,7 @@ export function parseTokens( tokens: number[], options?: { parseColor?: boolean; source?: SourceFile, arena?: Arena } | null, errors?: ErrorDescription[], -): Token[] { +): number[] { const stack: number[] = []; const arena = options?.arena as Arena; @@ -4595,7 +4595,7 @@ export function parseTokens( action: "drop", message: `Unbalanced token ')'`, node, - location: options.source!.getSourceLocation(node), + location: options.source!.getSourceLocation(arena.getSpanStart(node)), }); // return []; @@ -4641,7 +4641,7 @@ export function parseTokens( action: "drop", message: `Unbalanced token ']'`, node, - location: options.source!.getSourceLocation(node), + location: options.source!.getSourceLocation(arena.getSpanStart(node)), }); continue; } @@ -4805,8 +4805,11 @@ export function parseTokens( if (typ2 == EnumToken.FunctionTokenType) { typ2 = EnumToken.PseudoClassFuncTokenType; } else if (typ2 == EnumToken.IdenTokenType) { - (tokens[i + 1] as PseudoClassToken).val = ":" + (tokens[i + 1] as PseudoClassToken).val; - tokens[i + 1].typ = EnumToken.PseudoClassTokenType; + + arena.set(tokens[i + 1], EnumToken.PseudoClassTokenType, arena.internString( ":" + arena.getValue(tokens[i + 1]))); + // (tokens[i + 1] as PseudoClassToken).val = ":" + (tokens[i + 1] as PseudoClassToken).val; + // tokens[i + 1].typ = EnumToken.PseudoClassTokenType; + typ2 = EnumToken.FunctionTokenType; } if (typ2 == EnumToken.FunctionTokenType || typ2 == EnumToken.IdenTokenType) { @@ -4816,12 +4819,12 @@ export function parseTokens( } } - if (t.typ === EnumToken.CommaTokenType) { - if (tokens[i + 1]?.typ === EnumToken.WhitespaceTokenType) { + if (typ === EnumToken.CommaTokenType) { + if (typ2 === EnumToken.WhitespaceTokenType) { tokens.splice(i + 1, 1); } - if (tokens[i - 1]?.typ === EnumToken.WhitespaceTokenType) { + if (arena.getType(tokens[i - 1]) === EnumToken.WhitespaceTokenType) { tokens.splice(i - 1, 1); i--; } @@ -4829,13 +4832,13 @@ export function parseTokens( } if (stack.length > 0) { - const node: Token = stack.at(-1) as Token; + const node: number = stack.at(-1) as number; errors?.push?.({ action: "drop", - message: `Unbalanced token. Expecting ${node.typ === EnumToken.AttrStartTokenType ? "']'" : ")"}'`, + message: `Unbalanced token. Expecting ${typ! === EnumToken.AttrStartTokenType ? "']'" : ")"}'`, node, - location: options.source!.getSourceLocation(node[LOCSTA]!), + location: options.source!.getSourceLocation(arena.getSpanStart(node)), }); } diff --git a/src/lib/parser/utils/at-rule-container.ts b/src/lib/parser/utils/at-rule-container.ts index 9b2bd89d8..7f8502585 100644 --- a/src/lib/parser/utils/at-rule-container.ts +++ b/src/lib/parser/utils/at-rule-container.ts @@ -19,10 +19,11 @@ import type { ValidationFunctionToken, ValidationToken } from "../../validation/ import type { ValidationMatch } from "../../validation/types.d.ts"; import { getSyntaxRule } from "../../validation/config.ts"; import { equalsIgnoreCase } from "./text.ts"; +import { Arena } from "../arena.ts"; export function parseAtRuleContainerQueryList( - stream: Token[], - context: AstAtRule | AtRuleToken, + stream: number[], + context: number, options: ParserOptions = {}, ): { success: boolean; @@ -30,19 +31,24 @@ export function parseAtRuleContainerQueryList( } { let matchCount: number = 0; let hasErrors: boolean = false; + let typ: EnumToken; + const arena = options.arena as Arena; + const name = arena.getName(context); const errors: ErrorDescription[] = []; const syntaxRules = getSyntaxRule(ValidationSyntaxGroupEnum.AtRules, "@" + context.nam); const syntax: ValidationToken[] = syntaxRules?.getPreludeRules()?.slice?.(1) as ValidationToken[]; const parts = stream.reduce( (acc, t) => { - if (t.typ === EnumToken.CommaTokenType && matchCount === 0) { + + typ = arena.getType(t); + if (typ === EnumToken.CommaTokenType && matchCount === 0) { acc.push([]); } else { acc[acc.length - 1].push(t); - if (t.typ === EnumToken.StartParensTokenType || tokensfuncDefMap.has(t.typ)) { + if (typ === EnumToken.StartParensTokenType || tokensfuncDefMap.has(typ)) { matchCount++; - } else if (t.typ === EnumToken.EndParensTokenType) { + } else if (typ === EnumToken.EndParensTokenType) { if (matchCount > 0) { matchCount--; } @@ -51,12 +57,12 @@ export function parseAtRuleContainerQueryList( return acc; }, - [[]] as Token[][], + [[]] as number[][], ); const result: ValidationMatch = matchAllSyntaxes( syntax as ValidationFunctionToken[], - createValidationContext(stream), + createValidationContext(stream,options), options, ); @@ -77,8 +83,8 @@ export function parseAtRuleContainerQueryList( let i: number; let currentScope: Set = new Set(); const scopes: Array> = [currentScope]; - const stack: Token[] = []; - const tokens: Token[] = []; + const stack: number[] = []; + const tokens: number[] = []; let expectAndOr: boolean = false; i = 0; @@ -109,7 +115,7 @@ export function parseAtRuleContainerQueryList( { action: "drop", node: stream[i], - location: options.source!.getSourceLocation(stream[i]?.[LOCSTA]!), + location: options.source!.getSourceLocation(stream[i]), message: `expecting `, }, ], @@ -119,7 +125,9 @@ export function parseAtRuleContainerQueryList( for (; i < stream.length; i++) { tokens.push(stream[i]); - if (stream[i].typ === EnumToken.WhitespaceTokenType || stream[i].typ === EnumToken.CommentTokenType) { + typ = arena.getType(stream[i]); + + if (typ === EnumToken.WhitespaceTokenType || typ === EnumToken.CommentTokenType) { continue; } @@ -130,7 +138,7 @@ export function parseAtRuleContainerQueryList( // const val: string = (stream[i] as IdentToken).val.toLowerCase(); // valid = val === "and" || val === "or"; // } else { - valid = stream[i].typ !== EnumToken.CommaTokenType; + valid = typ !== EnumToken.CommaTokenType; // } if (!valid) { @@ -139,19 +147,19 @@ export function parseAtRuleContainerQueryList( action: "drop", node: stream[i], message: `expecting , or comma`, - location: options.source!.getSourceLocation(stream[i]?.[LOCSTA]!), + location: options.source!.getSourceLocation(stream[i]), }); break; } } - if (stream[i].typ === EnumToken.StartParensTokenType || tokensfuncDefMap.has(stream[i].typ)) { + if (typ === EnumToken.StartParensTokenType || tokensfuncDefMap.has(typ)) { scopes.push((currentScope = new Set())); stack.push(stream[i]); continue; } - switch (stream[i].typ) { + switch (typ) { case EnumToken.ColonTokenType: case EnumToken.LtTokenType: case EnumToken.LteTokenType: @@ -163,7 +171,7 @@ export function parseAtRuleContainerQueryList( case EnumToken.IdenTokenType: { - const val: string = (stream[i] as IdentToken).val.toLowerCase(); + const val: string = arena.getValue(stream[i] ); if (val === "not") { Object.assign(stream[i], { diff --git a/src/lib/validation/match.ts b/src/lib/validation/match.ts index 76ad7727b..ab7b5c2fb 100644 --- a/src/lib/validation/match.ts +++ b/src/lib/validation/match.ts @@ -41,6 +41,7 @@ import { isColor } from "../syntax/syntax.ts"; import { equalsIgnoreCase } from "../parser/utils/text.ts"; import { cloneNode } from "../ast/clone.ts"; import { parseTokens } from "../parser/parse.ts"; +import { Arena } from "../parser/arena.ts"; const config: ValidationConfiguration = getSyntaxConfig(); @@ -239,12 +240,12 @@ export function isMFValue( * @returns */ export function createValidationContext(tokens: number[], options: ParserOptions): ValidationContext { - const arena = options.arena!; - tokens = trimArray(tokens.filter((t) => arena.getType(t) !== EnumToken.CommentTokenType)); + const arena = options.arena as Arena; + tokens = trimArray(tokens.filter((t) => arena.getType(t) !== EnumToken.CommentTokenType), options); - if (arena.getType(tokens.at(-1)) === EnumToken.ImportantTokenType) { + if (arena.getType(tokens.at(-1)!) === EnumToken.ImportantTokenType) { tokens.pop(); - trimArray(tokens); + trimArray(tokens, options); } const token = { @@ -260,12 +261,15 @@ export function createValidationContext(tokens: number[], options: ParserOptions peek(offset: number = 0) { let index: number = this.index; let token: number = this.tokens[++index]; + let typ: EnumToken = arena.getType(this.tokens[++index]); while (offset >= 0 && index < this.tokens.length) { + + typ= arena.getType(token); while ( - token?.typ == EnumToken.WhitespaceTokenType || - token?.typ == EnumToken.CommentTokenType || - token?.typ == EnumToken.CDOCOMMTokenType + typ == EnumToken.WhitespaceTokenType || + typ == EnumToken.CommentTokenType || + typ == EnumToken.CDOCOMMTokenType ) { token = this.tokens[++index]; } @@ -294,7 +298,7 @@ export function createValidationContext(tokens: number[], options: ParserOptions counter: number = 0, ): Token[] { let index: number = this.index; - let token: Token = this.tokens[index]; + let token: number = this.tokens[index]; // track balanced parens let matchCount: number = 0; const tokens: Token[] = []; From 529a25bf08aaa521e3c2a913204f63bb77070ef7 Mon Sep 17 00:00:00 2001 From: Thierry Bela Nanga Date: Mon, 19 Oct 2026 23:08:24 -0400 Subject: [PATCH 08/24] work in progress --- dist/index-umd-web.js | 376 ++++--- dist/index.cjs | 376 ++++--- dist/index.d.ts | 9 +- dist/src/lib/parser/arena.js | 46 +- dist/src/lib/parser/parse.js | 245 +++-- .../src/lib/parser/utils/at-rule-container.js | 29 +- dist/src/lib/parser/utils/at-rule-generic.js | 45 +- dist/src/lib/printer/print.js | 996 ++++++++++++++++++ dist/src/lib/validation/match.js | 12 +- 9 files changed, 1709 insertions(+), 425 deletions(-) create mode 100644 dist/src/lib/printer/print.js diff --git a/dist/index-umd-web.js b/dist/index-umd-web.js index 99fe9ac4b..d3a0979fe 100644 --- a/dist/index-umd-web.js +++ b/dist/index-umd-web.js @@ -12483,10 +12483,10 @@ */ function createValidationContext(tokens, options) { const arena = options.arena; - tokens = trimArray(tokens.filter((t) => arena.getType(t) !== exports.EnumToken.CommentTokenType)); + tokens = trimArray(tokens.filter((t) => arena.getType(t) !== exports.EnumToken.CommentTokenType), options); if (arena.getType(tokens.at(-1)) === exports.EnumToken.ImportantTokenType) { tokens.pop(); - trimArray(tokens); + trimArray(tokens, options); } const token = { tokens, @@ -12500,10 +12500,12 @@ peek(offset = 0) { let index = this.index; let token = this.tokens[++index]; + let typ = arena.getType(this.tokens[++index]); while (offset >= 0 && index < this.tokens.length) { - while (token?.typ == exports.EnumToken.WhitespaceTokenType || - token?.typ == exports.EnumToken.CommentTokenType || - token?.typ == exports.EnumToken.CDOCOMMTokenType) { + typ = arena.getType(token); + while (typ == exports.EnumToken.WhitespaceTokenType || + typ == exports.EnumToken.CommentTokenType || + typ == exports.EnumToken.CDOCOMMTokenType) { token = this.tokens[++index]; } if (token == null || offset === 0) { @@ -21384,6 +21386,9 @@ getFlags(id) { return this.flags[id]; } + // addFlags(id: number, flags: number): void { + // this.flags[id] |= flags; + // } hasFlags(id, flag) { return (this.flags[id] & flag) != 0; } @@ -21403,6 +21408,9 @@ this.nextSibling[child] = this.nextSibling[this.firstChild[id]]; this.previousSibling[this.nextSibling[this.firstChild[id]]] = child; } + if (this.lastChild[id] == this.firstChild[id]) { + this.lastChild[id] = child; + } this.firstChild[id] = child; this.parents[child] = id; } @@ -21431,6 +21439,9 @@ this.nextSibling[currentChild] = 0; this.parents[currentChild] = 0; } + if (currentChild == this.lastChild[id]) { + this.lastChild[id] = child; + } this.parents[child] = id; } /** @@ -21438,17 +21449,42 @@ * @param id * @param child */ - setLeft(id, child) { + setLeft(id, ...children) { + let child = this.firstChild[id]; + if (child != 0 && this.dataTypes[child] != exports.EnumToken.ListTokenType) { + this.detach(child); + child = this.allocate(); + this.dataTypes[child] = exports.EnumToken.ListTokenType; + } + this.setChildren(child, ...children); this.setChildAt(id, 0, child); } getLeft(id) { - return this.getChildAt(id, 0); + const child = this.getChildAt(id, 0); + if (child == null) { + return null; + } + return this.children(child); } - setRight(id, rightId) { - this.setChildAt(id, 1, rightId); + setRight(id, ...rightId) { + let child = this.getChildAt(id, 1); + if (child == null || this.dataTypes[child] != exports.EnumToken.ListTokenType) { + if (child != null) { + this.detach(child); + } + child = this.allocate(); + this.dataTypes[child] = exports.EnumToken.ListTokenType; + this.setChildAt(id, 1, child); + } + this.setChildren(child, ...(rightId == null || rightId[0] == null ? [] : rightId)); + this.setChildAt(id, 1, child); } getRight(id) { - return this.getChildAt(id, 1); + const child = this.getChildAt(id, 1); + if (child == null) { + return null; + } + return this.children(child); } setOp(id, opId) { this.setChildAt(id, 2, opId); @@ -21784,7 +21820,7 @@ this.nextSibling[lastChild] = children[0]; } this.parents[children[0]] = id; - for (let i = 1; i < children.length - 1; i++) { + for (let i = 1; i < children.length; i++) { this.detach(children[i]); this.previousSibling[children[i]] = children[i - 1]; this.nextSibling[children[i - 1]] = children[i]; @@ -32569,19 +32605,23 @@ function parseAtRuleContainerQueryList(stream, context, options = {}) { let matchCount = 0; + let typ; + const arena = options.arena; + arena.getName(context); const errors = []; const syntaxRules = getSyntaxRule(ValidationSyntaxGroupEnum.AtRules, "@" + context.nam); const syntax = syntaxRules?.getPreludeRules()?.slice?.(1); const parts = stream.reduce((acc, t) => { - if (t.typ === exports.EnumToken.CommaTokenType && matchCount === 0) { + typ = arena.getType(t); + if (typ === exports.EnumToken.CommaTokenType && matchCount === 0) { acc.push([]); } else { acc[acc.length - 1].push(t); - if (t.typ === exports.EnumToken.StartParensTokenType || tokensfuncDefMap.has(t.typ)) { + if (typ === exports.EnumToken.StartParensTokenType || tokensfuncDefMap.has(typ)) { matchCount++; } - else if (t.typ === exports.EnumToken.EndParensTokenType) { + else if (typ === exports.EnumToken.EndParensTokenType) { if (matchCount > 0) { matchCount--; } @@ -32589,7 +32629,7 @@ } return acc; }, [[]]); - const result = matchAllSyntaxes(syntax, createValidationContext(stream), options); + const result = matchAllSyntaxes(syntax, createValidationContext(stream, options), options); if (!result.success) { for (const error of result.errors) { errors.push(error); @@ -32628,7 +32668,7 @@ { action: "drop", node: stream[i], - location: options.source.getSourceLocation(stream[i]?.[LOCSTA]), + location: options.source.getSourceLocation(stream[i]), message: `expecting `, }, ], @@ -32636,7 +32676,8 @@ } for (; i < stream.length; i++) { tokens.push(stream[i]); - if (stream[i].typ === exports.EnumToken.WhitespaceTokenType || stream[i].typ === exports.EnumToken.CommentTokenType) { + typ = arena.getType(stream[i]); + if (typ === exports.EnumToken.WhitespaceTokenType || typ === exports.EnumToken.CommentTokenType) { continue; } if (expectAndOr) { @@ -32645,7 +32686,7 @@ // const val: string = (stream[i] as IdentToken).val.toLowerCase(); // valid = val === "and" || val === "or"; // } else { - valid = stream[i].typ !== exports.EnumToken.CommaTokenType; + valid = typ !== exports.EnumToken.CommaTokenType; // } if (!valid) { success = false; @@ -32653,17 +32694,17 @@ action: "drop", node: stream[i], message: `expecting , or comma`, - location: options.source.getSourceLocation(stream[i]?.[LOCSTA]), + location: options.source.getSourceLocation(stream[i]), }); break; } } - if (stream[i].typ === exports.EnumToken.StartParensTokenType || tokensfuncDefMap.has(stream[i].typ)) { + if (typ === exports.EnumToken.StartParensTokenType || tokensfuncDefMap.has(typ)) { scopes.push((currentScope = new Set())); stack.push(stream[i]); continue; } - switch (stream[i].typ) { + switch (typ) { case exports.EnumToken.ColonTokenType: case exports.EnumToken.LtTokenType: case exports.EnumToken.LteTokenType: @@ -32674,7 +32715,7 @@ break; case exports.EnumToken.IdenTokenType: { - const val = stream[i].val.toLowerCase(); + const val = arena.getValue(stream[i]); if (val === "not") { Object.assign(stream[i], { typ: exports.EnumToken.NotTokenType, @@ -32932,32 +32973,35 @@ } function matchGenericSyntax(stream, options) { + const arena = options.arena; const stack = []; let i = 0; let success = true; let expectAndOr = false; let expectComma = false; + let typ; const errors = []; const scopes = [new Set()]; for (; i < stream.length; i++) { const token = stream[i]; - if (token.typ === exports.EnumToken.WhitespaceTokenType || token.typ === exports.EnumToken.CommentTokenType) { + typ = arena.getType(token); + if (typ === exports.EnumToken.WhitespaceTokenType || typ === exports.EnumToken.CommentTokenType) { continue; } - if (token.typ === exports.EnumToken.StartParensTokenType || tokensfuncDefMap.has(token.typ)) { + if (typ === exports.EnumToken.StartParensTokenType || tokensfuncDefMap.has(typ)) { stack.push(token); scopes.push(new Set()); expectAndOr = false; continue; } - if (token.typ === exports.EnumToken.EndParensTokenType) { + if (typ === exports.EnumToken.EndParensTokenType) { if (stack.length === 0 || - (stack.at(-1)?.typ !== exports.EnumToken.StartParensTokenType && !tokensfuncDefMap.has(stack.at(-1)?.typ))) { + (arena.getType(stack.at(-1)) !== exports.EnumToken.StartParensTokenType && !tokensfuncDefMap.has(arena.getType(stack.at(-1))))) { errors.push({ action: "drop", - message: `unexpected token ${exports.EnumToken[token.typ]}`, + message: `unexpected token ${exports.EnumToken[typ]}`, node: token, - location: options.source.getSourceLocation(token[LOCSTA]), + location: options.source.getSourceLocation(token), }); success = false; break; @@ -32966,29 +33010,30 @@ scopes.pop(); continue; } - if (token.typ === exports.EnumToken.IdenTokenType && equalsIgnoreCase("and", token.val)) { + if (typ === exports.EnumToken.IdenTokenType && arena.equalsIgnoreCase(token, 'val', "and")) { if (!expectAndOr || scopes.at(-1)?.has(exports.EnumToken.OrTokenType)) { errors.push({ action: "drop", - message: `unexpected token ${exports.EnumToken[token.typ]}`, + message: `unexpected token ${exports.EnumToken[typ]}`, node: token, - location: options.source.getSourceLocation(token[LOCSTA]), + location: options.source.getSourceLocation(token), }); success = false; break; } - Object.assign(token, { typ: exports.EnumToken.AndTokenType }); + typ = exports.EnumToken.AndTokenType; + arena.setType(token, typ); scopes.at(-1).add(exports.EnumToken.AndTokenType); expectComma = false; continue; } - if (token.typ === exports.EnumToken.IdenTokenType && equalsIgnoreCase("or", token.val)) { + if (typ === exports.EnumToken.IdenTokenType && arena.equalsIgnoreCase(token, 'val', "or")) { if (!expectAndOr || scopes.at(-1)?.has(exports.EnumToken.AndTokenType)) { errors.push({ action: "drop", - message: `unexpected token ${exports.EnumToken[token.typ]}`, + message: `unexpected token ${exports.EnumToken[typ]}`, node: token, - location: options.source.getSourceLocation(token[LOCSTA]), + location: options.source.getSourceLocation(token), }); success = false; break; @@ -32998,13 +33043,13 @@ scopes.at(-1).add(exports.EnumToken.OrTokenType); continue; } - if (token.typ === exports.EnumToken.CommaTokenType) { + if (typ === exports.EnumToken.CommaTokenType) { if (!expectComma) { errors.push({ action: "drop", - message: `unexpected token ${exports.EnumToken[token.typ]}`, + message: `unexpected token ${exports.EnumToken[typ]}`, node: token, - location: options.source.getSourceLocation(token[LOCSTA]), + location: options.source.getSourceLocation(token), }); success = false; break; @@ -33015,16 +33060,16 @@ } expectAndOr = true; expectComma = true; - if (stack.length > 0 && stack.at(-1)?.typ === exports.EnumToken.CommaTokenType) { + if (stack.length > 0 && arena.getType(stack.at(-1)) === exports.EnumToken.CommaTokenType) { stack.pop(); } } if (stack.length > 0) { errors.push({ action: "drop", - message: `unexpected token ${exports.EnumToken[stack.at(-1)?.typ]}`, + message: `unexpected token ${exports.EnumToken[arena.getType(stack.at(-1))]}`, node: stack.at(-1), - location: options.source.getSourceLocation(stack.at(-1)?.[LOCSTA]), + location: options.source.getSourceLocation(stack.at(-1)), }); success = false; } @@ -35618,12 +35663,11 @@ if (node == null) { return null; } - if (node[STATE] & - (exports.EnumAstNodeStatus.Invalid | - exports.EnumAstNodeStatus.Disallowed | - exports.EnumAstNodeStatus.Unknown | - exports.EnumAstNodeStatus.Unparsed | - exports.EnumAstNodeStatus.Malformed)) { + if (arena.hasFlags(node, exports.EnumAstNodeStatus.Invalid | + exports.EnumAstNodeStatus.Disallowed | + exports.EnumAstNodeStatus.Unknown | + exports.EnumAstNodeStatus.Unparsed | + exports.EnumAstNodeStatus.Malformed)) { invalidNodes.push(node); } stats.nodesCount++; @@ -35657,8 +35701,8 @@ const node = parseDeclaration(tokens, context, options, errors); // node[PARENT] = context; // node[ROOT] = context[ROOT]; - if (contextType === exports.EnumToken.StyleSheetNodeType && node.typ === exports.EnumToken.DeclarationNodeType) { - node[STATE] = exports.EnumAstNodeStatus.Invalid; + if (contextType === exports.EnumToken.StyleSheetNodeType && arena.getType(node) === exports.EnumToken.DeclarationNodeType) { + arena.setFlags(node, exports.EnumAstNodeStatus.Invalid); errors.push({ message: " not allowed in ", action: "drop", @@ -35666,16 +35710,15 @@ location: options.source.getSourceLocation(arena.getSpanStart(node)), }); } - else if (options.lenient || node.typ === exports.EnumToken.DeclarationNodeType) { + else if (options.lenient || arena.getType(node) === exports.EnumToken.DeclarationNodeType) { // context.chi!.push(node); arena.appendChildren(context, node); } - if (node[STATE] & - (exports.EnumAstNodeStatus.Invalid | - exports.EnumAstNodeStatus.Disallowed | - exports.EnumAstNodeStatus.Unknown | - exports.EnumAstNodeStatus.Unparsed | - exports.EnumAstNodeStatus.Malformed)) { + if (arena.hasFlags(node, exports.EnumAstNodeStatus.Invalid | + exports.EnumAstNodeStatus.Disallowed | + exports.EnumAstNodeStatus.Unknown | + exports.EnumAstNodeStatus.Unparsed | + exports.EnumAstNodeStatus.Malformed)) { invalidNodes.push(node); } } @@ -35690,13 +35733,15 @@ * @param parseAsBlock */ function parseAtRule(stream, context, options, errors, parseAsBlock = null) { + const arena = options.arena; let success = true; - let atRuleName = stream[0].nam; + let atRuleName = arena.getName(stream[0]); if (atRuleName.startsWith("-")) { atRuleName = atRuleName.replace(/^-[a-z]+-/, "").toLowerCase(); } const atRule = stream.shift(); - const syntaxRules = getSyntaxRule(ValidationSyntaxGroupEnum.AtRules, "@" + atRule.nam); + const name = arena.getName(atRule); + const syntaxRules = getSyntaxRule(ValidationSyntaxGroupEnum.AtRules, "@" + name); const syntax = syntaxRules?.getPreludeRules()?.slice?.(1); const blockAllowed = syntaxRules?.getBlockRules() != null; if (syntaxRules == null) { @@ -35704,7 +35749,7 @@ errors.push({ action: "drop", node: atRule, - location: options.source.getSourceLocation(atRule[LOCSTA]), + location: options.source.getSourceLocation(atRule), message: "unknown at-rule", }); const result = matchGenericSyntax(stream, options); @@ -36560,100 +36605,123 @@ */ function parseTokens(tokens, options, errors) { const stack = []; + const arena = options?.arena; let i = 0; let index; let t; + let typ; + let typ2; options ??= { parseColor: true }; for (; i < tokens.length; i++) { t = tokens[i]; - if (t.typ === exports.EnumToken.FunctionTokenDefType) { - if (tokens[i - 1]?.typ === exports.EnumToken.ColonTokenType || - tokens[i - 1]?.typ === exports.EnumToken.DoubleColonTokenType) { - t = Object.assign(tokens[i - 1], { - typ: exports.EnumToken.PseudoClassFunctionTokenDefType, - val: (tokens[i - 1].typ === exports.EnumToken.ColonTokenType ? ":" : "::") + - tokens[i].val, - }); - t[LOCEND] = tokens[i][LOCEND]; + typ = arena.getType(t); + if (typ === exports.EnumToken.FunctionTokenDefType) { + typ2 = arena.getType(tokens[i - 1]); + if (typ2 === exports.EnumToken.ColonTokenType || + typ2 === exports.EnumToken.DoubleColonTokenType) { + arena.set(tokens[i - 1], exports.EnumToken.PseudoClassFunctionTokenDefType, arena.internString((typ2 === exports.EnumToken.ColonTokenType ? ":" : "::") + + arena.getValue(tokens[i]))); + // t = Object.assign(tokens[i - 1], { + // typ: EnumToken.PseudoClassFunctionTokenDefType, + // val: + // (tokens[i - 1].typ === EnumToken.ColonTokenType ? ":" : "::") + + // (tokens[i] as FunctionToken).val, + // }); + arena.setSpanEnd(tokens[i - 1], arena.getSpanEnd(tokens[i])); + // t[LOCEND] = tokens[i][LOCEND]; + t = tokens[i - 1]; tokens.splice(i--, 1); + typ = exports.EnumToken.PseudoClassFunctionTokenDefType; } } - if (t.typ === exports.EnumToken.AttrStartTokenType || - t.typ === exports.EnumToken.StartParensTokenType || - tokensfuncDefMap.has(t.typ)) { + if (typ === exports.EnumToken.AttrStartTokenType || + typ === exports.EnumToken.StartParensTokenType || + tokensfuncDefMap.has(typ)) { stack.push(t); continue; } - if (t.typ === exports.EnumToken.EndParensTokenType) { + if (typ === exports.EnumToken.EndParensTokenType) { + typ2 = arena.getType(stack.at(-1)); if (stack.length === 0 || - (stack.at(-1)?.typ !== exports.EnumToken.StartParensTokenType && !tokensfuncDefMap.has(stack.at(-1)?.typ))) { + (typ2 !== exports.EnumToken.StartParensTokenType && !tokensfuncDefMap.has(typ2))) { // unbalanced parens const node = stack.at(-1); errors?.push?.({ action: "drop", message: `Unbalanced token ')'`, node, - location: options.source.getSourceLocation(node[LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(node)), }); // return []; continue; } tokens.splice(i, 1); index = tokens.indexOf(stack.at(-1)); - Object.assign(tokens[index], { - typ: tokens[index].typ === exports.EnumToken.StartParensTokenType - ? exports.EnumToken.ParensTokenType - : tokensfuncDefMap.get(tokens[index].typ), - chi: trimArray(tokens.splice(index + 1, i - index - 1)), - }); + typ2 = arena.getType(tokens[index]); + typ2 = typ2 === exports.EnumToken.StartParensTokenType + ? exports.EnumToken.ParensTokenType + : tokensfuncDefMap.get(typ2); + arena.setType(tokens[index], typ2); + arena.setChildren(tokens[index], ...trimArray(tokens.splice(index + 1, i - index - 1), options)); + // Object.assign(tokens[index], { + // typ: + // tokens[index].typ === EnumToken.StartParensTokenType + // ? EnumToken.ParensTokenType + // : (tokensfuncDefMap.get(tokens[index].typ) as EnumToken), + // chi: trimArray(tokens.splice(index + 1, i - index - 1)), + // }); i = index; - if (tokens[index].typ === exports.EnumToken.ColorTokenType && options?.parseColor) { - parseColor(tokens[index]); + if (typ2 === exports.EnumToken.ColorTokenType && options?.parseColor) { + parseColor(tokens[index], arena); } stack.pop(); continue; } - if (t.typ === exports.EnumToken.AttrEndTokenType) { - if (stack.at(-1)?.typ !== exports.EnumToken.AttrStartTokenType) { + if (typ === exports.EnumToken.AttrEndTokenType) { + if (arena.getType(stack.at(-1)) !== exports.EnumToken.AttrStartTokenType) { // unbalanced const node = stack.at(-1); errors?.push?.({ action: "drop", message: `Unbalanced token ']'`, node, - location: options.source.getSourceLocation(node[LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(node)), }); continue; } index = tokens.indexOf(stack.at(-1)); const attr = stack.at(-1); - attr[LOCEND] = t[LOCEND]; + arena.setSpanEnd(attr, arena.getSpanEnd(t)); + // attr[LOCEND] = t[LOCEND]; tokens.splice(i, 1); - Object.assign(attr, { - typ: exports.EnumToken.AttrTokenType, - chi: tokens.splice(index + 1, i - index - 1), - }); + arena.setType(attr, exports.EnumToken.AttrTokenType); + arena.setChildren(attr, ...trimArray(tokens.splice(index + 1, i - index - 1), options)); + // Object.assign(attr, { + // typ: EnumToken.AttrTokenType, + // chi: tokens.splice(index + 1, i - index - 1), + // }); tokens[index] = attr; i = index; stack.pop(); continue; } - if (t.typ == exports.EnumToken.IdenTokenType) { - if (t.val == "from" && i > 0) { + if (typ == exports.EnumToken.IdenTokenType) { + if (arena.equalsIgnoreCase(t, 'val', "from") && i > 0) { const left = []; const right = []; let foundLeft = 0; let foundRight = 0; let k = i; let l = i; + typ2 = arena.getType(tokens[k - 1]); while (k > 0) { - if (tokens[k - 1].typ == exports.EnumToken.CommentTokenType || - tokens[k - 1].typ == exports.EnumToken.WhitespaceTokenType) { + if (typ2 == exports.EnumToken.CommentTokenType || + typ2 == exports.EnumToken.WhitespaceTokenType) { left.push(tokens[--k]); continue; } - if (tokens[k - 1].typ == exports.EnumToken.IdenTokenType || - tokens[k - 1].typ == exports.EnumToken.DashedIdenTokenType) { + if (typ2 == exports.EnumToken.IdenTokenType || + typ2 == exports.EnumToken.DashedIdenTokenType) { foundLeft++; left.push(tokens[--k]); continue; @@ -36661,11 +36729,12 @@ break; } while (++l < tokens.length) { - if (tokens[l].typ == exports.EnumToken.CommentTokenType || tokens[l].typ == exports.EnumToken.WhitespaceTokenType) { + typ2 = arena.getType(tokens[l]); + if (typ2 == exports.EnumToken.CommentTokenType || typ2 == exports.EnumToken.WhitespaceTokenType) { right.push(tokens[l]); continue; } - if (tokens[l].typ == exports.EnumToken.IdenTokenType || tokens[l].typ == exports.EnumToken.StringTokenType) { + if (typ2 == exports.EnumToken.IdenTokenType || typ2 == exports.EnumToken.StringTokenType) { foundRight++; right.push(tokens[l]); continue; @@ -36673,65 +36742,92 @@ break; } if (foundLeft > 0 && foundRight == 1) { - while (left?.[0].typ == exports.EnumToken.WhitespaceTokenType) { - left.shift(); - } - while (left.at(-1)?.typ == exports.EnumToken.WhitespaceTokenType) { - left.pop(); - } - tokens.splice(k, l - k + 1, { - typ: exports.EnumToken.ComposesSelectorNodeType, - l: left, - r: right.reduce((a, b) => { - return a == null + trimArray(left, options); + // while (left?.[0].typ == EnumToken.WhitespaceTokenType) { + // left.shift(); + // } + // while (left.at(-1)?.typ == EnumToken.WhitespaceTokenType) { + // left.pop(); + // } + arena.setLeft(t, ...left); + // @ts-ignore + arena.setRight(t, right.reduce((a, b) => { + typ = arena.getType(b); + return a == null + ? b + : typ == exports.EnumToken.IdenTokenType || typ == exports.EnumToken.StringTokenType ? b - : b.typ == exports.EnumToken.IdenTokenType || b.typ == exports.EnumToken.StringTokenType - ? b - : a; - }, null), - }); + : a; + }, null)); + const composeSelector = arena.allocate(); + arena.setType(composeSelector, exports.EnumToken.ComposesSelectorNodeType); + // arena.setChildren(composeSelector, t, ...right); + arena.setLeft(t, ...left); + // @ts-ignore + arena.setRight(composeSelector, right.reduce((a, b) => { + typ = arena.getType(b); + return a == null + ? b + : typ == exports.EnumToken.IdenTokenType || typ == exports.EnumToken.StringTokenType + ? b + : a; + }, null)); + tokens.splice(k, l - k + 1, composeSelector); + // tokens.splice(k, l - k + 1, { + // typ: EnumToken.ComposesSelectorNodeType, + // l: left, + // r: right.reduce((a: Token | null, b: Token) => { + // return a == null + // ? b + // : b.typ == EnumToken.IdenTokenType || b.typ == EnumToken.StringTokenType + // ? b + // : a; + // }, null), + // }); i = k; continue; } } - else if (options?.parseColor && isColor(t)) { - parseColor(t); + else if (options?.parseColor && isColor(t, arena)) { + parseColor(t, arena); } } - if (t.typ == exports.EnumToken.WhitespaceTokenType && + typ = arena.getType(t); + typ2 = arena.getType(tokens[i + 1]); + if (typ == exports.EnumToken.WhitespaceTokenType && (i == 0 || i + 1 == tokens.length || - [ - exports.EnumToken.CommaTokenType, - exports.EnumToken.GteTokenType, - exports.EnumToken.LteTokenType, - exports.EnumToken.ColumnCombinatorTokenType, - ].includes(tokens[i + 1].typ) || - (i > 0 && trimWhiteSpace.includes(tokens[i - 1].typ)))) { + (exports.EnumToken.CommaTokenType == typ2 || + exports.EnumToken.GteTokenType == typ2 || + exports.EnumToken.LteTokenType == typ2 || + exports.EnumToken.ColumnCombinatorTokenType == typ2)) || + (i > 0 && trimWhiteSpace.includes(arena.getType(tokens[i - 1])))) { tokens.splice(i--, 1); continue; } - if (t.typ == exports.EnumToken.ColonTokenType) { - const typ = tokens[i + 1]?.typ; - if (typ != null) { - if (typ == exports.EnumToken.FunctionTokenType) { - tokens[i + 1].typ = exports.EnumToken.PseudoClassFuncTokenType; - } - else if (typ == exports.EnumToken.IdenTokenType) { - tokens[i + 1].val = ":" + tokens[i + 1].val; - tokens[i + 1].typ = exports.EnumToken.PseudoClassTokenType; - } - if (typ == exports.EnumToken.FunctionTokenType || typ == exports.EnumToken.IdenTokenType) { + if (typ == exports.EnumToken.ColonTokenType) { + // const typ2: EnumToken = tokens[i + 1]?.typ; + if (typ2 != null) { + if (typ2 == exports.EnumToken.FunctionTokenType) { + typ2 = exports.EnumToken.PseudoClassFuncTokenType; + } + else if (typ2 == exports.EnumToken.IdenTokenType) { + arena.set(tokens[i + 1], exports.EnumToken.PseudoClassTokenType, arena.internString(":" + arena.getValue(tokens[i + 1]))); + // (tokens[i + 1] as PseudoClassToken).val = ":" + (tokens[i + 1] as PseudoClassToken).val; + // tokens[i + 1].typ = EnumToken.PseudoClassTokenType; + typ2 = exports.EnumToken.FunctionTokenType; + } + if (typ2 == exports.EnumToken.FunctionTokenType || typ2 == exports.EnumToken.IdenTokenType) { tokens.splice(i, 1); i--; } } } - if (t.typ === exports.EnumToken.CommaTokenType) { - if (tokens[i + 1]?.typ === exports.EnumToken.WhitespaceTokenType) { + if (typ === exports.EnumToken.CommaTokenType) { + if (typ2 === exports.EnumToken.WhitespaceTokenType) { tokens.splice(i + 1, 1); } - if (tokens[i - 1]?.typ === exports.EnumToken.WhitespaceTokenType) { + if (arena.getType(tokens[i - 1]) === exports.EnumToken.WhitespaceTokenType) { tokens.splice(i - 1, 1); i--; } @@ -36741,9 +36837,9 @@ const node = stack.at(-1); errors?.push?.({ action: "drop", - message: `Unbalanced token. Expecting ${node.typ === exports.EnumToken.AttrStartTokenType ? "']'" : ")"}'`, + message: `Unbalanced token. Expecting ${typ === exports.EnumToken.AttrStartTokenType ? "']'" : ")"}'`, node, - location: options.source.getSourceLocation(node[LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(node)), }); } return tokens; diff --git a/dist/index.cjs b/dist/index.cjs index 659c5b9ce..bb6b4cb66 100644 --- a/dist/index.cjs +++ b/dist/index.cjs @@ -12479,10 +12479,10 @@ function isMFValue(featureName, tokens, isMFRange) { */ function createValidationContext(tokens, options) { const arena = options.arena; - tokens = trimArray(tokens.filter((t) => arena.getType(t) !== exports.EnumToken.CommentTokenType)); + tokens = trimArray(tokens.filter((t) => arena.getType(t) !== exports.EnumToken.CommentTokenType), options); if (arena.getType(tokens.at(-1)) === exports.EnumToken.ImportantTokenType) { tokens.pop(); - trimArray(tokens); + trimArray(tokens, options); } const token = { tokens, @@ -12496,10 +12496,12 @@ function createValidationContext(tokens, options) { peek(offset = 0) { let index = this.index; let token = this.tokens[++index]; + let typ = arena.getType(this.tokens[++index]); while (offset >= 0 && index < this.tokens.length) { - while (token?.typ == exports.EnumToken.WhitespaceTokenType || - token?.typ == exports.EnumToken.CommentTokenType || - token?.typ == exports.EnumToken.CDOCOMMTokenType) { + typ = arena.getType(token); + while (typ == exports.EnumToken.WhitespaceTokenType || + typ == exports.EnumToken.CommentTokenType || + typ == exports.EnumToken.CDOCOMMTokenType) { token = this.tokens[++index]; } if (token == null || offset === 0) { @@ -31709,19 +31711,23 @@ function matchAtRuleWhenElseSyntax(stream, context, options = {}) { function parseAtRuleContainerQueryList(stream, context, options = {}) { let matchCount = 0; + let typ; + const arena = options.arena; + arena.getName(context); const errors = []; const syntaxRules = getSyntaxRule(ValidationSyntaxGroupEnum.AtRules, "@" + context.nam); const syntax = syntaxRules?.getPreludeRules()?.slice?.(1); const parts = stream.reduce((acc, t) => { - if (t.typ === exports.EnumToken.CommaTokenType && matchCount === 0) { + typ = arena.getType(t); + if (typ === exports.EnumToken.CommaTokenType && matchCount === 0) { acc.push([]); } else { acc[acc.length - 1].push(t); - if (t.typ === exports.EnumToken.StartParensTokenType || tokensfuncDefMap.has(t.typ)) { + if (typ === exports.EnumToken.StartParensTokenType || tokensfuncDefMap.has(typ)) { matchCount++; } - else if (t.typ === exports.EnumToken.EndParensTokenType) { + else if (typ === exports.EnumToken.EndParensTokenType) { if (matchCount > 0) { matchCount--; } @@ -31729,7 +31735,7 @@ function parseAtRuleContainerQueryList(stream, context, options = {}) { } return acc; }, [[]]); - const result = matchAllSyntaxes(syntax, createValidationContext(stream), options); + const result = matchAllSyntaxes(syntax, createValidationContext(stream, options), options); if (!result.success) { for (const error of result.errors) { errors.push(error); @@ -31768,7 +31774,7 @@ function parseAtRuleContainerQueryList(stream, context, options = {}) { { action: "drop", node: stream[i], - location: options.source.getSourceLocation(stream[i]?.[LOCSTA]), + location: options.source.getSourceLocation(stream[i]), message: `expecting `, }, ], @@ -31776,7 +31782,8 @@ function parseAtRuleContainerQueryList(stream, context, options = {}) { } for (; i < stream.length; i++) { tokens.push(stream[i]); - if (stream[i].typ === exports.EnumToken.WhitespaceTokenType || stream[i].typ === exports.EnumToken.CommentTokenType) { + typ = arena.getType(stream[i]); + if (typ === exports.EnumToken.WhitespaceTokenType || typ === exports.EnumToken.CommentTokenType) { continue; } if (expectAndOr) { @@ -31785,7 +31792,7 @@ function parseAtRuleContainerQueryList(stream, context, options = {}) { // const val: string = (stream[i] as IdentToken).val.toLowerCase(); // valid = val === "and" || val === "or"; // } else { - valid = stream[i].typ !== exports.EnumToken.CommaTokenType; + valid = typ !== exports.EnumToken.CommaTokenType; // } if (!valid) { success = false; @@ -31793,17 +31800,17 @@ function parseAtRuleContainerQueryList(stream, context, options = {}) { action: "drop", node: stream[i], message: `expecting , or comma`, - location: options.source.getSourceLocation(stream[i]?.[LOCSTA]), + location: options.source.getSourceLocation(stream[i]), }); break; } } - if (stream[i].typ === exports.EnumToken.StartParensTokenType || tokensfuncDefMap.has(stream[i].typ)) { + if (typ === exports.EnumToken.StartParensTokenType || tokensfuncDefMap.has(typ)) { scopes.push((currentScope = new Set())); stack.push(stream[i]); continue; } - switch (stream[i].typ) { + switch (typ) { case exports.EnumToken.ColonTokenType: case exports.EnumToken.LtTokenType: case exports.EnumToken.LteTokenType: @@ -31814,7 +31821,7 @@ function parseAtRuleContainerQueryList(stream, context, options = {}) { break; case exports.EnumToken.IdenTokenType: { - const val = stream[i].val.toLowerCase(); + const val = arena.getValue(stream[i]); if (val === "not") { Object.assign(stream[i], { typ: exports.EnumToken.NotTokenType, @@ -32072,32 +32079,35 @@ function parseAtRuleFontFeatureValues(stream, context, options = {}) { } function matchGenericSyntax(stream, options) { + const arena = options.arena; const stack = []; let i = 0; let success = true; let expectAndOr = false; let expectComma = false; + let typ; const errors = []; const scopes = [new Set()]; for (; i < stream.length; i++) { const token = stream[i]; - if (token.typ === exports.EnumToken.WhitespaceTokenType || token.typ === exports.EnumToken.CommentTokenType) { + typ = arena.getType(token); + if (typ === exports.EnumToken.WhitespaceTokenType || typ === exports.EnumToken.CommentTokenType) { continue; } - if (token.typ === exports.EnumToken.StartParensTokenType || tokensfuncDefMap.has(token.typ)) { + if (typ === exports.EnumToken.StartParensTokenType || tokensfuncDefMap.has(typ)) { stack.push(token); scopes.push(new Set()); expectAndOr = false; continue; } - if (token.typ === exports.EnumToken.EndParensTokenType) { + if (typ === exports.EnumToken.EndParensTokenType) { if (stack.length === 0 || - (stack.at(-1)?.typ !== exports.EnumToken.StartParensTokenType && !tokensfuncDefMap.has(stack.at(-1)?.typ))) { + (arena.getType(stack.at(-1)) !== exports.EnumToken.StartParensTokenType && !tokensfuncDefMap.has(arena.getType(stack.at(-1))))) { errors.push({ action: "drop", - message: `unexpected token ${exports.EnumToken[token.typ]}`, + message: `unexpected token ${exports.EnumToken[typ]}`, node: token, - location: options.source.getSourceLocation(token[LOCSTA]), + location: options.source.getSourceLocation(token), }); success = false; break; @@ -32106,29 +32116,30 @@ function matchGenericSyntax(stream, options) { scopes.pop(); continue; } - if (token.typ === exports.EnumToken.IdenTokenType && equalsIgnoreCase("and", token.val)) { + if (typ === exports.EnumToken.IdenTokenType && arena.equalsIgnoreCase(token, 'val', "and")) { if (!expectAndOr || scopes.at(-1)?.has(exports.EnumToken.OrTokenType)) { errors.push({ action: "drop", - message: `unexpected token ${exports.EnumToken[token.typ]}`, + message: `unexpected token ${exports.EnumToken[typ]}`, node: token, - location: options.source.getSourceLocation(token[LOCSTA]), + location: options.source.getSourceLocation(token), }); success = false; break; } - Object.assign(token, { typ: exports.EnumToken.AndTokenType }); + typ = exports.EnumToken.AndTokenType; + arena.setType(token, typ); scopes.at(-1).add(exports.EnumToken.AndTokenType); expectComma = false; continue; } - if (token.typ === exports.EnumToken.IdenTokenType && equalsIgnoreCase("or", token.val)) { + if (typ === exports.EnumToken.IdenTokenType && arena.equalsIgnoreCase(token, 'val', "or")) { if (!expectAndOr || scopes.at(-1)?.has(exports.EnumToken.AndTokenType)) { errors.push({ action: "drop", - message: `unexpected token ${exports.EnumToken[token.typ]}`, + message: `unexpected token ${exports.EnumToken[typ]}`, node: token, - location: options.source.getSourceLocation(token[LOCSTA]), + location: options.source.getSourceLocation(token), }); success = false; break; @@ -32138,13 +32149,13 @@ function matchGenericSyntax(stream, options) { scopes.at(-1).add(exports.EnumToken.OrTokenType); continue; } - if (token.typ === exports.EnumToken.CommaTokenType) { + if (typ === exports.EnumToken.CommaTokenType) { if (!expectComma) { errors.push({ action: "drop", - message: `unexpected token ${exports.EnumToken[token.typ]}`, + message: `unexpected token ${exports.EnumToken[typ]}`, node: token, - location: options.source.getSourceLocation(token[LOCSTA]), + location: options.source.getSourceLocation(token), }); success = false; break; @@ -32155,16 +32166,16 @@ function matchGenericSyntax(stream, options) { } expectAndOr = true; expectComma = true; - if (stack.length > 0 && stack.at(-1)?.typ === exports.EnumToken.CommaTokenType) { + if (stack.length > 0 && arena.getType(stack.at(-1)) === exports.EnumToken.CommaTokenType) { stack.pop(); } } if (stack.length > 0) { errors.push({ action: "drop", - message: `unexpected token ${exports.EnumToken[stack.at(-1)?.typ]}`, + message: `unexpected token ${exports.EnumToken[arena.getType(stack.at(-1))]}`, node: stack.at(-1), - location: options.source.getSourceLocation(stack.at(-1)?.[LOCSTA]), + location: options.source.getSourceLocation(stack.at(-1)), }); success = false; } @@ -34758,12 +34769,11 @@ function parseNode(tokens, context, options, errors, stats, invalidNodes) { if (node == null) { return null; } - if (node[STATE] & - (exports.EnumAstNodeStatus.Invalid | - exports.EnumAstNodeStatus.Disallowed | - exports.EnumAstNodeStatus.Unknown | - exports.EnumAstNodeStatus.Unparsed | - exports.EnumAstNodeStatus.Malformed)) { + if (arena.hasFlags(node, exports.EnumAstNodeStatus.Invalid | + exports.EnumAstNodeStatus.Disallowed | + exports.EnumAstNodeStatus.Unknown | + exports.EnumAstNodeStatus.Unparsed | + exports.EnumAstNodeStatus.Malformed)) { invalidNodes.push(node); } stats.nodesCount++; @@ -34797,8 +34807,8 @@ function parseNode(tokens, context, options, errors, stats, invalidNodes) { const node = parseDeclaration(tokens, context, options, errors); // node[PARENT] = context; // node[ROOT] = context[ROOT]; - if (contextType === exports.EnumToken.StyleSheetNodeType && node.typ === exports.EnumToken.DeclarationNodeType) { - node[STATE] = exports.EnumAstNodeStatus.Invalid; + if (contextType === exports.EnumToken.StyleSheetNodeType && arena.getType(node) === exports.EnumToken.DeclarationNodeType) { + arena.setFlags(node, exports.EnumAstNodeStatus.Invalid); errors.push({ message: " not allowed in ", action: "drop", @@ -34806,16 +34816,15 @@ function parseNode(tokens, context, options, errors, stats, invalidNodes) { location: options.source.getSourceLocation(arena.getSpanStart(node)), }); } - else if (options.lenient || node.typ === exports.EnumToken.DeclarationNodeType) { + else if (options.lenient || arena.getType(node) === exports.EnumToken.DeclarationNodeType) { // context.chi!.push(node); arena.appendChildren(context, node); } - if (node[STATE] & - (exports.EnumAstNodeStatus.Invalid | - exports.EnumAstNodeStatus.Disallowed | - exports.EnumAstNodeStatus.Unknown | - exports.EnumAstNodeStatus.Unparsed | - exports.EnumAstNodeStatus.Malformed)) { + if (arena.hasFlags(node, exports.EnumAstNodeStatus.Invalid | + exports.EnumAstNodeStatus.Disallowed | + exports.EnumAstNodeStatus.Unknown | + exports.EnumAstNodeStatus.Unparsed | + exports.EnumAstNodeStatus.Malformed)) { invalidNodes.push(node); } } @@ -34830,13 +34839,15 @@ function parseNode(tokens, context, options, errors, stats, invalidNodes) { * @param parseAsBlock */ function parseAtRule(stream, context, options, errors, parseAsBlock = null) { + const arena = options.arena; let success = true; - let atRuleName = stream[0].nam; + let atRuleName = arena.getName(stream[0]); if (atRuleName.startsWith("-")) { atRuleName = atRuleName.replace(/^-[a-z]+-/, "").toLowerCase(); } const atRule = stream.shift(); - const syntaxRules = getSyntaxRule(ValidationSyntaxGroupEnum.AtRules, "@" + atRule.nam); + const name = arena.getName(atRule); + const syntaxRules = getSyntaxRule(ValidationSyntaxGroupEnum.AtRules, "@" + name); const syntax = syntaxRules?.getPreludeRules()?.slice?.(1); const blockAllowed = syntaxRules?.getBlockRules() != null; if (syntaxRules == null) { @@ -34844,7 +34855,7 @@ function parseAtRule(stream, context, options, errors, parseAsBlock = null) { errors.push({ action: "drop", node: atRule, - location: options.source.getSourceLocation(atRule[LOCSTA]), + location: options.source.getSourceLocation(atRule), message: "unknown at-rule", }); const result = matchGenericSyntax(stream, options); @@ -35700,100 +35711,123 @@ function parseString(src, options = { parseColor: true }, errors) { */ function parseTokens(tokens, options, errors) { const stack = []; + const arena = options?.arena; let i = 0; let index; let t; + let typ; + let typ2; options ??= { parseColor: true }; for (; i < tokens.length; i++) { t = tokens[i]; - if (t.typ === exports.EnumToken.FunctionTokenDefType) { - if (tokens[i - 1]?.typ === exports.EnumToken.ColonTokenType || - tokens[i - 1]?.typ === exports.EnumToken.DoubleColonTokenType) { - t = Object.assign(tokens[i - 1], { - typ: exports.EnumToken.PseudoClassFunctionTokenDefType, - val: (tokens[i - 1].typ === exports.EnumToken.ColonTokenType ? ":" : "::") + - tokens[i].val, - }); - t[LOCEND] = tokens[i][LOCEND]; + typ = arena.getType(t); + if (typ === exports.EnumToken.FunctionTokenDefType) { + typ2 = arena.getType(tokens[i - 1]); + if (typ2 === exports.EnumToken.ColonTokenType || + typ2 === exports.EnumToken.DoubleColonTokenType) { + arena.set(tokens[i - 1], exports.EnumToken.PseudoClassFunctionTokenDefType, arena.internString((typ2 === exports.EnumToken.ColonTokenType ? ":" : "::") + + arena.getValue(tokens[i]))); + // t = Object.assign(tokens[i - 1], { + // typ: EnumToken.PseudoClassFunctionTokenDefType, + // val: + // (tokens[i - 1].typ === EnumToken.ColonTokenType ? ":" : "::") + + // (tokens[i] as FunctionToken).val, + // }); + arena.setSpanEnd(tokens[i - 1], arena.getSpanEnd(tokens[i])); + // t[LOCEND] = tokens[i][LOCEND]; + t = tokens[i - 1]; tokens.splice(i--, 1); + typ = exports.EnumToken.PseudoClassFunctionTokenDefType; } } - if (t.typ === exports.EnumToken.AttrStartTokenType || - t.typ === exports.EnumToken.StartParensTokenType || - tokensfuncDefMap.has(t.typ)) { + if (typ === exports.EnumToken.AttrStartTokenType || + typ === exports.EnumToken.StartParensTokenType || + tokensfuncDefMap.has(typ)) { stack.push(t); continue; } - if (t.typ === exports.EnumToken.EndParensTokenType) { + if (typ === exports.EnumToken.EndParensTokenType) { + typ2 = arena.getType(stack.at(-1)); if (stack.length === 0 || - (stack.at(-1)?.typ !== exports.EnumToken.StartParensTokenType && !tokensfuncDefMap.has(stack.at(-1)?.typ))) { + (typ2 !== exports.EnumToken.StartParensTokenType && !tokensfuncDefMap.has(typ2))) { // unbalanced parens const node = stack.at(-1); errors?.push?.({ action: "drop", message: `Unbalanced token ')'`, node, - location: options.source.getSourceLocation(node[LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(node)), }); // return []; continue; } tokens.splice(i, 1); index = tokens.indexOf(stack.at(-1)); - Object.assign(tokens[index], { - typ: tokens[index].typ === exports.EnumToken.StartParensTokenType - ? exports.EnumToken.ParensTokenType - : tokensfuncDefMap.get(tokens[index].typ), - chi: trimArray(tokens.splice(index + 1, i - index - 1)), - }); + typ2 = arena.getType(tokens[index]); + typ2 = typ2 === exports.EnumToken.StartParensTokenType + ? exports.EnumToken.ParensTokenType + : tokensfuncDefMap.get(typ2); + arena.setType(tokens[index], typ2); + arena.setChildren(tokens[index], ...trimArray(tokens.splice(index + 1, i - index - 1), options)); + // Object.assign(tokens[index], { + // typ: + // tokens[index].typ === EnumToken.StartParensTokenType + // ? EnumToken.ParensTokenType + // : (tokensfuncDefMap.get(tokens[index].typ) as EnumToken), + // chi: trimArray(tokens.splice(index + 1, i - index - 1)), + // }); i = index; - if (tokens[index].typ === exports.EnumToken.ColorTokenType && options?.parseColor) { - parseColor(tokens[index]); + if (typ2 === exports.EnumToken.ColorTokenType && options?.parseColor) { + parseColor(tokens[index], arena); } stack.pop(); continue; } - if (t.typ === exports.EnumToken.AttrEndTokenType) { - if (stack.at(-1)?.typ !== exports.EnumToken.AttrStartTokenType) { + if (typ === exports.EnumToken.AttrEndTokenType) { + if (arena.getType(stack.at(-1)) !== exports.EnumToken.AttrStartTokenType) { // unbalanced const node = stack.at(-1); errors?.push?.({ action: "drop", message: `Unbalanced token ']'`, node, - location: options.source.getSourceLocation(node[LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(node)), }); continue; } index = tokens.indexOf(stack.at(-1)); const attr = stack.at(-1); - attr[LOCEND] = t[LOCEND]; + arena.setSpanEnd(attr, arena.getSpanEnd(t)); + // attr[LOCEND] = t[LOCEND]; tokens.splice(i, 1); - Object.assign(attr, { - typ: exports.EnumToken.AttrTokenType, - chi: tokens.splice(index + 1, i - index - 1), - }); + arena.setType(attr, exports.EnumToken.AttrTokenType); + arena.setChildren(attr, ...trimArray(tokens.splice(index + 1, i - index - 1), options)); + // Object.assign(attr, { + // typ: EnumToken.AttrTokenType, + // chi: tokens.splice(index + 1, i - index - 1), + // }); tokens[index] = attr; i = index; stack.pop(); continue; } - if (t.typ == exports.EnumToken.IdenTokenType) { - if (t.val == "from" && i > 0) { + if (typ == exports.EnumToken.IdenTokenType) { + if (arena.equalsIgnoreCase(t, 'val', "from") && i > 0) { const left = []; const right = []; let foundLeft = 0; let foundRight = 0; let k = i; let l = i; + typ2 = arena.getType(tokens[k - 1]); while (k > 0) { - if (tokens[k - 1].typ == exports.EnumToken.CommentTokenType || - tokens[k - 1].typ == exports.EnumToken.WhitespaceTokenType) { + if (typ2 == exports.EnumToken.CommentTokenType || + typ2 == exports.EnumToken.WhitespaceTokenType) { left.push(tokens[--k]); continue; } - if (tokens[k - 1].typ == exports.EnumToken.IdenTokenType || - tokens[k - 1].typ == exports.EnumToken.DashedIdenTokenType) { + if (typ2 == exports.EnumToken.IdenTokenType || + typ2 == exports.EnumToken.DashedIdenTokenType) { foundLeft++; left.push(tokens[--k]); continue; @@ -35801,11 +35835,12 @@ function parseTokens(tokens, options, errors) { break; } while (++l < tokens.length) { - if (tokens[l].typ == exports.EnumToken.CommentTokenType || tokens[l].typ == exports.EnumToken.WhitespaceTokenType) { + typ2 = arena.getType(tokens[l]); + if (typ2 == exports.EnumToken.CommentTokenType || typ2 == exports.EnumToken.WhitespaceTokenType) { right.push(tokens[l]); continue; } - if (tokens[l].typ == exports.EnumToken.IdenTokenType || tokens[l].typ == exports.EnumToken.StringTokenType) { + if (typ2 == exports.EnumToken.IdenTokenType || typ2 == exports.EnumToken.StringTokenType) { foundRight++; right.push(tokens[l]); continue; @@ -35813,65 +35848,92 @@ function parseTokens(tokens, options, errors) { break; } if (foundLeft > 0 && foundRight == 1) { - while (left?.[0].typ == exports.EnumToken.WhitespaceTokenType) { - left.shift(); - } - while (left.at(-1)?.typ == exports.EnumToken.WhitespaceTokenType) { - left.pop(); - } - tokens.splice(k, l - k + 1, { - typ: exports.EnumToken.ComposesSelectorNodeType, - l: left, - r: right.reduce((a, b) => { - return a == null + trimArray(left, options); + // while (left?.[0].typ == EnumToken.WhitespaceTokenType) { + // left.shift(); + // } + // while (left.at(-1)?.typ == EnumToken.WhitespaceTokenType) { + // left.pop(); + // } + arena.setLeft(t, ...left); + // @ts-ignore + arena.setRight(t, right.reduce((a, b) => { + typ = arena.getType(b); + return a == null + ? b + : typ == exports.EnumToken.IdenTokenType || typ == exports.EnumToken.StringTokenType ? b - : b.typ == exports.EnumToken.IdenTokenType || b.typ == exports.EnumToken.StringTokenType - ? b - : a; - }, null), - }); + : a; + }, null)); + const composeSelector = arena.allocate(); + arena.setType(composeSelector, exports.EnumToken.ComposesSelectorNodeType); + // arena.setChildren(composeSelector, t, ...right); + arena.setLeft(t, ...left); + // @ts-ignore + arena.setRight(composeSelector, right.reduce((a, b) => { + typ = arena.getType(b); + return a == null + ? b + : typ == exports.EnumToken.IdenTokenType || typ == exports.EnumToken.StringTokenType + ? b + : a; + }, null)); + tokens.splice(k, l - k + 1, composeSelector); + // tokens.splice(k, l - k + 1, { + // typ: EnumToken.ComposesSelectorNodeType, + // l: left, + // r: right.reduce((a: Token | null, b: Token) => { + // return a == null + // ? b + // : b.typ == EnumToken.IdenTokenType || b.typ == EnumToken.StringTokenType + // ? b + // : a; + // }, null), + // }); i = k; continue; } } - else if (options?.parseColor && isColor(t)) { - parseColor(t); + else if (options?.parseColor && isColor(t, arena)) { + parseColor(t, arena); } } - if (t.typ == exports.EnumToken.WhitespaceTokenType && + typ = arena.getType(t); + typ2 = arena.getType(tokens[i + 1]); + if (typ == exports.EnumToken.WhitespaceTokenType && (i == 0 || i + 1 == tokens.length || - [ - exports.EnumToken.CommaTokenType, - exports.EnumToken.GteTokenType, - exports.EnumToken.LteTokenType, - exports.EnumToken.ColumnCombinatorTokenType, - ].includes(tokens[i + 1].typ) || - (i > 0 && trimWhiteSpace.includes(tokens[i - 1].typ)))) { + (exports.EnumToken.CommaTokenType == typ2 || + exports.EnumToken.GteTokenType == typ2 || + exports.EnumToken.LteTokenType == typ2 || + exports.EnumToken.ColumnCombinatorTokenType == typ2)) || + (i > 0 && trimWhiteSpace.includes(arena.getType(tokens[i - 1])))) { tokens.splice(i--, 1); continue; } - if (t.typ == exports.EnumToken.ColonTokenType) { - const typ = tokens[i + 1]?.typ; - if (typ != null) { - if (typ == exports.EnumToken.FunctionTokenType) { - tokens[i + 1].typ = exports.EnumToken.PseudoClassFuncTokenType; - } - else if (typ == exports.EnumToken.IdenTokenType) { - tokens[i + 1].val = ":" + tokens[i + 1].val; - tokens[i + 1].typ = exports.EnumToken.PseudoClassTokenType; - } - if (typ == exports.EnumToken.FunctionTokenType || typ == exports.EnumToken.IdenTokenType) { + if (typ == exports.EnumToken.ColonTokenType) { + // const typ2: EnumToken = tokens[i + 1]?.typ; + if (typ2 != null) { + if (typ2 == exports.EnumToken.FunctionTokenType) { + typ2 = exports.EnumToken.PseudoClassFuncTokenType; + } + else if (typ2 == exports.EnumToken.IdenTokenType) { + arena.set(tokens[i + 1], exports.EnumToken.PseudoClassTokenType, arena.internString(":" + arena.getValue(tokens[i + 1]))); + // (tokens[i + 1] as PseudoClassToken).val = ":" + (tokens[i + 1] as PseudoClassToken).val; + // tokens[i + 1].typ = EnumToken.PseudoClassTokenType; + typ2 = exports.EnumToken.FunctionTokenType; + } + if (typ2 == exports.EnumToken.FunctionTokenType || typ2 == exports.EnumToken.IdenTokenType) { tokens.splice(i, 1); i--; } } } - if (t.typ === exports.EnumToken.CommaTokenType) { - if (tokens[i + 1]?.typ === exports.EnumToken.WhitespaceTokenType) { + if (typ === exports.EnumToken.CommaTokenType) { + if (typ2 === exports.EnumToken.WhitespaceTokenType) { tokens.splice(i + 1, 1); } - if (tokens[i - 1]?.typ === exports.EnumToken.WhitespaceTokenType) { + if (arena.getType(tokens[i - 1]) === exports.EnumToken.WhitespaceTokenType) { tokens.splice(i - 1, 1); i--; } @@ -35881,9 +35943,9 @@ function parseTokens(tokens, options, errors) { const node = stack.at(-1); errors?.push?.({ action: "drop", - message: `Unbalanced token. Expecting ${node.typ === exports.EnumToken.AttrStartTokenType ? "']'" : ")"}'`, + message: `Unbalanced token. Expecting ${typ === exports.EnumToken.AttrStartTokenType ? "']'" : ")"}'`, node, - location: options.source.getSourceLocation(node[LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(node)), }); } return tokens; @@ -36176,6 +36238,9 @@ class Arena { getFlags(id) { return this.flags[id]; } + // addFlags(id: number, flags: number): void { + // this.flags[id] |= flags; + // } hasFlags(id, flag) { return (this.flags[id] & flag) != 0; } @@ -36195,6 +36260,9 @@ class Arena { this.nextSibling[child] = this.nextSibling[this.firstChild[id]]; this.previousSibling[this.nextSibling[this.firstChild[id]]] = child; } + if (this.lastChild[id] == this.firstChild[id]) { + this.lastChild[id] = child; + } this.firstChild[id] = child; this.parents[child] = id; } @@ -36223,6 +36291,9 @@ class Arena { this.nextSibling[currentChild] = 0; this.parents[currentChild] = 0; } + if (currentChild == this.lastChild[id]) { + this.lastChild[id] = child; + } this.parents[child] = id; } /** @@ -36230,17 +36301,42 @@ class Arena { * @param id * @param child */ - setLeft(id, child) { + setLeft(id, ...children) { + let child = this.firstChild[id]; + if (child != 0 && this.dataTypes[child] != exports.EnumToken.ListTokenType) { + this.detach(child); + child = this.allocate(); + this.dataTypes[child] = exports.EnumToken.ListTokenType; + } + this.setChildren(child, ...children); this.setChildAt(id, 0, child); } getLeft(id) { - return this.getChildAt(id, 0); + const child = this.getChildAt(id, 0); + if (child == null) { + return null; + } + return this.children(child); } - setRight(id, rightId) { - this.setChildAt(id, 1, rightId); + setRight(id, ...rightId) { + let child = this.getChildAt(id, 1); + if (child == null || this.dataTypes[child] != exports.EnumToken.ListTokenType) { + if (child != null) { + this.detach(child); + } + child = this.allocate(); + this.dataTypes[child] = exports.EnumToken.ListTokenType; + this.setChildAt(id, 1, child); + } + this.setChildren(child, ...(rightId == null || rightId[0] == null ? [] : rightId)); + this.setChildAt(id, 1, child); } getRight(id) { - return this.getChildAt(id, 1); + const child = this.getChildAt(id, 1); + if (child == null) { + return null; + } + return this.children(child); } setOp(id, opId) { this.setChildAt(id, 2, opId); @@ -36576,7 +36672,7 @@ class Arena { this.nextSibling[lastChild] = children[0]; } this.parents[children[0]] = id; - for (let i = 1; i < children.length - 1; i++) { + for (let i = 1; i < children.length; i++) { this.detach(children[i]); this.previousSibling[children[i]] = children[i - 1]; this.nextSibling[children[i - 1]] = children[i]; diff --git a/dist/index.d.ts b/dist/index.d.ts index ea77b4057..679ca701a 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -4570,10 +4570,11 @@ declare class Arena { * @param id * @param child */ - setLeft(id: number, child: number): void; - getLeft(id: number): number; - setRight(id: number, rightId: number): void; - getRight(id: number): number; + setLeft(id: number, ...children: number[]): void; + getLeft(id: number): number[] | null; + setRight(id: number, rightId: null): void; + setRight(id: number, ...rightId: number[]): void; + getRight(id: number): number[] | null; setOp(id: number, opId: number): void; getOp(id: number): number; setOp2(id: number, op2Id: number): void; diff --git a/dist/src/lib/parser/arena.js b/dist/src/lib/parser/arena.js index e59b834e3..2b587ea1a 100644 --- a/dist/src/lib/parser/arena.js +++ b/dist/src/lib/parser/arena.js @@ -206,6 +206,9 @@ class Arena { getFlags(id) { return this.flags[id]; } + // addFlags(id: number, flags: number): void { + // this.flags[id] |= flags; + // } hasFlags(id, flag) { return (this.flags[id] & flag) != 0; } @@ -225,6 +228,9 @@ class Arena { this.nextSibling[child] = this.nextSibling[this.firstChild[id]]; this.previousSibling[this.nextSibling[this.firstChild[id]]] = child; } + if (this.lastChild[id] == this.firstChild[id]) { + this.lastChild[id] = child; + } this.firstChild[id] = child; this.parents[child] = id; } @@ -253,6 +259,9 @@ class Arena { this.nextSibling[currentChild] = 0; this.parents[currentChild] = 0; } + if (currentChild == this.lastChild[id]) { + this.lastChild[id] = child; + } this.parents[child] = id; } /** @@ -260,17 +269,42 @@ class Arena { * @param id * @param child */ - setLeft(id, child) { + setLeft(id, ...children) { + let child = this.firstChild[id]; + if (child != 0 && this.dataTypes[child] != EnumToken.ListTokenType) { + this.detach(child); + child = this.allocate(); + this.dataTypes[child] = EnumToken.ListTokenType; + } + this.setChildren(child, ...children); this.setChildAt(id, 0, child); } getLeft(id) { - return this.getChildAt(id, 0); + const child = this.getChildAt(id, 0); + if (child == null) { + return null; + } + return this.children(child); } - setRight(id, rightId) { - this.setChildAt(id, 1, rightId); + setRight(id, ...rightId) { + let child = this.getChildAt(id, 1); + if (child == null || this.dataTypes[child] != EnumToken.ListTokenType) { + if (child != null) { + this.detach(child); + } + child = this.allocate(); + this.dataTypes[child] = EnumToken.ListTokenType; + this.setChildAt(id, 1, child); + } + this.setChildren(child, ...(rightId == null || rightId[0] == null ? [] : rightId)); + this.setChildAt(id, 1, child); } getRight(id) { - return this.getChildAt(id, 1); + const child = this.getChildAt(id, 1); + if (child == null) { + return null; + } + return this.children(child); } setOp(id, opId) { this.setChildAt(id, 2, opId); @@ -606,7 +640,7 @@ class Arena { this.nextSibling[lastChild] = children[0]; } this.parents[children[0]] = id; - for (let i = 1; i < children.length - 1; i++) { + for (let i = 1; i < children.length; i++) { this.detach(children[i]); this.previousSibling[children[i]] = children[i - 1]; this.nextSibling[children[i - 1]] = children[i]; diff --git a/dist/src/lib/parser/parse.js b/dist/src/lib/parser/parse.js index 9a7b5ee8d..f527e3f61 100644 --- a/dist/src/lib/parser/parse.js +++ b/dist/src/lib/parser/parse.js @@ -2619,12 +2619,11 @@ function parseNode(tokens, context, options, errors, stats, invalidNodes) { if (node == null) { return null; } - if (node[STATE] & - (EnumAstNodeStatus.Invalid | - EnumAstNodeStatus.Disallowed | - EnumAstNodeStatus.Unknown | - EnumAstNodeStatus.Unparsed | - EnumAstNodeStatus.Malformed)) { + if (arena.hasFlags(node, EnumAstNodeStatus.Invalid | + EnumAstNodeStatus.Disallowed | + EnumAstNodeStatus.Unknown | + EnumAstNodeStatus.Unparsed | + EnumAstNodeStatus.Malformed)) { invalidNodes.push(node); } stats.nodesCount++; @@ -2658,8 +2657,8 @@ function parseNode(tokens, context, options, errors, stats, invalidNodes) { const node = parseDeclaration(tokens, context, options, errors); // node[PARENT] = context; // node[ROOT] = context[ROOT]; - if (contextType === EnumToken.StyleSheetNodeType && node.typ === EnumToken.DeclarationNodeType) { - node[STATE] = EnumAstNodeStatus.Invalid; + if (contextType === EnumToken.StyleSheetNodeType && arena.getType(node) === EnumToken.DeclarationNodeType) { + arena.setFlags(node, EnumAstNodeStatus.Invalid); errors.push({ message: " not allowed in ", action: "drop", @@ -2667,16 +2666,15 @@ function parseNode(tokens, context, options, errors, stats, invalidNodes) { location: options.source.getSourceLocation(arena.getSpanStart(node)), }); } - else if (options.lenient || node.typ === EnumToken.DeclarationNodeType) { + else if (options.lenient || arena.getType(node) === EnumToken.DeclarationNodeType) { // context.chi!.push(node); arena.appendChildren(context, node); } - if (node[STATE] & - (EnumAstNodeStatus.Invalid | - EnumAstNodeStatus.Disallowed | - EnumAstNodeStatus.Unknown | - EnumAstNodeStatus.Unparsed | - EnumAstNodeStatus.Malformed)) { + if (arena.hasFlags(node, EnumAstNodeStatus.Invalid | + EnumAstNodeStatus.Disallowed | + EnumAstNodeStatus.Unknown | + EnumAstNodeStatus.Unparsed | + EnumAstNodeStatus.Malformed)) { invalidNodes.push(node); } } @@ -2691,13 +2689,15 @@ function parseNode(tokens, context, options, errors, stats, invalidNodes) { * @param parseAsBlock */ function parseAtRule(stream, context, options, errors, parseAsBlock = null) { + const arena = options.arena; let success = true; - let atRuleName = stream[0].nam; + let atRuleName = arena.getName(stream[0]); if (atRuleName.startsWith("-")) { atRuleName = atRuleName.replace(/^-[a-z]+-/, "").toLowerCase(); } const atRule = stream.shift(); - const syntaxRules = getSyntaxRule(ValidationSyntaxGroupEnum.AtRules, "@" + atRule.nam); + const name = arena.getName(atRule); + const syntaxRules = getSyntaxRule(ValidationSyntaxGroupEnum.AtRules, "@" + name); const syntax = syntaxRules?.getPreludeRules()?.slice?.(1); const blockAllowed = syntaxRules?.getBlockRules() != null; if (syntaxRules == null) { @@ -2705,7 +2705,7 @@ function parseAtRule(stream, context, options, errors, parseAsBlock = null) { errors.push({ action: "drop", node: atRule, - location: options.source.getSourceLocation(atRule[LOCSTA]), + location: options.source.getSourceLocation(atRule), message: "unknown at-rule", }); const result = matchGenericSyntax(stream, options); @@ -3561,100 +3561,123 @@ function parseString(src, options = { parseColor: true }, errors) { */ function parseTokens(tokens, options, errors) { const stack = []; + const arena = options?.arena; let i = 0; let index; let t; + let typ; + let typ2; options ??= { parseColor: true }; for (; i < tokens.length; i++) { t = tokens[i]; - if (t.typ === EnumToken.FunctionTokenDefType) { - if (tokens[i - 1]?.typ === EnumToken.ColonTokenType || - tokens[i - 1]?.typ === EnumToken.DoubleColonTokenType) { - t = Object.assign(tokens[i - 1], { - typ: EnumToken.PseudoClassFunctionTokenDefType, - val: (tokens[i - 1].typ === EnumToken.ColonTokenType ? ":" : "::") + - tokens[i].val, - }); - t[LOCEND] = tokens[i][LOCEND]; + typ = arena.getType(t); + if (typ === EnumToken.FunctionTokenDefType) { + typ2 = arena.getType(tokens[i - 1]); + if (typ2 === EnumToken.ColonTokenType || + typ2 === EnumToken.DoubleColonTokenType) { + arena.set(tokens[i - 1], EnumToken.PseudoClassFunctionTokenDefType, arena.internString((typ2 === EnumToken.ColonTokenType ? ":" : "::") + + arena.getValue(tokens[i]))); + // t = Object.assign(tokens[i - 1], { + // typ: EnumToken.PseudoClassFunctionTokenDefType, + // val: + // (tokens[i - 1].typ === EnumToken.ColonTokenType ? ":" : "::") + + // (tokens[i] as FunctionToken).val, + // }); + arena.setSpanEnd(tokens[i - 1], arena.getSpanEnd(tokens[i])); + // t[LOCEND] = tokens[i][LOCEND]; + t = tokens[i - 1]; tokens.splice(i--, 1); + typ = EnumToken.PseudoClassFunctionTokenDefType; } } - if (t.typ === EnumToken.AttrStartTokenType || - t.typ === EnumToken.StartParensTokenType || - tokensfuncDefMap.has(t.typ)) { + if (typ === EnumToken.AttrStartTokenType || + typ === EnumToken.StartParensTokenType || + tokensfuncDefMap.has(typ)) { stack.push(t); continue; } - if (t.typ === EnumToken.EndParensTokenType) { + if (typ === EnumToken.EndParensTokenType) { + typ2 = arena.getType(stack.at(-1)); if (stack.length === 0 || - (stack.at(-1)?.typ !== EnumToken.StartParensTokenType && !tokensfuncDefMap.has(stack.at(-1)?.typ))) { + (typ2 !== EnumToken.StartParensTokenType && !tokensfuncDefMap.has(typ2))) { // unbalanced parens const node = stack.at(-1); errors?.push?.({ action: "drop", message: `Unbalanced token ')'`, node, - location: options.source.getSourceLocation(node[LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(node)), }); // return []; continue; } tokens.splice(i, 1); index = tokens.indexOf(stack.at(-1)); - Object.assign(tokens[index], { - typ: tokens[index].typ === EnumToken.StartParensTokenType - ? EnumToken.ParensTokenType - : tokensfuncDefMap.get(tokens[index].typ), - chi: trimArray(tokens.splice(index + 1, i - index - 1)), - }); + typ2 = arena.getType(tokens[index]); + typ2 = typ2 === EnumToken.StartParensTokenType + ? EnumToken.ParensTokenType + : tokensfuncDefMap.get(typ2); + arena.setType(tokens[index], typ2); + arena.setChildren(tokens[index], ...trimArray(tokens.splice(index + 1, i - index - 1), options)); + // Object.assign(tokens[index], { + // typ: + // tokens[index].typ === EnumToken.StartParensTokenType + // ? EnumToken.ParensTokenType + // : (tokensfuncDefMap.get(tokens[index].typ) as EnumToken), + // chi: trimArray(tokens.splice(index + 1, i - index - 1)), + // }); i = index; - if (tokens[index].typ === EnumToken.ColorTokenType && options?.parseColor) { - parseColor(tokens[index]); + if (typ2 === EnumToken.ColorTokenType && options?.parseColor) { + parseColor(tokens[index], arena); } stack.pop(); continue; } - if (t.typ === EnumToken.AttrEndTokenType) { - if (stack.at(-1)?.typ !== EnumToken.AttrStartTokenType) { + if (typ === EnumToken.AttrEndTokenType) { + if (arena.getType(stack.at(-1)) !== EnumToken.AttrStartTokenType) { // unbalanced const node = stack.at(-1); errors?.push?.({ action: "drop", message: `Unbalanced token ']'`, node, - location: options.source.getSourceLocation(node[LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(node)), }); continue; } index = tokens.indexOf(stack.at(-1)); const attr = stack.at(-1); - attr[LOCEND] = t[LOCEND]; + arena.setSpanEnd(attr, arena.getSpanEnd(t)); + // attr[LOCEND] = t[LOCEND]; tokens.splice(i, 1); - Object.assign(attr, { - typ: EnumToken.AttrTokenType, - chi: tokens.splice(index + 1, i - index - 1), - }); + arena.setType(attr, EnumToken.AttrTokenType); + arena.setChildren(attr, ...trimArray(tokens.splice(index + 1, i - index - 1), options)); + // Object.assign(attr, { + // typ: EnumToken.AttrTokenType, + // chi: tokens.splice(index + 1, i - index - 1), + // }); tokens[index] = attr; i = index; stack.pop(); continue; } - if (t.typ == EnumToken.IdenTokenType) { - if (t.val == "from" && i > 0) { + if (typ == EnumToken.IdenTokenType) { + if (arena.equalsIgnoreCase(t, 'val', "from") && i > 0) { const left = []; const right = []; let foundLeft = 0; let foundRight = 0; let k = i; let l = i; + typ2 = arena.getType(tokens[k - 1]); while (k > 0) { - if (tokens[k - 1].typ == EnumToken.CommentTokenType || - tokens[k - 1].typ == EnumToken.WhitespaceTokenType) { + if (typ2 == EnumToken.CommentTokenType || + typ2 == EnumToken.WhitespaceTokenType) { left.push(tokens[--k]); continue; } - if (tokens[k - 1].typ == EnumToken.IdenTokenType || - tokens[k - 1].typ == EnumToken.DashedIdenTokenType) { + if (typ2 == EnumToken.IdenTokenType || + typ2 == EnumToken.DashedIdenTokenType) { foundLeft++; left.push(tokens[--k]); continue; @@ -3662,11 +3685,12 @@ function parseTokens(tokens, options, errors) { break; } while (++l < tokens.length) { - if (tokens[l].typ == EnumToken.CommentTokenType || tokens[l].typ == EnumToken.WhitespaceTokenType) { + typ2 = arena.getType(tokens[l]); + if (typ2 == EnumToken.CommentTokenType || typ2 == EnumToken.WhitespaceTokenType) { right.push(tokens[l]); continue; } - if (tokens[l].typ == EnumToken.IdenTokenType || tokens[l].typ == EnumToken.StringTokenType) { + if (typ2 == EnumToken.IdenTokenType || typ2 == EnumToken.StringTokenType) { foundRight++; right.push(tokens[l]); continue; @@ -3674,65 +3698,92 @@ function parseTokens(tokens, options, errors) { break; } if (foundLeft > 0 && foundRight == 1) { - while (left?.[0].typ == EnumToken.WhitespaceTokenType) { - left.shift(); - } - while (left.at(-1)?.typ == EnumToken.WhitespaceTokenType) { - left.pop(); - } - tokens.splice(k, l - k + 1, { - typ: EnumToken.ComposesSelectorNodeType, - l: left, - r: right.reduce((a, b) => { - return a == null + trimArray(left, options); + // while (left?.[0].typ == EnumToken.WhitespaceTokenType) { + // left.shift(); + // } + // while (left.at(-1)?.typ == EnumToken.WhitespaceTokenType) { + // left.pop(); + // } + arena.setLeft(t, ...left); + // @ts-ignore + arena.setRight(t, right.reduce((a, b) => { + typ = arena.getType(b); + return a == null + ? b + : typ == EnumToken.IdenTokenType || typ == EnumToken.StringTokenType ? b - : b.typ == EnumToken.IdenTokenType || b.typ == EnumToken.StringTokenType - ? b - : a; - }, null), - }); + : a; + }, null)); + const composeSelector = arena.allocate(); + arena.setType(composeSelector, EnumToken.ComposesSelectorNodeType); + // arena.setChildren(composeSelector, t, ...right); + arena.setLeft(t, ...left); + // @ts-ignore + arena.setRight(composeSelector, right.reduce((a, b) => { + typ = arena.getType(b); + return a == null + ? b + : typ == EnumToken.IdenTokenType || typ == EnumToken.StringTokenType + ? b + : a; + }, null)); + tokens.splice(k, l - k + 1, composeSelector); + // tokens.splice(k, l - k + 1, { + // typ: EnumToken.ComposesSelectorNodeType, + // l: left, + // r: right.reduce((a: Token | null, b: Token) => { + // return a == null + // ? b + // : b.typ == EnumToken.IdenTokenType || b.typ == EnumToken.StringTokenType + // ? b + // : a; + // }, null), + // }); i = k; continue; } } - else if (options?.parseColor && isColor(t)) { - parseColor(t); + else if (options?.parseColor && isColor(t, arena)) { + parseColor(t, arena); } } - if (t.typ == EnumToken.WhitespaceTokenType && + typ = arena.getType(t); + typ2 = arena.getType(tokens[i + 1]); + if (typ == EnumToken.WhitespaceTokenType && (i == 0 || i + 1 == tokens.length || - [ - EnumToken.CommaTokenType, - EnumToken.GteTokenType, - EnumToken.LteTokenType, - EnumToken.ColumnCombinatorTokenType, - ].includes(tokens[i + 1].typ) || - (i > 0 && trimWhiteSpace.includes(tokens[i - 1].typ)))) { + (EnumToken.CommaTokenType == typ2 || + EnumToken.GteTokenType == typ2 || + EnumToken.LteTokenType == typ2 || + EnumToken.ColumnCombinatorTokenType == typ2)) || + (i > 0 && trimWhiteSpace.includes(arena.getType(tokens[i - 1])))) { tokens.splice(i--, 1); continue; } - if (t.typ == EnumToken.ColonTokenType) { - const typ = tokens[i + 1]?.typ; - if (typ != null) { - if (typ == EnumToken.FunctionTokenType) { - tokens[i + 1].typ = EnumToken.PseudoClassFuncTokenType; + if (typ == EnumToken.ColonTokenType) { + // const typ2: EnumToken = tokens[i + 1]?.typ; + if (typ2 != null) { + if (typ2 == EnumToken.FunctionTokenType) { + typ2 = EnumToken.PseudoClassFuncTokenType; } - else if (typ == EnumToken.IdenTokenType) { - tokens[i + 1].val = ":" + tokens[i + 1].val; - tokens[i + 1].typ = EnumToken.PseudoClassTokenType; + else if (typ2 == EnumToken.IdenTokenType) { + arena.set(tokens[i + 1], EnumToken.PseudoClassTokenType, arena.internString(":" + arena.getValue(tokens[i + 1]))); + // (tokens[i + 1] as PseudoClassToken).val = ":" + (tokens[i + 1] as PseudoClassToken).val; + // tokens[i + 1].typ = EnumToken.PseudoClassTokenType; + typ2 = EnumToken.FunctionTokenType; } - if (typ == EnumToken.FunctionTokenType || typ == EnumToken.IdenTokenType) { + if (typ2 == EnumToken.FunctionTokenType || typ2 == EnumToken.IdenTokenType) { tokens.splice(i, 1); i--; } } } - if (t.typ === EnumToken.CommaTokenType) { - if (tokens[i + 1]?.typ === EnumToken.WhitespaceTokenType) { + if (typ === EnumToken.CommaTokenType) { + if (typ2 === EnumToken.WhitespaceTokenType) { tokens.splice(i + 1, 1); } - if (tokens[i - 1]?.typ === EnumToken.WhitespaceTokenType) { + if (arena.getType(tokens[i - 1]) === EnumToken.WhitespaceTokenType) { tokens.splice(i - 1, 1); i--; } @@ -3742,9 +3793,9 @@ function parseTokens(tokens, options, errors) { const node = stack.at(-1); errors?.push?.({ action: "drop", - message: `Unbalanced token. Expecting ${node.typ === EnumToken.AttrStartTokenType ? "']'" : ")"}'`, + message: `Unbalanced token. Expecting ${typ === EnumToken.AttrStartTokenType ? "']'" : ")"}'`, node, - location: options.source.getSourceLocation(node[LOCSTA]), + location: options.source.getSourceLocation(arena.getSpanStart(node)), }); } return tokens; diff --git a/dist/src/lib/parser/utils/at-rule-container.js b/dist/src/lib/parser/utils/at-rule-container.js index 039edb78b..864ef159c 100644 --- a/dist/src/lib/parser/utils/at-rule-container.js +++ b/dist/src/lib/parser/utils/at-rule-container.js @@ -6,7 +6,7 @@ License: MIT */ import { EnumToken } from '../../ast/types.js'; -import { tokensfuncDefMap, LOCSTA, mFGT, mFLT, LOCEND, LOCSRCID } from '../../syntax/constants.js'; +import { tokensfuncDefMap, mFGT, mFLT, LOCEND, LOCSTA, LOCSRCID } from '../../syntax/constants.js'; import { matchAllSyntaxes, createValidationContext, trimArray } from '../../validation/match.js'; import { ValidationSyntaxGroupEnum } from '../../validation/parser/typedef.js'; import { getSyntaxRule } from '../../validation/config.js'; @@ -14,19 +14,23 @@ import { equalsIgnoreCase } from './text.js'; function parseAtRuleContainerQueryList(stream, context, options = {}) { let matchCount = 0; + let typ; + const arena = options.arena; + arena.getName(context); const errors = []; const syntaxRules = getSyntaxRule(ValidationSyntaxGroupEnum.AtRules, "@" + context.nam); const syntax = syntaxRules?.getPreludeRules()?.slice?.(1); const parts = stream.reduce((acc, t) => { - if (t.typ === EnumToken.CommaTokenType && matchCount === 0) { + typ = arena.getType(t); + if (typ === EnumToken.CommaTokenType && matchCount === 0) { acc.push([]); } else { acc[acc.length - 1].push(t); - if (t.typ === EnumToken.StartParensTokenType || tokensfuncDefMap.has(t.typ)) { + if (typ === EnumToken.StartParensTokenType || tokensfuncDefMap.has(typ)) { matchCount++; } - else if (t.typ === EnumToken.EndParensTokenType) { + else if (typ === EnumToken.EndParensTokenType) { if (matchCount > 0) { matchCount--; } @@ -34,7 +38,7 @@ function parseAtRuleContainerQueryList(stream, context, options = {}) { } return acc; }, [[]]); - const result = matchAllSyntaxes(syntax, createValidationContext(stream), options); + const result = matchAllSyntaxes(syntax, createValidationContext(stream, options), options); if (!result.success) { for (const error of result.errors) { errors.push(error); @@ -73,7 +77,7 @@ function parseAtRuleContainerQueryList(stream, context, options = {}) { { action: "drop", node: stream[i], - location: options.source.getSourceLocation(stream[i]?.[LOCSTA]), + location: options.source.getSourceLocation(stream[i]), message: `expecting `, }, ], @@ -81,7 +85,8 @@ function parseAtRuleContainerQueryList(stream, context, options = {}) { } for (; i < stream.length; i++) { tokens.push(stream[i]); - if (stream[i].typ === EnumToken.WhitespaceTokenType || stream[i].typ === EnumToken.CommentTokenType) { + typ = arena.getType(stream[i]); + if (typ === EnumToken.WhitespaceTokenType || typ === EnumToken.CommentTokenType) { continue; } if (expectAndOr) { @@ -90,7 +95,7 @@ function parseAtRuleContainerQueryList(stream, context, options = {}) { // const val: string = (stream[i] as IdentToken).val.toLowerCase(); // valid = val === "and" || val === "or"; // } else { - valid = stream[i].typ !== EnumToken.CommaTokenType; + valid = typ !== EnumToken.CommaTokenType; // } if (!valid) { success = false; @@ -98,17 +103,17 @@ function parseAtRuleContainerQueryList(stream, context, options = {}) { action: "drop", node: stream[i], message: `expecting , or comma`, - location: options.source.getSourceLocation(stream[i]?.[LOCSTA]), + location: options.source.getSourceLocation(stream[i]), }); break; } } - if (stream[i].typ === EnumToken.StartParensTokenType || tokensfuncDefMap.has(stream[i].typ)) { + if (typ === EnumToken.StartParensTokenType || tokensfuncDefMap.has(typ)) { scopes.push((currentScope = new Set())); stack.push(stream[i]); continue; } - switch (stream[i].typ) { + switch (typ) { case EnumToken.ColonTokenType: case EnumToken.LtTokenType: case EnumToken.LteTokenType: @@ -119,7 +124,7 @@ function parseAtRuleContainerQueryList(stream, context, options = {}) { break; case EnumToken.IdenTokenType: { - const val = stream[i].val.toLowerCase(); + const val = arena.getValue(stream[i]); if (val === "not") { Object.assign(stream[i], { typ: EnumToken.NotTokenType, diff --git a/dist/src/lib/parser/utils/at-rule-generic.js b/dist/src/lib/parser/utils/at-rule-generic.js index cb4257746..e6fd09810 100644 --- a/dist/src/lib/parser/utils/at-rule-generic.js +++ b/dist/src/lib/parser/utils/at-rule-generic.js @@ -6,36 +6,38 @@ License: MIT */ import { EnumToken } from '../../ast/types.js'; -import { tokensfuncDefMap, LOCSTA } from '../../syntax/constants.js'; -import { equalsIgnoreCase } from './text.js'; +import { tokensfuncDefMap } from '../../syntax/constants.js'; function matchGenericSyntax(stream, options) { + const arena = options.arena; const stack = []; let i = 0; let success = true; let expectAndOr = false; let expectComma = false; + let typ; const errors = []; const scopes = [new Set()]; for (; i < stream.length; i++) { const token = stream[i]; - if (token.typ === EnumToken.WhitespaceTokenType || token.typ === EnumToken.CommentTokenType) { + typ = arena.getType(token); + if (typ === EnumToken.WhitespaceTokenType || typ === EnumToken.CommentTokenType) { continue; } - if (token.typ === EnumToken.StartParensTokenType || tokensfuncDefMap.has(token.typ)) { + if (typ === EnumToken.StartParensTokenType || tokensfuncDefMap.has(typ)) { stack.push(token); scopes.push(new Set()); expectAndOr = false; continue; } - if (token.typ === EnumToken.EndParensTokenType) { + if (typ === EnumToken.EndParensTokenType) { if (stack.length === 0 || - (stack.at(-1)?.typ !== EnumToken.StartParensTokenType && !tokensfuncDefMap.has(stack.at(-1)?.typ))) { + (arena.getType(stack.at(-1)) !== EnumToken.StartParensTokenType && !tokensfuncDefMap.has(arena.getType(stack.at(-1))))) { errors.push({ action: "drop", - message: `unexpected token ${EnumToken[token.typ]}`, + message: `unexpected token ${EnumToken[typ]}`, node: token, - location: options.source.getSourceLocation(token[LOCSTA]), + location: options.source.getSourceLocation(token), }); success = false; break; @@ -44,29 +46,30 @@ function matchGenericSyntax(stream, options) { scopes.pop(); continue; } - if (token.typ === EnumToken.IdenTokenType && equalsIgnoreCase("and", token.val)) { + if (typ === EnumToken.IdenTokenType && arena.equalsIgnoreCase(token, 'val', "and")) { if (!expectAndOr || scopes.at(-1)?.has(EnumToken.OrTokenType)) { errors.push({ action: "drop", - message: `unexpected token ${EnumToken[token.typ]}`, + message: `unexpected token ${EnumToken[typ]}`, node: token, - location: options.source.getSourceLocation(token[LOCSTA]), + location: options.source.getSourceLocation(token), }); success = false; break; } - Object.assign(token, { typ: EnumToken.AndTokenType }); + typ = EnumToken.AndTokenType; + arena.setType(token, typ); scopes.at(-1).add(EnumToken.AndTokenType); expectComma = false; continue; } - if (token.typ === EnumToken.IdenTokenType && equalsIgnoreCase("or", token.val)) { + if (typ === EnumToken.IdenTokenType && arena.equalsIgnoreCase(token, 'val', "or")) { if (!expectAndOr || scopes.at(-1)?.has(EnumToken.AndTokenType)) { errors.push({ action: "drop", - message: `unexpected token ${EnumToken[token.typ]}`, + message: `unexpected token ${EnumToken[typ]}`, node: token, - location: options.source.getSourceLocation(token[LOCSTA]), + location: options.source.getSourceLocation(token), }); success = false; break; @@ -76,13 +79,13 @@ function matchGenericSyntax(stream, options) { scopes.at(-1).add(EnumToken.OrTokenType); continue; } - if (token.typ === EnumToken.CommaTokenType) { + if (typ === EnumToken.CommaTokenType) { if (!expectComma) { errors.push({ action: "drop", - message: `unexpected token ${EnumToken[token.typ]}`, + message: `unexpected token ${EnumToken[typ]}`, node: token, - location: options.source.getSourceLocation(token[LOCSTA]), + location: options.source.getSourceLocation(token), }); success = false; break; @@ -93,16 +96,16 @@ function matchGenericSyntax(stream, options) { } expectAndOr = true; expectComma = true; - if (stack.length > 0 && stack.at(-1)?.typ === EnumToken.CommaTokenType) { + if (stack.length > 0 && arena.getType(stack.at(-1)) === EnumToken.CommaTokenType) { stack.pop(); } } if (stack.length > 0) { errors.push({ action: "drop", - message: `unexpected token ${EnumToken[stack.at(-1)?.typ]}`, + message: `unexpected token ${EnumToken[arena.getType(stack.at(-1))]}`, node: stack.at(-1), - location: options.source.getSourceLocation(stack.at(-1)?.[LOCSTA]), + location: options.source.getSourceLocation(stack.at(-1)), }); success = false; } diff --git a/dist/src/lib/printer/print.js b/dist/src/lib/printer/print.js new file mode 100644 index 000000000..f4022e87e --- /dev/null +++ b/dist/src/lib/printer/print.js @@ -0,0 +1,996 @@ +/* + CSS parser, minifier and validator for node and the browser + + Author(s): Thierry Bela + Version: 2.0.0-alpha.1 + License: MIT + */ +import { EnumToken, EnumAstNodeStatus, ColorType } from '../ast/types.js'; +import { toDegrees } from '../parser/utils/angle.js'; +import { equalsIgnoreCase } from '../parser/utils/text.js'; +import { getAngle, convertColor } from '../syntax/color/color.js'; +import { reduceHexValue } from '../syntax/color/hex.js'; +import { pseudoElements, anglePrecision, urlTokenMatcher, tokensfuncSet } from '../syntax/constants.js'; +import { minifyNumber, toPrecisionAngle, reducegradientBackgroundPosition, reduceConicColorStops, reduceColorStops, parseColor, toPrecisionValue } from '../syntax/syntax.js'; + +/** + * render ast token + * @param token + * @param options + * @param cache + * @param reducer + * @param errors + * @private + */ +function printValue(token, arena, options, cache = Object.create(null), reducer, errors) { + const typ = arena.getType(token); + // console.error(EnumToken[typ]); + if (typ === EnumToken.WhenElseFunctionTokenType && arena.equalsIgnoreCase(token, "val", "supports")) { + options = { ...options, minifyValues: false, minify: false, convertColor: false }; + reducer = null; + } + options.minifyValues ??= options.minify; + if (reducer == null) { + reducer = function (acc, curr) { + if (arena.getType(curr) == EnumToken.CommentTokenType && options.removeComments) { + if (!options.preserveLicense || !arena.startsWith(curr, "val", "/*!")) { + return acc; + } + return acc + arena.getValue(curr); + } + return acc + printValue(curr, arena, options, cache, reducer, errors); + }; + } + switch (typ) { + case EnumToken.UrlFunctionTokenDefType: + case EnumToken.FunctionTokenDefType: + case EnumToken.MathFunctionTokenDefType: + case EnumToken.ImageFunctionTokenDefType: + case EnumToken.ColorFunctionTokenDefType: + case EnumToken.TimingFunctionTokenDefType: + case EnumToken.WhenElseFunctionTokenDefType: + case EnumToken.SupportsFunctionTokenDefType: + case EnumToken.TimelineFunctionTokenDefType: + case EnumToken.GridTemplateFuncTokenDefType: + case EnumToken.TransformFunctionTokenDefType: + case EnumToken.ContainerFunctionTokenDefType: + case EnumToken.PseudoClassFunctionTokenDefType: + case EnumToken.GeneralEnclosedFunctionTokenDefType: + case EnumToken.CustomFunctionTokenDefType: + case EnumToken.WildCardFunctionTokenDefType: + return arena.getValue(token) + "("; + case EnumToken.ListTokenType: + return arena + .children(token) + .reduce((acc, curr) => acc + printValue(curr, arena, options, cache), ""); + case EnumToken.BinaryExpressionTokenType: + if (EnumToken.Mul === token.op || + EnumToken.Div === token.op) { + let result = ""; + if (token.l.typ == EnumToken.BinaryExpressionTokenType && + [EnumToken.Add, EnumToken.Sub].includes(token.l.op)) { + result = "(" + printValue(token.l, arena, options, cache) + ")"; + } + else { + result = printValue(token.l, options, cache); + } + result += token.op == EnumToken.Mul ? "*" : "/"; + if (token.r.typ == EnumToken.BinaryExpressionTokenType && + [EnumToken.Add, EnumToken.Sub].includes(token.r.op)) { + result += "(" + printValue(token.r, options, cache) + ")"; + } + else { + result += printValue(token.r, options, cache); + } + return result; + } + return (printValue(token.l, options, cache) + + (token.op == EnumToken.Add ? " + " : " - ") + + printValue(token.r, options, cache)); + case EnumToken.FractionTokenType: + const fraction = printValue(token.l) + "/" + printValue(token.r); + if (+token.r.val != 0) { + const value = minifyNumber(+token.l.val / +token.r.val); + if (value.length <= fraction.length) { + return value; + } + } + return fraction; + case EnumToken.Add: + return " + "; + case EnumToken.Sub: + return " - "; + case EnumToken.UniversalSelectorTokenType: + case EnumToken.Star: + case EnumToken.Mul: + return "*"; + case EnumToken.Div: + return "/"; + case EnumToken.WrappedValuesTokenType: + return ("{" + + token.chi.reduce((acc, curr) => acc + printValue(curr, options, cache), "") + + "}"); + case EnumToken.ColorTokenType: + if (token.kin == ColorType.LIGHT_DARK || + ("chi" in token && options.convertColor === false && token.chi.length == 2)) { + return (token.val + + "(" + + token.chi.reduce((acc, curr) => { + if (curr.typ === EnumToken.IdenTokenType) { + parseColor(curr); + } + return acc + printValue(curr, options, cache); + }, "") + + ")"); + } + if (options.convertColor !== false) { + const value = convertColor(token, typeof options.convertColor === "boolean" + ? ColorType.HEX + : (ColorType[ColorType[options.convertColor ?? "HEX"] + ?.toUpperCase?.() + .replaceAll?.("-", "_")] ?? ColorType.HEX)); + if (value != null) { + token = value; + } + if (token.kin == ColorType.HEX || token.kin == ColorType.LIT) { + return reduceHexValue(token.val); + } + } + if (token.kin === ColorType.SYS || + token.kin === ColorType.DPSYS || + token.kin === ColorType.NON_STD) { + return token.val; + } + if (token.kin == ColorType.HEX || token.kin == ColorType.LIT) { + return token.val; + } + if (Array.isArray(token.chi)) { + const fnName = token.val; + const isLegacy = ["rgb", "rgba", "hsl", "hsla"].includes(token.val); + const hasAlpha = [ + "rgb", + "rgba", + "hsl", + "hsla", + "hwb", + "oklab", + "oklch", + "lab", + "lch", + ].includes(fnName); + const useAlpha = (hasAlpha && token.chi.length == (isLegacy ? 7 : 4)) || + ("color" == token.val && token.chi.length == 5); + return ((hasAlpha && token.val.endsWith("a") ? fnName.slice(0, -1) : fnName) + + "(" + + token + .chi.reduce((acc, curr, index, array) => { + if (/[,/]\s*$/.test(acc)) { + if (curr.typ == EnumToken.WhitespaceTokenType) { + return acc.trimEnd(); + } + return acc.trimStart() + printValue(curr, options, cache); + } + if (isLegacy && curr.typ == EnumToken.CommaTokenType) { + return acc.trimEnd() + " "; + } + if (curr.typ == EnumToken.WhitespaceTokenType) { + return acc.trimEnd() + " "; + } + if (curr.typ == EnumToken.CommaTokenType || + (curr.typ == EnumToken.LiteralTokenType && curr.val == "/")) { + return acc.trimEnd() + (curr.typ == EnumToken.CommaTokenType ? "," : "/"); + } + return (acc.trimEnd() + + (useAlpha && index == array.length - 1 ? "/" : " ") + + printValue(curr, options, cache)); + }, "") + .trimStart() + + ")"); + } + case EnumToken.UrlFunctionTokenType: + if (options.minifyValues && token.typ === EnumToken.UrlFunctionTokenType) { + if (token.chi[0]?.typ === EnumToken.BadUrlTokenType) { + return "url()"; + } + for (const child of token.chi) { + if (child.typ === EnumToken.StringTokenType) { + if (child.val.slice(1, 5) !== "data:" && + urlTokenMatcher.test(child.val)) { + Object.assign(child, { + typ: EnumToken.UrlTokenTokenType, + val: child.val.slice(1, -1), + }); + } + break; + } + } + } + case EnumToken.ParensTokenType: + case EnumToken.FunctionTokenType: + case EnumToken.MathFunctionTokenType: + case EnumToken.ImageFunctionTokenType: + if (options.minifyValues && token.typ === EnumToken.ImageFunctionTokenType) { + const slice = token.chi.slice(); + switch (token.val) { + case "linear-gradient": + case "repeating-linear-gradient": + { + let i; + let k; + let j; + let key; + for (i = 0; i < slice.length; i++) { + if (slice[i].typ == EnumToken.ColorTokenType || + slice[i].typ === EnumToken.CommaTokenType) { + break; + } + if (slice[i].typ == EnumToken.IdenTokenType && + equalsIgnoreCase(slice[i].val, "to")) { + k = i + 1; + while (k < slice.length && + (slice[k].typ === EnumToken.WhitespaceTokenType || + slice[k].typ === EnumToken.CommentTokenType)) { + k++; + } + if (k < slice.length && slice[k].typ === EnumToken.IdenTokenType) { + // convert keyword to angle + key = slice[k].val.toLowerCase(); + switch (key) { + case "right": + slice.splice(i, k - i + 1, { + typ: EnumToken.AngleTokenType, + val: 90, + }); + break; + case "left": + slice.splice(i, k - i + 1, { + typ: EnumToken.AngleTokenType, + val: 270, + }); + break; + case "top": + case "bottom": + j = k + 1; + while (j < slice.length && + (slice[j].typ === EnumToken.WhitespaceTokenType || + slice[j].typ === EnumToken.CommentTokenType)) { + j++; + } + if (j < slice.length && slice[j].typ !== EnumToken.CommaTokenType) { + i = j - 1; + break; + } + // https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/gradient/linear-gradient#:~:text=If%20unspecified%2C%20it%20defaults%20to%20to%20bottom + if (key === "bottom") { + // to bottom -> 180deg is the default angle and will be removed + while (k < slice.length && + slice[k].typ != EnumToken.CommaTokenType) { + k++; + } + slice.splice(i, k - i + 1); + i--; + continue; + } + slice.splice(i, k - i + 1, { + typ: EnumToken.AngleTokenType, + val: key === "top" ? 0 : 180, + }); + i--; + break; + } + } + } + else if (slice[i].typ == EnumToken.AngleTokenType) { + if ((180 - toDegrees(slice[i]).val) % 360 === 0) { + k = i + 1; + // while (k < slice.length && slice[k].typ !== EnumToken.CommaTokenType) { + // k++; + // } + slice.splice(i, k - i + 1); + } + } + } + if (slice[i]?.typ === EnumToken.CommaTokenType) { + i++; + // while ( + // i < slice.length && + // (slice[i].typ === EnumToken.WhitespaceTokenType || + // slice[i].typ === EnumToken.CommentTokenType) + // ) { + // i++; + // } + } + if (slice[i]?.typ === EnumToken.ColorTokenType) { + for (const token of reduceColorStops(slice.splice(i, slice.length - i))) { + slice.push(token); + } + } + } + break; + case "radial-gradient": + case "repeating-radial-gradient": + { + let i = 0; + // while ( + // i < slice.length && + // (slice[i].typ === EnumToken.WhitespaceTokenType || + // slice[i].typ === EnumToken.CommentTokenType) + // ) { + // i++; + // } + const positions = []; + const form = []; + const size = []; + const colorSpaceDef = []; + if (slice[i]?.typ === EnumToken.IdenTokenType) { + if (equalsIgnoreCase(slice[i].val, "farthest-corner") || + equalsIgnoreCase(slice[i].val, "ellipse")) { + i++; + } + else if (equalsIgnoreCase(slice[i].val, "closest-side") || + equalsIgnoreCase(slice[i].val, "closest-corner") || + equalsIgnoreCase(slice[i].val, "farthest-side")) { + size.push(slice[i++]); + } + else if (equalsIgnoreCase(slice[i].val, "circle")) { + form.push(slice[i++]); + } + while (slice[i]?.typ === EnumToken.WhitespaceTokenType || + slice[i]?.typ === EnumToken.CommentTokenType) { + i++; + } + } + if (slice[i]?.typ === EnumToken.IdenTokenType) { + if (equalsIgnoreCase(slice[i].val, "farthest-corner") || + equalsIgnoreCase(slice[i].val, "ellipse")) { + i++; + } + else if (equalsIgnoreCase(slice[i].val, "closest-side") || + equalsIgnoreCase(slice[i].val, "closest-corner") || + equalsIgnoreCase(slice[i].val, "farthest-side")) { + size.push(slice[i++]); + } + else if (equalsIgnoreCase(slice[i].val, "circle")) { + form.push(slice[i++]); + } + while (slice[i]?.typ === EnumToken.WhitespaceTokenType || + slice[i]?.typ === EnumToken.CommentTokenType) { + i++; + } + } + if (slice[i]?.typ === EnumToken.IdenTokenType && + equalsIgnoreCase(slice[i].val, "at")) { + i++; + } + while (slice[i]?.typ === EnumToken.WhitespaceTokenType || + slice[i]?.typ === EnumToken.CommentTokenType) { + i++; + } + if (slice[i]?.typ === EnumToken.PercentageTokenType || + (slice[i]?.typ === EnumToken.NumberTokenType && 0 === slice[i].val) || + (slice[i]?.typ === EnumToken.IdenTokenType && + (equalsIgnoreCase(slice[i].val, "center") || + equalsIgnoreCase(slice[i].val, "top") || + equalsIgnoreCase(slice[i].val, "bottom") || + equalsIgnoreCase(slice[i].val, "left") || + equalsIgnoreCase(slice[i].val, "right")))) { + do { + if (slice[i]?.typ === EnumToken.IdenTokenType && + "in" === slice[i].val) { + break; + } + positions.push(slice[i]); + i++; + } while (slice[i]?.typ !== EnumToken.CommaTokenType); + } + while (slice[i]?.typ !== EnumToken.CommaTokenType) { + colorSpaceDef.push(slice[i++]); + } + if (slice[i]?.typ === EnumToken.CommaTokenType) { + i++; + } + if (positions.length > 0) { + let k; + let position1 = ""; + let position2 = ""; + for (k = 0; k < positions.length; k++) { + if (positions[k].typ === EnumToken.IdenTokenType) { + if (position1.length === 0) { + position1 = positions[k].val; + } + else { + position2 = positions[k].val; + } + } + else if (positions[k].typ === EnumToken.PercentageTokenType || + positions[k].typ === EnumToken.NumberTokenType) { + if (position1.length === 0) { + position1 = positions[k].val + "%"; + } + else { + position2 = positions[k].val + "%"; + } + } + if (position2.length > 0) { + break; + } + } + if (position1.length > 0) { + let key = `${position1} ${position2}`.trim(); + if (key === "50% 50%" || + key === "center center" || + key === "50%" || + key === "center") { + positions.length = 0; + } + else if (key === "0% 0%" || + key === "0 0" || + key === "top left" || + key === "left top") { + positions.splice(0, positions.length, { + typ: EnumToken.PercentageTokenType, + val: 0, + }); + } + else if (key === "50% 0%" || + key === "50% 0" || + key === "center top" || + key === "top center") { + positions.splice(0, positions.length, { + typ: EnumToken.IdenTokenType, + val: "top", + }); + } + else if (key === "top right" || key === "right top") { + positions.splice(0, positions.length, { + typ: EnumToken.PercentageTokenType, + val: 100, + }, { typ: EnumToken.WhitespaceTokenType }, { typ: EnumToken.PercentageTokenType, val: 0 }); + } + else if (key === "0 50%" || + key === "0% 50%" || + key === "left center" || + key === "center left") { + positions.splice(0, positions.length, { + typ: EnumToken.IdenTokenType, + val: "left", + }); + } + else if (key === "100% 50%" || key === "right center" || key === "center right") { + positions.splice(0, positions.length, { + typ: EnumToken.IdenTokenType, + val: "right", + }); + } + else if (key === "0 100%" || + key === "0% 100%" || + key === "left bottom" || + key === "bottom left") { + positions.splice(0, positions.length, { + typ: EnumToken.IdenTokenType, + val: "bottom", + }); + } + else if (key === "50% 100%" || + key === "center bottom" || + key === "bottom center") { + positions.splice(0, positions.length, { + typ: EnumToken.IdenTokenType, + val: "bottom", + }); + } + else if (key === "100% 100%" || + key === "right bottom" || + key === "bottom right") { + positions.splice(0, positions.length, { + typ: EnumToken.PercentageTokenType, + val: 100, + }); + } + else if (position1 === position2) { + positions.splice(0, positions.length, positions[k]); + } + } + } + const result = []; + if (form.length > 0) { + for (const token of form) { + result.push(token); + } + } + if (size.length > 0) { + if (result.length > 0) { + result.push({ typ: EnumToken.WhitespaceTokenType }); + } + for (const token of size) { + result.push(token); + } + } + if (positions.length > 0) { + if (result.length > 0) { + result.push({ typ: EnumToken.WhitespaceTokenType }); + } + result.push({ typ: EnumToken.IdenTokenType, val: "at" }, { typ: EnumToken.WhitespaceTokenType }); + for (const token of positions) { + result.push(token); + } + } + if (colorSpaceDef.length > 0) { + if (result.length > 0) { + result.push({ typ: EnumToken.WhitespaceTokenType }); + } + for (const token of colorSpaceDef) { + result.push(token); + } + } + if (result.length > 0) { + result.push({ typ: EnumToken.CommaTokenType }); + } + for (const token of reduceColorStops(slice.slice(i))) { + result.push(token); + } + slice.length = 0; + for (const token of result) { + slice.push(token); + } + } + break; + case "conic-gradient": + case "repeating-conic-gradient": + { + let i = 0; + const angles = []; + const positions = []; + const colorSpaceDef = []; + // while ( + // i < slice.length && + // (slice[i].typ === EnumToken.WhitespaceTokenType || + // slice[i].typ === EnumToken.CommentTokenType) + // ) { + // i++; + // } + if (slice[i]?.typ === EnumToken.IdenTokenType && + equalsIgnoreCase(slice[i].val, "from")) { + angles.push(slice[i++]); + while (i < slice.length && + (slice[i].typ === EnumToken.WhitespaceTokenType || + slice[i].typ === EnumToken.CommentTokenType)) { + angles.push(slice[i++]); + } + if ((slice[i]?.typ === EnumToken.NumberTokenType || + slice[i]?.typ === EnumToken.AngleTokenType) && + 0 === toDegrees(slice[i]).val) { + angles.length = 0; + i++; + } + else if (slice[i]?.typ !== EnumToken.CommaTokenType && + slice[i].typ != EnumToken.IdenTokenType) { + angles.push(slice[i++]); + } + while (i < slice.length && + (slice[i].typ === EnumToken.WhitespaceTokenType || + slice[i].typ === EnumToken.CommentTokenType)) { + i++; + } + } + if (slice[i]?.typ === EnumToken.IdenTokenType && + equalsIgnoreCase(slice[i].val, "at")) { + i++; + while (i < slice.length && + (slice[i].typ === EnumToken.WhitespaceTokenType || + slice[i].typ === EnumToken.CommentTokenType)) { + i++; + } + let position1 = ""; + let position2 = ""; + if (slice[i]?.typ === EnumToken.IdenTokenType && + !equalsIgnoreCase("in", slice[i].val)) { + position1 = slice[i].val; + positions.push(slice[i++]); + } + else if (slice[i]?.typ === EnumToken.PercentageTokenType || + slice[i]?.typ === EnumToken.NumberTokenType) { + position1 = slice[i].val + "%"; + positions.push(slice[i++]); + } + while (i < slice.length && + (slice[i].typ === EnumToken.WhitespaceTokenType || + slice[i].typ === EnumToken.CommentTokenType)) { + positions.push(slice[i++]); + } + if (slice[i]?.typ === EnumToken.IdenTokenType && + !equalsIgnoreCase("in", slice[i].val)) { + position2 = slice[i].val; + positions.push(slice[i++]); + } + else if (slice[i]?.typ === EnumToken.PercentageTokenType || + slice[i]?.typ === EnumToken.NumberTokenType) { + position2 = slice[i].val + "%"; + positions.push(slice[i++]); + } + while (i < slice.length && + (slice[i].typ === EnumToken.WhitespaceTokenType || + slice[i].typ === EnumToken.CommentTokenType)) { + i++; + } + if (position1.length > 0) { + reducegradientBackgroundPosition(positions, `${position1} ${position2}`.trim()); + } + } + while (i < slice.length && + (slice[i].typ === EnumToken.WhitespaceTokenType || + slice[i].typ === EnumToken.CommentTokenType)) { + i++; + } + if (slice[i]?.typ === EnumToken.IdenTokenType && + equalsIgnoreCase("in", slice[i].val)) { + while (i < slice.length && slice[i].typ !== EnumToken.CommaTokenType) { + colorSpaceDef.push(slice[i++]); + } + } + if (slice[i]?.typ === EnumToken.CommaTokenType) { + i++; + } + const result = []; + if (positions.length > 0) { + if (positions.length > 0) { + if (angles.length > 0) { + angles.push({ typ: EnumToken.WhitespaceTokenType }); + } + angles.push({ typ: EnumToken.IdenTokenType, val: "at" }, { typ: EnumToken.WhitespaceTokenType }); + for (const position of positions) { + angles.push(position); + } + } + } + if (angles.length > 0) { + for (const angle of angles) { + result.push(angle); + } + result.push({ typ: EnumToken.CommaTokenType }); + } + if (colorSpaceDef.length > 0) { + if (colorSpaceDef.length > 0) { + if (result.length > 0) { + result.push({ typ: EnumToken.WhitespaceTokenType }); + } + for (const token of colorSpaceDef) { + result.push(token); + } + } + result.push({ typ: EnumToken.CommaTokenType }); + } + for (const token of reduceConicColorStops(slice.slice(i))) { + result.push(token); + } + slice.length = 0; + for (let j = 0; j < result.length; j++) { + slice.push(result[j]); + } + } + break; + } + return token.val + "(" + slice.reduce(reducer, "") + ")"; + } + case EnumToken.TimingFunctionTokenType: + case EnumToken.PseudoClassFuncTokenType: + case EnumToken.WhenElseFunctionTokenType: + case EnumToken.TimelineFunctionTokenType: + case EnumToken.GridTemplateFuncTokenType: + case EnumToken.SupportsFunctionTokenType: + case EnumToken.ContainerFunctionTokenType: + case EnumToken.TransformFunctionTokenType: + case EnumToken.GeneralEnclosedFunctionTokenType: + case EnumToken.CustomFunctionTokenType: + case EnumToken.WildCardFunctionTokenType: + if (token.typ == EnumToken.MathFunctionTokenType && + token.chi.length == 1 && + ![EnumToken.BinaryExpressionTokenType, EnumToken.FractionTokenType, EnumToken.IdenTokenType].includes(token.chi[0].typ) && + // @ts-ignore + token.chi[0].val + ?.typ != EnumToken.FractionTokenType) { + return (token.val + + "(" + + token.chi.reduce((acc, curr) => acc + + printValue(curr, token.typ == EnumToken.FunctionTokenType ? { minify: false } : options, cache, reducer), "") + + ")"); + } + return (arena.getValue(token) ?? "") + "(" + arena.children(token).reduce(reducer, "") + ")"; + case EnumToken.BlockStartTokenType: + return "{"; + case EnumToken.BlockEndTokenType: + return "}"; + case EnumToken.StartParensTokenType: + return "("; + case EnumToken.DelimTokenType: + case EnumToken.EqualMatchTokenType: + return "="; + case EnumToken.IncludeMatchTokenType: + return "~="; + case EnumToken.DashMatchTokenType: + return "|="; + case EnumToken.StartMatchTokenType: + return "^="; + case EnumToken.EndMatchTokenType: + return "$="; + case EnumToken.ContainMatchTokenType: + return "*="; + case EnumToken.LtTokenType: + return "<"; + case EnumToken.LteTokenType: + return "<="; + case EnumToken.Tilda: + case EnumToken.SubsequentSiblingCombinatorTokenType: + return "~"; + case EnumToken.Plus: + case EnumToken.NextSiblingCombinatorTokenType: + return "+"; + case EnumToken.GtTokenType: + case EnumToken.ChildCombinatorTokenType: + return ">"; + case EnumToken.GteTokenType: + return ">="; + case EnumToken.ColumnCombinatorTokenType: + return "||"; + case EnumToken.EndParensTokenType: + return ")"; + case EnumToken.AttrStartTokenType: + return "["; + case EnumToken.AttrEndTokenType: + return "]"; + case EnumToken.DescendantCombinatorTokenType: + case EnumToken.WhitespaceTokenType: + return " "; + case EnumToken.ColonTokenType: + return ":"; + case EnumToken.DoubleColonTokenType: + return "::"; + case EnumToken.SemiColonTokenType: + return ";"; + case EnumToken.CommaTokenType: + return ","; + case EnumToken.ImportantTokenType: + return "!important"; + case EnumToken.Pipe: + return "|"; + case EnumToken.AttrTokenType: + case EnumToken.IdenListTokenType: + return "[" + arena.children(token).reduce(reducer, "") + "]"; + case EnumToken.TimeTokenType: + case EnumToken.AngleTokenType: + case EnumToken.LengthTokenType: + case EnumToken.DimensionTokenType: + case EnumToken.FrequencyTokenType: + case EnumToken.ResolutionTokenType: + let val = arena.hasFlags(token, EnumAstNodeStatus.HasFractionValue) ? + printValue(arena.getNumericValue(token), arena, options, cache) + : minifyNumber(arena.getNumericValue(token)); + let unit = arena.getUnit(token); + if (typ == EnumToken.AngleTokenType && !val.includes("/")) { + const angle = getAngle(token, arena); + let v; + let value = val + unit; + for (const u of ["deg", "turn", "rad", "grad"]) { + if (unit == u) { + continue; + } + switch (u) { + case "deg": + v = minifyNumber(toPrecisionAngle(angle * 360, anglePrecision, false).toFixed(anglePrecision)); + if (v.length + 3 < value.length) { + val = v; + unit = u; + value = v + u; + } + break; + case "turn": + v = minifyNumber(toPrecisionAngle(angle, anglePrecision, false).toFixed(anglePrecision)); + if (v.length + 4 < value.length) { + val = v; + unit = u; + value = v + u; + } + break; + case "rad": + v = minifyNumber(toPrecisionAngle(angle * (2 * Math.PI), anglePrecision, false).toFixed(anglePrecision)); + if (v.length + 3 < value.length) { + val = v; + unit = u; + value = v + u; + } + break; + case "grad": + v = minifyNumber(toPrecisionAngle(angle * 400, anglePrecision, false).toFixed(anglePrecision)); + if (v.length + 4 < value.length) { + val = v; + unit = u; + value = v + u; + } + break; + } + } + } + if (val === "0") { + if (typ == EnumToken.TimeTokenType) { + return "0s"; + } + if (typ == EnumToken.FrequencyTokenType) { + return "0Hz"; + } + // @ts-ignore + if (typ == EnumToken.ResolutionTokenType) { + return "0x"; + } + return "0"; + } + if (typ == EnumToken.TimeTokenType) { + if (unit == "ms") { + // @ts-ignore + const v = minifyNumber(val / 1000); + if (v.length + 1 <= val.length) { + return v + "s"; + } + return val + "ms"; + } + return val + "s"; + } + if (typ == EnumToken.ResolutionTokenType && unit == "dppx") { + unit = "x"; + } + return val.includes("/") ? val.replace("/", unit + "/") : minifyNumber(toPrecisionValue(val)) + unit; + case EnumToken.FlexTokenType: + case EnumToken.PercentageTokenType: + const uni = typ == EnumToken.PercentageTokenType ? "%" : "fr"; + const perc = arena.hasFlags(token, EnumAstNodeStatus.HasFractionValue) + ? printValue(arena.getNumericValue(token), arena, options, cache) + : minifyNumber(arena.getNumericValue(token)); + return options.minifyValues && perc == "0" + ? "0" + : perc.includes("/") + ? perc.replace("/", uni + "/") + : perc + uni; + case EnumToken.NumberTokenType: + return arena.hasFlags(token, EnumAstNodeStatus.HasFractionValue) + ? printValue(arena.getNumericValue(token), arena, options, cache) + : minifyNumber(arena.getNumericValue(token)); + case EnumToken.InfinityTokenType: + return "1/0"; + case EnumToken.NegativeInfinityTokenType: + return "-1/0"; + case EnumToken.NaNTokenType: + return "NaN"; + case EnumToken.AtRuleTokenType: + return "@" + arena.getName(token); + case EnumToken.CommentTokenType: + case EnumToken.CDOCOMMNodeType: + if (options.removeComments && (!options.preserveLicense || !arena.startsWith(token, "val", "/*!"))) { + return ""; + } + case EnumToken.PseudoClassTokenType: + case EnumToken.PseudoElementTokenType: + // https://www.w3.org/TR/selectors-4/#single-colon-pseudos + if (typ == EnumToken.PseudoElementTokenType) { + let val = arena.getValue(token); + let slice = val.slice(1); + if (pseudoElements.includes(slice)) { + return slice; + } + } + case EnumToken.UrlTokenTokenType: + case EnumToken.HashTokenType: + case EnumToken.IdenTokenType: + case EnumToken.StringTokenType: + case EnumToken.LiteralTokenType: + case EnumToken.DashedIdenTokenType: + case EnumToken.PseudoPageTokenType: + case EnumToken.ClassSelectorTokenType: + return arena.getValue(token); + case EnumToken.NestingSelectorTokenType: + return "&"; + case EnumToken.InvalidAttrTokenType: + return ("[" + + arena.children(token).reduce((acc, curr) => acc + printValue(curr, arena, options, cache), "")); + case EnumToken.InvalidClassSelectorTokenType: + return arena.getValue(token); + case EnumToken.SupportsQueryUnaryConditionTokenType: + case EnumToken.WhenElseUnaryConditionTokenType: + return (printValue(arena.getLeft(token), arena, options, cache, reducer, errors) + + " " + + printValue(arena.getRight(token), arena, options, cache, reducer, errors)); + case EnumToken.SupportsQueryConditionTokenType: + case EnumToken.WhenElseQueryConditionTokenType: + return (printValue(arena.getLeft(token), arena, options, cache, reducer, errors) + + " " + + printValue(arena.getOp(token), arena, options, cache, reducer, errors) + + " " + + printValue(arena.getRight(token), arena, options, cache, reducer, errors)); + case EnumToken.IfConditionTokenType: + return token.l.length == 0 + ? "" + : token.l.reduce((acc, curr) => acc + printValue(curr, options, cache, reducer, errors), "") + + ":" + + (options.indent ?? "") + + token.r.reduce((acc, curr) => acc + printValue(curr, options, cache, reducer, errors), ""); + case EnumToken.IfElseConditionTokenType: + return (printValue(token.l, options, cache, reducer, errors) + + (options.indent ?? "") + + printValue(token.r, options, cache, reducer, errors)); + case EnumToken.DeclarationNodeType: + return (arena.getName(token) + + ":" + + (options.minifyValues ? filterValues(arena.children(token), arena) : arena.children(token)).reduce((acc, curr) => acc + printValue(curr, arena, options, cache, reducer, errors), "")); + case EnumToken.MediaQueryUnaryFeatureTokenType: + return (printValue(arena.getLeft(token), arena, options, cache, reducer, errors) + + " " + + printValue(arena.getRight(token), arena, options, cache, reducer, errors)); + case EnumToken.MediaQueryConditionTokenType: { + const indent = token.op.typ == EnumToken.LtTokenType || + token.op.typ == EnumToken.GtTokenType || + token.op.typ == EnumToken.ColonTokenType || + token.op.typ == EnumToken.DelimTokenType || + token.op.typ == EnumToken.LteTokenType || + token.op.typ == EnumToken.GteTokenType + ? "" + : " "; + return (token.l.reduce((acc, curr) => acc + printValue(curr, options, cache, reducer, errors), "") + + indent + + printValue(token.op, options, cache, reducer, errors) + + indent + + token.r.reduce((acc, curr) => acc + printValue(curr, options, cache, reducer, errors), "")); + } + case EnumToken.MediaRangeQueryTokenType: + return (token.l.reduce((acc, curr) => acc + printValue(curr, options, cache, reducer, errors), "") + + printValue(token.op1, options, cache, reducer, errors) + + token.val.reduce((acc, curr) => acc + printValue(curr, options, cache, reducer, errors), "") + + printValue(token.op2, options, cache, reducer, errors) + + token.r.reduce((acc, curr) => acc + printValue(curr, options, cache, reducer, errors), "")); + case EnumToken.MediaFeatureTokenType: + return arena.getValue(token); + case EnumToken.NotTokenType: + return "not"; + case EnumToken.OnlyTokenType: + return "only"; + case EnumToken.AndTokenType: + return "and"; + case EnumToken.OrTokenType: + return "or"; + case EnumToken.InvalidMediaQueryTokenType: + case EnumToken.InvalidCommentTokenType: + case EnumToken.BadCommentTokenType: + case EnumToken.BadCdoTokenType: + case EnumToken.BadStringTokenType: + case EnumToken.BadUrlTokenType: + case EnumToken.EOFTokenType: + return ""; + default: + console.debug({ token }); + throw new Error(`Unsupported token type for ${EnumToken[typ]}`); + } + // errors?.push({ action: "ignore", message: `render: unexpected token ${JSON.stringify(token, null, 1)}` }); + // return ""; +} +/** + * Remove whitespace tokens that are not needed + * @param values + * + * @internal + */ +function filterValues(values, arena) { + let i = 0; + let typ; + for (; i < values.length; i++) { + typ = arena.getType(values[i]); + if (typ == EnumToken.ImportantTokenType && arena.getType(values[i - 1]) === EnumToken.WhitespaceTokenType) { + values.splice(i - 1, 1); + } + else if (tokensfuncSet.has(typ) && + arena.hasFlags(values[i], EnumAstNodeStatus.HasChildren) && + typ != EnumToken.WildCardFunctionTokenType && + arena.getType(values[i + 1]) == EnumToken.WhitespaceTokenType) { + values.splice(i + 1, 1); + } + } + return values; +} + +export { filterValues, printValue }; diff --git a/dist/src/lib/validation/match.js b/dist/src/lib/validation/match.js index c6804f3ca..4834a5e41 100644 --- a/dist/src/lib/validation/match.js +++ b/dist/src/lib/validation/match.js @@ -158,10 +158,10 @@ function isMFValue(featureName, tokens, isMFRange) { */ function createValidationContext(tokens, options) { const arena = options.arena; - tokens = trimArray(tokens.filter((t) => arena.getType(t) !== EnumToken.CommentTokenType)); + tokens = trimArray(tokens.filter((t) => arena.getType(t) !== EnumToken.CommentTokenType), options); if (arena.getType(tokens.at(-1)) === EnumToken.ImportantTokenType) { tokens.pop(); - trimArray(tokens); + trimArray(tokens, options); } const token = { tokens, @@ -175,10 +175,12 @@ function createValidationContext(tokens, options) { peek(offset = 0) { let index = this.index; let token = this.tokens[++index]; + let typ = arena.getType(this.tokens[++index]); while (offset >= 0 && index < this.tokens.length) { - while (token?.typ == EnumToken.WhitespaceTokenType || - token?.typ == EnumToken.CommentTokenType || - token?.typ == EnumToken.CDOCOMMTokenType) { + typ = arena.getType(token); + while (typ == EnumToken.WhitespaceTokenType || + typ == EnumToken.CommentTokenType || + typ == EnumToken.CDOCOMMTokenType) { token = this.tokens[++index]; } if (token == null || offset === 0) { From c9cace17ad2ed2c0e9361d9473f45f9bf5e2b6c3 Mon Sep 17 00:00:00 2001 From: Thierry Bela Nanga Date: Mon, 26 Oct 2026 19:28:13 -0400 Subject: [PATCH 09/24] work in progress --- src/lib/ast/expand.ts | 14 ++-- src/lib/ast/features/prefix.ts | 22 +++--- src/lib/ast/helper/minify.ts | 119 +++++++++++++++++++++++++++++++++ src/lib/parser/parse.ts | 3 - src/lib/validation/match.ts | 10 +-- 5 files changed, 138 insertions(+), 30 deletions(-) create mode 100644 src/lib/ast/helper/minify.ts diff --git a/src/lib/ast/expand.ts b/src/lib/ast/expand.ts index 50c8e0c99..cbbea244b 100644 --- a/src/lib/ast/expand.ts +++ b/src/lib/ast/expand.ts @@ -1,10 +1,10 @@ -import {splitRule} from "./minify.ts"; -import {combinators, PARENT, RAW, STATE} from "../syntax/constants.ts"; -import {parseString} from "../parser/parse.ts"; -import {walkValues} from "./walk.ts"; -import type {AstAtRule, AstNode, AstRule, AstStyleSheet, LiteralToken, Token} from "../../@types/index.d.ts"; -import {EnumAstNodeStatus, EnumToken} from "./types.ts"; -import {cloneNode} from "./clone.ts"; +import { splitRule } from "./minify.ts"; +import { combinators, PARENT, RAW, STATE } from "../syntax/constants.ts"; +import { parseString } from "../parser/parse.ts"; +import { walkValues } from "./walk.ts"; +import type { AstAtRule, AstNode, AstRule, AstStyleSheet, LiteralToken, Token } from "../../@types/index.d.ts"; +import { EnumAstNodeStatus, EnumToken } from "./types.ts"; +import { cloneNode } from "./clone.ts"; /** * expand css nesting ast nodes diff --git a/src/lib/ast/features/prefix.ts b/src/lib/ast/features/prefix.ts index f695af0de..9a2bce3be 100644 --- a/src/lib/ast/features/prefix.ts +++ b/src/lib/ast/features/prefix.ts @@ -14,17 +14,17 @@ import type { PseudoClassToken, Token, } from "../../../@types/index.d.ts"; -import {walkValues} from "../walk.ts"; -import {pseudoAliasMap} from "../../syntax/syntax.ts"; -import {splitRule} from "../minify.ts"; -import type {ValidationConfiguration} from "../../../@types/validation.d.ts"; -import {funcLike, regMatchLinearGradient, regMatchRadialGradient, STATE, TOKENS} from "../../syntax/constants.ts"; -import {FeatureWalkMode} from "./type.ts"; -import {ValidationSyntaxGroupEnum} from "../../validation/parser/typedef.ts"; -import {getSyntaxConfig} from "../../validation/config.ts"; -import {splitTokenList} from "../../validation/utils/list.ts"; -import {equalsIgnoreCase} from "../../parser/utils/text.ts"; -import {toDegrees} from "../../parser/utils/angle.ts"; +import { walkValues } from "../walk.ts"; +import { pseudoAliasMap } from "../../syntax/syntax.ts"; +import { splitRule } from "../minify.ts"; +import type { ValidationConfiguration } from "../../../@types/validation.d.ts"; +import { funcLike, regMatchLinearGradient, regMatchRadialGradient, STATE, TOKENS } from "../../syntax/constants.ts"; +import { FeatureWalkMode } from "./type.ts"; +import { ValidationSyntaxGroupEnum } from "../../validation/parser/typedef.ts"; +import { getSyntaxConfig } from "../../validation/config.ts"; +import { splitTokenList } from "../../validation/utils/list.ts"; +import { equalsIgnoreCase } from "../../parser/utils/text.ts"; +import { toDegrees } from "../../parser/utils/angle.ts"; import { printValue } from "../../printer/print.ts"; const config: ValidationConfiguration = getSyntaxConfig(); diff --git a/src/lib/ast/helper/minify.ts b/src/lib/ast/helper/minify.ts new file mode 100644 index 000000000..f65073255 --- /dev/null +++ b/src/lib/ast/helper/minify.ts @@ -0,0 +1,119 @@ +import {isWhiteSpace} from "../../syntax/syntax.ts"; +import {combinators} from "../../syntax/constants.ts"; + +/** + * Split selector string + * @param buffer + * + * @internal + */ +export function splitRule(buffer: string): string[][] { + const result: string[][] = [[]]; + let str: string = ""; + + for (let i = 0; i < buffer.length; i++) { + let chr: string = buffer.charAt(i); + + if (isWhiteSpace(chr.charCodeAt(0))) { + if (str !== "") { + // @ts-ignore + result.at(-1).push(str); + str = ""; + } + + // @ts-ignore + if (result.at(-1).length > 0) { + // @ts-ignore + result.at(-1).push(" "); + } + + // i = k; + continue; + } + + if (chr == ",") { + if (str !== "") { + result.at(-1)!.push(str); + str = ""; + } + + result.push([]); + continue; + } + + if (chr == ".") { + if (str !== "") { + result.at(-1)!.push(str); + str = ""; + } + + str += chr; + continue; + } + + if (combinators.includes(chr)) { + if (str !== "") { + result.at(-1)!.push(str); + str = ""; + } + + if (chr == "|" && buffer.charAt(i + 1) == "|") { + chr += buffer.charAt(++i); + } + + result.at(-1)!.push(chr); + continue; + } + + if (chr == ":") { + if (str !== "") { + result.at(-1)!.push(str); + str = ""; + } + + if (buffer.charAt(i + 1) == ":") { + chr += buffer.charAt(++i); + } + + str += chr; + continue; + } + + str += chr; + if (chr == "\\") { + str += buffer.charAt(++i); + continue; + } + + if (chr == "(" || chr == "[") { + const open = chr; + const close = chr == "(" ? ")" : "]"; + let inParens: number = 1; + let k = i; + while (++k < buffer.length) { + chr = buffer.charAt(k); + if (chr == "\\") { + str += buffer.slice(k, k + 2); + k++; + continue; + } + str += chr; + if (chr == open) { + inParens++; + } else if (chr == close) { + inParens--; + } + if (inParens == 0) { + break; + } + } + i = k; + } + } + + if (str !== "") { + result.at(-1)!.push(str); + } + + return result; +} \ No newline at end of file diff --git a/src/lib/parser/parse.ts b/src/lib/parser/parse.ts index bdde059a6..a12d76ef6 100644 --- a/src/lib/parser/parse.ts +++ b/src/lib/parser/parse.ts @@ -747,11 +747,8 @@ export function doParseSync(tokenizer: Tokenizer, options: ParserSyncOptions = { // @ts-ignore ignore error let parensMatch: number = 0; let curlyBracketMatch: number = 0; -<<<<<<< HEAD let tokenId: number; // let currentItemIndex: number; -======= ->>>>>>> v.next ast[LOCSRCID] = options.source!.id; ast[LOCSTA] = 0; diff --git a/src/lib/validation/match.ts b/src/lib/validation/match.ts index 6771dbb85..0229047ae 100644 --- a/src/lib/validation/match.ts +++ b/src/lib/validation/match.ts @@ -9,9 +9,8 @@ import type { ParserOptions, PseudoClassFunctionToken, PseudoElementToken, - StringToken, Token, - ValidationOptions, + ValidationOptions } from "../../@types/index.d.ts"; import { getParsedSyntax, getSyntaxConfig } from "./config.ts"; import { EnumToken } from "../ast/types.ts"; @@ -699,12 +698,10 @@ export function matchSelectorSyntax( slice.push(tokens[n]); } - if (slice[1]?.typ === EnumToken.PipeTokenType) { if (arena.getType(slice[1]) === EnumToken.PipeTokenType) { slice.shift(); } - if (slice[0].typ === EnumToken.PipeTokenType) { if (arena.getType(slice[0]) === EnumToken.PipeTokenType) { slice.shift(); } @@ -2314,8 +2311,6 @@ function matchProperty( case "display-internal": case "display-outside": case "display-legacy": - case "content-position": { - let values = propertyKeywordValues.get(property.val); case "content-position": { let values = propertyKeywordValues.get(property.val); @@ -2339,9 +2334,6 @@ function matchProperty( success = token?.typ == EnumToken.IdenTokenType && values.has((token as IdentToken).val.toLowerCase()); break; } - success = token?.typ == EnumToken.IdenTokenType && values.has((token as IdentToken).val.toLowerCase()); - break; - } case "mf-name": { From 982ca3e9a4a7715581b1e34dbf36fe8f8bbdcf65 Mon Sep 17 00:00:00 2001 From: Thierry Bela Nanga Date: Sat, 31 Oct 2026 13:40:39 -0400 Subject: [PATCH 10/24] work in progress --- src/lib/parser/arena.ts | 17 ++++++--- src/lib/parser/parse.ts | 32 +++++++++++------ src/lib/parser/utils/selector.ts | 14 ++++---- src/lib/parser/utils/token.ts | 2 +- src/lib/syntax/color/utils/components.ts | 2 +- src/lib/syntax/syntax.ts | 45 +++++++++++------------- 6 files changed, 64 insertions(+), 48 deletions(-) diff --git a/src/lib/parser/arena.ts b/src/lib/parser/arena.ts index ee083b87c..c67af80df 100644 --- a/src/lib/parser/arena.ts +++ b/src/lib/parser/arena.ts @@ -47,6 +47,7 @@ export class Arena { public renderedTokens: Uint32Array; constructor(size: number = 4096) { + this.dataTypes = new Uint8Array(size); this.parents = new Uint32Array(size); this.nextSibling = new Uint32Array(size); @@ -60,10 +61,7 @@ export class Arena { this.numericValue = new Float64Array(size); this.unit = new Uint32Array(size); this.flags = new Uint32Array(size); - // this.left = new Uint32Array(size); - // this.right = new Uint32Array(size); - // this.op = new Uint32Array(size); - // this.op2 = new Uint32Array(size); + this.renderedTokens = new Uint32Array(size); this.colorType = new Uint8Array(size); this.spans = new BigUint64Array(size); @@ -285,6 +283,10 @@ export class Arena { this.colorType[id] = colorType; } + getColorType(id: number): number { + return this.colorType[id]; + } + setFlags(id: number, flags: number): void { this.flags[id] = flags; } @@ -799,10 +801,15 @@ export class Arena { return siblings; } - children(id: number): number[] { + children(id: number, filter?: ((id: number) => boolean)): number[] { let children: number[] = []; let child = this.firstChild[id]; while (child != 0) { + + if (filter != null && !filter(child)) { + child = this.nextSibling[child]; + continue; + } children.push(child); child = this.nextSibling[child]; } diff --git a/src/lib/parser/parse.ts b/src/lib/parser/parse.ts index a12d76ef6..3a685874c 100644 --- a/src/lib/parser/parse.ts +++ b/src/lib/parser/parse.ts @@ -81,6 +81,7 @@ import { memoize } from "./utils/cache.ts"; import { SourceFile } from "./source.ts"; import { dirname } from "../fs/resolve.ts"; import { Arena } from "./arena.ts"; +import { printValue } from "../../node.ts"; function renderTokens(tokens: Token[] | null | undefined, options?: any): string { if (tokens == null || tokens.length === 0) return ""; @@ -3788,17 +3789,28 @@ export function parseAtRule( } } - atRule[LOCEND] = (stream.at(-1)! ?? atRule)[LOCEND]; - atRule[TOKENS] = stream; - atRule[STATE] = result.success ? EnumAstNodeStatus.Validated : EnumAstNodeStatus.Invalid; - atRule[ERRORS] = result.success ? [] : result.errors; + arena.setType(atRule, EnumToken.AtRuleNodeType); + arena.setSpan(atRule, arena.getSourceId(atRule), arena.getSpanStart(atRule), arena.getSpanEnd(stream.at(-1)! ?? atRule)); + arena.setTokens(atRule, ...trimArray(stream, options)); + arena.setFlags(atRule, EnumAstNodeStatus.Validated | EnumAstNodeStatus.HasChildren); - // @ts-expect-error - return Object.assign(atRule, { - typ: EnumToken.AtRuleNodeType, - val: renderTokens(trimWhiteSpaceTokens(stream), options), - chi: [] as Token[], - }) as AstAtRule; + arena.setValue(atRule, arena.internString(trimArray(stream, options).reduce((acc, token) => { + + return acc + printValue(token, arena, options); + }, ''))); + + return atRule; + + // atRule[LOCEND] = (stream.at(-1)! ?? atRule)[LOCEND]; + // atRule[TOKENS] = stream; + // atRule[STATE] = result.success ? EnumAstNodeStatus.Validated : EnumAstNodeStatus.Invalid; + // atRule[ERRORS] = result.success ? [] : result.errors; + + // return Object.assign(atRule, { + // typ: EnumToken.AtRuleNodeType, + // val: renderTokens(trimWhiteSpaceTokens(stream), options), + // chi: [] as Token[], + // }) as AstAtRule; } case "custom-media": { const tokens = trimArray(stream.slice(1)); diff --git a/src/lib/parser/utils/selector.ts b/src/lib/parser/utils/selector.ts index b3664a3cf..aa879fb52 100644 --- a/src/lib/parser/utils/selector.ts +++ b/src/lib/parser/utils/selector.ts @@ -207,7 +207,7 @@ export function parseSelector( newToken = arena.allocate(); all[write++] = newToken; - arena.set(newToken, EnumToken.PseudoClassTokenType, arena.interString(":" + arena.getValue(next))); + arena.set(newToken, EnumToken.PseudoClassTokenType, arena.internString(":" + arena.getValue(next))); arena.setSpan(newToken, arena.getSourceId(token), arena.getStart(token), arena.getEnd(next)); read++; @@ -235,7 +235,7 @@ export function parseSelector( val + "()" in getSyntaxConfig().selectors ? EnumToken.PseudoClassFunctionTokenDefType : arena.getType(next), - arena.interString(":" + arena.getValue(next)), + arena.internString(":" + arena.getValue(next)), ); arena.setSpan(newToken, arena.getSourceId(token), arena.getStart(token), arena.getEnd(next)); @@ -262,7 +262,7 @@ export function parseSelector( arena.set( newToken, EnumToken.PseudoClassTokenType, - arena.interString((pseudoElements.includes(val) ? "" : ":") + val), + arena.internString((pseudoElements.includes(val) ? "" : ":") + val), ); arena.setSpan(newToken, arena.getSourceId(token), arena.getStart(token), arena.getEnd(next)); @@ -290,7 +290,7 @@ export function parseSelector( val + "()" in getSyntaxConfig().selectors ? EnumToken.PseudoClassFunctionTokenDefType : EnumToken.FunctionTokenDefType, - arena.interString(val), + arena.internString(val), ); arena.setSpan(newToken, arena.getSourceId(token), arena.getStart(token), arena.getEnd(next)); @@ -308,7 +308,7 @@ export function parseSelector( const node = arena.allocate(); - node.set( + arena.set( node, EnumToken.RuleNodeType, arena.internString( @@ -327,7 +327,7 @@ export function parseSelector( } } - let t: string = printValue(curr, { minify: false, arena }); + let t: string = printValue(curr, arena, { minify: false, arena }); if (t == ",") { acc.push([]); @@ -618,7 +618,7 @@ export function parseSelector( arena.getType(list[0]) == EnumToken.IdenTokenType && arena.equalsIgnoreCase(list[0], "val", "-n") ) { - arena.setValue(list[0], arena.interString("n")); + arena.setValue(list[0], arena.internString("n")); } } diff --git a/src/lib/parser/utils/token.ts b/src/lib/parser/utils/token.ts index 7a99c3e0e..93d039644 100644 --- a/src/lib/parser/utils/token.ts +++ b/src/lib/parser/utils/token.ts @@ -116,7 +116,7 @@ export function replaceNodeOrValue( return true; } -export function trimWhiteSpaceTokens(tokens: Token[]): Token[] { +export function trimWhiteSpaceTokens(tokens: Token[], options: ParserOptions): Token[] { let i: number = 0; if (tokens[0]?.typ === EnumToken.WhitespaceTokenType) { diff --git a/src/lib/syntax/color/utils/components.ts b/src/lib/syntax/color/utils/components.ts index 1277cb872..3b7114479 100644 --- a/src/lib/syntax/color/utils/components.ts +++ b/src/lib/syntax/color/utils/components.ts @@ -4,7 +4,7 @@ import { COLORS_NAMES } from "../../constants.ts"; import { expandHexValue } from "../hex.ts"; import { equalsIgnoreCase } from "../../../parser/utils/text.ts"; -export function getColorComponents(token: ColorToken | IdentToken): Token[] | null { +export function getColorComponents(token: number, arena: Arena): number[] | null { if ((token as ColorToken).kin == ColorType.HEX || (token as ColorToken).kin == ColorType.LIT) { if (equalsIgnoreCase("currentcolor", (token as ColorToken).val)) { diff --git a/src/lib/syntax/syntax.ts b/src/lib/syntax/syntax.ts index 6e613e8c2..2a0af427c 100644 --- a/src/lib/syntax/syntax.ts +++ b/src/lib/syntax/syntax.ts @@ -16,7 +16,7 @@ import type { Token, } from "../../@types/index.d.ts"; import { isOkLabClose } from "./color/utils/distance.ts"; -import { ColorType, EnumToken } from "../ast/types.ts"; +import { ColorType, EnumAstNodeStatus, EnumToken } from "../ast/types.ts"; import { WalkerOptionEnum, walkValues } from "../ast/walk.ts"; import { toDegrees } from "../parser/utils/angle.ts"; import { memoize } from "../parser/utils/cache.ts"; @@ -872,51 +872,49 @@ export function isIdentColor(token: Token): boolean { // ); // } -export function isColor(token: number, arena: Arena): boolean { - +export function isColor(token: number, arena: Arena, errors?: ErrorDescription[]): boolean { const typ: EnumToken = arena.getType(token); if (typ == EnumToken.WildCardFunctionTokenType) { return true; } if (typ == EnumToken.ColorTokenType) { - if ("kin" in token && !("chi" in token)) { + // color kind and no children + if (arena.getColorType(token) != 0 && !arena.hasFlags(token, EnumAstNodeStatus.HasChildren)) { return true; } } + const val = arena.getValue(token).toLowerCase(); + + if (typ == EnumToken.IdenTokenType) { - const val = (token as IdentToken).val.toLowerCase(); if (systemColors.has(val) || deprecatedSystemColors.has(val) || nonStandardColors.has(val)) { return true; } // named color - return val in COLORS_NAMES || "currentcolor" === val || "transparent" === val; + return COLORS_NAMES[val] != null || "currentcolor" === val || "transparent" === val; } - let isLegacySyntax: boolean = false; - if (typ === EnumToken.FunctionTokenType || typ === EnumToken.ColorTokenType) { // if (!colorsFunc.includes((token as FunctionToken).val.toLowerCase())) { // return false; // } - if ((token as FunctionToken).chi.length > 0) { + if (arena.firstChild[token] != 0) { // @ts-ignore - if ((token as ColorToken).val === "light-dark") { - // @ts-ignore - const children: Token[] = ((token as ColorToken).chi as Token[]).filter((t: Token) => - [ - EnumToken.IdenTokenType, - EnumToken.NumberTokenType, - EnumToken.LiteralTokenType, - EnumToken.ColorTokenType, - EnumToken.FunctionTokenType, - EnumToken.PercentageTokenType, - EnumToken.WildCardFunctionTokenType, - ].includes(t.typ), + if (val === "light-dark") { + + const children: number[] = arena.children(token , (t: number) => + arena.dataTypes[t] == EnumToken.IdenTokenType || + arena.dataTypes[t] == EnumToken.NumberTokenType || + arena.dataTypes[t] == EnumToken.LiteralTokenType || + arena.dataTypes[t] == EnumToken.ColorTokenType || + arena.dataTypes[t] == EnumToken.FunctionTokenType || + arena.dataTypes[t] == EnumToken.PercentageTokenType || + arena.dataTypes[t] == EnumToken.WildCardFunctionTokenType ); if (children.length != 2) { @@ -928,7 +926,7 @@ export function isColor(token: number, arena: Arena): boolean { return false; } - if (isColor(children[0]) && isColor(children[1])) { + if (isColor(children[0], arena) && isColor(children[1], arena)) { return true; } } @@ -936,7 +934,7 @@ export function isColor(token: number, arena: Arena): boolean { // adding numbers and percentages is disallowed // https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/color_value/lch#defining_relative_color_output_channel_components:~:text=Adding%20a%20%3Cpercentage%3E%20to%20a%20%3Cnumber%3E%2C%20for%20example%2C%20doesn%27t%20work - const components = getColorComponents(token as ColorToken); + const components = getColorComponents(token, arena); if (components !== null) { const colorSpace = getColorSpace(token as ColorToken)?.split?.("") as string[] | null; @@ -1241,7 +1239,6 @@ export function isColor(token: number, arena: Arena): boolean { } export function parseColor(token: number, arena: Arena): number { - const typ = arena.getType(token); if (typ === EnumToken.IdenTokenType) { const val: string = (token as IdentToken).val.toLowerCase(); From 758ba0245856ad1679434f0dc0bd51598cfb7865 Mon Sep 17 00:00:00 2001 From: Thierry Bela Nanga Date: Sun, 1 Nov 2026 22:23:51 -0500 Subject: [PATCH 11/24] implement the ast as an array of structures --- dist/index-umd-web.js | 49866 +++++------ dist/index.cjs | 69870 ++++++++-------- dist/index.d.ts | 4736 +- dist/package.json.js | 7 - dist/src/data/properties.json.js | 4 - dist/src/data/validation.json.js | 15 +- dist/src/lib/ast/arena.js | 2479 + dist/src/lib/ast/clone.js | 63 +- dist/src/lib/ast/expand.js | 253 +- dist/src/lib/ast/features/calc.js | 126 +- dist/src/lib/ast/features/if.js | 269 +- dist/src/lib/ast/features/index.js | 4 - .../lib/ast/features/inlinecssvariables.js | 156 +- dist/src/lib/ast/features/prefix.js | 357 +- dist/src/lib/ast/features/shorthand.js | 88 +- dist/src/lib/ast/features/transform.js | 47 +- dist/src/lib/ast/features/type.js | 4 - dist/src/lib/ast/find.js | 223 +- dist/src/lib/ast/math/expression.js | 992 +- dist/src/lib/ast/math/math.js | 62 +- dist/src/lib/ast/minify.js | 1132 +- dist/src/lib/ast/node.js | 49 +- dist/src/lib/ast/transform/compute.js | 169 +- dist/src/lib/ast/transform/matrix.js | 61 +- dist/src/lib/ast/transform/minify.js | 276 +- dist/src/lib/ast/transform/perspective.js | 7 +- dist/src/lib/ast/transform/rotate.js | 4 - dist/src/lib/ast/transform/scale.js | 4 - dist/src/lib/ast/transform/skew.js | 4 - dist/src/lib/ast/transform/translate.js | 4 - dist/src/lib/ast/transform/utils.js | 6 +- dist/src/lib/ast/types.js | 90 +- dist/src/lib/ast/walk.js | 336 +- dist/src/lib/fs/resolve.js | 4 - dist/src/lib/parser/declaration/list.js | 130 +- dist/src/lib/parser/declaration/map.js | 460 +- dist/src/lib/parser/declaration/set.js | 137 +- dist/src/lib/parser/linesmap.js | 4 - dist/src/lib/parser/parse.js | 2712 +- dist/src/lib/parser/source.js | 4 - dist/src/lib/parser/tokenize.js | 45 +- dist/src/lib/parser/utils/angle.js | 26 +- .../src/lib/parser/utils/at-rule-container.js | 300 +- .../utils/at-rule-font-feature-values.js | 12 +- dist/src/lib/parser/utils/at-rule-generic.js | 54 +- dist/src/lib/parser/utils/at-rule-import.js | 87 +- dist/src/lib/parser/utils/at-rule-media.js | 357 +- dist/src/lib/parser/utils/at-rule-support.js | 158 +- .../src/lib/parser/utils/at-rule-when-else.js | 93 +- dist/src/lib/parser/utils/at-rule.js | 12 +- dist/src/lib/parser/utils/base64.js | 6 +- dist/src/lib/parser/utils/cache.js | 4 - dist/src/lib/parser/utils/config.js | 4 - dist/src/lib/parser/utils/declaration.js | 545 +- dist/src/lib/parser/utils/eq.js | 19 +- dist/src/lib/parser/utils/hash.js | 52 +- dist/src/lib/parser/utils/selector.js | 782 +- dist/src/lib/parser/utils/text.js | 4 - dist/src/lib/parser/utils/token.js | 104 +- dist/src/lib/parser/utils/type.js | 63 +- dist/src/lib/printer/render.js | 811 +- dist/src/lib/printer/sourcemap/lib/codec.js | 4 - dist/src/lib/printer/sourcemap/sourcemap.js | 4 - dist/src/lib/syntax/color/a98rgb.js | 4 - dist/src/lib/syntax/color/alpha.js | 72 +- dist/src/lib/syntax/color/cmyk.js | 89 +- dist/src/lib/syntax/color/color-mix.js | 228 +- dist/src/lib/syntax/color/color.js | 440 +- dist/src/lib/syntax/color/hex.js | 102 +- dist/src/lib/syntax/color/hsl.js | 118 +- dist/src/lib/syntax/color/hwb.js | 128 +- dist/src/lib/syntax/color/lab.js | 128 +- dist/src/lib/syntax/color/lch.js | 128 +- dist/src/lib/syntax/color/oklab.js | 130 +- dist/src/lib/syntax/color/oklch.js | 129 +- dist/src/lib/syntax/color/p3.js | 4 - dist/src/lib/syntax/color/prophotorgb.js | 4 - dist/src/lib/syntax/color/rec2020.js | 4 - dist/src/lib/syntax/color/relative-color.js | 235 +- dist/src/lib/syntax/color/rgb.js | 113 +- dist/src/lib/syntax/color/srgb.js | 110 +- dist/src/lib/syntax/color/utils/color-type.js | 25 +- dist/src/lib/syntax/color/utils/colorspace.js | 24 +- dist/src/lib/syntax/color/utils/components.js | 41 +- dist/src/lib/syntax/color/utils/distance.js | 30 +- dist/src/lib/syntax/color/utils/make-color.js | 35 +- dist/src/lib/syntax/color/utils/matrix.js | 6 +- dist/src/lib/syntax/color/xyz.js | 4 - dist/src/lib/syntax/color/xyzd50.js | 4 - dist/src/lib/syntax/constants.js | 19 +- dist/src/lib/syntax/syntax.js | 681 +- dist/src/lib/validation/config.js | 25 +- dist/src/lib/validation/match.js | 891 +- dist/src/lib/validation/parser/parse.js | 15 +- dist/src/lib/validation/parser/typedef.js | 4 - dist/src/lib/validation/utils/list.js | 43 +- dist/src/node.js | 31 +- dist/src/types.js | 4 - dist/src/utils/sync.js | 17 +- dist/src/web.js | 32 +- src/@types/ast.d.ts | 376 +- src/@types/index.d.ts | 44 +- src/@types/parse.d.ts | 9 + src/@types/shorthand.d.ts | 228 +- src/@types/token.d.ts | 139 +- src/@types/visitor.d.ts | 149 +- src/@types/walker.d.ts | 30 +- src/config.json | 1506 + src/data/validation.json | 2 +- src/lib/ast/arena.ts | 3073 + src/lib/ast/clone.ts | 64 +- src/lib/ast/expand.ts | 310 +- src/lib/ast/features/calc.ts | 165 +- src/lib/ast/features/if.ts | 428 +- src/lib/ast/features/index.ts | 12 +- src/lib/ast/features/inlinecssvariables.ts | 190 +- src/lib/ast/features/prefix.ts | 490 +- src/lib/ast/features/shorthand.ts | 91 +- src/lib/ast/features/transform.ts | 71 +- src/lib/ast/features/type.ts | 5 +- src/lib/ast/find.ts | 289 +- src/lib/ast/helper/minify.ts | 119 - src/lib/ast/math/expression.ts | 1151 +- src/lib/ast/math/math.ts | 74 +- src/lib/ast/minify.ts | 1334 +- src/lib/ast/node.ts | 176 +- src/lib/ast/transform/compute.ts | 178 +- src/lib/ast/transform/matrix.ts | 52 +- src/lib/ast/transform/minify.ts | 282 +- src/lib/ast/transform/perspective.ts | 7 +- src/lib/ast/transform/utils.ts | 2 +- src/lib/ast/types.ts | 90 +- src/lib/ast/walk.ts | 514 +- src/lib/parser/arena.ts | 1014 - src/lib/parser/declaration/list.ts | 153 +- src/lib/parser/declaration/map.ts | 553 +- src/lib/parser/declaration/set.ts | 188 +- src/lib/parser/parse.ts | 3038 +- src/lib/parser/tokenize.ts | 47 +- src/lib/parser/utils/angle.ts | 39 +- src/lib/parser/utils/at-rule-container.ts | 404 +- .../utils/at-rule-font-feature-values.ts | 15 +- src/lib/parser/utils/at-rule-generic.ts | 58 +- src/lib/parser/utils/at-rule-import.ts | 177 +- src/lib/parser/utils/at-rule-media.ts | 416 +- src/lib/parser/utils/at-rule-page.ts | 42 + src/lib/parser/utils/at-rule-support.ts | 193 +- src/lib/parser/utils/at-rule-token.ts | 1 + src/lib/parser/utils/at-rule-when-else.ts | 116 +- src/lib/parser/utils/at-rule.ts | 17 +- src/lib/parser/utils/base64.ts | 12 +- src/lib/parser/utils/declaration-list.ts | 50 + src/lib/parser/utils/declaration.ts | 691 +- src/lib/parser/utils/eq.ts | 36 +- src/lib/parser/utils/hash.ts | 45 +- src/lib/parser/utils/selector.ts | 1049 +- src/lib/parser/utils/text.ts | 24 +- src/lib/parser/utils/token.ts | 145 +- src/lib/parser/utils/type.ts | 67 +- src/lib/printer/print.ts | 1960 - src/lib/printer/render.ts | 1001 +- src/lib/printer/sourcemap/lib/codec.ts | 16 +- src/lib/printer/sourcemap/sourcemap.ts | 10 +- src/lib/syntax/color/alpha.ts | 73 +- src/lib/syntax/color/cmyk.ts | 93 +- src/lib/syntax/color/color-mix.ts | 244 +- src/lib/syntax/color/color.ts | 537 +- src/lib/syntax/color/hex.ts | 99 +- src/lib/syntax/color/hsl.ts | 121 +- src/lib/syntax/color/hwb.ts | 138 +- src/lib/syntax/color/lab.ts | 137 +- src/lib/syntax/color/lch.ts | 137 +- src/lib/syntax/color/oklab.ts | 133 +- src/lib/syntax/color/oklch.ts | 137 +- src/lib/syntax/color/relative-color.ts | 268 +- src/lib/syntax/color/rgb.ts | 110 +- src/lib/syntax/color/srgb.ts | 131 +- src/lib/syntax/color/utils/color-type.ts | 27 +- src/lib/syntax/color/utils/colorspace.ts | 23 +- src/lib/syntax/color/utils/components.ts | 47 +- src/lib/syntax/color/utils/distance.ts | 42 +- src/lib/syntax/color/utils/make-color.ts | 43 +- src/lib/syntax/color/utils/matrix.ts | 2 +- src/lib/syntax/constants.ts | 14 - src/lib/syntax/syntax.ts | 726 +- src/lib/validation/config.json | 5347 ++ src/lib/validation/config.ts | 30 +- src/lib/validation/match.ts | 1101 +- src/lib/validation/parser/parse.ts | 18 +- src/lib/validation/types.d.ts | 25 +- src/lib/validation/utils/list.ts | 64 +- src/node.ts | 31 +- src/utils/sync.ts | 14 +- src/web.ts | 26 +- test/allFiles.js | 2 +- test/inspect.js | 2 +- test/specs/code/arena.js | 582 + test/specs/code/at-rules.js | 312 +- test/specs/code/block.js | 74 +- test/specs/code/calc.js | 28 +- test/specs/code/color-a98-rgb.js | 1204 +- test/specs/code/color-alpha.js | 10 +- test/specs/code/color-cmyk.js | 643 +- test/specs/code/color-display-p3.js | 1172 +- test/specs/code/color-hex.js | 327 +- test/specs/code/color-hsl.js | 636 +- test/specs/code/color-hwb.js | 684 +- test/specs/code/color-lab.js | 632 +- test/specs/code/color-lch.js | 617 +- test/specs/code/color-linear-srgb.js | 1217 +- test/specs/code/color-mix.js | 33 +- test/specs/code/color-oklab.js | 615 +- test/specs/code/color-oklch.js | 634 +- test/specs/code/color-prophoto-rgb.js | 1165 +- test/specs/code/color-rec2020.js | 1134 +- test/specs/code/color-rgb.js | 590 +- test/specs/code/color-srgb.js | 1111 +- test/specs/code/color-xyz-d50.js | 1148 +- test/specs/code/color-xyz.js | 1148 +- test/specs/code/color.js | 276 +- test/specs/code/expand.js | 22 +- test/specs/code/index.js | 3 +- test/specs/code/malformed.js | 18 +- test/specs/code/modules.js | 2 + test/specs/code/nesting.js | 4 +- test/specs/code/prefix.js | 7 +- test/specs/code/shorthand.js | 4 +- test/specs/code/sourcemaps.js | 1 + test/specs/code/sync.js | 13 +- test/specs/code/validation.js | 59 +- test/specs/code/vars.js | 4 +- test/specs/code/visitors.js | 209 +- test/specs/code/walk.js | 45 +- test/specs/node.spec.js | 2 +- test/specs/specs/code/angle.js | 19 + test/specs/specs/code/arena.js | 582 + test/specs/specs/code/at-rules.js | 2076 + test/specs/specs/code/block.js | 1590 + test/specs/specs/code/calc.js | 744 + test/specs/specs/code/color-a98-rgb.js | 615 + test/specs/specs/code/color-alpha.js | 390 + test/specs/specs/code/color-cmyk.js | 339 + test/specs/specs/code/color-display-p3.js | 613 + test/specs/specs/code/color-hex.js | 204 + test/specs/specs/code/color-hsl.js | 340 + test/specs/specs/code/color-hwb.js | 339 + test/specs/specs/code/color-lab.js | 340 + test/specs/specs/code/color-lch.js | 323 + test/specs/specs/code/color-linear-srgb.js | 628 + test/specs/specs/code/color-mix.js | 200 + test/specs/specs/code/color-oklab.js | 323 + test/specs/specs/code/color-oklch.js | 322 + test/specs/specs/code/color-prophoto-rgb.js | 602 + test/specs/specs/code/color-rec2020.js | 592 + test/specs/specs/code/color-rgb.js | 323 + test/specs/specs/code/color-srgb.js | 592 + test/specs/specs/code/color-xyz-d50.js | 592 + test/specs/specs/code/color-xyz.js | 592 + test/specs/specs/code/color.js | 1901 + test/specs/specs/code/expand.js | 588 + test/specs/specs/code/grid.js | 20 + test/specs/specs/code/import1.js | 26 + test/specs/specs/code/import2.js | 17 + test/specs/specs/code/index.js | 43 + test/specs/specs/code/lenient.js | 50 + test/specs/specs/code/malformed.js | 391 + test/specs/specs/code/minify.js | 833 + test/specs/specs/code/modules.js | 1182 + test/specs/specs/code/nesting.js | 682 + test/specs/specs/code/prefix.js | 456 + test/specs/specs/code/shorthand.js | 639 + test/specs/specs/code/sourcemaps.js | 73 + test/specs/specs/code/sync.js | 41 + test/specs/specs/code/transform.js | 825 + test/specs/specs/code/validation.js | 849 + test/specs/specs/code/vars.js | 130 + test/specs/specs/code/visitors.js | 810 + test/specs/specs/code/walk.js | 160 + test/specs/specs/node.spec.js | 31 + test/specs/specs/web.spec.js | 37 + 280 files changed, 118440 insertions(+), 97035 deletions(-) create mode 100644 dist/src/lib/ast/arena.js create mode 100644 src/config.json create mode 100644 src/lib/ast/arena.ts delete mode 100644 src/lib/ast/helper/minify.ts delete mode 100644 src/lib/parser/arena.ts create mode 100644 src/lib/parser/utils/at-rule-page.ts create mode 100644 src/lib/parser/utils/at-rule-token.ts create mode 100644 src/lib/parser/utils/declaration-list.ts delete mode 100644 src/lib/printer/print.ts create mode 100644 src/lib/validation/config.json create mode 100644 test/specs/code/arena.js create mode 100644 test/specs/specs/code/angle.js create mode 100644 test/specs/specs/code/arena.js create mode 100644 test/specs/specs/code/at-rules.js create mode 100644 test/specs/specs/code/block.js create mode 100644 test/specs/specs/code/calc.js create mode 100644 test/specs/specs/code/color-a98-rgb.js create mode 100644 test/specs/specs/code/color-alpha.js create mode 100644 test/specs/specs/code/color-cmyk.js create mode 100644 test/specs/specs/code/color-display-p3.js create mode 100644 test/specs/specs/code/color-hex.js create mode 100644 test/specs/specs/code/color-hsl.js create mode 100644 test/specs/specs/code/color-hwb.js create mode 100644 test/specs/specs/code/color-lab.js create mode 100644 test/specs/specs/code/color-lch.js create mode 100644 test/specs/specs/code/color-linear-srgb.js create mode 100644 test/specs/specs/code/color-mix.js create mode 100644 test/specs/specs/code/color-oklab.js create mode 100644 test/specs/specs/code/color-oklch.js create mode 100644 test/specs/specs/code/color-prophoto-rgb.js create mode 100644 test/specs/specs/code/color-rec2020.js create mode 100644 test/specs/specs/code/color-rgb.js create mode 100644 test/specs/specs/code/color-srgb.js create mode 100644 test/specs/specs/code/color-xyz-d50.js create mode 100644 test/specs/specs/code/color-xyz.js create mode 100644 test/specs/specs/code/color.js create mode 100644 test/specs/specs/code/expand.js create mode 100644 test/specs/specs/code/grid.js create mode 100644 test/specs/specs/code/import1.js create mode 100644 test/specs/specs/code/import2.js create mode 100644 test/specs/specs/code/index.js create mode 100644 test/specs/specs/code/lenient.js create mode 100644 test/specs/specs/code/malformed.js create mode 100644 test/specs/specs/code/minify.js create mode 100644 test/specs/specs/code/modules.js create mode 100644 test/specs/specs/code/nesting.js create mode 100644 test/specs/specs/code/prefix.js create mode 100644 test/specs/specs/code/shorthand.js create mode 100644 test/specs/specs/code/sourcemaps.js create mode 100644 test/specs/specs/code/sync.js create mode 100644 test/specs/specs/code/transform.js create mode 100644 test/specs/specs/code/validation.js create mode 100644 test/specs/specs/code/vars.js create mode 100644 test/specs/specs/code/visitors.js create mode 100644 test/specs/specs/code/walk.js create mode 100644 test/specs/specs/node.spec.js create mode 100644 test/specs/specs/web.spec.js diff --git a/dist/index-umd-web.js b/dist/index-umd-web.js index 6be368c37..7fe53df35 100644 --- a/dist/index-umd-web.js +++ b/dist/index-umd-web.js @@ -2,11 +2,7 @@ CSS parser, minifier and validator for node and the browser Author(s): Thierry Bela -<<<<<<< HEAD Version: 2.0.0-alpha.1 -======= - Version: 1.6.8 ->>>>>>> v.next License: MIT */ (function (global, factory) { @@ -65,26 +61,66 @@ */ EnumAstNodeStatus[EnumAstNodeStatus["Malformed"] = 128] = "Malformed"; /** - * Node has children + * Important declaration */ - EnumAstNodeStatus[EnumAstNodeStatus["HasChildren"] = 256] = "HasChildren"; + EnumAstNodeStatus[EnumAstNodeStatus["ImportantDeclaration"] = 256] = "ImportantDeclaration"; /** - * Node has values + * Color component without unit */ - EnumAstNodeStatus[EnumAstNodeStatus["HasValues"] = 512] = "HasValues"; + EnumAstNodeStatus[EnumAstNodeStatus["PowerlessColorComponent"] = 512] = "PowerlessColorComponent"; + /* node flags stored in the arena `flags` column next to the validation state */ /** - * Node has fraction value + * mask of the validation state bits */ - EnumAstNodeStatus[EnumAstNodeStatus["HasFractionValue"] = 1024] = "HasFractionValue"; + EnumAstNodeStatus[EnumAstNodeStatus["StateMask"] = 65535] = "StateMask"; /** - * Important declaration + * the node has a children list (stylesheet, rule, at-rule with a block) */ - EnumAstNodeStatus[EnumAstNodeStatus["ImportantDeclaration"] = 2048] = "ImportantDeclaration"; + EnumAstNodeStatus[EnumAstNodeStatus["HasChildren"] = 65536] = "HasChildren"; /** - * Color component without unit + * the node has value tokens (declaration) */ - EnumAstNodeStatus[EnumAstNodeStatus["PowerlessColorComponent"] = 4096] = "PowerlessColorComponent"; + EnumAstNodeStatus[EnumAstNodeStatus["HasValues"] = 131072] = "HasValues"; + /** + * the node has a source location + */ + EnumAstNodeStatus[EnumAstNodeStatus["HasLocation"] = 262144] = "HasLocation"; })(exports.EnumAstNodeStatus || (exports.EnumAstNodeStatus = {})); + /** + * Enum of validation levels + * @deprecated + */ + exports.ValidationLevel = void 0; + (function (ValidationLevel) { + /** + * disable validation + */ + ValidationLevel[ValidationLevel["None"] = 0] = "None"; + /** + * validate selectors + */ + ValidationLevel[ValidationLevel["Selector"] = 1] = "Selector"; + /** + * validate at-rules + */ + ValidationLevel[ValidationLevel["AtRule"] = 2] = "AtRule"; + /** + * validate declarations + */ + ValidationLevel[ValidationLevel["Declaration"] = 4] = "Declaration"; + /** + * validate selectors and at-rules + */ + ValidationLevel[ValidationLevel["Default"] = 3] = "Default"; + /** + * validate selectors, at-rules and declarations + */ + ValidationLevel[ValidationLevel["All"] = 7] = "All"; + /** + * Report only. Apply validation and report nodes that are marked as invalid + */ + ValidationLevel[ValidationLevel["ReportOnly"] = 8] = "ReportOnly"; + })(exports.ValidationLevel || (exports.ValidationLevel = {})); /** * Enum of all token types */ @@ -329,7 +365,7 @@ /** * token list token type */ - EnumToken[EnumToken["ListTokenType"] = 59] = "ListTokenType"; + EnumToken[EnumToken["ListToken"] = 59] = "ListToken"; /* arithmetic tokens */ /** * addition token type @@ -662,22 +698,30 @@ * wrapped values token type like {Arial, sans-serif} */ EnumToken[EnumToken["WrappedValuesTokenType"] = 140] = "WrappedValuesTokenType"; + /** + * list of arena tokens (binary expression operands) + */ + EnumToken[EnumToken["ListTokenType"] = 141] = "ListTokenType"; /** * infinity token */ - EnumToken[EnumToken["NaNTokenType"] = 141] = "NaNTokenType"; + EnumToken[EnumToken["NaNTokenType"] = 142] = "NaNTokenType"; /** * infinity token */ - EnumToken[EnumToken["InfinityTokenType"] = 142] = "InfinityTokenType"; + EnumToken[EnumToken["InfinityTokenType"] = 143] = "InfinityTokenType"; /** * negative infinity token */ - EnumToken[EnumToken["NegativeInfinityTokenType"] = 143] = "NegativeInfinityTokenType"; + EnumToken[EnumToken["NegativeInfinityTokenType"] = 144] = "NegativeInfinityTokenType"; /** * CSS type token */ - EnumToken[EnumToken["CssTypeTokenType"] = 144] = "CssTypeTokenType"; + EnumToken[EnumToken["CssTypeTokenType"] = 145] = "CssTypeTokenType"; + /** + * user defined dimension token type + */ + EnumToken[EnumToken["UserDefinedDimensionTokenType"] = 146] = "UserDefinedDimensionTokenType"; /* aliases */ /** * alias for time token type @@ -794,7 +838,11 @@ exports.ColorType = void 0; (function (ColorType) { /** - * deprecated system colors + * the color type is not known yet (the color function has not been parsed) + */ + ColorType[ColorType["NONE"] = 0] = "NONE"; + /** + * system colors */ ColorType[ColorType["SYS"] = 1] = "SYS"; /** @@ -995,22282 +1043,18033 @@ ModuleScopeEnumOptions[ModuleScopeEnumOptions["Shortest"] = 512] = "Shortest"; })(exports.ModuleScopeEnumOptions || (exports.ModuleScopeEnumOptions = {})); - var declarations = { - "-ms-accelerator": { - syntax: "false | true" - }, - "-ms-block-progression": { - syntax: "tb | rl | bt | lr" - }, - "-ms-content-zoom-chaining": { - syntax: "none | chained" - }, - "-ms-content-zoom-limit": { - syntax: "<'-ms-content-zoom-limit-min'> <'-ms-content-zoom-limit-max'>" - }, - "-ms-content-zoom-limit-max": { - syntax: "" - }, - "-ms-content-zoom-limit-min": { - syntax: "" - }, - "-ms-content-zoom-snap": { - syntax: "<'-ms-content-zoom-snap-type'> || <'-ms-content-zoom-snap-points'>" - }, - "-ms-content-zoom-snap-points": { - syntax: "snapInterval( , ) | snapList( # )" - }, - "-ms-content-zoom-snap-type": { - syntax: "none | proximity | mandatory" - }, - "-ms-content-zooming": { - syntax: "none | zoom" - }, - "-ms-filter": { - syntax: "" - }, - "-ms-flow-from": { - syntax: "[ none | ]#" - }, - "-ms-flow-into": { - syntax: "[ none | ]#" - }, - "-ms-grid-columns": { - syntax: "none | | " - }, - "-ms-grid-rows": { - syntax: "none | | " - }, - "-ms-high-contrast-adjust": { - syntax: "auto | none" - }, - "-ms-hyphenate-limit-chars": { - syntax: "auto | {1,3}" - }, - "-ms-hyphenate-limit-lines": { - syntax: "no-limit | " - }, - "-ms-hyphenate-limit-zone": { - syntax: " | " - }, - "-ms-ime-align": { - syntax: "auto | after" - }, - "-ms-overflow-style": { - syntax: " auto | none | scrollbar | -ms-autohiding-scrollbar" - }, - "-ms-scroll-chaining": { - syntax: "chained | none" - }, - "-ms-scroll-limit": { - syntax: "<'-ms-scroll-limit-x-min'> <'-ms-scroll-limit-y-min'> <'-ms-scroll-limit-x-max'> <'-ms-scroll-limit-y-max'>" - }, - "-ms-scroll-limit-x-max": { - syntax: "auto | " - }, - "-ms-scroll-limit-x-min": { - syntax: "" - }, - "-ms-scroll-limit-y-max": { - syntax: "auto | " - }, - "-ms-scroll-limit-y-min": { - syntax: "" - }, - "-ms-scroll-rails": { - syntax: "none | railed" - }, - "-ms-scroll-snap-points-x": { - syntax: "snapInterval( , ) | snapList( # )" - }, - "-ms-scroll-snap-points-y": { - syntax: "snapInterval( , ) | snapList( # )" - }, - "-ms-scroll-snap-type": { - syntax: "none | proximity | mandatory" - }, - "-ms-scroll-snap-x": { - syntax: "<'-ms-scroll-snap-type'> <'-ms-scroll-snap-points-x'>" - }, - "-ms-scroll-snap-y": { - syntax: "<'-ms-scroll-snap-type'> <'-ms-scroll-snap-points-y'>" - }, - "-ms-scroll-translation": { - syntax: "none | vertical-to-horizontal" - }, - "-ms-scrollbar-3dlight-color": { - syntax: "" - }, - "-ms-scrollbar-arrow-color": { - syntax: "" - }, - "-ms-scrollbar-base-color": { - syntax: "" - }, - "-ms-scrollbar-darkshadow-color": { - syntax: "" - }, - "-ms-scrollbar-face-color": { - syntax: "" - }, - "-ms-scrollbar-highlight-color": { - syntax: "" - }, - "-ms-scrollbar-shadow-color": { - syntax: "" - }, - "-ms-scrollbar-track-color": { - syntax: "" - }, - "-ms-text-autospace": { - syntax: "none | ideograph-alpha | ideograph-numeric | ideograph-parenthesis | ideograph-space" - }, - "-ms-touch-select": { - syntax: "grippers | none" - }, - "-ms-user-select": { - syntax: "none | element | text" - }, - "-ms-wrap-flow": { - syntax: "auto | both | start | end | maximum | clear" - }, - "-ms-wrap-margin": { - syntax: "" - }, - "-ms-wrap-through": { - syntax: "wrap | none" - }, - "-moz-appearance": { - syntax: "none | button | button-arrow-down | button-arrow-next | button-arrow-previous | button-arrow-up | button-bevel | button-focus | caret | checkbox | checkbox-container | checkbox-label | checkmenuitem | dualbutton | groupbox | listbox | listitem | menuarrow | menubar | menucheckbox | menuimage | menuitem | menuitemtext | menulist | menulist-button | menulist-text | menulist-textfield | menupopup | menuradio | menuseparator | meterbar | meterchunk | progressbar | progressbar-vertical | progresschunk | progresschunk-vertical | radio | radio-container | radio-label | radiomenuitem | range | range-thumb | resizer | resizerpanel | scale-horizontal | scalethumbend | scalethumb-horizontal | scalethumbstart | scalethumbtick | scalethumb-vertical | scale-vertical | scrollbarbutton-down | scrollbarbutton-left | scrollbarbutton-right | scrollbarbutton-up | scrollbarthumb-horizontal | scrollbarthumb-vertical | scrollbartrack-horizontal | scrollbartrack-vertical | searchfield | separator | sheet | spinner | spinner-downbutton | spinner-textfield | spinner-upbutton | splitter | statusbar | statusbarpanel | tab | tabpanel | tabpanels | tab-scroll-arrow-back | tab-scroll-arrow-forward | textfield | textfield-multiline | toolbar | toolbarbutton | toolbarbutton-dropdown | toolbargripper | toolbox | tooltip | treeheader | treeheadercell | treeheadersortarrow | treeitem | treeline | treetwisty | treetwistyopen | treeview | -moz-mac-unified-toolbar | -moz-win-borderless-glass | -moz-win-browsertabbar-toolbox | -moz-win-communicationstext | -moz-win-communications-toolbox | -moz-win-exclude-glass | -moz-win-glass | -moz-win-mediatext | -moz-win-media-toolbox | -moz-window-button-box | -moz-window-button-box-maximized | -moz-window-button-close | -moz-window-button-maximize | -moz-window-button-minimize | -moz-window-button-restore | -moz-window-frame-bottom | -moz-window-frame-left | -moz-window-frame-right | -moz-window-titlebar | -moz-window-titlebar-maximized" - }, - "-moz-binding": { - syntax: " | none" - }, - "-moz-border-bottom-colors": { - syntax: "+ | none" - }, - "-moz-border-left-colors": { - syntax: "+ | none" - }, - "-moz-border-right-colors": { - syntax: "+ | none" - }, - "-moz-border-top-colors": { - syntax: "+ | none" - }, - "-moz-context-properties": { - syntax: "none | [ fill | fill-opacity | stroke | stroke-opacity ]#" - }, - "-moz-float-edge": { - syntax: "border-box | content-box | margin-box | padding-box" - }, - "-moz-force-broken-image-icon": { - syntax: "0 | 1" - }, - "-moz-orient": { - syntax: "inline | block | horizontal | vertical" - }, - "-moz-outline-radius": { - syntax: "{1,4} [ / {1,4} ]?" - }, - "-moz-outline-radius-bottomleft": { - syntax: "" - }, - "-moz-outline-radius-bottomright": { - syntax: "" - }, - "-moz-outline-radius-topleft": { - syntax: "" - }, - "-moz-outline-radius-topright": { - syntax: "" - }, - "-moz-stack-sizing": { - syntax: "ignore | stretch-to-fit" - }, - "-moz-text-blink": { - syntax: "none | blink" - }, - "-moz-user-focus": { - syntax: "ignore | normal | select-after | select-before | select-menu | select-same | select-all | none" - }, - "-moz-user-input": { - syntax: "auto | none | enabled | disabled" - }, - "-moz-user-modify": { - syntax: "read-only | read-write | write-only" - }, - "-moz-window-dragging": { - syntax: "drag | no-drag" - }, - "-moz-window-shadow": { - syntax: "default | menu | tooltip | sheet | none" - }, - "-webkit-appearance": { - syntax: " none | button | button-bevel | caps-lock-indicator | caret | checkbox | default-button | inner-spin-button | listbox | listitem | media-controls-background | media-controls-fullscreen-background | media-current-time-display | media-enter-fullscreen-button | media-exit-fullscreen-button | media-fullscreen-button | media-mute-button | media-overlay-play-button | media-play-button | media-seek-back-button | media-seek-forward-button | media-slider | media-sliderthumb | media-time-remaining-display | media-toggle-closed-captions-button | media-volume-slider | media-volume-slider-container | media-volume-sliderthumb | menulist | menulist-button | menulist-text | menulist-textfield | meter | progress-bar | progress-bar-value | push-button | radio | scrollbarbutton-down | scrollbarbutton-left | scrollbarbutton-right | scrollbarbutton-up | scrollbargripper-horizontal | scrollbargripper-vertical | scrollbarthumb-horizontal | scrollbarthumb-vertical | scrollbartrack-horizontal | scrollbartrack-vertical | searchfield | searchfield-cancel-button | searchfield-decoration | searchfield-results-button | searchfield-results-decoration | slider-horizontal | slider-vertical | sliderthumb-horizontal | sliderthumb-vertical | square-button | textarea | textfield | -apple-pay-button" - }, - "-webkit-border-after": { - syntax: "<'border-top'>" - }, - "-webkit-border-after-color": { - syntax: "<'border-top-color'>" - }, - "-webkit-border-after-style": { - syntax: "<'border-top-style'>" - }, - "-webkit-border-after-width": { - syntax: "<'border-top-width'>" - }, - "-webkit-border-before": { - syntax: "<'border-top'>" - }, - "-webkit-border-before-color": { - syntax: "<'border-top-color'>" - }, - "-webkit-border-before-style": { - syntax: "<'border-top-style'>" - }, - "-webkit-border-before-width": { - syntax: "<'border-top-width'>" - }, - "-webkit-border-end": { - syntax: "<'border-top'>" - }, - "-webkit-border-end-color": { - syntax: "<'border-top-color'>" - }, - "-webkit-border-end-style": { - syntax: "<'border-top-style'>" - }, - "-webkit-border-end-width": { - syntax: "<'border-top-width'>" - }, - "-webkit-border-start": { - syntax: "<'border-top'>" - }, - "-webkit-border-start-color": { - syntax: "<'border-top-color'>" - }, - "-webkit-border-start-style": { - syntax: "<'border-top-style'>" - }, - "-webkit-border-start-width": { - syntax: "<'border-top-width'>" - }, - "-webkit-box-reflect": { - syntax: "[ above | below | right | left ]? ? ?" - }, - "-webkit-line-clamp": { - syntax: "none | " - }, - "-webkit-mask": { - syntax: "[ || [ / ]? || || [ | border | padding | content | text ] || [ | border | padding | content ] ]#" - }, - "-webkit-mask-attachment": { - syntax: "#" - }, - "-webkit-mask-clip": { - syntax: "[ | no-clip | border | padding | content | text ]#" - }, - "-webkit-mask-composite": { - syntax: "#" - }, - "-webkit-mask-image": { - syntax: "#" - }, - "-webkit-mask-origin": { - syntax: "[ | border | padding | content ]#" - }, - "-webkit-mask-position": { - syntax: "#" - }, - "-webkit-mask-position-x": { - syntax: "[ | left | center | right ]#" - }, - "-webkit-mask-position-y": { - syntax: "[ | top | center | bottom ]#" - }, - "-webkit-mask-repeat": { - syntax: "#" - }, - "-webkit-mask-repeat-x": { - syntax: "repeat | no-repeat | space | round" - }, - "-webkit-mask-repeat-y": { - syntax: "repeat | no-repeat | space | round" - }, - "-webkit-mask-size": { - syntax: "#" - }, - "-webkit-overflow-scrolling": { - syntax: "auto | touch" - }, - "-webkit-tap-highlight-color": { - syntax: "" - }, - "-webkit-text-fill-color": { - syntax: "" - }, - "-webkit-text-stroke": { - syntax: " || " - }, - "-webkit-text-stroke-color": { - syntax: "" - }, - "-webkit-text-stroke-width": { - syntax: "" - }, - "-webkit-touch-callout": { - syntax: "default | none" - }, - "-webkit-user-modify": { - syntax: "read-only | read-write | read-write-plaintext-only" - }, - "-webkit-user-select": { - syntax: " auto | none | text | all" - }, - "accent-color": { - syntax: "auto | " - }, - "align-content": { - syntax: "normal | | | ? " - }, - "align-items": { - syntax: "normal | stretch | | [ ? ] | anchor-center" - }, - "align-self": { - syntax: "auto | normal | stretch | | ? | anchor-center" - }, - "align-tracks": { - syntax: "[ normal | | | ? ]#" - }, - "alignment-baseline": { - syntax: " auto | baseline | before-edge | text-before-edge | middle | central | after-edge | text-after-edge | ideographic | alphabetic | hanging | mathematical" - }, - all: { - syntax: "initial | inherit | unset | revert | revert-layer" - }, - "anchor-name": { - syntax: "none | #" - }, - "anchor-scope": { - syntax: "none | all | #" - }, - animation: { - syntax: "#" - }, - "animation-composition": { - syntax: "#" - }, - "animation-delay": { - syntax: "