From 69111aad8e5bad4cbe86b3e137783afa7769fc6f Mon Sep 17 00:00:00 2001 From: Paul Irish Date: Tue, 27 Oct 2026 09:42:27 -0700 Subject: [PATCH 1/4] feat: integrate cssomnom/parse and replace custom cssstyledeclaration --- cjs/dom/string-map.js | 2 +- cjs/html/style-element.js | 2 +- cjs/interface/css-style-declaration.js | 172 +- esm/dom/string-map.js | 2 +- esm/html/style-element.js | 2 +- esm/interface/css-style-declaration.js | 172 +- package-lock.json | 26 +- package.json | 5 +- test/html/style-element.js | 1 + test/interface/css-style-declaration.js | 52 +- .../esm/interface/css-style-declaration.d.ts | 21 +- types/esm/shared/node.d.ts | 2 +- worker.js | 18702 +++++++++------- 13 files changed, 10540 insertions(+), 8621 deletions(-) diff --git a/cjs/dom/string-map.js b/cjs/dom/string-map.js index 7d0e7126..eed28c61 100644 --- a/cjs/dom/string-map.js +++ b/cjs/dom/string-map.js @@ -1,5 +1,5 @@ 'use strict'; -const uhyphen = (m => /* c8 ignore start */ m.__esModule ? m.default : m /* c8 ignore stop */)(require('uhyphen')); +const uhyphen = camel => camel.replace(/(([A-Z0-9])([A-Z0-9][a-z]))|(([a-z0-9]+)([A-Z]))/g, '$2$5-$3$6').toLowerCase(); const {setPrototypeOf} = require('../shared/object.js'); const refs = new WeakMap; diff --git a/cjs/html/style-element.js b/cjs/html/style-element.js index 78cf8a0f..e4bc2b11 100644 --- a/cjs/html/style-element.js +++ b/cjs/html/style-element.js @@ -1,5 +1,5 @@ 'use strict'; -const {parse} = require('@acemir/cssom'); +const {parse} = require('cssomnom/parse'); const {registerHTMLClass} = require('../shared/register-html-class.js'); const {SHEET} = require('../shared/symbols.js'); diff --git a/cjs/interface/css-style-declaration.js b/cjs/interface/css-style-declaration.js index 845be4d9..d81442b3 100644 --- a/cjs/interface/css-style-declaration.js +++ b/cjs/interface/css-style-declaration.js @@ -1,130 +1,98 @@ 'use strict'; -const uhyphen = (m => /* c8 ignore start */ m.__esModule ? m.default : m /* c8 ignore stop */)(require('uhyphen')); - -const {CHANGED, PRIVATE, VALUE} = require('../shared/symbols.js'); +const {CSSStyleDeclaration: BaseCSSStyleDeclaration} = require('cssomnom/parse'); +const {CHANGED, VALUE} = require('../shared/symbols.js'); const refs = new WeakMap; -const getKeys = style => [...style.keys()].filter(key => key !== PRIVATE); - -const updateKeys = style => { - const attr = refs.get(style).getAttributeNode('style'); - if (!attr || attr[CHANGED] || style.get(PRIVATE) !== attr) { - style.clear(); - if (attr) { - style.set(PRIVATE, attr); - for (const rule of attr[VALUE].split(/\s*;\s*/)) { - let [key, ...rest] = rule.split(':'); - if (rest.length > 0) { - key = key.trim(); - const value = rest.join(':').trim(); - if (key && value) - style.set(key, value); - } - } - } +const sync = decl => { + const data = refs.get(decl); + if (!data) return; + const {element} = data; + const attr = element.getAttributeNode('style'); + if (attr !== data.attr || (attr && attr[CHANGED])) { + data.attr = attr; + const {onChange} = decl; + decl.onChange = null; + decl.cssText = attr ? attr[VALUE] : ''; + decl.onChange = onChange; + if (attr) attr[CHANGED] = false; } - return attr; }; const handler = { - get(style, name) { - if (name in prototype) - return style[name]; - updateKeys(style); - if (name === 'length') - return getKeys(style).length; - if (/^\d+$/.test(name)) - return getKeys(style)[name]; - return style.get(uhyphen(name)) ?? ''; + get(target, prop, receiver) { + sync(target); + return Reflect.get(target, prop, receiver); }, - set(style, name, value) { - if (name === 'cssText') - style[name] = value; - else { - let attr = updateKeys(style); - if (value == null) - style.delete(uhyphen(name)); - else - style.set(uhyphen(name), value); - if (!attr) { - const element = refs.get(style); - attr = element.ownerDocument.createAttribute('style'); - element.setAttributeNode(attr); - style.set(PRIVATE, attr); - } - attr[CHANGED] = false; - attr[VALUE] = style.toString(); - } - return true; + set(target, prop, value, receiver) { + sync(target); + return Reflect.set(target, prop, value, receiver); + }, + + has(target, prop) { + sync(target); + return Reflect.has(target, prop); + }, + + ownKeys(target) { + sync(target); + return Reflect.ownKeys(target); + }, + + getOwnPropertyDescriptor(target, prop) { + sync(target); + return Reflect.getOwnPropertyDescriptor(target, prop); } }; /** * @implements globalThis.CSSStyleDeclaration */ -class CSSStyleDeclaration extends Map { +class CSSStyleDeclaration extends BaseCSSStyleDeclaration { + /** + * @param {Element} [element] + */ constructor(element) { - super(); - refs.set(this, element); - /* c8 ignore start */ - return new Proxy(this, handler); - /* c8 ignore stop */ - } - - get cssText() { - return this.toString(); - } - - set cssText(value) { - refs.get(this).setAttribute('style', value); - } - - getPropertyValue(name) { - const self = this[PRIVATE]; - return handler.get(self, name); - } + const decl = super(); + if (!element) return decl; - setProperty(name, value) { - const self = this[PRIVATE]; - handler.set(self, name, value); - } + const attr = element.getAttributeNode('style'); + if (attr && attr[VALUE]) { + decl.cssText = attr[VALUE]; + } - removeProperty(name) { - const self = this[PRIVATE]; - handler.set(self, name, null); - } + const data = {element, attr}; + refs.set(decl, data); - [Symbol.iterator]() { - const self = this[PRIVATE]; - updateKeys(self); - const keys = getKeys(self); - const {length} = keys; - let i = 0; - return { - next() { - const done = i === length; - return {done, value: done ? null : keys[i++]}; + decl.onChange = () => { + let attr = element.getAttributeNode('style'); + const cssText = decl.cssText; + if (!attr) { + if (cssText) { + attr = element.ownerDocument.createAttribute('style'); + attr[VALUE] = cssText; + element.setAttributeNode(attr); + data.attr = attr; + attr[CHANGED] = false; + } + } else { + data.attr = attr; + if (attr[VALUE] !== cssText) { + attr.value = cssText; + attr[CHANGED] = false; + } } }; - } - get[PRIVATE]() { return this; } + const proxy = new Proxy(decl, handler); + refs.set(proxy, data); + return proxy; + } toString() { - const self = this[PRIVATE]; - updateKeys(self); - const cssText = []; - self.forEach(push, cssText); - return cssText.join(';'); + sync(this); + return this.cssText; } } exports.CSSStyleDeclaration = CSSStyleDeclaration - -const {prototype} = CSSStyleDeclaration; - -function push(value, key) { - if (key !== PRIVATE) - this.push(`${key}:${value}`); -} diff --git a/esm/dom/string-map.js b/esm/dom/string-map.js index 6fb92fcc..a651ffee 100644 --- a/esm/dom/string-map.js +++ b/esm/dom/string-map.js @@ -1,4 +1,4 @@ -import uhyphen from 'uhyphen'; +const uhyphen = camel => camel.replace(/(([A-Z0-9])([A-Z0-9][a-z]))|(([a-z0-9]+)([A-Z]))/g, '$2$5-$3$6').toLowerCase(); import {setPrototypeOf} from '../shared/object.js'; const refs = new WeakMap; diff --git a/esm/html/style-element.js b/esm/html/style-element.js index 9f23dde0..59b36160 100644 --- a/esm/html/style-element.js +++ b/esm/html/style-element.js @@ -1,4 +1,4 @@ -import {parse} from '@acemir/cssom'; +import {parse} from 'cssomnom/parse'; import {registerHTMLClass} from '../shared/register-html-class.js'; import {SHEET} from '../shared/symbols.js'; diff --git a/esm/interface/css-style-declaration.js b/esm/interface/css-style-declaration.js index deb8b59a..ffc8cdbd 100644 --- a/esm/interface/css-style-declaration.js +++ b/esm/interface/css-style-declaration.js @@ -1,128 +1,96 @@ -import uhyphen from 'uhyphen'; - -import {CHANGED, PRIVATE, VALUE} from '../shared/symbols.js'; +import {CSSStyleDeclaration as BaseCSSStyleDeclaration} from 'cssomnom/parse'; +import {CHANGED, VALUE} from '../shared/symbols.js'; const refs = new WeakMap; -const getKeys = style => [...style.keys()].filter(key => key !== PRIVATE); - -const updateKeys = style => { - const attr = refs.get(style).getAttributeNode('style'); - if (!attr || attr[CHANGED] || style.get(PRIVATE) !== attr) { - style.clear(); - if (attr) { - style.set(PRIVATE, attr); - for (const rule of attr[VALUE].split(/\s*;\s*/)) { - let [key, ...rest] = rule.split(':'); - if (rest.length > 0) { - key = key.trim(); - const value = rest.join(':').trim(); - if (key && value) - style.set(key, value); - } - } - } +const sync = decl => { + const data = refs.get(decl); + if (!data) return; + const {element} = data; + const attr = element.getAttributeNode('style'); + if (attr !== data.attr || (attr && attr[CHANGED])) { + data.attr = attr; + const {onChange} = decl; + decl.onChange = null; + decl.cssText = attr ? attr[VALUE] : ''; + decl.onChange = onChange; + if (attr) attr[CHANGED] = false; } - return attr; }; const handler = { - get(style, name) { - if (name in prototype) - return style[name]; - updateKeys(style); - if (name === 'length') - return getKeys(style).length; - if (/^\d+$/.test(name)) - return getKeys(style)[name]; - return style.get(uhyphen(name)) ?? ''; + get(target, prop, receiver) { + sync(target); + return Reflect.get(target, prop, receiver); }, - set(style, name, value) { - if (name === 'cssText') - style[name] = value; - else { - let attr = updateKeys(style); - if (value == null) - style.delete(uhyphen(name)); - else - style.set(uhyphen(name), value); - if (!attr) { - const element = refs.get(style); - attr = element.ownerDocument.createAttribute('style'); - element.setAttributeNode(attr); - style.set(PRIVATE, attr); - } - attr[CHANGED] = false; - attr[VALUE] = style.toString(); - } - return true; + set(target, prop, value, receiver) { + sync(target); + return Reflect.set(target, prop, value, receiver); + }, + + has(target, prop) { + sync(target); + return Reflect.has(target, prop); + }, + + ownKeys(target) { + sync(target); + return Reflect.ownKeys(target); + }, + + getOwnPropertyDescriptor(target, prop) { + sync(target); + return Reflect.getOwnPropertyDescriptor(target, prop); } }; /** * @implements globalThis.CSSStyleDeclaration */ -export class CSSStyleDeclaration extends Map { +export class CSSStyleDeclaration extends BaseCSSStyleDeclaration { + /** + * @param {Element} [element] + */ constructor(element) { - super(); - refs.set(this, element); - /* c8 ignore start */ - return new Proxy(this, handler); - /* c8 ignore stop */ - } - - get cssText() { - return this.toString(); - } - - set cssText(value) { - refs.get(this).setAttribute('style', value); - } - - getPropertyValue(name) { - const self = this[PRIVATE]; - return handler.get(self, name); - } + const decl = super(); + if (!element) return decl; - setProperty(name, value) { - const self = this[PRIVATE]; - handler.set(self, name, value); - } + const attr = element.getAttributeNode('style'); + if (attr && attr[VALUE]) { + decl.cssText = attr[VALUE]; + } - removeProperty(name) { - const self = this[PRIVATE]; - handler.set(self, name, null); - } + const data = {element, attr}; + refs.set(decl, data); - [Symbol.iterator]() { - const self = this[PRIVATE]; - updateKeys(self); - const keys = getKeys(self); - const {length} = keys; - let i = 0; - return { - next() { - const done = i === length; - return {done, value: done ? null : keys[i++]}; + decl.onChange = () => { + let attr = element.getAttributeNode('style'); + const cssText = decl.cssText; + if (!attr) { + if (cssText) { + attr = element.ownerDocument.createAttribute('style'); + attr[VALUE] = cssText; + element.setAttributeNode(attr); + data.attr = attr; + attr[CHANGED] = false; + } + } else { + data.attr = attr; + if (attr[VALUE] !== cssText) { + attr.value = cssText; + attr[CHANGED] = false; + } } }; - } - get[PRIVATE]() { return this; } + const proxy = new Proxy(decl, handler); + refs.set(proxy, data); + return proxy; + } toString() { - const self = this[PRIVATE]; - updateKeys(self); - const cssText = []; - self.forEach(push, cssText); - return cssText.join(';'); + sync(this); + return this.cssText; } } - -const {prototype} = CSSStyleDeclaration; - -function push(value, key) { - if (key !== PRIVATE) - this.push(`${key}:${value}`); -} diff --git a/package-lock.json b/package-lock.json index 0c7a320b..17a23f54 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,11 +9,10 @@ "version": "0.18.14", "license": "ISC", "dependencies": { - "@acemir/cssom": "^0.9.32", "css-select": "^7.0.0", + "cssomnom": "^0.1.10", "html-escaper": "^3.0.3", - "htmlparser2": "^10.1.0", - "uhyphen": "^0.2.0" + "htmlparser2": "^10.1.0" }, "devDependencies": { "@rollup/plugin-commonjs": "^29.0.2", @@ -37,12 +36,6 @@ } } }, - "node_modules/@acemir/cssom": { - "version": "0.9.32", - "resolved": "https://registry.npmjs.org/@acemir/cssom/-/cssom-0.9.32.tgz", - "integrity": "sha512-OijrAUu7rrJhSIoHNg6LMPS5e344nwUHA+1EFYYjrh64D+GmytUsfBQjci45TjT+rd5Spsp8B5xsZDT+TgyXXQ==", - "license": "MIT" - }, "node_modules/@babel/helper-string-parser": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", @@ -1104,6 +1097,15 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/cssomnom": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/cssomnom/-/cssomnom-0.1.10.tgz", + "integrity": "sha512-HKqGsqRNIeliLmNqg2iJn4FfGTNHb2xFG7O9XaWd37zXpW+umKHkFjrb2s0Va3e6guuEALiqkeih7anDe5E6Bw==", + "license": "Apache-2.0", + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -2708,12 +2710,6 @@ "node": ">=14.17" } }, - "node_modules/uhyphen": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/uhyphen/-/uhyphen-0.2.0.tgz", - "integrity": "sha512-qz3o9CHXmJJPGBdqzab7qAYuW8kQGKNEuoHFYrBwV6hWIMcpAmxDLXojcHfFr9US1Pe6zUswEIJIbLI610fuqA==", - "license": "ISC" - }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", diff --git a/package.json b/package.json index e370264d..00aa5aca 100644 --- a/package.json +++ b/package.json @@ -70,11 +70,10 @@ } }, "dependencies": { - "@acemir/cssom": "^0.9.32", "css-select": "^7.0.0", + "cssomnom": "^0.1.10", "html-escaper": "^3.0.3", - "htmlparser2": "^10.1.0", - "uhyphen": "^0.2.0" + "htmlparser2": "^10.1.0" }, "peerDependencies": { "canvas": ">= 2" diff --git a/test/html/style-element.js b/test/html/style-element.js index f50259b5..59b97260 100644 --- a/test/html/style-element.js +++ b/test/html/style-element.js @@ -28,6 +28,7 @@ assert(style.sheet.cssRules.length === 1, true, 'style.sheet.cssRules.length'); assert(style.sheet.cssRules[0].selectorText, '.test'); assert(style.sheet.cssRules[0].style.color, 'white'); assert(style.sheet.cssRules[0].style['font-size'], '12px'); +assert(style.sheet.cssRules[0].style.fontSize, '12px', 'rule.style.fontSize camelCase (linkedom#201)'); document = parseHTML('').document; diff --git a/test/interface/css-style-declaration.js b/test/interface/css-style-declaration.js index baa5af2f..c202edbb 100644 --- a/test/interface/css-style-declaration.js +++ b/test/interface/css-style-declaration.js @@ -13,20 +13,20 @@ assert(node.style.getPropertyValue('color'), '', 'unset getPropertyValue returns node.style.cssText = 'background-color: blue; background-image: url("https://t.co/i.png");'; assert(node.style.backgroundColor, 'blue', 'style getter'); assert(node.style.backgroundImage, 'url("https://t.co/i.png")', 'style value with colon'); -assert(node.style.toString(), 'background-color:blue;background-image:url("https://t.co/i.png")', 'cssText setter'); +assert(node.style.toString(), 'background-color: blue; background-image: url("https://t.co/i.png");', 'cssText setter'); assert([...node.style].join(','), 'background-color,background-image', 'iterable'); assert(node.style.length, 2, 'style.length'); assert(node.style[0], 'background-color', 'style[0]'); node.getAttributeNode('style').value = 'color: red'; -assert(node.style.toString(), 'color:red', 'cssText indirect setter'); +assert(node.style.toString(), 'color: red;', 'cssText indirect setter'); let style = document.createAttribute('style'); node.setAttributeNode(style); assert(node.toString(), '
', 'cssText cleanup'); node.style.backgroundColor = 'green'; -assert(node.toString(), '
', 'cssText indirect property'); +assert(node.toString(), '
', 'cssText indirect property'); node.removeAttributeNode(style); node.style.color = 'green'; -assert(node.toString(), '
', 'cssText indirect setter again'); +assert(node.toString(), '
', 'cssText indirect setter again'); node.style.color = null; assert(node.toString(), '
', 'setter as null'); @@ -35,7 +35,7 @@ node.className = ''; assert(node.toString(), '
', 'setter as null'); node.style.setProperty('background-color', 'purple'); -assert(node.toString(), '
', 'setProperty'); +assert(node.toString(), '
', 'setProperty'); assert(node.style.getPropertyValue('background-color'), 'purple', 'getPropertyValue'); node.style.removeProperty('background-color') assert(node.toString(), '
', 'removeProperty'); @@ -53,3 +53,45 @@ assert(Array.from(divWithStyle.style).join(','), 'display', 'Array.from after ch divWithStyle.style.setProperty('color', 'green'); assert([...divWithStyle.style].join(','), 'display,color', 'iterable after adding property'); assert(Array.from(divWithStyle.style).join(','), 'display,color', 'Array.from after adding property'); + +// Issue #327: el.style.color = '' removes the property cleanly +const el327 = document.createElement('div'); +el327.style.color = 'red'; +el327.style.fontSize = '12px'; +assert(el327.style.color, 'red', 'el327 color set'); +el327.style.color = ''; +assert(el327.style.color, '', 'el327 color removed'); +assert(el327.style.cssText, 'font-size: 12px;', 'el327 valid cssText'); +assert(el327.toString(), '
', 'el327 serialization'); + +// Semicolons inside url("data:...") +const elDataUrl = document.createElement('div'); +const dataUrl = 'url("data:image/svg+xml;utf8,")'; +elDataUrl.style.backgroundImage = dataUrl; +assert(elDataUrl.style.backgroundImage, dataUrl, 'dataUrl with semicolons preserved'); +assert(elDataUrl.style.cssText, `background-image: ${dataUrl};`, 'dataUrl cssText'); + +// Preserving case on --customProperty +const elCustom = document.createElement('div'); +elCustom.style.setProperty('--customProperty', '100px'); +assert(elCustom.style.getPropertyValue('--customProperty'), '100px', 'customProperty getPropertyValue'); +assert(elCustom.style['--customProperty'], '100px', 'customProperty proxy getter'); +elCustom.style['--anotherCamelCase'] = 'blue'; +assert(elCustom.style.getPropertyValue('--anotherCamelCase'), 'blue', 'anotherCamelCase getPropertyValue'); +assert(elCustom.style['--anotherCamelCase'], 'blue', 'anotherCamelCase proxy getter'); +assert(elCustom.style.cssText.includes('--customProperty: 100px;'), true, 'cssText contains --customProperty'); +assert(elCustom.style.cssText.includes('--anotherCamelCase: blue;'), true, 'cssText contains --anotherCamelCase'); + +// Proxy trap coverage (has, ownKeys, getOwnPropertyDescriptor, standalone constructor) +const elTraps = document.createElement('div'); +elTraps.style.color = 'red'; +assert('color' in elTraps.style, true, 'has trap'); +assert(Object.keys(elTraps.style).length > 0, true, 'ownKeys trap'); +Object.getOwnPropertyDescriptor(elTraps.style, 'color'); + +const {CSSStyleDeclaration} = require('../../cjs/interface/css-style-declaration.js'); +const standalone = new CSSStyleDeclaration(); +assert(standalone.length, 0, 'standalone style declaration'); +assert(standalone.toString(), '', 'standalone style declaration toString'); + + diff --git a/types/esm/interface/css-style-declaration.d.ts b/types/esm/interface/css-style-declaration.d.ts index 45e32d2f..115593fd 100644 --- a/types/esm/interface/css-style-declaration.d.ts +++ b/types/esm/interface/css-style-declaration.d.ts @@ -1,19 +1,10 @@ /** * @implements globalThis.CSSStyleDeclaration */ -export class CSSStyleDeclaration extends Map implements globalThis.CSSStyleDeclaration { - constructor(element: any); - set cssText(value: string); - get cssText(): string; - getPropertyValue(name: any): any; - setProperty(name: any, value: any): void; - removeProperty(name: any): void; - [Symbol.iterator](): { - next(): { - done: boolean; - value: any; - }; - }; - get [PRIVATE](): this; +export class CSSStyleDeclaration extends BaseCSSStyleDeclaration implements globalThis.CSSStyleDeclaration { + /** + * @param {Element} [element] + */ + constructor(element?: Element); } -import { PRIVATE } from '../shared/symbols.js'; +import { CSSStyleDeclaration as BaseCSSStyleDeclaration } from 'cssomnom/parse'; diff --git a/types/esm/shared/node.d.ts b/types/esm/shared/node.d.ts index 2cb31545..2fd910a0 100644 --- a/types/esm/shared/node.d.ts +++ b/types/esm/shared/node.d.ts @@ -6,7 +6,7 @@ export function parentElement({ parentNode }: { parentNode: any; }): any; export function previousSibling({ [PREV]: prev }: { - "__@PREV@24211": any; + "__@PREV@24212": any; }): any; export function nextSibling(node: any): any; import { PREV } from './symbols.js'; diff --git a/worker.js b/worker.js index 31bb78a1..a4841749 100644 --- a/worker.js +++ b/worker.js @@ -757,7 +757,7 @@ const Sequences = { ]), // ` { const end = self[END]; @@ -2276,7 +2276,7 @@ const parseFromString = (document, isHTML, markupLanguage) => { let ownerSVGElement = null; let parsingCData = false; - const content = new Parser({ + const content = new Parser$1({ // onprocessinginstruction(name, data) { if (name.toLowerCase() === '!doctype') @@ -7154,8 +7154,7 @@ function setOwnerDocument(node) { return node; } -var uhyphen = camel => camel.replace(/(([A-Z0-9])([A-Z0-9][a-z]))|(([a-z0-9]+)([A-Z]))/g, '$2$5-$3$6') - .toLowerCase(); +const uhyphen = camel => camel.replace(/(([A-Z0-9])([A-Z0-9][a-z]))|(([a-z0-9]+)([A-Z]))/g, '$2$5-$3$6').toLowerCase(); const refs$1 = new WeakMap; @@ -7297,8766 +7296,10721 @@ class DOMTokenList extends Set { supports() { return true; } } -const refs = new WeakMap; - -const getKeys = style => [...style.keys()].filter(key => key !== PRIVATE); - -const updateKeys = style => { - const attr = refs.get(style).getAttributeNode('style'); - if (!attr || attr[CHANGED] || style.get(PRIVATE) !== attr) { - style.clear(); - if (attr) { - style.set(PRIVATE, attr); - for (const rule of attr[VALUE].split(/\s*;\s*/)) { - let [key, ...rest] = rule.split(':'); - if (rest.length > 0) { - key = key.trim(); - const value = rest.join(':').trim(); - if (key && value) - style.set(key, value); - } - } - } - } - return attr; -}; - -const handler$1 = { - get(style, name) { - if (name in prototype) - return style[name]; - updateKeys(style); - if (name === 'length') - return getKeys(style).length; - if (/^\d+$/.test(name)) - return getKeys(style)[name]; - return style.get(uhyphen(name)) ?? ''; - }, - - set(style, name, value) { - if (name === 'cssText') - style[name] = value; - else { - let attr = updateKeys(style); - if (value == null) - style.delete(uhyphen(name)); - else - style.set(uhyphen(name), value); - if (!attr) { - const element = refs.get(style); - attr = element.ownerDocument.createAttribute('style'); - element.setAttributeNode(attr); - style.set(PRIVATE, attr); - } - attr[CHANGED] = false; - attr[VALUE] = style.toString(); - } - return true; - } -}; - +// @ts-nocheck +// src/data/gen/property-list.ts /** - * @implements globalThis.CSSStyleDeclaration - */ -let CSSStyleDeclaration$1 = class CSSStyleDeclaration extends Map { - constructor(element) { - super(); - refs.set(this, element); - /* c8 ignore start */ - return new Proxy(this, handler$1); - /* c8 ignore stop */ - } - - get cssText() { - return this.toString(); - } - - set cssText(value) { - refs.get(this).setAttribute('style', value); - } - - getPropertyValue(name) { - const self = this[PRIVATE]; - return handler$1.get(self, name); - } - - setProperty(name, value) { - const self = this[PRIVATE]; - handler$1.set(self, name, value); - } - - removeProperty(name) { - const self = this[PRIVATE]; - handler$1.set(self, name, null); - } - - [Symbol.iterator]() { - const self = this[PRIVATE]; - updateKeys(self); - const keys = getKeys(self); - const {length} = keys; - let i = 0; - return { - next() { - const done = i === length; - return {done, value: done ? null : keys[i++]}; - } - }; - } - - get[PRIVATE]() { return this; } - - toString() { - const self = this[PRIVATE]; - updateKeys(self); - const cssText = []; - self.forEach(push, cssText); - return cssText.join(';'); - } +* @license +* Copyright 2026 Google LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +const SUPPORTED_PROPERTIES = /* @__PURE__ */ new Set("-webkit-align-content,-webkit-align-items,-webkit-align-self,-webkit-animation,-webkit-animation-delay,-webkit-animation-direction,-webkit-animation-duration,-webkit-animation-fill-mode,-webkit-animation-iteration-count,-webkit-animation-name,-webkit-animation-play-state,-webkit-animation-timing-function,-webkit-appearance,-webkit-backface-visibility,-webkit-background-clip,-webkit-background-origin,-webkit-background-size,-webkit-border-bottom-left-radius,-webkit-border-bottom-right-radius,-webkit-border-radius,-webkit-border-top-left-radius,-webkit-border-top-right-radius,-webkit-box-align,-webkit-box-flex,-webkit-box-ordinal-group,-webkit-box-orient,-webkit-box-pack,-webkit-box-shadow,-webkit-box-sizing,-webkit-filter,-webkit-flex,-webkit-flex-basis,-webkit-flex-direction,-webkit-flex-flow,-webkit-flex-grow,-webkit-flex-shrink,-webkit-flex-wrap,-webkit-justify-content,-webkit-line-clamp,-webkit-mask,-webkit-mask-box-image,-webkit-mask-box-image-outset,-webkit-mask-box-image-repeat,-webkit-mask-box-image-slice,-webkit-mask-box-image-source,-webkit-mask-box-image-width,-webkit-mask-clip,-webkit-mask-composite,-webkit-mask-image,-webkit-mask-origin,-webkit-mask-position,-webkit-mask-repeat,-webkit-mask-size,-webkit-order,-webkit-perspective,-webkit-perspective-origin,-webkit-text-fill-color,-webkit-text-size-adjust,-webkit-text-stroke,-webkit-text-stroke-color,-webkit-text-stroke-width,-webkit-transform,-webkit-transform-origin,-webkit-transform-style,-webkit-transition,-webkit-transition-delay,-webkit-transition-duration,-webkit-transition-property,-webkit-transition-timing-function,-webkit-user-select,accent-color,align-content,align-items,align-self,alignment-baseline,all,anchor-name,anchor-scope,animation,animation-composition,animation-delay,animation-delay-end,animation-delay-start,animation-direction,animation-duration,animation-fill-mode,animation-iteration-count,animation-name,animation-play-state,animation-range,animation-range-center,animation-range-end,animation-range-start,animation-timeline,animation-timing-function,animation-trigger,appearance,aspect-ratio,backdrop-filter,backface-visibility,background,background-attachment,background-blend-mode,background-clip,background-color,background-image,background-origin,background-position,background-position-block,background-position-inline,background-position-x,background-position-y,background-repeat,background-repeat-block,background-repeat-inline,background-repeat-x,background-repeat-y,background-size,background-tbd,baseline-shift,baseline-source,block-ellipsis,block-size,block-step,block-step-align,block-step-insert,block-step-round,block-step-size,bookmark-label,bookmark-level,bookmark-state,border,border-block,border-block-clip,border-block-color,border-block-end,border-block-end-clip,border-block-end-color,border-block-end-radius,border-block-end-style,border-block-end-width,border-block-start,border-block-start-clip,border-block-start-color,border-block-start-radius,border-block-start-style,border-block-start-width,border-block-style,border-block-width,border-bottom,border-bottom-clip,border-bottom-color,border-bottom-left-radius,border-bottom-radius,border-bottom-right-radius,border-bottom-style,border-bottom-width,border-boundary,border-clip,border-collapse,border-color,border-end-end-radius,border-end-start-radius,border-image,border-image-outset,border-image-repeat,border-image-slice,border-image-source,border-image-width,border-inline,border-inline-clip,border-inline-color,border-inline-end,border-inline-end-clip,border-inline-end-color,border-inline-end-radius,border-inline-end-style,border-inline-end-width,border-inline-start,border-inline-start-clip,border-inline-start-color,border-inline-start-radius,border-inline-start-style,border-inline-start-width,border-inline-style,border-inline-width,border-left,border-left-clip,border-left-color,border-left-radius,border-left-style,border-left-width,border-limit,border-radius,border-right,border-right-clip,border-right-color,border-right-radius,border-right-style,border-right-width,border-shape,border-spacing,border-start-end-radius,border-start-start-radius,border-style,border-top,border-top-clip,border-top-color,border-top-left-radius,border-top-radius,border-top-right-radius,border-top-style,border-top-width,border-width,bottom,box-decoration-break,box-shadow,box-shadow-blur,box-shadow-color,box-shadow-offset,box-shadow-position,box-shadow-spread,box-sizing,box-snap,break-after,break-before,break-inside,caption-side,caret,caret-animation,caret-color,caret-shape,clear,clip,clip-path,clip-rule,color,color-adjust,color-interpolation,color-interpolation-filters,color-scheme,column-count,column-fill,column-gap,column-height,column-rule,column-rule-break,column-rule-color,column-rule-inset,column-rule-inset-cap,column-rule-inset-cap-end,column-rule-inset-cap-start,column-rule-inset-end,column-rule-inset-junction,column-rule-inset-junction-end,column-rule-inset-junction-start,column-rule-inset-start,column-rule-style,column-rule-visibility-items,column-rule-width,column-span,column-width,column-wrap,columns,contain,contain-intrinsic-block-size,contain-intrinsic-height,contain-intrinsic-inline-size,contain-intrinsic-size,contain-intrinsic-width,container,container-name,container-type,content,content-visibility,continue,copy-into,corner,corner-block-end,corner-block-end-shape,corner-block-start,corner-block-start-shape,corner-bottom,corner-bottom-left,corner-bottom-left-shape,corner-bottom-right,corner-bottom-right-shape,corner-bottom-shape,corner-end-end,corner-end-end-shape,corner-end-start,corner-end-start-shape,corner-inline-end,corner-inline-end-shape,corner-inline-start,corner-inline-start-shape,corner-left,corner-left-shape,corner-right,corner-right-shape,corner-shape,corner-start-end,corner-start-end-shape,corner-start-start,corner-start-start-shape,corner-top,corner-top-left,corner-top-left-shape,corner-top-right,corner-top-right-shape,corner-top-shape,counter-increment,counter-reset,counter-set,cue,cue-after,cue-before,cursor,cx,cy,d,direction,display,dominant-baseline,dynamic-range-limit,empty-cells,event-trigger,event-trigger-name,event-trigger-source,field-sizing,fill,fill-break,fill-color,fill-image,fill-opacity,fill-origin,fill-position,fill-repeat,fill-rule,fill-size,filter,fit-tolerance,flex,flex-basis,flex-direction,flex-flow,flex-grow,flex-line-count,flex-shrink,flex-wrap,float,float-defer,float-offset,float-reference,flood-color,flood-opacity,flow-from,flow-into,font,font-family,font-feature-settings,font-kerning,font-language-override,font-optical-sizing,font-palette,font-presentation,font-size,font-size-adjust,font-stretch,font-style,font-synthesis,font-synthesis-position,font-synthesis-small-caps,font-synthesis-style,font-synthesis-weight,font-variant,font-variant-alternates,font-variant-caps,font-variant-east-asian,font-variant-emoji,font-variant-ligatures,font-variant-numeric,font-variant-position,font-variation-settings,font-weight,font-width,footnote-display,footnote-policy,forced-color-adjust,frame-sizing,gap,glyph-orientation-vertical,grid,grid-area,grid-auto-columns,grid-auto-flow,grid-auto-rows,grid-column,grid-column-end,grid-column-gap,grid-column-start,grid-gap,grid-row,grid-row-end,grid-row-gap,grid-row-start,grid-template,grid-template-areas,grid-template-columns,grid-template-rows,hanging-punctuation,height,hyphenate-character,hyphenate-limit-chars,hyphenate-limit-last,hyphenate-limit-lines,hyphenate-limit-zone,hyphens,image-animation,image-orientation,image-rendering,image-resolution,initial-letter,initial-letter-align,initial-letter-wrap,inline-size,inline-sizing,input-security,inset,inset-block,inset-block-end,inset-block-start,inset-inline,inset-inline-end,inset-inline-start,interactivity,interest-delay,interest-delay-end,interest-delay-start,interpolate-size,isolation,justify-content,justify-items,justify-self,left,letter-spacing,lighting-color,line-break,line-clamp,line-fit-edge,line-grid,line-height,line-height-step,line-padding,line-snap,link-parameters,list-style,list-style-image,list-style-position,list-style-type,margin,margin-block,margin-block-end,margin-block-start,margin-bottom,margin-break,margin-inline,margin-inline-end,margin-inline-start,margin-left,margin-right,margin-top,margin-trim,marker,marker-end,marker-mid,marker-side,marker-start,mask,mask-border,mask-border-mode,mask-border-outset,mask-border-repeat,mask-border-slice,mask-border-source,mask-border-width,mask-clip,mask-composite,mask-image,mask-mode,mask-origin,mask-position,mask-repeat,mask-size,mask-type,math-depth,math-shift,math-style,max-block-size,max-height,max-inline-size,max-lines,max-size,max-width,min-block-size,min-height,min-inline-size,min-intrinsic-sizing,min-size,min-width,mix-blend-mode,nav-down,nav-left,nav-right,nav-up,object-fit,object-position,object-view-box,offset,offset-anchor,offset-distance,offset-path,offset-position,offset-rotate,opacity,order,orphans,outline,outline-color,outline-offset,outline-style,outline-width,overflow,overflow-anchor,overflow-block,overflow-clip-margin,overflow-clip-margin-block,overflow-clip-margin-block-end,overflow-clip-margin-block-start,overflow-clip-margin-bottom,overflow-clip-margin-inline,overflow-clip-margin-inline-end,overflow-clip-margin-inline-start,overflow-clip-margin-left,overflow-clip-margin-right,overflow-clip-margin-top,overflow-inline,overflow-wrap,overflow-x,overflow-y,overlay,overscroll-behavior,overscroll-behavior-block,overscroll-behavior-inline,overscroll-behavior-x,overscroll-behavior-y,padding,padding-block,padding-block-end,padding-block-start,padding-bottom,padding-inline,padding-inline-end,padding-inline-start,padding-left,padding-right,padding-top,page,page-break-after,page-break-before,page-break-inside,paint-order,path-length,pause,pause-after,pause-before,perspective,perspective-origin,place-content,place-items,place-self,pointer-events,pointer-timeline,pointer-timeline-axis,pointer-timeline-name,position,position-anchor,position-area,position-try,position-try-fallbacks,position-try-order,position-visibility,print-color-adjust,quotes,r,reading-flow,reading-order,region-fragment,resize,rest,rest-after,rest-before,right,rotate,row-gap,row-rule,row-rule-break,row-rule-color,row-rule-inset,row-rule-inset-cap,row-rule-inset-cap-end,row-rule-inset-cap-start,row-rule-inset-end,row-rule-inset-junction,row-rule-inset-junction-end,row-rule-inset-junction-start,row-rule-inset-start,row-rule-style,row-rule-visibility-items,row-rule-width,ruby-align,ruby-merge,ruby-overhang,ruby-position,rule,rule-break,rule-color,rule-inset,rule-inset-cap,rule-inset-end,rule-inset-junction,rule-inset-start,rule-overlap,rule-style,rule-visibility-items,rule-width,rx,ry,scale,scroll-axis-lock,scroll-behavior,scroll-initial-target,scroll-margin,scroll-margin-block,scroll-margin-block-end,scroll-margin-block-start,scroll-margin-bottom,scroll-margin-inline,scroll-margin-inline-end,scroll-margin-inline-start,scroll-margin-left,scroll-margin-right,scroll-margin-top,scroll-marker-group,scroll-padding,scroll-padding-block,scroll-padding-block-end,scroll-padding-block-start,scroll-padding-bottom,scroll-padding-inline,scroll-padding-inline-end,scroll-padding-inline-start,scroll-padding-left,scroll-padding-right,scroll-padding-top,scroll-snap-align,scroll-snap-stop,scroll-snap-type,scroll-target-group,scroll-timeline,scroll-timeline-axis,scroll-timeline-name,scrollbar-color,scrollbar-gutter,scrollbar-width,shape-image-threshold,shape-inside,shape-margin,shape-outside,shape-padding,shape-rendering,size,slider-orientation,spatial-navigation-action,spatial-navigation-contain,spatial-navigation-function,speak,speak-as,stop-color,stop-opacity,string-set,stroke,stroke-align,stroke-alignment,stroke-break,stroke-color,stroke-dash-corner,stroke-dash-justify,stroke-dashadjust,stroke-dasharray,stroke-dashcorner,stroke-dashoffset,stroke-image,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,stroke-origin,stroke-position,stroke-repeat,stroke-size,stroke-width,tab-size,table-layout,text-align,text-align-all,text-align-last,text-anchor,text-autospace,text-box,text-box-edge,text-box-trim,text-combine-upright,text-decoration,text-decoration-color,text-decoration-inset,text-decoration-line,text-decoration-skip,text-decoration-skip-box,text-decoration-skip-ink,text-decoration-skip-self,text-decoration-skip-spaces,text-decoration-style,text-decoration-thickness,text-emphasis,text-emphasis-color,text-emphasis-position,text-emphasis-skip,text-emphasis-style,text-fit,text-group-align,text-indent,text-justify,text-orientation,text-overflow,text-rendering,text-shadow,text-size-adjust,text-spacing,text-spacing-trim,text-transform,text-underline-offset,text-underline-position,text-wrap,text-wrap-mode,text-wrap-style,timeline-scope,timeline-trigger,timeline-trigger-activation-range,timeline-trigger-activation-range-end,timeline-trigger-activation-range-start,timeline-trigger-active-range,timeline-trigger-active-range-end,timeline-trigger-active-range-start,timeline-trigger-name,timeline-trigger-range,timeline-trigger-source,top,touch-action,transform,transform-box,transform-origin,transform-style,transition,transition-behavior,transition-delay,transition-duration,transition-property,transition-timing-function,translate,trigger-scope,unicode-bidi,user-select,vector-effect,vertical-align,view-timeline,view-timeline-axis,view-timeline-inset,view-timeline-name,view-transition-class,view-transition-group,view-transition-name,view-transition-scope,visibility,voice-balance,voice-duration,voice-family,voice-pitch,voice-range,voice-rate,voice-stress,voice-volume,white-space,white-space-collapse,white-space-trim,widows,width,will-change,window-drag,word-break,word-space-transform,word-spacing,word-wrap,wrap-after,wrap-before,wrap-flow,wrap-inside,wrap-through,writing-mode,x,y,z-index,zoom".split(",")); +const CUSTOM_IDENT_PROPERTIES = /* @__PURE__ */ new Set("-webkit-animation,-webkit-animation-name,-webkit-transition,-webkit-transition-property,animation,animation-name,color-scheme,container,container-name,copy-into,counter-increment,counter-reset,counter-set,flow-from,flow-into,font,font-family,grid,grid-area,grid-column,grid-column-end,grid-column-start,grid-row,grid-row-end,grid-row-start,grid-template,grid-template-columns,grid-template-rows,list-style,list-style-type,page,string-set,transition,transition-property,view-transition-class,view-transition-group,view-transition-name,will-change".split(",")); +const FONT_FACE_DESCRIPTORS = /* @__PURE__ */ new Set("ascent-override,descent-override,font-display,font-family,font-feature-settings,font-language-override,font-named-instance,font-size,font-stretch,font-style,font-variant,font-variation-settings,font-weight,font-width,line-gap-override,size-adjust,src,subscript-position-override,subscript-size-override,superscript-position-override,superscript-size-override,unicode-range".split(",")); +const PAGE_ALLOWED_PROPERTIES = /* @__PURE__ */ new Set("bleed,break-after,break-before,break-inside,counter-increment,counter-reset,counter-set,margin,margin-block,margin-block-end,margin-block-start,margin-bottom,margin-inline,margin-inline-end,margin-inline-start,margin-left,margin-right,margin-top,marks,orphans,page,page-break-after,page-break-before,page-break-inside,page-margin-safety,page-orientation,quotes,size,widows".split(",")); +const PAGE_DESCRIPTOR_PROPERTIES = [ + "margin", + "marginTop", + "marginRight", + "marginBottom", + "marginLeft", + "margin-top", + "margin-right", + "margin-bottom", + "margin-left", + "size", + "pageOrientation", + "page-orientation", + "marks", + "bleed" +]; +const COUNTER_STYLE_DESCRIPTORS = [ + ["additive-symbols", "additiveSymbols"], + ["fallback", "fallback"], + ["negative", "negative"], + ["pad", "pad"], + ["prefix", "prefix"], + ["range", "range"], + ["speak-as", "speakAs"], + ["suffix", "suffix"], + ["symbols", "symbols"], + ["system", "system"] +]; +const COUNTER_STYLE_PROP_BY_DESCRIPTOR = /* @__PURE__ */ new Map(COUNTER_STYLE_DESCRIPTORS); +const FONT_FEATURE_VALUE_MAP_DEFS = [ + ["annotation", "annotation"], + ["ornaments", "ornaments"], + ["stylistic", "stylistic"], + ["swash", "swash"], + ["character-variant", "characterVariant"], + ["styleset", "styleset"], + ["historical-forms", "historicalForms"] +]; +const FONT_FEATURE_VALUE_BLOCKS = { + "annotation": "annotation", + "ornaments": "ornaments", + "stylistic": "stylistic", + "swash": "swash", + "character-variant": "characterVariant", + "charactervariant": "characterVariant", + "styleset": "styleset", + "historical-forms": "historicalForms", + "historicalforms": "historicalForms" }; - -const {prototype} = CSSStyleDeclaration$1; - -function push(value, key) { - if (key !== PRIVATE) - this.push(`${key}:${value}`); +const MARGIN_RULE_NAMES = /* @__PURE__ */ new Set("bottom-center,bottom-left,bottom-left-corner,bottom-right,bottom-right-corner,left-bottom,left-middle,left-top,right-bottom,right-middle,right-top,top-center,top-left,top-left-corner,top-right,top-right-corner".split(",")); +const GENERIC_FONT_FAMILIES = /* @__PURE__ */ new Set("cursive,emoji,fangsong,fantasy,math,monospace,sans-serif,serif,system-ui,ui-monospace,ui-rounded,ui-sans-serif,ui-serif".split(",")); +const FONT_VARIANT_LIGATURES_KEYWORDS = /* @__PURE__ */ new Set("common-ligatures,no-common-ligatures,discretionary-ligatures,no-discretionary-ligatures,historical-ligatures,no-historical-ligatures,contextual,no-contextual".split(",")); +const FONT_VARIANT_CAPS_KEYWORDS = /* @__PURE__ */ new Set("small-caps,all-small-caps,petite-caps,all-petite-caps,unicase,titling-caps".split(",")); +const FONT_VARIANT_NUMERIC_KEYWORDS = /* @__PURE__ */ new Set("lining-nums,oldstyle-nums,proportional-nums,tabular-nums,diagonal-fractions,stacked-fractions,ordinal,slashed-zero".split(",")); +const FONT_VARIANT_EAST_ASIAN_KEYWORDS = /* @__PURE__ */ new Set("jis78,jis83,jis90,jis04,simplified,traditional,full-width,proportional-width,ruby".split(",")); +const FONT_VARIANT_POSITION_KEYWORDS = /* @__PURE__ */ new Set("sub,super".split(",")); +const FONT_VARIANT_EMOJI_KEYWORDS = /* @__PURE__ */ new Set("text,emoji,unicode".split(",")); +const FONT_VARIANT_ALTERNATES_FUNCTIONS = /* @__PURE__ */ new Set("stylistic,styleset,character-variant,swash,ornaments,annotation".split(",")); +const SYSTEM_FONT_KEYWORDS = /* @__PURE__ */ new Set("initial,inherit,unset,revert,revert-layer,default,caption,icon,menu,message-box,small-caption,status-bar".split(",")); +const FONT_STRETCH_KEYWORDS = /* @__PURE__ */ new Set("ultra-condensed,extra-condensed,condensed,semi-condensed,semi-expanded,expanded,extra-expanded,ultra-expanded".split(",")); +const FONT_SIZE_KEYWORDS = /* @__PURE__ */ new Set("xx-small,x-small,small,medium,large,x-large,xx-large,xxx-large,smaller,larger".split(",")); +const FLEX_BASIS_KEYWORDS = /* @__PURE__ */ new Set("auto,content,max-content,min-content,fit-content".split(",")); +// src/utils.ts +/** +* @license +* Copyright 2026 Google LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +function camelToDashed(str) { + return str.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase()).replace(/^ms-/, "-ms-"); } - -// https://dom.spec.whatwg.org/#interface-event - -/* c8 ignore start */ - -// Node 15 has Event but 14 and 12 don't -const BUBBLING_PHASE = 3; -const AT_TARGET = 2; -const CAPTURING_PHASE = 1; -const NONE = 0; - -function getCurrentTarget(ev) { - return ev.currentTarget; +function createIndexedProxy(target, getArray, mapValue) { + return new Proxy(target, { get(t, prop) { + if (typeof prop === "string" && !isNaN(Number(prop))) { + const index = Number(prop); + const val = getArray(t)[index]; + return val !== void 0 ? mapValue ? mapValue(val) : val : void 0; + } + return t[prop]; + } }); } - -/** - * @implements globalThis.Event - */ -class GlobalEvent { - static get BUBBLING_PHASE() { return BUBBLING_PHASE; } - static get AT_TARGET() { return AT_TARGET; } - static get CAPTURING_PHASE() { return CAPTURING_PHASE; } - static get NONE() { return NONE; } - - constructor(type, eventInitDict = {}) { - this.type = type; - this.bubbles = !!eventInitDict.bubbles; - this.cancelBubble = false; - this._stopImmediatePropagationFlag = false; - this.cancelable = !!eventInitDict.cancelable; - this.eventPhase = this.NONE; - this.timeStamp = Date.now(); - this.defaultPrevented = false; - this.originalTarget = null; - this.returnValue = null; - this.srcElement = null; - this.target = null; - this._path = []; - } - - get BUBBLING_PHASE() { return BUBBLING_PHASE; } - get AT_TARGET() { return AT_TARGET; } - get CAPTURING_PHASE() { return CAPTURING_PHASE; } - get NONE() { return NONE; } - - preventDefault() { this.defaultPrevented = true; } - - // simplified implementation, should be https://dom.spec.whatwg.org/#dom-event-composedpath - composedPath() { - return this._path.map(getCurrentTarget); - } - - stopPropagation() { - this.cancelBubble = true; - } - - stopImmediatePropagation() { - this.stopPropagation(); - this._stopImmediatePropagationFlag = true; - } - } - -/* c8 ignore stop */ - -/** - * @implements globalThis.NamedNodeMap - */ -class NamedNodeMap extends Array { - constructor(ownerElement) { - super(); - this.ownerElement = ownerElement; - } - - getNamedItem(name) { - return this.ownerElement.getAttributeNode(name); - } - - setNamedItem(attr) { - this.ownerElement.setAttributeNode(attr); - this.unshift(attr); - } - - removeNamedItem(name) { - const item = this.getNamedItem(name); - this.ownerElement.removeAttribute(name); - this.splice(this.indexOf(item), 1); - } - - item(index) { - return index < this.length ? this[index] : null; - } - - /* c8 ignore start */ - getNamedItemNS(_, name) { - return this.getNamedItem(name); - } - - setNamedItemNS(_, attr) { - return this.setNamedItem(attr); - } - - removeNamedItemNS(_, name) { - return this.removeNamedItem(name); - } - /* c8 ignore stop */ +function deleteRuleFromArray(rules, index) { + if (index < 0 || index >= rules.length) throw new DOMException("Index size error", "IndexSizeError"); + const oldRule = rules[index]; + rules.splice(index, 1); + if (oldRule && typeof oldRule === "object") { + if ("_parentRule" in oldRule) oldRule._parentRule = null; + if ("_parentStyleSheet" in oldRule) oldRule._parentStyleSheet = null; + } + return oldRule; } - +// src/parse-hooks.ts /** - * @implements globalThis.ShadowRoot - */ -let ShadowRoot$1 = class ShadowRoot extends NonElementParentNode { - constructor(host) { - super(host.ownerDocument, '#shadow-root', DOCUMENT_FRAGMENT_NODE); - this.host = host; - } - - get innerHTML() { - return getInnerHtml(this); - } - set innerHTML(html) { - setInnerHtml(this, html); - } +* @license +* Copyright 2026 Google LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +/** +* Upward optional feature hooks allowing `cssomnom/parse` (`./parse`) to tree-shake +* out Typed OM, `@property` registry, `MediaParser`, and `CSS.supports`. +* +* Downward parser functions are imported directly from `src/parser.ts`. +*/ +const ParseHooks = { + parseMediaQueryList: (text) => { + return text.split(",").map((s) => s.trim()).filter(Boolean).map((m) => ({ + type: "media-query", + mediaType: m, + tokens: [], + raw: m + })); + }, + serializeMediaQuery: (query) => { + return query.raw ?? query.mediaType ?? ""; + }, + validatePropertyValue: (_property, _value, _rawValue) => { + return true; + }, + supports: (_conditionText) => { + return false; + }, + createStylePropertyMap: (_style) => { + throw new Error("createStylePropertyMap not injected (import cssomnom/typed-om)"); + }, + validatePropertyRuleDescriptor: (_def) => { + return true; + }, + registerPropertyRule: (_def) => { + return false; + }, + unregisterPropertyRule: (_name) => {} }; - -// https://dom.spec.whatwg.org/#interface-element - - -// -const attributesHandler = { - get(target, key) { - return key in target ? target[key] : target.find(({name}) => name === key); - } +// src/rules/collections.ts +/** +* @license +* Copyright 2026 Google LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +var StyleSheetList = class { + _sheets; + constructor(sheets) { + this._sheets = sheets; + return createIndexedProxy(this, (t) => t._sheets); + } + get length() { + return this._sheets.length; + } + item(index) { + return this._sheets[index] || null; + } + get [Symbol.toStringTag]() { + return "StyleSheetList"; + } + *[Symbol.iterator]() { + for (let i = 0; i < this.length; i++) yield this._sheets[i]; + } }; - -const create = (ownerDocument, element, localName) => { - if ('ownerSVGElement' in element) { - const svg = ownerDocument.createElementNS(SVG_NAMESPACE, localName); - svg.ownerSVGElement = element.ownerSVGElement; - return svg; - } - return ownerDocument.createElement(localName); +var MediaList = class { + _mediaQueries = []; + constructor(mediaText = "") { + this.mediaText = mediaText; + return createIndexedProxy(this, (t) => t._mediaQueries.map((q) => ParseHooks.serializeMediaQuery(q))); + } + get [Symbol.toStringTag]() { + return "MediaList"; + } + get mediaText() { + return this._mediaQueries.map((q) => ParseHooks.serializeMediaQuery(q)).join(", "); + } + set mediaText(value) { + if (!value) { + this._mediaQueries = []; + return; + } + this._mediaQueries = ParseHooks.parseMediaQueryList(value); + } + get length() { + return this._mediaQueries.length; + } + item(index) { + const q = this._mediaQueries[index]; + return q ? ParseHooks.serializeMediaQuery(q) : null; + } + toString() { + return this.mediaText; + } + get mediaQueriesAST() { + return this._mediaQueries; + } + appendMedium(medium) { + const parsed = ParseHooks.parseMediaQueryList(medium); + if (parsed.length !== 1) return; + const m = parsed[0]; + const mText = ParseHooks.serializeMediaQuery(m); + if (this._mediaQueries.some((q) => ParseHooks.serializeMediaQuery(q) === mText)) return; + this._mediaQueries.push(m); + } + deleteMedium(medium) { + const parsed = ParseHooks.parseMediaQueryList(medium); + if (parsed.length !== 1) return; + const mText = ParseHooks.serializeMediaQuery(parsed[0]); + let i = this._mediaQueries.length; + let found = false; + while (i--) if (ParseHooks.serializeMediaQuery(this._mediaQueries[i]) === mText) { + this._mediaQueries.splice(i, 1); + found = true; + } + if (!found) throw new DOMException(`The medium '${medium}' does not exist in the MediaList.`, "NotFoundError"); + } + *[Symbol.iterator]() { + for (let i = 0; i < this.length; i++) yield ParseHooks.serializeMediaQuery(this._mediaQueries[i]); + } }; - -const isVoid = ({localName, ownerDocument}) => { - return ownerDocument[MIME].voidElements.test(localName); +var CSSRuleList = class { + _getRules; + constructor(rulesOrGetter) { + this._getRules = typeof rulesOrGetter === "function" ? rulesOrGetter : () => rulesOrGetter; + return createIndexedProxy(this, (t) => t._getRules(), (v) => v); + } + get length() { + return this._getRules().length; + } + item(index) { + return this._getRules()[index] || null; + } + get [Symbol.toStringTag]() { + return "CSSRuleList"; + } + *[Symbol.iterator]() { + const rules = this._getRules(); + for (let i = 0; i < rules.length; i++) yield rules[i]; + } }; - -// - +// src/rules/utils.ts /** - * @implements globalThis.Element - */ -let Element$1 = class Element extends ParentNode { - constructor(ownerDocument, localName) { - super(ownerDocument, localName, ELEMENT_NODE); - this[CLASS_LIST] = null; - this[DATASET] = null; - this[STYLE] = null; - } - - // - get isConnected() { return isConnected(this); } - get parentElement() { return parentElement(this); } - get previousSibling() { return previousSibling(this); } - get nextSibling() { return nextSibling(this); } - get namespaceURI() { - return 'http://www.w3.org/1999/xhtml'; - } - - get previousElementSibling() { return previousElementSibling(this); } - get nextElementSibling() { return nextElementSibling$1(this); } - - before(...nodes) { before(this, nodes); } - after(...nodes) { after(this, nodes); } - replaceWith(...nodes) { replaceWith(this, nodes); } - remove() { remove(this[PREV], this, this[END][NEXT]); } - // - - // - get id() { return stringAttribute.get(this, 'id'); } - set id(value) { stringAttribute.set(this, 'id', value); } - - get className() { return this.classList.value; } - set className(value) { - const {classList} = this; - classList.clear(); - classList.add(...($String(value).split(/\s+/))); - } - - get nodeName() { return localCase(this); } - get tagName() { return localCase(this); } - - get classList() { - return this[CLASS_LIST] || ( - this[CLASS_LIST] = new DOMTokenList(this) - ); - } - - get dataset() { - return this[DATASET] || ( - this[DATASET] = new DOMStringMap(this) - ); - } - - getBoundingClientRect() { - return { - x: 0, - y: 0, - bottom: 0, - height: 0, - left: 0, - right: 0, - top: 0, - width: 0 - }; - } - - get nonce() { return stringAttribute.get(this, 'nonce'); } - set nonce(value) { stringAttribute.set(this, 'nonce', value); } - - get style() { - return this[STYLE] || ( - this[STYLE] = new CSSStyleDeclaration$1(this) - ); - } - - get tabIndex() { return numericAttribute.get(this, 'tabindex') || -1; } - set tabIndex(value) { numericAttribute.set(this, 'tabindex', value); } - - get slot() { return stringAttribute.get(this, 'slot'); } - set slot(value) { stringAttribute.set(this, 'slot', value); } - // - - - // - get innerText() { - const text = []; - let {[NEXT]: next, [END]: end} = this; - while (next !== end) { - if (next.nodeType === TEXT_NODE) { - text.push(next.textContent.replace(/\s+/g, ' ')); - } else if( - text.length && next[NEXT] != end && - BLOCK_ELEMENTS.has(next.tagName) - ) { - text.push('\n'); - } - next = next[NEXT]; - } - return text.join(''); - } - - /** - * @returns {String} - */ - get textContent() { - const text = []; - let {[NEXT]: next, [END]: end} = this; - while (next !== end) { - const nodeType = next.nodeType; - if (nodeType === TEXT_NODE || nodeType === CDATA_SECTION_NODE) - text.push(next.textContent); - next = next[NEXT]; - } - return text.join(''); - } - - set textContent(text) { - this.replaceChildren(); - if (text != null && text !== '') - this.appendChild(new Text$1(this.ownerDocument, text)); - } - - get innerHTML() { - return getInnerHtml(this); - } - set innerHTML(html) { - setInnerHtml(this, html); - } - - get outerHTML() { return this.toString(); } - set outerHTML(html) { - const template = this.ownerDocument.createElement(''); - template.innerHTML = html; - this.replaceWith(...template.childNodes); - } - // - - // - get attributes() { - const attributes = new NamedNodeMap(this); - let next = this[NEXT]; - while (next.nodeType === ATTRIBUTE_NODE) { - attributes.push(next); - next = next[NEXT]; - } - return new Proxy(attributes, attributesHandler); - } - - focus() { this.dispatchEvent(new GlobalEvent('focus')); } - - getAttribute(name) { - if (name === 'class') - return this.className; - const attribute = this.getAttributeNode(name); - return attribute && (ignoreCase(this) ? attribute.value : escape(attribute.value)); - } - - getAttributeNode(name) { - let next = this[NEXT]; - while (next.nodeType === ATTRIBUTE_NODE) { - if (next.name === name) - return next; - next = next[NEXT]; - } - return null; - } - - getAttributeNames() { - const attributes = new NodeList; - let next = this[NEXT]; - while (next.nodeType === ATTRIBUTE_NODE) { - attributes.push(next.name); - next = next[NEXT]; - } - return attributes; - } - - hasAttribute(name) { return !!this.getAttributeNode(name); } - hasAttributes() { return this[NEXT].nodeType === ATTRIBUTE_NODE; } - - removeAttribute(name) { - if (name === 'class' && this[CLASS_LIST]) - this[CLASS_LIST].clear(); - let next = this[NEXT]; - while (next.nodeType === ATTRIBUTE_NODE) { - if (next.name === name) { - removeAttribute(this, next); - return; - } - next = next[NEXT]; - } - } - - removeAttributeNode(attribute) { - let next = this[NEXT]; - while (next.nodeType === ATTRIBUTE_NODE) { - if (next === attribute) { - removeAttribute(this, next); - return; - } - next = next[NEXT]; - } - } - - setAttribute(name, value) { - if (name === 'class') - this.className = value; - else { - const attribute = this.getAttributeNode(name); - if (attribute) - attribute.value = value; - else - setAttribute(this, new Attr$1(this.ownerDocument, name, value)); - } - } - - setAttributeNode(attribute) { - const {name} = attribute; - const previously = this.getAttributeNode(name); - if (previously !== attribute) { - if (previously) - this.removeAttributeNode(previously); - const {ownerElement} = attribute; - if (ownerElement) - ownerElement.removeAttributeNode(attribute); - setAttribute(this, attribute); - } - return previously; - } - - toggleAttribute(name, force) { - if (this.hasAttribute(name)) { - if (!force) { - this.removeAttribute(name); - return false; - } - return true; - } - else if (force || arguments.length === 1) { - this.setAttribute(name, ''); - return true; - } - return false; - } - // - - // - get shadowRoot() { - if (shadowRoots.has(this)) { - const {mode, shadowRoot} = shadowRoots.get(this); - if (mode === 'open') - return shadowRoot; - } - return null; - } - - attachShadow(init) { - if (shadowRoots.has(this)) - throw new Error('operation not supported'); - // TODO: shadowRoot should be likely a specialized class that extends DocumentFragment - // but until DSD is out, I am not sure I should spend time on this. - const shadowRoot = new ShadowRoot$1(this); - shadowRoots.set(this, { - mode: init.mode, - shadowRoot - }); - return shadowRoot; - } - // - - // - matches(selectors) { return matches(this, selectors); } - closest(selectors) { - let parentElement = this; - const matches = prepareMatch(parentElement, selectors); - while (parentElement && !matches(parentElement)) - parentElement = parentElement.parentElement; - return parentElement; - } - // - - // - insertAdjacentElement(position, element) { - const {parentElement} = this; - switch (position) { - case 'beforebegin': - if (parentElement) { - parentElement.insertBefore(element, this); - break; - } - return null; - case 'afterbegin': - this.insertBefore(element, this.firstChild); - break; - case 'beforeend': - this.insertBefore(element, null); - break; - case 'afterend': - if (parentElement) { - parentElement.insertBefore(element, this.nextSibling); - break; - } - return null; - } - return element; - } - - insertAdjacentHTML(position, html) { - this.insertAdjacentElement(position, htmlToFragment(this.ownerDocument, html)); - } - - insertAdjacentText(position, text) { - const node = this.ownerDocument.createTextNode(text); - this.insertAdjacentElement(position, node); - } - // - - cloneNode(deep = false) { - const {ownerDocument, localName} = this; - const addNext = next => { - next.parentNode = parentNode; - knownAdjacent($next, next); - $next = next; - }; - const clone = create(ownerDocument, this, localName); - let parentNode = clone, $next = clone; - let {[NEXT]: next, [END]: prev} = this; - while (next !== prev && (deep || next.nodeType === ATTRIBUTE_NODE)) { - switch (next.nodeType) { - case NODE_END: - knownAdjacent($next, parentNode[END]); - $next = parentNode[END]; - parentNode = parentNode.parentNode; - break; - case ELEMENT_NODE: { - const node = create(ownerDocument, next, next.localName); - addNext(node); - parentNode = node; - break; - } - case ATTRIBUTE_NODE: { - const attr = next.cloneNode(deep); - attr.ownerElement = parentNode; - addNext(attr); - break; - } - case TEXT_NODE: - case COMMENT_NODE: - case CDATA_SECTION_NODE: - addNext(next.cloneNode(deep)); - break; - } - next = next[NEXT]; - } - knownAdjacent($next, clone[END]); - return clone; - } - - // - toString() { - const out = []; - const {[END]: end} = this; - let next = {[NEXT]: this}; - let isOpened = false; - do { - next = next[NEXT]; - switch (next.nodeType) { - case ATTRIBUTE_NODE: { - const attr = ' ' + next; - switch (attr) { - case ' id': - case ' class': - case ' style': - break; - default: - out.push(attr); - } - break; - } - case NODE_END: { - const start = next[START]; - if (isOpened) { - if ('ownerSVGElement' in start) - out.push(' />'); - else if (isVoid(start)) - out.push(ignoreCase(start) ? '>' : ' />'); - else - out.push(`>`); - isOpened = false; - } - else - out.push(``); - break; - } - case ELEMENT_NODE: - if (isOpened) - out.push('>'); - if (next.toString !== this.toString) { - out.push(next.toString()); - next = next[END]; - isOpened = false; - } - else { - out.push(`<${next.localName}`); - isOpened = true; - } - break; - case TEXT_NODE: - case COMMENT_NODE: - case CDATA_SECTION_NODE: - out.push((isOpened ? '>' : '') + next); - isOpened = false; - break; - } - } while (next !== end); - return out.join(''); - } - - toJSON() { - const json = []; - elementAsJSON(this, json); - return json; - } - // - - - /* c8 ignore start */ - getAttributeNS(_, name) { return this.getAttribute(name); } - getElementsByTagNameNS(_, name) { return this.getElementsByTagName(name); } - hasAttributeNS(_, name) { return this.hasAttribute(name); } - removeAttributeNS(_, name) { this.removeAttribute(name); } - setAttributeNS(_, name, value) { this.setAttribute(name, value); } - setAttributeNodeNS(attr) { return this.setAttributeNode(attr); } - /* c8 ignore stop */ -}; - -const classNames = new WeakMap; - -const handler = { - get(target, name) { - return target[name]; - }, - set(target, name, value) { - target[name] = value; - return true; - } -}; - -/** - * @implements globalThis.SVGElement - */ -let SVGElement$1 = class SVGElement extends Element$1 { - constructor(ownerDocument, localName, ownerSVGElement = null) { - super(ownerDocument, localName); - this.ownerSVGElement = ownerSVGElement; - } - - get className() { - if (!classNames.has(this)) - classNames.set(this, new Proxy({baseVal: '', animVal: ''}, handler)); - return classNames.get(this); - } - - /* c8 ignore start */ - set className(value) { - const {classList} = this; - classList.clear(); - classList.add(...($String(value).split(/\s+/))); - } - /* c8 ignore stop */ - - get namespaceURI() { - return 'http://www.w3.org/2000/svg'; - } - - getAttribute(name) { - return name === 'class' ? - [...this.classList].join(' ') : - super.getAttribute(name); - } - - setAttribute(name, value) { - if (name === 'class') - this.className = value; - else if (name === 'style') { - const {className} = this; - className.baseVal = className.animVal = value; - } - super.setAttribute(name, value); - } -}; - -/* c8 ignore start */ -const illegalConstructor = () => { - throw new TypeError('Illegal constructor'); -}; - -function Attr() { illegalConstructor(); } -setPrototypeOf(Attr, Attr$1); -Attr.prototype = Attr$1.prototype; - -function CDATASection() { illegalConstructor(); } -setPrototypeOf(CDATASection, CDATASection$1); -CDATASection.prototype = CDATASection$1.prototype; - -function CharacterData() { illegalConstructor(); } -setPrototypeOf(CharacterData, CharacterData$1); -CharacterData.prototype = CharacterData$1.prototype; - -function Comment() { illegalConstructor(); } -setPrototypeOf(Comment, Comment$2); -Comment.prototype = Comment$2.prototype; - -function DocumentFragment() { illegalConstructor(); } -setPrototypeOf(DocumentFragment, DocumentFragment$1); -DocumentFragment.prototype = DocumentFragment$1.prototype; - -function DocumentType() { illegalConstructor(); } -setPrototypeOf(DocumentType, DocumentType$1); -DocumentType.prototype = DocumentType$1.prototype; - -function Element() { illegalConstructor(); } -setPrototypeOf(Element, Element$1); -Element.prototype = Element$1.prototype; - -function Node() { illegalConstructor(); } -setPrototypeOf(Node, Node$1); -Node.prototype = Node$1.prototype; - -function ShadowRoot() { illegalConstructor(); } -setPrototypeOf(ShadowRoot, ShadowRoot$1); -ShadowRoot.prototype = ShadowRoot$1.prototype; - -function Text() { illegalConstructor(); } -setPrototypeOf(Text, Text$1); -Text.prototype = Text$1.prototype; - -function SVGElement() { illegalConstructor(); } -setPrototypeOf(SVGElement, SVGElement$1); -SVGElement.prototype = SVGElement$1.prototype; -/* c8 ignore stop */ - -const Facades = { - Attr, - CDATASection, - CharacterData, - Comment, - DocumentFragment, - DocumentType, - Element, - Node, - ShadowRoot, - Text, - SVGElement -}; - -const Level0 = new WeakMap; -const level0 = { - get(element, name) { - return Level0.has(element) && Level0.get(element)[name] || null; - }, - set(element, name, value) { - if (!Level0.has(element)) - Level0.set(element, {}); - const handlers = Level0.get(element); - const type = name.slice(2); - if (handlers[name]) - element.removeEventListener(type, handlers[name], false); - if ((handlers[name] = value)) - element.addEventListener(type, value, false); - } -}; - -/** - * @implements globalThis.HTMLElement - */ -class HTMLElement extends Element$1 { - - static get observedAttributes() { return []; } - - constructor(ownerDocument = null, localName = '') { - super(ownerDocument, localName); - - const ownerLess = !ownerDocument; - let options; - - if (ownerLess) { - const {constructor: Class} = this; - if (!Classes.has(Class)) - throw new Error('unable to initialize this Custom Element'); - ({ownerDocument, localName, options} = Classes.get(Class)); - } - - if (ownerDocument[UPGRADE]) { - const {element, values} = ownerDocument[UPGRADE]; - ownerDocument[UPGRADE] = null; - for (const [key, value] of values) - element[key] = value; - return element; - } - - if (ownerLess) { - this.ownerDocument = this[END].ownerDocument = ownerDocument; - this.localName = localName; - customElements.set(this, {connected: false}); - if (options.is) - this.setAttribute('is', options.is); - } - } - - /* c8 ignore start */ - - /* TODO: what about these? - offsetHeight - offsetLeft - offsetParent - offsetTop - offsetWidth - */ - - blur() { this.dispatchEvent(new GlobalEvent('blur')); } - click() { - const clickEvent = new GlobalEvent('click', {bubbles: true, cancelable: true}); - clickEvent.button = 0; - - this.dispatchEvent(clickEvent); - } - - // Boolean getters - get accessKeyLabel() { - const {accessKey} = this; - return accessKey && `Alt+Shift+${accessKey}`; - } - get isContentEditable() { return this.hasAttribute('contenteditable'); } - - // Boolean Accessors - get contentEditable() { return booleanAttribute.get(this, 'contenteditable'); } - set contentEditable(value) { booleanAttribute.set(this, 'contenteditable', value); } - get draggable() { return booleanAttribute.get(this, 'draggable'); } - set draggable(value) { booleanAttribute.set(this, 'draggable', value); } - get hidden() { return booleanAttribute.get(this, 'hidden'); } - set hidden(value) { booleanAttribute.set(this, 'hidden', value); } - get spellcheck() { return booleanAttribute.get(this, 'spellcheck'); } - set spellcheck(value) { booleanAttribute.set(this, 'spellcheck', value); } - - // String Accessors - get accessKey() { return stringAttribute.get(this, 'accesskey'); } - set accessKey(value) { stringAttribute.set(this, 'accesskey', value); } - get dir() { return stringAttribute.get(this, 'dir'); } - set dir(value) { stringAttribute.set(this, 'dir', value); } - get lang() { return stringAttribute.get(this, 'lang'); } - set lang(value) { stringAttribute.set(this, 'lang', value); } - get title() { return stringAttribute.get(this, 'title'); } - set title(value) { stringAttribute.set(this, 'title', value); } - - // DOM Level 0 - get onabort() { return level0.get(this, 'onabort'); } - set onabort(value) { level0.set(this, 'onabort', value); } - - get onblur() { return level0.get(this, 'onblur'); } - set onblur(value) { level0.set(this, 'onblur', value); } - - get oncancel() { return level0.get(this, 'oncancel'); } - set oncancel(value) { level0.set(this, 'oncancel', value); } - - get oncanplay() { return level0.get(this, 'oncanplay'); } - set oncanplay(value) { level0.set(this, 'oncanplay', value); } - - get oncanplaythrough() { return level0.get(this, 'oncanplaythrough'); } - set oncanplaythrough(value) { level0.set(this, 'oncanplaythrough', value); } - - get onchange() { return level0.get(this, 'onchange'); } - set onchange(value) { level0.set(this, 'onchange', value); } - - get onclick() { return level0.get(this, 'onclick'); } - set onclick(value) { level0.set(this, 'onclick', value); } - - get onclose() { return level0.get(this, 'onclose'); } - set onclose(value) { level0.set(this, 'onclose', value); } - - get oncontextmenu() { return level0.get(this, 'oncontextmenu'); } - set oncontextmenu(value) { level0.set(this, 'oncontextmenu', value); } - - get oncuechange() { return level0.get(this, 'oncuechange'); } - set oncuechange(value) { level0.set(this, 'oncuechange', value); } - - get ondblclick() { return level0.get(this, 'ondblclick'); } - set ondblclick(value) { level0.set(this, 'ondblclick', value); } - - get ondrag() { return level0.get(this, 'ondrag'); } - set ondrag(value) { level0.set(this, 'ondrag', value); } - - get ondragend() { return level0.get(this, 'ondragend'); } - set ondragend(value) { level0.set(this, 'ondragend', value); } - - get ondragenter() { return level0.get(this, 'ondragenter'); } - set ondragenter(value) { level0.set(this, 'ondragenter', value); } - - get ondragleave() { return level0.get(this, 'ondragleave'); } - set ondragleave(value) { level0.set(this, 'ondragleave', value); } - - get ondragover() { return level0.get(this, 'ondragover'); } - set ondragover(value) { level0.set(this, 'ondragover', value); } - - get ondragstart() { return level0.get(this, 'ondragstart'); } - set ondragstart(value) { level0.set(this, 'ondragstart', value); } - - get ondrop() { return level0.get(this, 'ondrop'); } - set ondrop(value) { level0.set(this, 'ondrop', value); } - - get ondurationchange() { return level0.get(this, 'ondurationchange'); } - set ondurationchange(value) { level0.set(this, 'ondurationchange', value); } - - get onemptied() { return level0.get(this, 'onemptied'); } - set onemptied(value) { level0.set(this, 'onemptied', value); } - - get onended() { return level0.get(this, 'onended'); } - set onended(value) { level0.set(this, 'onended', value); } - - get onerror() { return level0.get(this, 'onerror'); } - set onerror(value) { level0.set(this, 'onerror', value); } - - get onfocus() { return level0.get(this, 'onfocus'); } - set onfocus(value) { level0.set(this, 'onfocus', value); } - - get oninput() { return level0.get(this, 'oninput'); } - set oninput(value) { level0.set(this, 'oninput', value); } - - get oninvalid() { return level0.get(this, 'oninvalid'); } - set oninvalid(value) { level0.set(this, 'oninvalid', value); } - - get onkeydown() { return level0.get(this, 'onkeydown'); } - set onkeydown(value) { level0.set(this, 'onkeydown', value); } - - get onkeypress() { return level0.get(this, 'onkeypress'); } - set onkeypress(value) { level0.set(this, 'onkeypress', value); } - - get onkeyup() { return level0.get(this, 'onkeyup'); } - set onkeyup(value) { level0.set(this, 'onkeyup', value); } - - get onload() { return level0.get(this, 'onload'); } - set onload(value) { level0.set(this, 'onload', value); } - - get onloadeddata() { return level0.get(this, 'onloadeddata'); } - set onloadeddata(value) { level0.set(this, 'onloadeddata', value); } - - get onloadedmetadata() { return level0.get(this, 'onloadedmetadata'); } - set onloadedmetadata(value) { level0.set(this, 'onloadedmetadata', value); } - - get onloadstart() { return level0.get(this, 'onloadstart'); } - set onloadstart(value) { level0.set(this, 'onloadstart', value); } - - get onmousedown() { return level0.get(this, 'onmousedown'); } - set onmousedown(value) { level0.set(this, 'onmousedown', value); } - - get onmouseenter() { return level0.get(this, 'onmouseenter'); } - set onmouseenter(value) { level0.set(this, 'onmouseenter', value); } - - get onmouseleave() { return level0.get(this, 'onmouseleave'); } - set onmouseleave(value) { level0.set(this, 'onmouseleave', value); } - - get onmousemove() { return level0.get(this, 'onmousemove'); } - set onmousemove(value) { level0.set(this, 'onmousemove', value); } - - get onmouseout() { return level0.get(this, 'onmouseout'); } - set onmouseout(value) { level0.set(this, 'onmouseout', value); } - - get onmouseover() { return level0.get(this, 'onmouseover'); } - set onmouseover(value) { level0.set(this, 'onmouseover', value); } - - get onmouseup() { return level0.get(this, 'onmouseup'); } - set onmouseup(value) { level0.set(this, 'onmouseup', value); } - - get onmousewheel() { return level0.get(this, 'onmousewheel'); } - set onmousewheel(value) { level0.set(this, 'onmousewheel', value); } - - get onpause() { return level0.get(this, 'onpause'); } - set onpause(value) { level0.set(this, 'onpause', value); } - - get onplay() { return level0.get(this, 'onplay'); } - set onplay(value) { level0.set(this, 'onplay', value); } - - get onplaying() { return level0.get(this, 'onplaying'); } - set onplaying(value) { level0.set(this, 'onplaying', value); } - - get onprogress() { return level0.get(this, 'onprogress'); } - set onprogress(value) { level0.set(this, 'onprogress', value); } - - get onratechange() { return level0.get(this, 'onratechange'); } - set onratechange(value) { level0.set(this, 'onratechange', value); } - - get onreset() { return level0.get(this, 'onreset'); } - set onreset(value) { level0.set(this, 'onreset', value); } - - get onresize() { return level0.get(this, 'onresize'); } - set onresize(value) { level0.set(this, 'onresize', value); } - - get onscroll() { return level0.get(this, 'onscroll'); } - set onscroll(value) { level0.set(this, 'onscroll', value); } - - get onseeked() { return level0.get(this, 'onseeked'); } - set onseeked(value) { level0.set(this, 'onseeked', value); } - - get onseeking() { return level0.get(this, 'onseeking'); } - set onseeking(value) { level0.set(this, 'onseeking', value); } - - get onselect() { return level0.get(this, 'onselect'); } - set onselect(value) { level0.set(this, 'onselect', value); } - - get onshow() { return level0.get(this, 'onshow'); } - set onshow(value) { level0.set(this, 'onshow', value); } - - get onstalled() { return level0.get(this, 'onstalled'); } - set onstalled(value) { level0.set(this, 'onstalled', value); } - - get onsubmit() { return level0.get(this, 'onsubmit'); } - set onsubmit(value) { level0.set(this, 'onsubmit', value); } - - get onsuspend() { return level0.get(this, 'onsuspend'); } - set onsuspend(value) { level0.set(this, 'onsuspend', value); } - - get ontimeupdate() { return level0.get(this, 'ontimeupdate'); } - set ontimeupdate(value) { level0.set(this, 'ontimeupdate', value); } - - get ontoggle() { return level0.get(this, 'ontoggle'); } - set ontoggle(value) { level0.set(this, 'ontoggle', value); } - - get onvolumechange() { return level0.get(this, 'onvolumechange'); } - set onvolumechange(value) { level0.set(this, 'onvolumechange', value); } - - get onwaiting() { return level0.get(this, 'onwaiting'); } - set onwaiting(value) { level0.set(this, 'onwaiting', value); } - - get onauxclick() { return level0.get(this, 'onauxclick'); } - set onauxclick(value) { level0.set(this, 'onauxclick', value); } - - get ongotpointercapture() { return level0.get(this, 'ongotpointercapture'); } - set ongotpointercapture(value) { level0.set(this, 'ongotpointercapture', value); } - - get onlostpointercapture() { return level0.get(this, 'onlostpointercapture'); } - set onlostpointercapture(value) { level0.set(this, 'onlostpointercapture', value); } - - get onpointercancel() { return level0.get(this, 'onpointercancel'); } - set onpointercancel(value) { level0.set(this, 'onpointercancel', value); } - - get onpointerdown() { return level0.get(this, 'onpointerdown'); } - set onpointerdown(value) { level0.set(this, 'onpointerdown', value); } - - get onpointerenter() { return level0.get(this, 'onpointerenter'); } - set onpointerenter(value) { level0.set(this, 'onpointerenter', value); } - - get onpointerleave() { return level0.get(this, 'onpointerleave'); } - set onpointerleave(value) { level0.set(this, 'onpointerleave', value); } - - get onpointermove() { return level0.get(this, 'onpointermove'); } - set onpointermove(value) { level0.set(this, 'onpointermove', value); } - - get onpointerout() { return level0.get(this, 'onpointerout'); } - set onpointerout(value) { level0.set(this, 'onpointerout', value); } - - get onpointerover() { return level0.get(this, 'onpointerover'); } - set onpointerover(value) { level0.set(this, 'onpointerover', value); } - - get onpointerup() { return level0.get(this, 'onpointerup'); } - set onpointerup(value) { level0.set(this, 'onpointerup', value); } - /* c8 ignore stop */ - +* @license +* Copyright 2026 Google LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +function isImportRule(r) { + return r.type === 3; +} +function isNamespaceRule(r) { + return r.type === 10; +} +function isRegularRule(r) { + return !isImportRule(r) && !isNamespaceRule(r); +} +function serializeGroupingRule(atKeyword, condition, rules) { + const cond = condition ? " " + condition : ""; + const ruleTexts = rules.map((r) => r.cssText).filter((p) => p !== ""); + if (ruleTexts.length === 0) return atKeyword === "keyframes" ? `@${atKeyword}${cond} { }` : `@${atKeyword}${cond} {\n}`; + return `@${atKeyword}${cond} {\n${ruleTexts.map((t) => " " + t).join("\n")}\n}`; +} +function findParentStyleSheet(rule) { + let sheet = rule.parentStyleSheet; + let curr = rule.parentRule; + while (!sheet && curr) { + sheet = curr.parentStyleSheet; + curr = curr.parentRule; + } + return sheet; +} +/** +* Validates that component values match grammar. +* css-conditional-3 #at-supports +*/ +function isGrammaticallyValidSupportsCondition(values) { + const items = values.filter((v) => v.type !== "whitespace" && v.type !== "comment"); + if (items.length === 0) return false; + const isSupportsInParens = (item) => { + if (item.type === "function") return true; + if (item.type === "simple-block" && item.associatedToken.value === "(") { + const blockValues = item.value.filter((v) => v.type !== "whitespace" && v.type !== "comment"); + if (blockValues.length === 0) return true; + const hasTopOp = blockValues.some((v) => v.type === "ident" && [ + "and", + "or", + "not" + ].includes(String(v.value).toLowerCase())); + const isNestedParenBlock = blockValues.length === 1 && blockValues[0].type === "simple-block" && blockValues[0].associatedToken.value === "("; + if (hasTopOp || isNestedParenBlock) return isGrammaticallyValidSupportsCondition(item.value); + return true; + } + return false; + }; + const operands = []; + const operators = []; + let i = 0; + while (i < items.length) { + if (items[i].type === "ident" && String(items[i].value).toLowerCase() === "not") { + if (i + 1 >= items.length) return false; + if (!isSupportsInParens(items[i + 1])) return false; + operands.push(true); + i += 2; + } else if (isSupportsInParens(items[i])) { + operands.push(true); + i += 1; + } else return false; + if (i < items.length) { + if (items[i].type !== "ident") return false; + const op = String(items[i].value).toLowerCase(); + if (op !== "and" && op !== "or") return false; + operators.push(op); + i += 1; + } + } + if (operands.length === 0) return false; + if (operands.length !== operators.length + 1) return false; + if (operators.length > 0) { + const firstOp = operators[0]; + if (!operators.every((op) => op === firstOp)) return false; + } + return true; +} +// src/webidl.ts +/** +* @license +* Copyright 2026 Google LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +function hideInternalFields(instance) { + for (const key of Object.getOwnPropertyNames(instance)) if (key.startsWith("_")) Object.defineProperty(instance, key, { enumerable: false }); +} +const appliedCtors = /* @__PURE__ */ new WeakSet(); +/** +* Aligns regular prototype properties of a WebIDL interface object with specification requirements: +* 1. Interface prototype members are { enumerable: true, configurable: true }. +* 2. Attribute getters/setters perform brand checks, throwing TypeError when invoked on the prototype +* itself or on an incompatible receiver (WebIDL § 3.6.1, § 3.6.2). +* 3. Operations are { writable: true, enumerable: true, configurable: true } and throw TypeError +* when invoked with fewer arguments than required (WebIDL § 3.7). +*/ +function applyWebIDLInterface(ctor, options = {}) { + const proto = ctor.prototype; + if (!proto) return; + if (appliedCtors.has(ctor)) return; + appliedCtors.add(ctor); + const brandCheck = options.brandCheck ?? true; + const arityCheck = options.arityCheck ?? true; + const interfaceName = options.interfaceName || ctor.name; + const excludeSet = new Set(options.exclude || []); + const rejectSet = new Set(options.rejectOperations || []); + Object.defineProperty(ctor, "length", { + value: 0, + writable: false, + enumerable: false, + configurable: true + }); + const names = Object.getOwnPropertyNames(proto); + for (const name of names) { + if (name === "constructor" || name.startsWith("_") || excludeSet.has(name)) continue; + const desc = Object.getOwnPropertyDescriptor(proto, name); + if (!desc) continue; + if (desc.get || desc.set) { + const origGet = desc.get; + const origSet = desc.set; + let get = origGet; + if (origGet && brandCheck) { + get = function() { + if (!this || this === proto || !(this instanceof ctor)) throw new TypeError(`Failed to read the '${name}' property from '${interfaceName}': The provided value is not of type '${interfaceName}'.`); + return origGet.call(this); + }; + Object.defineProperty(get, "name", { + value: `get ${name}`, + configurable: true + }); + } + let set = origSet; + if (origSet && brandCheck) { + set = function(val) { + if (!this || this === proto || !(this instanceof ctor)) throw new TypeError(`Failed to set the '${name}' property on '${interfaceName}': The provided value is not of type '${interfaceName}'.`); + return origSet.call(this, val); + }; + Object.defineProperty(set, "name", { + value: `set ${name}`, + configurable: true + }); + } + Object.defineProperty(proto, name, { + get, + set: origSet ? set : void 0, + enumerable: true, + configurable: true + }); + continue; + } + if (typeof desc.value === "function") { + const origFn = desc.value; + const minArgs = origFn.length; + let fn = origFn; + if (brandCheck || arityCheck && minArgs > 0) { + fn = function(...args) { + if (brandCheck && (!this || this === proto || !(this instanceof ctor))) { + const err = /* @__PURE__ */ new TypeError(`Failed to execute '${name}' on '${interfaceName}': The provided value is not of type '${interfaceName}'.`); + if (rejectSet.has(name)) return Promise.reject(err); + throw err; + } + if (arityCheck && arguments.length < minArgs) { + const err = /* @__PURE__ */ new TypeError(`Failed to execute '${name}' on '${interfaceName}': ${minArgs} argument${minArgs === 1 ? "" : "s"} required, but only ${arguments.length} present.`); + if (rejectSet.has(name)) return Promise.reject(err); + throw err; + } + return origFn.apply(this, args); + }; + Object.defineProperty(fn, "name", { + value: origFn.name, + configurable: true + }); + Object.defineProperty(fn, "length", { + value: minArgs, + configurable: true + }); + } + Object.defineProperty(proto, name, { + value: fn, + writable: true, + enumerable: true, + configurable: true + }); + } + } +} +/** +* Defines constants on an interface object and its prototype per WebIDL: +* - WebIDL #es-constants: { [[Writable]]: false, [[Enumerable]]: true, [[Configurable]]: false } +* - WebIDL #constants-on-interface-prototype-object: same attributes on prototype +*/ +function applyWebIDLConstants(ctor, constants) { + for (const [name, val] of Object.entries(constants)) { + Object.defineProperty(ctor, name, { + value: val, + writable: false, + enumerable: true, + configurable: false + }); + if (ctor.prototype) Object.defineProperty(ctor.prototype, name, { + value: val, + writable: false, + enumerable: true, + configurable: false + }); + } +} +// src/rules/base.ts +/** +* @license +* Copyright 2026 Google LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +const RULE_CONSTANTS = { + STYLE_RULE: 1, + CHARSET_RULE: 2, + IMPORT_RULE: 3, + MEDIA_RULE: 4, + FONT_FACE_RULE: 5, + PAGE_RULE: 6, + KEYFRAMES_RULE: 7, + KEYFRAME_RULE: 8, + MARGIN_RULE: 9, + NAMESPACE_RULE: 10, + COUNTER_STYLE_RULE: 11, + SUPPORTS_RULE: 12, + FONT_FEATURE_VALUES_RULE: 14 +}; +var CSSRule = class { + get [Symbol.toStringTag]() { + return "CSSRule"; + } + /** @internal */ + _parentRule = null; + /** @internal */ + _parentStyleSheet = null; + /** @internal */ + _location; + /** @internal */ + _assignedLayerName = null; + constructor() { + hideInternalFields(this); + } + get location() { + return this._location; + } + get parentRule() { + return this._parentRule; + } + get parentStyleSheet() { + if (this._parentStyleSheet) return this._parentStyleSheet; + if (this._parentRule) return this._parentRule.parentStyleSheet; + return null; + } + get type() { + return 0; + } + get cssText() { + throw new Error("Not implemented"); + } + set cssText(_value) {} +}; +applyWebIDLConstants(CSSRule, RULE_CONSTANTS); +var CSSGroupingRule = class CSSGroupingRule extends CSSRule { + /** @internal */ + static _parser = null; + /** @internal */ + _cssRules; + /** @internal */ + _rules; + get cssRules() { + if (!this._cssRules) this._cssRules = new CSSRuleList(() => this._rules); + return this._cssRules; + } + constructor(rules, _parseRuleInBlock) { + super(); + this._rules = rules; + this._cssRules = new CSSRuleList(() => this._rules); + for (const rule of rules) if (rule instanceof CSSRule) rule._parentRule = this; + hideInternalFields(this); + } + insertRule(rule, index = 0) { + if (index < 0 || index > this._rules.length) throw new DOMException("Index size error", "IndexSizeError"); + const isScopeRule = (r) => r[Symbol.toStringTag] === "CSSScopeRule"; + const isStyleRule = (r) => r[Symbol.toStringTag] === "CSSStyleRule"; + const isNested = isStyleRule(this) || isScopeRule(this) || this.parentRule !== null; + let isInsideStyleRule = isStyleRule(this); + if (!isInsideStyleRule) { + let p = this.parentRule; + while (p) { + if (isStyleRule(p)) { + isInsideStyleRule = true; + break; + } + p = p.parentRule; + } + } + if (!CSSGroupingRule._parser) throw new DOMException("Parser not initialized", "SyntaxError"); + const { parseRule, parseRuleInBlock, parseRuleInScopeBlock } = CSSGroupingRule._parser; + let topRule = null; + try { + topRule = parseRule(rule); + } catch {} + if (topRule) { + if (isImportRule(topRule) || isNamespaceRule(topRule)) throw new DOMException("HierarchyRequestError: @import and @namespace rules are not allowed inside grouping rules", "HierarchyRequestError"); + if (isInsideStyleRule && !(topRule instanceof CSSGroupingRule)) throw new DOMException("HierarchyRequestError: This rule cannot be inserted inside a nested rule", "HierarchyRequestError"); + } + let isDirectlyInScope = isScopeRule(this); + if (!isDirectlyInScope && !isStyleRule(this)) { + let parent = this.parentRule; + while (parent) { + if (isStyleRule(parent)) break; + if (isScopeRule(parent)) { + isDirectlyInScope = true; + break; + } + parent = parent.parentRule; + } + } + const parsedRule = isDirectlyInScope ? parseRuleInScopeBlock(rule) : parseRuleInBlock(rule, isNested); + if (!parsedRule) throw new DOMException("Syntax error", "SyntaxError"); + if (isImportRule(parsedRule) || isNamespaceRule(parsedRule)) throw new DOMException("HierarchyRequestError: @import and @namespace rules are not allowed inside grouping rules", "HierarchyRequestError"); + if (parsedRule[Symbol.toStringTag] === "CSSNestedDeclarations") { + if (!isNested) throw new DOMException("Syntax error: CSSNestedDeclarations cannot be inserted into top-level grouping rule", "SyntaxError"); + if (parsedRule.style._declarations.filter((d) => { + const name = d.name.toLowerCase(); + return name.startsWith("--") || SUPPORTED_PROPERTIES.has(name); + }).length === 0) throw new DOMException("Syntax error: CSSNestedDeclarations contains no valid declarations", "SyntaxError"); + } + if (parsedRule instanceof CSSRule) { + parsedRule._parentRule = this; + parsedRule._parentStyleSheet = null; + } + this._rules.splice(index, 0, parsedRule); + return index; + } + deleteRule(index) { + deleteRuleFromArray(this._rules, index); + } +}; +// src/data/gen/shorthands.ts +/** +* @license +* Copyright 2026 Google LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +const SHORTHANDS_DATA = { + "-webkit-animation": [ + "animation-name", + "animation-duration", + "animation-timing-function", + "animation-delay", + "animation-iteration-count", + "animation-direction", + "animation-fill-mode", + "animation-play-state", + "animation-timeline" + ], + "-webkit-border-radius": [ + "border-top-left-radius", + "border-top-right-radius", + "border-bottom-right-radius", + "border-bottom-left-radius" + ], + "-webkit-box-shadow": [ + "box-shadow-color", + "box-shadow-offset", + "box-shadow-blur", + "box-shadow-spread", + "box-shadow-position" + ], + "-webkit-flex": [ + "flex-grow", + "flex-shrink", + "flex-basis" + ], + "-webkit-flex-flow": ["flex-direction", "flex-wrap"], + "-webkit-line-clamp": [ + "max-lines", + "block-ellipsis", + "continue" + ], + "-webkit-mask": [ + "mask-image", + "mask-position", + "mask-size", + "mask-repeat", + "mask-origin", + "mask-clip", + "mask-composite", + "mask-mode" + ], + "-webkit-mask-box-image": [ + "mask-border-source", + "mask-border-slice", + "mask-border-width", + "mask-border-outset", + "mask-border-repeat", + "mask-border-mode" + ], + "-webkit-text-stroke": ["-webkit-text-stroke-width", "-webkit-text-stroke-color"], + "-webkit-transition": [ + "transition-property", + "transition-duration", + "transition-timing-function", + "transition-delay", + "transition-behavior" + ], + "animation": [ + "animation-name", + "animation-duration", + "animation-timing-function", + "animation-delay", + "animation-iteration-count", + "animation-direction", + "animation-fill-mode", + "animation-play-state", + "animation-timeline" + ], + "animation-delay": ["animation-delay-start", "animation-delay-end"], + "animation-range": ["animation-range-start", "animation-range-end"], + "background": [ + "background-image", + "background-position", + "background-size", + "background-repeat", + "background-attachment", + "background-origin", + "background-clip", + "background-color" + ], + "background-position": ["background-position-x", "background-position-y"], + "block-step": [ + "block-step-size", + "block-step-insert", + "block-step-align", + "block-step-round" + ], + "border": [ + "border-width", + "border-style", + "border-color" + ], + "border-block": ["border-block-start", "border-block-end"], + "border-block-clip": ["border-block-start-clip", "border-block-end-clip"], + "border-block-color": ["border-block-start-color", "border-block-end-color"], + "border-block-end": [ + "border-block-end-width", + "border-block-end-style", + "border-block-end-color" + ], + "border-block-end-radius": ["border-end-start-radius", "border-end-end-radius"], + "border-block-start": [ + "border-block-start-width", + "border-block-start-style", + "border-block-start-color" + ], + "border-block-start-radius": ["border-start-start-radius", "border-start-end-radius"], + "border-block-style": ["border-block-start-style", "border-block-end-style"], + "border-block-width": ["border-block-start-width", "border-block-end-width"], + "border-bottom": [ + "border-bottom-width", + "border-bottom-style", + "border-bottom-color" + ], + "border-bottom-radius": ["border-bottom-left-radius", "border-bottom-right-radius"], + "border-clip": [ + "border-top-clip", + "border-right-clip", + "border-bottom-clip", + "border-left-clip" + ], + "border-color": [ + "border-top-color", + "border-right-color", + "border-bottom-color", + "border-left-color" + ], + "border-image": [ + "border-image-source", + "border-image-slice", + "border-image-width", + "border-image-outset", + "border-image-repeat" + ], + "border-inline": ["border-inline-start", "border-inline-end"], + "border-inline-clip": ["border-inline-start-clip", "border-inline-end-clip"], + "border-inline-color": ["border-inline-start-color", "border-inline-end-color"], + "border-inline-end": [ + "border-inline-end-width", + "border-inline-end-style", + "border-inline-end-color" + ], + "border-inline-end-radius": ["border-start-end-radius", "border-end-end-radius"], + "border-inline-start": [ + "border-inline-start-width", + "border-inline-start-style", + "border-inline-start-color" + ], + "border-inline-start-radius": ["border-start-start-radius", "border-end-start-radius"], + "border-inline-style": ["border-inline-start-style", "border-inline-end-style"], + "border-inline-width": ["border-inline-start-width", "border-inline-end-width"], + "border-left": [ + "border-left-width", + "border-left-style", + "border-left-color" + ], + "border-left-radius": ["border-top-left-radius", "border-bottom-left-radius"], + "border-radius": [ + "border-top-left-radius", + "border-top-right-radius", + "border-bottom-right-radius", + "border-bottom-left-radius" + ], + "border-right": [ + "border-right-width", + "border-right-style", + "border-right-color" + ], + "border-right-radius": ["border-top-right-radius", "border-bottom-right-radius"], + "border-style": [ + "border-top-style", + "border-right-style", + "border-bottom-style", + "border-left-style" + ], + "border-top": [ + "border-top-width", + "border-top-style", + "border-top-color" + ], + "border-top-radius": ["border-top-left-radius", "border-top-right-radius"], + "border-width": [ + "border-top-width", + "border-right-width", + "border-bottom-width", + "border-left-width" + ], + "box-shadow": [ + "box-shadow-color", + "box-shadow-offset", + "box-shadow-blur", + "box-shadow-spread", + "box-shadow-position" + ], + "caret": [ + "caret-color", + "caret-animation", + "caret-shape" + ], + "color-adjust": ["print-color-adjust"], + "column-rule": [ + "column-rule-width", + "column-rule-style", + "column-rule-color" + ], + "column-rule-inset": [ + "column-rule-inset-cap-start", + "column-rule-inset-cap-end", + "column-rule-inset-junction-start", + "column-rule-inset-junction-end" + ], + "column-rule-inset-cap": ["column-rule-inset-cap-start", "column-rule-inset-cap-end"], + "column-rule-inset-end": ["column-rule-inset-cap-end", "column-rule-inset-junction-end"], + "column-rule-inset-junction": ["column-rule-inset-junction-start", "column-rule-inset-junction-end"], + "column-rule-inset-start": ["column-rule-inset-cap-start", "column-rule-inset-junction-start"], + "columns": [ + "column-width", + "column-count", + "column-height" + ], + "contain-intrinsic-size": ["contain-intrinsic-width", "contain-intrinsic-height"], + "container": ["container-name", "container-type"], + "corner": ["corner-shape", "border-radius"], + "corner-block-end": ["border-block-end-radius", "corner-block-end-shape"], + "corner-block-end-shape": ["corner-end-start-shape", "corner-end-end-shape"], + "corner-block-start": ["border-block-start-radius", "corner-block-start-shape"], + "corner-block-start-shape": ["corner-start-start-shape", "corner-start-end-shape"], + "corner-bottom": ["border-bottom-radius", "corner-bottom-shape"], + "corner-bottom-left": ["border-bottom-left-radius", "corner-bottom-left-shape"], + "corner-bottom-right": ["border-bottom-right-radius", "corner-bottom-right-shape"], + "corner-bottom-shape": ["corner-bottom-left-shape", "corner-bottom-right-shape"], + "corner-end-end": ["border-end-end-radius", "corner-end-end-shape"], + "corner-end-start": ["border-end-start-radius", "corner-end-start-shape"], + "corner-inline-end": ["border-inline-end-radius", "corner-inline-end-shape"], + "corner-inline-end-shape": ["corner-start-end-shape", "corner-end-end-shape"], + "corner-inline-start": ["border-inline-start-radius", "corner-inline-start-shape"], + "corner-inline-start-shape": ["corner-start-start-shape", "corner-end-start-shape"], + "corner-left": ["border-left-radius", "corner-left-shape"], + "corner-left-shape": ["corner-top-left-shape", "corner-bottom-left-shape"], + "corner-right": ["border-right-radius", "corner-right-shape"], + "corner-right-shape": ["corner-top-right-shape", "corner-bottom-right-shape"], + "corner-shape": [ + "corner-top-left-shape", + "corner-top-right-shape", + "corner-bottom-right-shape", + "corner-bottom-left-shape" + ], + "corner-start-end": ["border-start-end-radius", "corner-start-end-shape"], + "corner-start-start": ["border-start-start-radius", "corner-start-start-shape"], + "corner-top": ["border-top-radius", "corner-top-shape"], + "corner-top-left": ["border-top-left-radius", "corner-top-left-shape"], + "corner-top-right": ["border-top-right-radius", "corner-top-right-shape"], + "corner-top-shape": ["corner-top-left-shape", "corner-top-right-shape"], + "cue": ["cue-before", "cue-after"], + "flex": [ + "flex-grow", + "flex-shrink", + "flex-basis" + ], + "flex-flow": ["flex-direction", "flex-wrap"], + "font": [ + "font-style", + "font-variant", + "font-weight", + "font-stretch", + "font-size", + "line-height", + "font-family" + ], + "font-synthesis": [ + "font-synthesis-weight", + "font-synthesis-style", + "font-synthesis-small-caps" + ], + "font-variant": [ + "font-variant-ligatures", + "font-variant-caps", + "font-variant-alternates", + "font-variant-numeric", + "font-variant-east-asian", + "font-variant-position", + "font-variant-emoji" + ], + "gap": ["row-gap", "column-gap"], + "grid": [ + "grid-template-rows", + "grid-template-columns", + "grid-template-areas", + "grid-auto-rows", + "grid-auto-columns", + "grid-auto-flow" + ], + "grid-area": [ + "grid-row-start", + "grid-column-start", + "grid-row-end", + "grid-column-end" + ], + "grid-column": ["grid-column-start", "grid-column-end"], + "grid-gap": ["row-gap", "column-gap"], + "grid-row": ["grid-row-start", "grid-row-end"], + "grid-template": [ + "grid-template-rows", + "grid-template-columns", + "grid-template-areas" + ], + "inset": [ + "top", + "right", + "bottom", + "left" + ], + "inset-block": ["inset-block-start", "inset-block-end"], + "inset-inline": ["inset-inline-start", "inset-inline-end"], + "interest-delay": ["interest-delay-start", "interest-delay-end"], + "line-clamp": [ + "max-lines", + "block-ellipsis", + "continue" + ], + "list-style": [ + "list-style-type", + "list-style-position", + "list-style-image" + ], + "margin": [ + "margin-top", + "margin-right", + "margin-bottom", + "margin-left" + ], + "margin-block": ["margin-block-start", "margin-block-end"], + "margin-inline": ["margin-inline-start", "margin-inline-end"], + "marker": [ + "marker-start", + "marker-mid", + "marker-end" + ], + "mask": [ + "mask-image", + "mask-position", + "mask-size", + "mask-repeat", + "mask-origin", + "mask-clip", + "mask-composite", + "mask-mode" + ], + "mask-border": [ + "mask-border-source", + "mask-border-slice", + "mask-border-width", + "mask-border-outset", + "mask-border-repeat", + "mask-border-mode" + ], + "max-size": ["max-width", "max-height"], + "min-size": ["min-width", "min-height"], + "offset": [ + "offset-path", + "offset-distance", + "offset-rotate", + "offset-anchor", + "offset-position" + ], + "outline": [ + "outline-width", + "outline-style", + "outline-color" + ], + "overflow": ["overflow-x", "overflow-y"], + "overflow-clip-margin": [ + "overflow-clip-margin-top", + "overflow-clip-margin-right", + "overflow-clip-margin-bottom", + "overflow-clip-margin-left" + ], + "overflow-clip-margin-block": ["overflow-clip-margin-block-start", "overflow-clip-margin-block-end"], + "overflow-clip-margin-inline": ["overflow-clip-margin-inline-start", "overflow-clip-margin-inline-end"], + "overscroll-behavior": ["overscroll-behavior-x", "overscroll-behavior-y"], + "padding": [ + "padding-top", + "padding-right", + "padding-bottom", + "padding-left" + ], + "padding-block": ["padding-block-start", "padding-block-end"], + "padding-inline": ["padding-inline-start", "padding-inline-end"], + "pause": ["pause-before", "pause-after"], + "place-content": ["align-content", "justify-content"], + "place-items": ["align-items", "justify-items"], + "place-self": ["align-self", "justify-self"], + "pointer-timeline": ["pointer-timeline-name", "pointer-timeline-axis"], + "position-try": ["position-try-order", "position-try-fallbacks"], + "rest": ["rest-before", "rest-after"], + "row-rule": [ + "row-rule-width", + "row-rule-style", + "row-rule-color" + ], + "row-rule-inset": [ + "row-rule-inset-cap-start", + "row-rule-inset-cap-end", + "row-rule-inset-junction-start", + "row-rule-inset-junction-end" + ], + "row-rule-inset-cap": ["row-rule-inset-cap-start", "row-rule-inset-cap-end"], + "row-rule-inset-end": ["row-rule-inset-cap-end", "row-rule-inset-junction-end"], + "row-rule-inset-junction": ["row-rule-inset-junction-start", "row-rule-inset-junction-end"], + "row-rule-inset-start": ["row-rule-inset-cap-start", "row-rule-inset-junction-start"], + "rule": ["column-rule", "row-rule"], + "rule-break": ["column-rule-break", "row-rule-break"], + "rule-color": ["column-rule-color", "row-rule-color"], + "rule-inset": ["column-rule-inset", "row-rule-inset"], + "rule-inset-cap": ["column-rule-inset-cap"], + "rule-inset-end": ["column-rule-inset-end", "row-rule-inset-end"], + "rule-inset-junction": ["column-rule-inset-junction"], + "rule-inset-start": ["column-rule-inset-start", "row-rule-inset-start"], + "rule-style": ["column-rule-style", "row-rule-style"], + "rule-visibility-items": ["column-rule-visibility-items"], + "rule-width": ["column-rule-width", "row-rule-width"], + "scroll-margin": [ + "scroll-margin-top", + "scroll-margin-right", + "scroll-margin-bottom", + "scroll-margin-left" + ], + "scroll-margin-block": ["scroll-margin-block-start", "scroll-margin-block-end"], + "scroll-margin-inline": ["scroll-margin-inline-start", "scroll-margin-inline-end"], + "scroll-padding": [ + "scroll-padding-top", + "scroll-padding-right", + "scroll-padding-bottom", + "scroll-padding-left" + ], + "scroll-padding-block": ["scroll-padding-block-start", "scroll-padding-block-end"], + "scroll-padding-inline": ["scroll-padding-inline-start", "scroll-padding-inline-end"], + "scroll-timeline": ["scroll-timeline-name", "scroll-timeline-axis"], + "size": ["width", "height"], + "text-align": ["text-align-all", "text-align-last"], + "text-decoration": [ + "text-decoration-line", + "text-decoration-thickness", + "text-decoration-style", + "text-decoration-color" + ], + "text-decoration-skip": [ + "text-decoration-skip-self", + "text-decoration-skip-box", + "text-decoration-skip-spaces", + "text-decoration-skip-ink" + ], + "text-emphasis": ["text-emphasis-style", "text-emphasis-color"], + "text-spacing": ["text-spacing-trim", "text-autospace"], + "text-wrap": ["text-wrap-mode", "text-wrap-style"], + "timeline-trigger": [ + "timeline-trigger-name", + "timeline-trigger-source", + "timeline-trigger-range" + ], + "timeline-trigger-activation-range": ["timeline-trigger-activation-range-start", "timeline-trigger-activation-range-end"], + "timeline-trigger-active-range": ["timeline-trigger-active-range-start", "timeline-trigger-active-range-end"], + "timeline-trigger-range": ["timeline-trigger-activation-range", "timeline-trigger-active-range"], + "transition": [ + "transition-property", + "transition-duration", + "transition-timing-function", + "transition-delay", + "transition-behavior" + ], + "vertical-align": [ + "alignment-baseline", + "baseline-shift", + "baseline-source" + ], + "view-timeline": ["view-timeline-name", "view-timeline-axis"], + "white-space": ["white-space-collapse", "text-wrap-mode"] +}; +// src/data/gen/LogicalMapping.ts +/** +* @license +* Copyright 2026 Google LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +/** +* Logical property to physical property mapping for default horizontal-tb, ltr. +* css-logical-1 #box +*/ +const LOGICAL_MAPPING = { + "background-position-block": "background-position-y", + "background-position-inline": "background-position-x", + "background-repeat-block": "background-repeat-y", + "background-repeat-inline": "background-repeat-x", + "block-size": "height", + "border-block-end-clip": "border-bottom-clip", + "border-block-end-color": "border-bottom-color", + "border-block-end-style": "border-bottom-style", + "border-block-end-width": "border-bottom-width", + "border-block-start-clip": "border-top-clip", + "border-block-start-color": "border-top-color", + "border-block-start-style": "border-top-style", + "border-block-start-width": "border-top-width", + "border-end-end-radius": "border-bottom-right-radius", + "border-end-start-radius": "border-bottom-left-radius", + "border-inline-end-clip": "border-right-clip", + "border-inline-end-color": "border-right-color", + "border-inline-end-style": "border-right-style", + "border-inline-end-width": "border-right-width", + "border-inline-start-clip": "border-left-clip", + "border-inline-start-color": "border-left-color", + "border-inline-start-style": "border-left-style", + "border-inline-start-width": "border-left-width", + "border-start-end-radius": "border-top-right-radius", + "border-start-start-radius": "border-top-left-radius", + "contain-intrinsic-block-size": "contain-intrinsic-height", + "contain-intrinsic-inline-size": "contain-intrinsic-width", + "corner-end-end-shape": "corner-bottom-right-shape", + "corner-end-start-shape": "corner-bottom-left-shape", + "corner-start-end-shape": "corner-top-right-shape", + "corner-start-start-shape": "corner-top-left-shape", + "inline-size": "width", + "inset-block-end": "bottom", + "inset-block-start": "top", + "inset-inline-end": "right", + "inset-inline-start": "left", + "margin-block-end": "margin-bottom", + "margin-block-start": "margin-top", + "margin-inline-end": "margin-right", + "margin-inline-start": "margin-left", + "max-block-size": "max-height", + "max-inline-size": "max-width", + "min-block-size": "min-height", + "min-inline-size": "min-width", + "overflow-block": "overflow-y", + "overflow-clip-margin-block-end": "overflow-clip-margin-bottom", + "overflow-clip-margin-block-start": "overflow-clip-margin-top", + "overflow-clip-margin-inline-end": "overflow-clip-margin-right", + "overflow-clip-margin-inline-start": "overflow-clip-margin-left", + "overflow-inline": "overflow-x", + "overscroll-behavior-block": "overscroll-behavior-y", + "overscroll-behavior-inline": "overscroll-behavior-x", + "padding-block-end": "padding-bottom", + "padding-block-start": "padding-top", + "padding-inline-end": "padding-right", + "padding-inline-start": "padding-left", + "scroll-margin-block-end": "scroll-margin-bottom", + "scroll-margin-block-start": "scroll-margin-top", + "scroll-margin-inline-end": "scroll-margin-right", + "scroll-margin-inline-start": "scroll-margin-left", + "scroll-padding-block-end": "scroll-padding-bottom", + "scroll-padding-block-start": "scroll-padding-top", + "scroll-padding-inline-end": "scroll-padding-right", + "scroll-padding-inline-start": "scroll-padding-left" +}; +const PHYSICAL_TO_LOGICAL = {}; +for (const [logical, physical] of Object.entries(LOGICAL_MAPPING)) { + if (!PHYSICAL_TO_LOGICAL[physical]) PHYSICAL_TO_LOGICAL[physical] = []; + PHYSICAL_TO_LOGICAL[physical].push(logical); +} +/** +* Logical property groups and mapping logic. +* css-logical-1 #logical-property-group +*/ +const LOGICAL_PROPERTY_INFO = { + "background-position-block": { + group: "background-position", + logic: "logical" + }, + "background-position-inline": { + group: "background-position", + logic: "logical" + }, + "background-position-x": { + group: "background-position", + logic: "physical" + }, + "background-position-y": { + group: "background-position", + logic: "physical" + }, + "background-repeat-block": { + group: "background-repeat", + logic: "logical" + }, + "background-repeat-inline": { + group: "background-repeat", + logic: "logical" + }, + "background-repeat-x": { + group: "background-repeat", + logic: "physical" + }, + "background-repeat-y": { + group: "background-repeat", + logic: "physical" + }, + "block-size": { + group: "size", + logic: "logical" + }, + "border-block-end-clip": { + group: "border-clip", + logic: "logical" + }, + "border-block-end-color": { + group: "border-color", + logic: "logical" + }, + "border-block-end-style": { + group: "border-style", + logic: "logical" + }, + "border-block-end-width": { + group: "border-width", + logic: "logical" + }, + "border-block-start-clip": { + group: "border-clip", + logic: "logical" + }, + "border-block-start-color": { + group: "border-color", + logic: "logical" + }, + "border-block-start-style": { + group: "border-style", + logic: "logical" + }, + "border-block-start-width": { + group: "border-width", + logic: "logical" + }, + "border-bottom-clip": { + group: "border-clip", + logic: "physical" + }, + "border-bottom-color": { + group: "border-color", + logic: "physical" + }, + "border-bottom-left-radius": { + group: "border-radius", + logic: "physical" + }, + "border-bottom-right-radius": { + group: "border-radius", + logic: "physical" + }, + "border-bottom-style": { + group: "border-style", + logic: "physical" + }, + "border-bottom-width": { + group: "border-width", + logic: "physical" + }, + "border-end-end-radius": { + group: "border-radius", + logic: "logical" + }, + "border-end-start-radius": { + group: "border-radius", + logic: "logical" + }, + "border-inline-end-clip": { + group: "border-clip", + logic: "logical" + }, + "border-inline-end-color": { + group: "border-color", + logic: "logical" + }, + "border-inline-end-style": { + group: "border-style", + logic: "logical" + }, + "border-inline-end-width": { + group: "border-width", + logic: "logical" + }, + "border-inline-start-clip": { + group: "border-clip", + logic: "logical" + }, + "border-inline-start-color": { + group: "border-color", + logic: "logical" + }, + "border-inline-start-style": { + group: "border-style", + logic: "logical" + }, + "border-inline-start-width": { + group: "border-width", + logic: "logical" + }, + "border-left-clip": { + group: "border-clip", + logic: "physical" + }, + "border-left-color": { + group: "border-color", + logic: "physical" + }, + "border-left-style": { + group: "border-style", + logic: "physical" + }, + "border-left-width": { + group: "border-width", + logic: "physical" + }, + "border-right-clip": { + group: "border-clip", + logic: "physical" + }, + "border-right-color": { + group: "border-color", + logic: "physical" + }, + "border-right-style": { + group: "border-style", + logic: "physical" + }, + "border-right-width": { + group: "border-width", + logic: "physical" + }, + "border-start-end-radius": { + group: "border-radius", + logic: "logical" + }, + "border-start-start-radius": { + group: "border-radius", + logic: "logical" + }, + "border-top-clip": { + group: "border-clip", + logic: "physical" + }, + "border-top-color": { + group: "border-color", + logic: "physical" + }, + "border-top-left-radius": { + group: "border-radius", + logic: "physical" + }, + "border-top-right-radius": { + group: "border-radius", + logic: "physical" + }, + "border-top-style": { + group: "border-style", + logic: "physical" + }, + "border-top-width": { + group: "border-width", + logic: "physical" + }, + "bottom": { + group: "inset", + logic: "physical" + }, + "contain-intrinsic-block-size": { + group: "contain-intrinsic-size", + logic: "logical" + }, + "contain-intrinsic-height": { + group: "contain-intrinsic-size", + logic: "physical" + }, + "contain-intrinsic-inline-size": { + group: "contain-intrinsic-size", + logic: "logical" + }, + "contain-intrinsic-width": { + group: "contain-intrinsic-size", + logic: "physical" + }, + "corner-bottom-left-shape": { + group: "corner-shape", + logic: "physical" + }, + "corner-bottom-right-shape": { + group: "corner-shape", + logic: "physical" + }, + "corner-end-end-shape": { + group: "corner-shape", + logic: "logical" + }, + "corner-end-start-shape": { + group: "corner-shape", + logic: "logical" + }, + "corner-start-end-shape": { + group: "corner-shape", + logic: "logical" + }, + "corner-start-start-shape": { + group: "corner-shape", + logic: "logical" + }, + "corner-top-left-shape": { + group: "corner-shape", + logic: "physical" + }, + "corner-top-right-shape": { + group: "corner-shape", + logic: "physical" + }, + "height": { + group: "size", + logic: "physical" + }, + "inline-size": { + group: "size", + logic: "logical" + }, + "inset-block-end": { + group: "inset", + logic: "logical" + }, + "inset-block-start": { + group: "inset", + logic: "logical" + }, + "inset-inline-end": { + group: "inset", + logic: "logical" + }, + "inset-inline-start": { + group: "inset", + logic: "logical" + }, + "left": { + group: "inset", + logic: "physical" + }, + "margin-block-end": { + group: "margin", + logic: "logical" + }, + "margin-block-start": { + group: "margin", + logic: "logical" + }, + "margin-bottom": { + group: "margin", + logic: "physical" + }, + "margin-inline-end": { + group: "margin", + logic: "logical" + }, + "margin-inline-start": { + group: "margin", + logic: "logical" + }, + "margin-left": { + group: "margin", + logic: "physical" + }, + "margin-right": { + group: "margin", + logic: "physical" + }, + "margin-top": { + group: "margin", + logic: "physical" + }, + "max-block-size": { + group: "max-size", + logic: "logical" + }, + "max-height": { + group: "max-size", + logic: "physical" + }, + "max-inline-size": { + group: "max-size", + logic: "logical" + }, + "max-width": { + group: "max-size", + logic: "physical" + }, + "min-block-size": { + group: "min-size", + logic: "logical" + }, + "min-height": { + group: "min-size", + logic: "physical" + }, + "min-inline-size": { + group: "min-size", + logic: "logical" + }, + "min-width": { + group: "min-size", + logic: "physical" + }, + "overflow-block": { + group: "overflow", + logic: "logical" + }, + "overflow-clip-margin-block-end": { + group: "overflow-clip-margin", + logic: "logical" + }, + "overflow-clip-margin-block-start": { + group: "overflow-clip-margin", + logic: "logical" + }, + "overflow-clip-margin-bottom": { + group: "overflow-clip-margin", + logic: "physical" + }, + "overflow-clip-margin-inline-end": { + group: "overflow-clip-margin", + logic: "logical" + }, + "overflow-clip-margin-inline-start": { + group: "overflow-clip-margin", + logic: "logical" + }, + "overflow-clip-margin-left": { + group: "overflow-clip-margin", + logic: "physical" + }, + "overflow-clip-margin-right": { + group: "overflow-clip-margin", + logic: "physical" + }, + "overflow-clip-margin-top": { + group: "overflow-clip-margin", + logic: "physical" + }, + "overflow-inline": { + group: "overflow", + logic: "logical" + }, + "overflow-x": { + group: "overflow", + logic: "physical" + }, + "overflow-y": { + group: "overflow", + logic: "physical" + }, + "overscroll-behavior-block": { + group: "overscroll-behavior", + logic: "logical" + }, + "overscroll-behavior-inline": { + group: "overscroll-behavior", + logic: "logical" + }, + "overscroll-behavior-x": { + group: "overscroll-behavior", + logic: "physical" + }, + "overscroll-behavior-y": { + group: "overscroll-behavior", + logic: "physical" + }, + "padding-block-end": { + group: "padding", + logic: "logical" + }, + "padding-block-start": { + group: "padding", + logic: "logical" + }, + "padding-bottom": { + group: "padding", + logic: "physical" + }, + "padding-inline-end": { + group: "padding", + logic: "logical" + }, + "padding-inline-start": { + group: "padding", + logic: "logical" + }, + "padding-left": { + group: "padding", + logic: "physical" + }, + "padding-right": { + group: "padding", + logic: "physical" + }, + "padding-top": { + group: "padding", + logic: "physical" + }, + "right": { + group: "inset", + logic: "physical" + }, + "scroll-margin-block-end": { + group: "scroll-margin", + logic: "logical" + }, + "scroll-margin-block-start": { + group: "scroll-margin", + logic: "logical" + }, + "scroll-margin-bottom": { + group: "scroll-margin", + logic: "physical" + }, + "scroll-margin-inline-end": { + group: "scroll-margin", + logic: "logical" + }, + "scroll-margin-inline-start": { + group: "scroll-margin", + logic: "logical" + }, + "scroll-margin-left": { + group: "scroll-margin", + logic: "physical" + }, + "scroll-margin-right": { + group: "scroll-margin", + logic: "physical" + }, + "scroll-margin-top": { + group: "scroll-margin", + logic: "physical" + }, + "scroll-padding-block-end": { + group: "scroll-padding", + logic: "logical" + }, + "scroll-padding-block-start": { + group: "scroll-padding", + logic: "logical" + }, + "scroll-padding-bottom": { + group: "scroll-padding", + logic: "physical" + }, + "scroll-padding-inline-end": { + group: "scroll-padding", + logic: "logical" + }, + "scroll-padding-inline-start": { + group: "scroll-padding", + logic: "logical" + }, + "scroll-padding-left": { + group: "scroll-padding", + logic: "physical" + }, + "scroll-padding-right": { + group: "scroll-padding", + logic: "physical" + }, + "scroll-padding-top": { + group: "scroll-padding", + logic: "physical" + }, + "top": { + group: "inset", + logic: "physical" + }, + "width": { + group: "size", + logic: "physical" + } +}; +/** +* Resolves a logical property to its physical counterpart based on writing mode and direction. +*/ +function resolveLogicalProperty(name, writingMode = "horizontal-tb", direction = "ltr") { + if (!(name in LOGICAL_MAPPING)) return name; + let blockStart = "top"; + let blockEnd = "bottom"; + let inlineStart = "left"; + let inlineEnd = "right"; + let blockAxis = "y"; + let inlineAxis = "x"; + if (writingMode === "vertical-rl" || writingMode === "sideways-rl") { + blockStart = "right"; + blockEnd = "left"; + inlineStart = "top"; + inlineEnd = "bottom"; + blockAxis = "x"; + inlineAxis = "y"; + } else if (writingMode === "vertical-lr") { + blockStart = "left"; + blockEnd = "right"; + inlineStart = "top"; + inlineEnd = "bottom"; + blockAxis = "x"; + inlineAxis = "y"; + } else if (writingMode === "sideways-lr") { + blockStart = "left"; + blockEnd = "right"; + inlineStart = "bottom"; + inlineEnd = "top"; + blockAxis = "x"; + inlineAxis = "y"; + } + if (direction === "rtl") { + const tmp = inlineStart; + inlineStart = inlineEnd; + inlineEnd = tmp; + } + if (name.startsWith("inset-block-start")) return blockStart; + if (name.startsWith("inset-block-end")) return blockEnd; + if (name.startsWith("inset-inline-start")) return inlineStart; + if (name.startsWith("inset-inline-end")) return inlineEnd; + const radius = (s1, s2) => { + return `-${s1 === "top" || s1 === "bottom" ? s1 : s2}-${s1 === "left" || s1 === "right" ? s1 : s2}-radius`; + }; + const shape = (s1, s2) => { + return `-${s1 === "top" || s1 === "bottom" ? s1 : s2}-${s1 === "left" || s1 === "right" ? s1 : s2}-shape`; + }; + const replacements = [ + { + logical: "-block-start", + physical: `-${blockStart}` + }, + { + logical: "-block-end", + physical: `-${blockEnd}` + }, + { + logical: "-inline-start", + physical: `-${inlineStart}` + }, + { + logical: "-inline-end", + physical: `-${inlineEnd}` + }, + { + logical: "-start-start-radius", + physical: radius(blockStart, inlineStart) + }, + { + logical: "-start-end-radius", + physical: radius(blockStart, inlineEnd) + }, + { + logical: "-end-start-radius", + physical: radius(blockEnd, inlineStart) + }, + { + logical: "-end-end-radius", + physical: radius(blockEnd, inlineEnd) + }, + { + logical: "-start-start-shape", + physical: shape(blockStart, inlineStart) + }, + { + logical: "-start-end-shape", + physical: shape(blockStart, inlineEnd) + }, + { + logical: "-end-start-shape", + physical: shape(blockEnd, inlineStart) + }, + { + logical: "-end-end-shape", + physical: shape(blockEnd, inlineEnd) + }, + { + logical: "-block", + physical: `-${blockAxis}` + }, + { + logical: "-inline", + physical: `-${inlineAxis}` + } + ]; + let resolved = name; + for (const { logical, physical } of replacements) if (resolved.includes(logical)) { + resolved = resolved.replace(logical, physical); + break; + } + const isVertical = writingMode.startsWith("vertical") || writingMode.startsWith("sideways"); + if (resolved === "block-size") return isVertical ? "width" : "height"; + if (resolved === "inline-size") return isVertical ? "height" : "width"; + if (resolved === "min-block-size") return isVertical ? "min-width" : "min-height"; + if (resolved === "min-inline-size") return isVertical ? "min-height" : "min-width"; + if (resolved === "max-block-size") return isVertical ? "max-width" : "max-height"; + if (resolved === "max-inline-size") return isVertical ? "max-height" : "max-width"; + if (resolved === "contain-intrinsic-block-size") return isVertical ? "contain-intrinsic-width" : "contain-intrinsic-height"; + if (resolved === "contain-intrinsic-inline-size") return isVertical ? "contain-intrinsic-height" : "contain-intrinsic-width"; + return resolved; +} +// src/data/gen/colors.ts +/** +* @license +* Copyright 2026 Google LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +const NAMED_COLORS = /* @__PURE__ */ new Set("accentcolor,accentcolortext,activeborder,activecaption,activetext,aliceblue,antiquewhite,appworkspace,aqua,aquamarine,azure,background,beige,bisque,black,blanchedalmond,blue,blueviolet,brown,burlywood,buttonborder,buttonface,buttonhighlight,buttonshadow,buttontext,cadetblue,canvas,canvastext,captiontext,chartreuse,chocolate,coral,cornflowerblue,cornsilk,crimson,currentcolor,cyan,darkblue,darkcyan,darkgoldenrod,darkgray,darkgreen,darkgrey,darkkhaki,darkmagenta,darkolivegreen,darkorange,darkorchid,darkred,darksalmon,darkseagreen,darkslateblue,darkslategray,darkslategrey,darkturquoise,darkviolet,deeppink,deepskyblue,dimgray,dimgrey,dodgerblue,field,fieldtext,firebrick,floralwhite,forestgreen,fuchsia,gainsboro,ghostwhite,gold,goldenrod,gray,graytext,green,greenyellow,grey,highlight,highlighttext,honeydew,hotpink,inactiveborder,inactivecaption,inactivecaptiontext,indianred,indigo,infobackground,infotext,ivory,khaki,lavender,lavenderblush,lawngreen,lemonchiffon,lightblue,lightcoral,lightcyan,lightgoldenrodyellow,lightgray,lightgreen,lightgrey,lightpink,lightsalmon,lightseagreen,lightskyblue,lightslategray,lightslategrey,lightsteelblue,lightyellow,lime,limegreen,linen,linktext,magenta,mark,marktext,maroon,mediumaquamarine,mediumblue,mediumorchid,mediumpurple,mediumseagreen,mediumslateblue,mediumspringgreen,mediumturquoise,mediumvioletred,menu,menutext,midnightblue,mintcream,mistyrose,moccasin,navajowhite,navy,oldlace,olive,olivedrab,orange,orangered,orchid,palegoldenrod,palegreen,paleturquoise,palevioletred,papayawhip,peachpuff,peru,pink,plum,powderblue,purple,rebeccapurple,red,rosybrown,royalblue,saddlebrown,salmon,sandybrown,scrollbar,seagreen,seashell,selecteditem,selecteditemtext,sienna,silver,skyblue,slateblue,slategray,slategrey,snow,springgreen,steelblue,tan,teal,thistle,threeddarkshadow,threedface,threedhighlight,threedlightshadow,threedshadow,tomato,transparent,turquoise,violet,visitedtext,wheat,white,whitesmoke,window,windowframe,windowtext,yellow,yellowgreen".split(",")); +const LENGTH_UNITS = /* @__PURE__ */ new Set("cap,ch,cm,cqb,cqh,cqi,cqmax,cqmin,cqw,dvb,dvh,dvi,dvmax,dvmin,dvw,em,ex,ic,in,lh,lvb,lvh,lvi,lvmax,lvmin,lvw,mm,pc,pt,px,q,rcap,rch,rem,rex,ric,rlh,svb,svh,svi,svmax,svmin,svw,vb,vh,vi,vmax,vmin,vw,%".split(",")); +// src/shorthands.ts +/** +* @license +* Copyright 2026 Google LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +const CSS_WIDE_KEYWORDS$2 = /* @__PURE__ */ new Set("initial,inherit,unset,revert,revert-layer".split(",")); +const REPEAT_KEYWORDS = /* @__PURE__ */ new Set("repeat,no-repeat,space,round,repeat-x,repeat-y".split(",")); +const ATTACHMENT_KEYWORDS = /* @__PURE__ */ new Set("scroll,fixed,local".split(",")); +const BOX_KEYWORDS = /* @__PURE__ */ new Set("border-box,padding-box,content-box,text,border-area".split(",")); +const CLIP_ONLY_BOX_KEYWORDS = /* @__PURE__ */ new Set("text,border-area".split(",")); +const COLOR_FUNCTIONS = /* @__PURE__ */ new Set("rgb,rgba,hsl,hsla,hwb,lab,lch,oklab,oklch,color".split(",")); +const IMAGE_FUNCTIONS = /* @__PURE__ */ new Set("url,src,image,image-set,linear-gradient,radial-gradient,conic-gradient,repeating-linear-gradient,repeating-radial-gradient,repeating-conic-gradient".split(",")); +const MATH_FUNCTIONS = /* @__PURE__ */ new Set("calc,min,max,clamp".split(",")); +const POSITION_OR_SIZE_KEYWORDS = /* @__PURE__ */ new Set("left,right,top,bottom,center,auto,cover,contain".split(",")); +const BORDER_WIDTH_KEYWORDS = /* @__PURE__ */ new Set("thin,medium,thick".split(",")); +const BORDER_STYLE_KEYWORDS = /* @__PURE__ */ new Set("none,hidden,dotted,dashed,solid,double,groove,ridge,inset,outset".split(",")); +function filterSignificantTokens(values) { + return values.filter((v) => v.type !== "whitespace" && v.type !== "comment" && v.type !== "EOF"); +} +function splitTokensByComma(tokens) { + const res = [[]]; + for (const t of tokens) if (t.type === "comma") res.push([]); + else res[res.length - 1].push(t); + return res; +} +function expandUniformLonghands(longhands, val) { + const res = {}; + for (const lh of longhands) res[lh] = val; + return res; +} +function tryExpandCssWide(filtered, longhands, extraKeywords) { + if (filtered.length === 1 && filtered[0].type === "ident") { + const v = filtered[0].value.toLowerCase(); + if (CSS_WIDE_KEYWORDS$2.has(v) || extraKeywords?.has(v)) return expandUniformLonghands(longhands, [filtered[0]]); + } + return null; +} +function checkUniformCssWide(serializedVals, checkVar = false) { + if (serializedVals.some((v) => CSS_WIDE_KEYWORDS$2.has(v.toLowerCase()) || checkVar && v.toLowerCase().startsWith("var("))) return serializedVals.every((v) => v.toLowerCase() === serializedVals[0].toLowerCase()) ? serializedVals[0] : null; +} +function expandFourSides(data) { + const s0 = [data[0]]; + const s1 = data.length > 1 ? [data[1]] : s0; + return [ + s0, + s1, + data.length > 2 ? [data[2]] : s0, + data.length > 3 ? [data[3]] : s1 + ]; +} +function formatFourSides(a, b, c, d) { + if (a === b && a === c && a === d) return a; + if (a === c && b === d) return `${a} ${b}`; + if (b === d) return `${a} ${b} ${c}`; + return `${a} ${b} ${c} ${d}`; +} +function getFunctionName(token) { + if (token.type === "function") { + if ("name" in token && typeof token.name === "string") return token.name.toLowerCase(); + if ("value" in token && typeof token.value === "string") return token.value.toLowerCase(); + } + return ""; +} +function isRepeatKeyword(token) { + return token.type === "ident" && REPEAT_KEYWORDS.has(token.value.toLowerCase()); +} +function isAttachmentKeyword(token) { + return token.type === "ident" && ATTACHMENT_KEYWORDS.has(token.value.toLowerCase()); +} +function isBoxKeyword(token) { + return token.type === "ident" && BOX_KEYWORDS.has(token.value.toLowerCase()); +} +function isClipOnlyBoxKeyword(keyword) { + return CLIP_ONLY_BOX_KEYWORDS.has(keyword.toLowerCase()); +} +function isColorToken(token) { + if (token.type === "hash") return true; + if (token.type === "ident") return NAMED_COLORS.has(token.value.toLowerCase()); + if (token.type === "function") return COLOR_FUNCTIONS.has(getFunctionName(token)); + return false; +} +function isImageToken(token) { + if (token.type === "ident" && token.value.toLowerCase() === "none") return true; + if (token.type === "url") return true; + if (token.type === "function") return IMAGE_FUNCTIONS.has(getFunctionName(token)); + return false; +} +function isPositionOrSizeValue(token) { + if (token.type === "ident") return POSITION_OR_SIZE_KEYWORDS.has(token.value.toLowerCase()); + if (token.type === "percentage" || token.type === "dimension") return true; + if (token.type === "number" && token.value === 0) return true; + if (token.type === "function") return MATH_FUNCTIONS.has(getFunctionName(token)); + return false; +} +function extractSizeTokens(tokens, slashIdx) { + if (slashIdx + 1 >= tokens.length) return null; + const first = tokens[slashIdx + 1]; + if (first.type === "ident" && ["cover", "contain"].includes(first.value.toLowerCase())) return { + size: [first], + consumed: 1 + }; + const isSizeVal = (t) => t.type === "ident" && t.value.toLowerCase() === "auto" || t.type === "percentage" || t.type === "dimension" || t.type === "number" && t.value === 0 || t.type === "function" && MATH_FUNCTIONS.has(getFunctionName(t)); + if (!isSizeVal(first)) return null; + if (slashIdx + 2 < tokens.length && isSizeVal(tokens[slashIdx + 2])) return { + size: [first, tokens[slashIdx + 2]], + consumed: 2 + }; + return { + size: [first], + consumed: 1 + }; +} +function mapBoxKeywords(keywords) { + if (keywords.length === 0) return { + origin: "padding-box", + clip: "border-box" + }; + if (keywords.length === 1) { + const a = keywords[0].toLowerCase(); + return isClipOnlyBoxKeyword(a) ? { + origin: "border-box", + clip: a + } : { + origin: a, + clip: a + }; + } + if (keywords.length === 2) { + const a = keywords[0].toLowerCase(); + const b = keywords[1].toLowerCase(); + const aClip = isClipOnlyBoxKeyword(a); + const bClip = isClipOnlyBoxKeyword(b); + if (aClip && bClip) return { + origin: "border-box", + clip: `${a} ${b}` + }; + if (aClip) return { + origin: b, + clip: a + }; + if (bClip) return { + origin: a, + clip: b + }; + return { + origin: a, + clip: b + }; + } + if (keywords.length === 3) { + const clips = keywords.filter(isClipOnlyBoxKeyword); + const origins = keywords.filter((k) => !isClipOnlyBoxKeyword(k)); + return clips.length === 2 && origins.length === 1 ? { + origin: origins[0], + clip: clips.join(" ") + } : null; + } + return null; +} +function parseRepeatTokens(tokens) { + if (tokens.length === 1) { + const val = tokens[0].value?.toString().toLowerCase(); + if (val === "repeat-x") return [{ + type: "ident", + value: "repeat" + }, { + type: "ident", + value: "no-repeat" + }]; + if (val === "repeat-y") return [{ + type: "ident", + value: "no-repeat" + }, { + type: "ident", + value: "repeat" + }]; + return [tokens[0]]; + } + return tokens.length === 2 ? tokens : null; +} +function normalizePositionTokens(tokens) { + if (tokens.length === 1) { + const t0 = tokens[0]; + if (t0.type === "ident") { + const v = t0.value.toLowerCase(); + if (v === "left" || v === "right") return [t0, { + type: "ident", + value: "center" + }]; + if (v === "top" || v === "bottom") return [{ + type: "ident", + value: "center" + }, t0]; + if (v === "center") return [t0, t0]; + } else return [t0, { + type: "percentage", + value: 50, + sign: null + }]; + } + if (tokens.length === 2 && tokens[0].type === "ident" && tokens[1].type === "ident") { + const [t0, t1] = tokens; + const v0 = t0.value.toLowerCase(); + const v1 = t1.value.toLowerCase(); + const isHoriz = (v) => v === "left" || v === "right"; + const isVert = (v) => v === "top" || v === "bottom"; + if (isVert(v0) && isHoriz(v1) || isVert(v0) && v1 === "center") return [t1, t0]; + } + return tokens; +} +function normalizeSizeTokens(tokens) { + if (tokens.length === 1) { + const t0 = tokens[0]; + if (t0.type === "ident" && ["cover", "contain"].includes(t0.value.toLowerCase())) return [t0]; + return [t0, { + type: "ident", + value: "auto" + }]; + } + return tokens; +} +function joinWithWhitespace(tokens) { + const res = []; + for (let i = 0; i < tokens.length; i++) { + if (i > 0) res.push({ + type: "whitespace", + value: " " + }); + res.push(tokens[i]); + } + return res; +} +function expandBackground(values) { + const cssWide = tryExpandCssWide(filterSignificantTokens(values), SHORTHANDS_DATA["background"]); + if (cssWide) return cssWide; + const layers = splitTokensByComma(values); + const numLayers = layers.length; + if (numLayers === 0) return null; + const imageLayers = []; + const positionLayers = []; + const sizeLayers = []; + const repeatLayers = []; + const attachmentLayers = []; + const originLayers = []; + const clipLayers = []; + let parsedColor = null; + for (let i = 0; i < numLayers; i++) { + const layerClean = filterSignificantTokens(layers[i]); + if (layerClean.length === 0) return null; + const slashIdx = layerClean.findIndex((t) => t.type === "delim" && t.value === "/"); + let sizeTokens = null; + if (slashIdx !== -1) { + const sizeResult = extractSizeTokens(layerClean, slashIdx); + if (!sizeResult) return null; + sizeTokens = sizeResult.size; + layerClean.splice(slashIdx, 1 + sizeResult.consumed); + } + const repeatTokens = []; + const attachmentTokens = []; + const boxKeywords = []; + let colorTokens = null; + let imageTokens = null; + const positionTokens = []; + for (const token of layerClean) if (isRepeatKeyword(token)) repeatTokens.push(token); + else if (isAttachmentKeyword(token)) attachmentTokens.push(token); + else if (isBoxKeyword(token)) boxKeywords.push(token.value); + else if (isColorToken(token)) { + if (i !== numLayers - 1 || colorTokens !== null) return null; + colorTokens = [token]; + } else if (isImageToken(token)) { + if (imageTokens !== null) return null; + imageTokens = [token]; + } else if (isPositionOrSizeValue(token)) positionTokens.push(token); + else return null; + if (sizeTokens !== null && positionTokens.length === 0 || positionTokens.length > 4) return null; + const boxMapped = mapBoxKeywords(boxKeywords); + if (!boxMapped) return null; + const repeatMapped = parseRepeatTokens(repeatTokens); + imageLayers.push(imageTokens || [{ + type: "ident", + value: "none" + }]); + positionLayers.push(positionTokens.length > 0 ? joinWithWhitespace(normalizePositionTokens(positionTokens)) : joinWithWhitespace([{ + type: "percentage", + value: 0, + sign: null + }, { + type: "percentage", + value: 0, + sign: null + }])); + sizeLayers.push(sizeTokens !== null ? joinWithWhitespace(normalizeSizeTokens(sizeTokens)) : [{ + type: "ident", + value: "auto" + }]); + repeatLayers.push(repeatMapped ? joinWithWhitespace(repeatMapped) : [{ + type: "ident", + value: "repeat" + }]); + attachmentLayers.push(attachmentTokens.length > 0 ? joinWithWhitespace(attachmentTokens) : [{ + type: "ident", + value: "scroll" + }]); + originLayers.push([{ + type: "ident", + value: boxMapped.origin + }]); + clipLayers.push(joinWithWhitespace(boxMapped.clip.split(" ").map((c) => ({ + type: "ident", + value: c + })))); + if (colorTokens !== null) parsedColor = colorTokens; + } + const joinLayers = (layerList) => { + const res = []; + for (let i = 0; i < layerList.length; i++) { + if (i > 0) res.push({ + type: "comma", + value: "," + }, { + type: "whitespace", + value: " " + }); + res.push(...layerList[i]); + } + return res; + }; + return { + "background-image": joinLayers(imageLayers), + "background-position": joinLayers(positionLayers), + "background-size": joinLayers(sizeLayers), + "background-repeat": joinLayers(repeatLayers), + "background-attachment": joinLayers(attachmentLayers), + "background-origin": joinLayers(originLayers), + "background-clip": joinLayers(clipLayers), + "background-color": parsedColor || [{ + type: "ident", + value: "transparent" + }] + }; +} +function contractBackground(longhands) { + const image = longhands["background-image"]; + const position = longhands["background-position"]; + const size = longhands["background-size"]; + const repeat = longhands["background-repeat"]; + const attachment = longhands["background-attachment"]; + const origin = longhands["background-origin"]; + const clip = longhands["background-clip"]; + const color = longhands["background-color"]; + if (!image || !position || !size || !repeat || !attachment || !origin || !clip || !color) return null; + const imageLayers = splitTokensByComma(image); + const positionLayers = splitTokensByComma(position); + const sizeLayers = splitTokensByComma(size); + const repeatLayers = splitTokensByComma(repeat); + const attachmentLayers = splitTokensByComma(attachment); + const originLayers = splitTokensByComma(origin); + const clipLayers = splitTokensByComma(clip); + const numLayers = imageLayers.length; + if (positionLayers.length !== numLayers || sizeLayers.length !== numLayers || repeatLayers.length !== numLayers || attachmentLayers.length !== numLayers || originLayers.length !== numLayers || clipLayers.length !== numLayers) return null; + const layerStrings = []; + for (let i = 0; i < numLayers; i++) { + const imgVal = serialize(imageLayers[i]).trim(); + const posVal = serialize(positionLayers[i]).trim(); + const sizeVal = serialize(sizeLayers[i]).trim(); + const repVal = serialize(repeatLayers[i]).trim(); + const attVal = serialize(attachmentLayers[i]).trim(); + const origVal = serialize(originLayers[i]).trim(); + const clipVal = serialize(clipLayers[i]).trim(); + const parts = []; + if (imgVal !== "none" && imgVal !== "") parts.push(imgVal); + const isInitialPosition = posVal !== "" && [ + "0% 0%", + "left top", + "0% center", + "center left", + "left center" + ].includes(posVal.toLowerCase()); + const isInitialSize = sizeVal !== "" && ["auto", "auto auto"].includes(sizeVal.toLowerCase()); + if (posVal !== "" && sizeVal !== "") { + if (!isInitialSize) parts.push(`${posVal} / ${sizeVal}`); + else if (!isInitialPosition) parts.push(posVal); + } + const isInitialRepeat = repVal !== "" && ["repeat", "repeat repeat"].includes(repVal.toLowerCase()); + if (repVal !== "" && !isInitialRepeat) { + const tokens = repeatLayers[i].filter((t) => t.type !== "whitespace" && t.type !== "EOF"); + if (tokens.length === 2) { + const v0 = tokens[0].value?.toString().toLowerCase(); + const v1 = tokens[1].value?.toString().toLowerCase(); + if (v0 === "repeat" && v1 === "no-repeat") parts.push("repeat-x"); + else if (v0 === "no-repeat" && v1 === "repeat") parts.push("repeat-y"); + else if (v0 === v1 && v0) parts.push(v0); + else parts.push(`${v0} ${v1}`); + } else parts.push(repVal); + } + if (attVal !== "" && attVal.toLowerCase() !== "scroll") parts.push(attVal); + if (origVal !== "" && clipVal !== "") { + const isClipOnly = ["text", "border-area"].includes(clipVal.toLowerCase()) || clipVal.toLowerCase().includes("text") || clipVal.toLowerCase().includes("border-area"); + const defaultOrigin = isClipOnly ? "border-box" : "padding-box"; + if (origVal.toLowerCase() !== "padding-box" || clipVal.toLowerCase() !== "border-box") { + if (isClipOnly) parts.push(origVal.toLowerCase() === defaultOrigin ? clipVal : `${origVal} ${clipVal}`); + else parts.push(origVal.toLowerCase() === clipVal.toLowerCase() ? origVal : `${origVal} ${clipVal}`); + } + } + if (i === numLayers - 1) { + const colVal = serialize(color).trim(); + if (colVal !== "" && colVal.toLowerCase() !== "transparent") parts.push(colVal); + } + if (parts.length === 0) parts.push("none"); + layerStrings.push(parts.join(" ")); + } + return layerStrings.join(", "); +} +function isValidLengthOrPercentage(val, allowNegative = true) { + if (val.type === "ident") { + const kw = (val.value ?? "").toString().toLowerCase(); + return kw === "auto" || CSS_WIDE_KEYWORDS$2.has(kw); + } + if (val.type === "dimension") { + if (!allowNegative && typeof val.value === "number" && val.value < 0) return false; + return LENGTH_UNITS.has((val.unit ?? "").toLowerCase()); + } + if (val.type === "percentage") return allowNegative || typeof val.value !== "number" || val.value >= 0; + if (val.type === "number" && val.value === 0) return true; + if (val.type === "function") { + const fnName = getFunctionName(val); + return MATH_FUNCTIONS.has(fnName) || fnName === "env"; + } + return false; +} +const expandBox = (physical, logical) => (values) => { + const filtered = filterSignificantTokens(values); + if (filtered.length === 0) return null; + const isLogical = filtered[0].type === "ident" && filtered[0].value.toLowerCase() === "logical"; + const data = isLogical ? filtered.slice(1) : filtered; + if (data.length < 1 || data.length > 4) return null; + const isLengthBox = physical[0].startsWith("margin") || physical[0].startsWith("padding") || physical[0] === "top" || physical[0].startsWith("scroll-") || physical[0].startsWith("border-") && physical[0].endsWith("-width"); + const allowNegative = !physical[0].startsWith("padding") && !physical[0].startsWith("scroll-padding") && !(physical[0].startsWith("border-") && physical[0].endsWith("-width")); + if (isLengthBox && !data.every((v) => isValidLengthOrPercentage(v, allowNegative))) return null; + const target = isLogical ? logical : physical; + const [s0, s1, s2, s3] = expandFourSides(data); + return { + [target[0]]: s0, + [target[1]]: s1, + [target[2]]: s2, + [target[3]]: s3 + }; +}; +const contractBox = (physical, logical) => (values) => { + const t = values[physical[0]]; + const r = values[physical[1]]; + const b = values[physical[2]]; + const l = values[physical[3]]; + if (t && r && b && l) { + const serialized = [ + t, + r, + b, + l + ].map((v) => serialize(v).trim()); + const cssWide = checkUniformCssWide(serialized); + if (cssWide !== void 0) return cssWide; + return formatFourSides(serialized[0], serialized[1], serialized[2], serialized[3]); + } + const lbs = values[logical[0]]; + const lbe = values[logical[2]]; + const lis = values[logical[1]]; + const lie = values[logical[3]]; + if (lbs && lbe && lis && lie) { + const [sbs, sis, sbe, sie] = [ + lbs, + lis, + lbe, + lie + ].map((v) => serialize(v).trim()); + const cssWide = checkUniformCssWide([ + sbs, + sis, + sbe, + sie + ]); + if (cssWide !== void 0) return cssWide; + return `logical ${formatFourSides(sbs, sis, sbe, sie)}`; + } + return null; +}; +const expandTwoValue = (longhands) => (values) => { + const filtered = filterSignificantTokens(values); + if (filtered.length < 1 || filtered.length > 2) return null; + if (longhands[0].startsWith("padding") || longhands[0].endsWith("-width")) { + for (const v of filtered) if ((v.type === "dimension" || v.type === "percentage" || v.type === "number") && typeof v.value === "number" && v.value < 0) return null; + } + return { + [longhands[0]]: [filtered[0]], + [longhands[1]]: filtered.length > 1 ? [filtered[1]] : [filtered[0]] + }; +}; +const contractTwoValue = (longhands) => (values) => { + const v1 = values[longhands[0]]; + const v2 = values[longhands[1]]; + if (!v1 || !v2) return null; + const s1 = serialize(v1).trim(); + const s2 = serialize(v2).trim(); + const cssWide = checkUniformCssWide([s1, s2]); + if (cssWide !== void 0) return cssWide; + return s1 === s2 ? s1 : `${s1} ${s2}`; +}; +function formatBorderSideValue(widthVal, styleVal, colorVal) { + const w = widthVal.trim(); + const s = styleVal.trim(); + const c = colorVal.trim(); + const cssWide = checkUniformCssWide([ + w, + s, + c + ]); + if (cssWide !== void 0) return cssWide; + const isInitialWidth = w.toLowerCase() === "medium"; + const isInitialStyle = s.toLowerCase() === "none"; + const isInitialColor = c.toLowerCase() === "currentcolor"; + if (isInitialWidth && isInitialStyle && isInitialColor) return "none"; + const parts = []; + if (!isInitialWidth) parts.push(w); + if (!isInitialStyle) parts.push(s); + if (!isInitialColor) parts.push(c); + return parts.length === 0 ? "none" : parts.join(" "); +} +function parseBorderTriplet(filtered, allowAutoStyle = false) { + let widthVal = [{ + type: "ident", + value: "medium" + }]; + let styleVal = [{ + type: "ident", + value: "none" + }]; + let colorVal = [{ + type: "ident", + value: "currentcolor" + }]; + for (const val of filtered) if (val.type === "ident") { + const v = val.value.toLowerCase(); + if (BORDER_WIDTH_KEYWORDS.has(v)) widthVal = [val]; + else if (BORDER_STYLE_KEYWORDS.has(v) || allowAutoStyle && v === "auto") styleVal = [val]; + else colorVal = [val]; + } else if (val.type === "dimension" || val.type === "percentage" || val.type === "number") widthVal = [val]; + else colorVal = [val]; + return { + widthVal, + styleVal, + colorVal + }; +} +const BORDER_IMAGE_LONGHANDS = [ + "border-image-source", + "border-image-slice", + "border-image-width", + "border-image-outset", + "border-image-repeat" +]; +const BORDER_ALL_LONGHANDS = [ + "border-top-width", + "border-right-width", + "border-bottom-width", + "border-left-width", + "border-top-style", + "border-right-style", + "border-bottom-style", + "border-left-style", + "border-top-color", + "border-right-color", + "border-bottom-color", + "border-left-color", + ...BORDER_IMAGE_LONGHANDS +]; +function createInitialBorderImageValues(source = [{ + type: "ident", + value: "none" +}]) { + return { + "border-image-source": source, + "border-image-slice": [{ + type: "percentage", + value: 100, + sign: null + }], + "border-image-width": [{ + type: "number", + value: 1, + sign: null, + numberType: "integer" + }], + "border-image-outset": [{ + type: "number", + value: 0, + sign: null, + numberType: "integer" + }], + "border-image-repeat": [{ + type: "ident", + value: "stretch" + }] + }; +} +function isInitialBorderImage(values) { + const src = values["border-image-source"]; + const slice = values["border-image-slice"]; + const width = values["border-image-width"]; + const outset = values["border-image-outset"]; + const repeat = values["border-image-repeat"]; + if (!src || !slice || !width || !outset || !repeat) return false; + const sSrc = serialize(src).trim().toLowerCase(); + const sSlice = serialize(slice).trim().toLowerCase(); + const sWidth = serialize(width).trim().toLowerCase(); + const sOutset = serialize(outset).trim().toLowerCase(); + const sRepeat = serialize(repeat).trim().toLowerCase(); + return (sSrc === "none" || sSrc === "") && (sSlice === "100%" || sSlice === "100% 100% 100% 100%" || sSlice === "") && (sWidth === "1" || sWidth === "1 1 1 1" || sWidth === "") && (sOutset === "0" || sOutset === "0px" || sOutset === "0s" || sOutset === "0 0 0 0" || sOutset === "") && (sRepeat === "stretch" || sRepeat === "stretch stretch" || sRepeat === ""); +} +const expandBorderSide = (prefix) => (values) => { + const filtered = filterSignificantTokens(values); + if (filtered.length === 0 || filtered.length > 3) return null; + const longhands = [ + `${prefix}-width`, + `${prefix}-style`, + `${prefix}-color` + ]; + const cssWide = tryExpandCssWide(filtered, longhands); + if (cssWide) return cssWide; + const { widthVal, styleVal, colorVal } = parseBorderTriplet(filtered); + return { + [longhands[0]]: widthVal, + [longhands[1]]: styleVal, + [longhands[2]]: colorVal + }; +}; +const contractBorderSide = (prefix) => (values) => { + const w = values[`${prefix}-width`]; + const s = values[`${prefix}-style`]; + const c = values[`${prefix}-color`]; + if (!w || !s || !c) return null; + return formatBorderSideValue(serialize(w), serialize(s), serialize(c)); +}; +function expandBorder(values) { + const filtered = filterSignificantTokens(values); + if (filtered.length === 0 || filtered.length > 3) return null; + const cssWide = tryExpandCssWide(filtered, BORDER_ALL_LONGHANDS); + if (cssWide) return cssWide; + const { widthVal, styleVal, colorVal } = parseBorderTriplet(filtered); + return { + "border-top-width": widthVal, + "border-right-width": widthVal, + "border-bottom-width": widthVal, + "border-left-width": widthVal, + "border-top-style": styleVal, + "border-right-style": styleVal, + "border-bottom-style": styleVal, + "border-left-style": styleVal, + "border-top-color": colorVal, + "border-right-color": colorVal, + "border-bottom-color": colorVal, + "border-left-color": colorVal, + ...createInitialBorderImageValues() + }; +} +function contractBorder(values) { + for (const lh of BORDER_ALL_LONGHANDS) if (!values[lh]) return null; + const allSerialized = BORDER_ALL_LONGHANDS.map((lh) => serialize(values[lh]).trim()); + const cssWide = checkUniformCssWide(allSerialized); + if (cssWide !== void 0) return cssWide; + if (!isInitialBorderImage(values)) return null; + const [w0, w1, w2, w3, s0, s1, s2, s3, c0, c1, c2, c3] = allSerialized; + if (w0 !== w1 || w0 !== w2 || w0 !== w3) return null; + if (s0 !== s1 || s0 !== s2 || s0 !== s3) return null; + if (c0 !== c1 || c0 !== c2 || c0 !== c3) return null; + return formatBorderSideValue(w0, s0, c0); +} +function expandBorderImage(values) { + const filtered = filterSignificantTokens(values); + if (filtered.length === 0) return null; + const cssWide = tryExpandCssWide(filtered, BORDER_IMAGE_LONGHANDS); + if (cssWide) return cssWide; + if (filtered.length === 1 && filtered[0].type === "ident" && filtered[0].value.toLowerCase() === "none") return createInitialBorderImageValues(); + if (filtered.some((t) => t.type === "function" && getFunctionName(t) === "var")) return expandUniformLonghands(BORDER_IMAGE_LONGHANDS, values); + let source = [{ + type: "ident", + value: "none" + }]; + for (const token of filtered) if (token.type === "url" || token.type === "function" && [ + "linear-gradient", + "radial-gradient", + "conic-gradient", + "image", + "image-set" + ].includes(getFunctionName(token))) source = [token]; + return createInitialBorderImageValues(source); +} +function contractBorderImage(values) { + for (const lh of BORDER_IMAGE_LONGHANDS) if (!values[lh]) return null; + const allVals = BORDER_IMAGE_LONGHANDS.map((lh) => serialize(values[lh]).trim()); + const cssWide = checkUniformCssWide(allVals, true); + if (cssWide !== void 0) return cssWide; + if (isInitialBorderImage(values)) return "none"; + const [sSrc, sSlice, sWidth, sOutset, sRepeat] = allVals; + return (sSlice === "100%" || sSlice === "100% 100% 100% 100%") && (sWidth === "1" || sWidth === "1 1 1 1") && (sOutset === "0" || sOutset === "0px" || sOutset === "0s" || sOutset === "0 0 0 0") && (sRepeat === "stretch" || sRepeat === "stretch stretch") ? sSrc : null; +} +function expandOutline(values) { + const filtered = filterSignificantTokens(values); + if (filtered.length === 0 || filtered.length > 3) return null; + const cssWide = tryExpandCssWide(filtered, [ + "outline-color", + "outline-style", + "outline-width" + ]); + if (cssWide) return cssWide; + const { widthVal, styleVal, colorVal } = parseBorderTriplet(filtered, true); + return { + "outline-color": colorVal, + "outline-style": styleVal, + "outline-width": widthVal + }; +} +function contractOutline(values) { + const c = values["outline-color"]; + const s = values["outline-style"]; + const w = values["outline-width"]; + if (!c || !s || !w) return null; + const sc = serialize(c).trim(); + const ss = serialize(s).trim(); + const sw = serialize(w).trim(); + const cssWide = checkUniformCssWide([ + sc, + ss, + sw + ]); + if (cssWide !== void 0) return cssWide; + const isInitialColor = sc.toLowerCase() === "currentcolor"; + const isInitialStyle = ss.toLowerCase() === "none"; + const isInitialWidth = sw.toLowerCase() === "medium"; + if (isInitialColor && isInitialStyle && isInitialWidth) return "none"; + const parts = []; + if (!isInitialColor) parts.push(sc); + if (!isInitialStyle) parts.push(ss); + if (!isInitialWidth) parts.push(sw); + return parts.length === 0 ? "none" : parts.join(" "); +} +const FONT_VARIANT_LONGHANDS = [ + "font-variant-ligatures", + "font-variant-caps", + "font-variant-alternates", + "font-variant-numeric", + "font-variant-east-asian", + "font-variant-position", + "font-variant-emoji" +]; +function expandFontVariant(values) { + const filtered = filterSignificantTokens(values); + if (filtered.length === 0) return null; + const wide = tryExpandCssWide(filtered, FONT_VARIANT_LONGHANDS); + if (wide) return wide; + if (filtered.length === 1 && filtered[0].type === "ident") { + const v = filtered[0].value.toLowerCase(); + if (v === "normal") return expandUniformLonghands(FONT_VARIANT_LONGHANDS, [{ + type: "ident", + value: "normal" + }]); + if (v === "none") { + const res = expandUniformLonghands(FONT_VARIANT_LONGHANDS, [{ + type: "ident", + value: "normal" + }]); + res["font-variant-ligatures"] = [{ + type: "ident", + value: "none" + }]; + return res; + } + } + const buckets = { + "font-variant-ligatures": [], + "font-variant-caps": [], + "font-variant-alternates": [], + "font-variant-numeric": [], + "font-variant-east-asian": [], + "font-variant-position": [], + "font-variant-emoji": [] + }; + for (const token of filtered) if (token.type === "ident") { + const val = token.value.toLowerCase(); + if (val === "normal") continue; + if (val === "none" || FONT_VARIANT_LIGATURES_KEYWORDS.has(val)) buckets["font-variant-ligatures"].push(token); + else if (FONT_VARIANT_CAPS_KEYWORDS.has(val)) buckets["font-variant-caps"].push(token); + else if (val === "historical-forms") buckets["font-variant-alternates"].push(token); + else if (FONT_VARIANT_NUMERIC_KEYWORDS.has(val)) buckets["font-variant-numeric"].push(token); + else if (FONT_VARIANT_EAST_ASIAN_KEYWORDS.has(val)) buckets["font-variant-east-asian"].push(token); + else if (FONT_VARIANT_POSITION_KEYWORDS.has(val)) buckets["font-variant-position"].push(token); + else if (FONT_VARIANT_EMOJI_KEYWORDS.has(val)) buckets["font-variant-emoji"].push(token); + else return null; + } else if (token.type === "function" && FONT_VARIANT_ALTERNATES_FUNCTIONS.has(getFunctionName(token))) buckets["font-variant-alternates"].push(token); + else return null; + const norm = [{ + type: "ident", + value: "normal" + }]; + const res = {}; + for (const lh of FONT_VARIANT_LONGHANDS) res[lh] = buckets[lh].length > 0 ? joinWithWhitespace(buckets[lh]) : norm; + return res; +} +function contractFontVariant(values) { + for (const lh of FONT_VARIANT_LONGHANDS) if (!values[lh]) return null; + const allVals = FONT_VARIANT_LONGHANDS.map((lh) => serialize(values[lh]).trim()); + const wide = checkUniformCssWide(allVals); + if (wide !== void 0) return wide; + if (allVals.every((v) => v.toLowerCase() === "normal")) return "normal"; + if (allVals[0].toLowerCase() === "none") return allVals.slice(1).every((v) => v.toLowerCase() === "normal") ? "none" : null; + const nonNormal = allVals.filter((v) => v.toLowerCase() !== "normal"); + return nonNormal.length === 0 ? "normal" : nonNormal.join(" "); +} +const FONT_LONGHANDS = [ + "font-style", + "font-variant-caps", + "font-variant-ligatures", + "font-variant-alternates", + "font-variant-numeric", + "font-variant-east-asian", + "font-variant-position", + "font-variant-emoji", + "font-weight", + "font-stretch", + "font-size", + "line-height", + "font-family" +]; +function expandFont(values) { + const filtered = filterSignificantTokens(values); + if (filtered.length === 0) return null; + if (filtered.length === 1 && filtered[0].type === "ident" && SYSTEM_FONT_KEYWORDS.has(filtered[0].value.toLowerCase())) return expandUniformLonghands(FONT_LONGHANDS, [filtered[0]]); + const norm = [{ + type: "ident", + value: "normal" + }]; + let styleVal = norm; + let capsVal = norm; + let weightVal = norm; + let stretchVal = norm; + let lineHeightVal = norm; + let i = 0; + while (i < filtered.length) { + const token = filtered[i]; + if (token.type === "ident") { + const v = token.value.toLowerCase(); + if (v === "italic" || v === "oblique") { + styleVal = [token]; + i++; + continue; + } + if (v === "small-caps") { + capsVal = [token]; + i++; + continue; + } + if (v === "bold" || v === "bolder" || v === "lighter") { + weightVal = [token]; + i++; + continue; + } + if (FONT_STRETCH_KEYWORDS.has(v)) { + stretchVal = [token]; + i++; + continue; + } + if (v === "normal") { + i++; + continue; + } + } else if (token.type === "number" && typeof token.value === "number" && token.value >= 1 && token.value <= 1e3) { + weightVal = [token]; + i++; + continue; + } + break; + } + if (i >= filtered.length) return null; + const sizeToken = filtered[i]; + if (!(sizeToken.type === "dimension" || sizeToken.type === "percentage" || sizeToken.type === "number" && sizeToken.value === 0 || sizeToken.type === "ident" && FONT_SIZE_KEYWORDS.has(sizeToken.value.toLowerCase()) || sizeToken.type === "function" && MATH_FUNCTIONS.has(getFunctionName(sizeToken)))) return null; + const sizeVal = [sizeToken]; + i++; + if (i < filtered.length && filtered[i].type === "delim" && filtered[i].value === "/") { + i++; + if (i >= filtered.length) return null; + const lhToken = filtered[i]; + if (!(lhToken.type === "number" || lhToken.type === "dimension" || lhToken.type === "percentage" || lhToken.type === "ident" && lhToken.value.toLowerCase() === "normal" || lhToken.type === "function" && MATH_FUNCTIONS.has(getFunctionName(lhToken)))) return null; + lineHeightVal = [lhToken]; + i++; + } + if (i >= filtered.length) return null; + const lastConsumed = lineHeightVal !== norm ? lineHeightVal[0] : sizeToken; + const lastIdx = values.indexOf(lastConsumed); + let familyVal; + if (lastIdx !== -1) { + familyVal = values.slice(lastIdx + 1).filter((t) => t.type !== "EOF"); + while (familyVal.length > 0 && (familyVal[0].type === "whitespace" || familyVal[0].type === "comment")) familyVal.shift(); + } else familyVal = filtered.slice(i); + const res = expandUniformLonghands(FONT_LONGHANDS, norm); + res["font-style"] = styleVal; + res["font-variant-caps"] = capsVal; + res["font-weight"] = weightVal; + res["font-stretch"] = stretchVal; + res["font-size"] = sizeVal; + res["line-height"] = lineHeightVal; + res["font-family"] = familyVal; + return res; +} +const FONT_PRIMARY_LONGHANDS = [ + "font-style", + "font-variant-caps", + "font-weight", + "font-stretch", + "font-size", + "line-height", + "font-family" +]; +const FONT_OTHER_VARIANTS = [ + "font-variant-ligatures", + "font-variant-alternates", + "font-variant-numeric", + "font-variant-east-asian", + "font-variant-position", + "font-variant-emoji" +]; +function contractFont(values) { + for (const lh of FONT_PRIMARY_LONGHANDS) if (!values[lh]) return null; + for (const lh of FONT_OTHER_VARIANTS) if (values[lh] && serialize(values[lh]).trim().toLowerCase() !== "normal") return null; + const sStyle = serialize(values["font-style"]).trim(); + const sCaps = serialize(values["font-variant-caps"]).trim(); + const sWeight = serialize(values["font-weight"]).trim(); + const sStretch = serialize(values["font-stretch"]).trim(); + const sSize = serialize(values["font-size"]).trim(); + const sLineHeight = serialize(values["line-height"]).trim(); + const sFamily = serialize(values["font-family"], false, "font-family").trim(); + const wide = checkUniformCssWide([ + sStyle, + sCaps, + sWeight, + sStretch, + sSize, + sLineHeight, + sFamily + ]); + if (wide !== void 0) return wide; + if (!sSize || !sFamily) return null; + const parts = []; + if (sStyle.toLowerCase() !== "normal") parts.push(sStyle); + if (sCaps.toLowerCase() !== "normal") parts.push(sCaps); + if (sWeight.toLowerCase() !== "normal" && sWeight !== "400") parts.push(sWeight); + if (sStretch.toLowerCase() !== "normal") parts.push(sStretch); + parts.push(sLineHeight.toLowerCase() !== "normal" ? `${sSize} / ${sLineHeight}` : sSize); + parts.push(sFamily); + return parts.join(" "); +} +const LIST_STYLE_LONGHANDS = [ + "list-style-type", + "list-style-position", + "list-style-image" +]; +function expandListStyle(values) { + const filtered = filterSignificantTokens(values); + if (filtered.length === 0 || filtered.length > 3) return null; + const wide = tryExpandCssWide(filtered, LIST_STYLE_LONGHANDS); + if (wide) return wide; + let typeVal = [{ + type: "ident", + value: "disc" + }]; + let posVal = [{ + type: "ident", + value: "outside" + }]; + let imgVal = [{ + type: "ident", + value: "none" + }]; + let hasType = false; + let hasPos = false; + let hasImg = false; + for (const token of filtered) if (token.type === "ident") { + const v = token.value.toLowerCase(); + if ((v === "inside" || v === "outside") && !hasPos) { + posVal = [token]; + hasPos = true; + } else if (v === "none") { + if (!hasImg && !hasType) { + imgVal = [token]; + typeVal = [token]; + hasImg = true; + hasType = true; + } else if (!hasImg) { + imgVal = [token]; + hasImg = true; + } else if (!hasType) { + typeVal = [token]; + hasType = true; + } + } else if (!hasType) { + typeVal = [token]; + hasType = true; + } else return null; + } else if ((token.type === "url" || token.type === "function" && IMAGE_FUNCTIONS.has(getFunctionName(token))) && !hasImg) { + imgVal = [token]; + hasImg = true; + } else return null; + return { + "list-style-type": typeVal, + "list-style-position": posVal, + "list-style-image": imgVal + }; +} +function contractListStyle(values) { + const t = values["list-style-type"]; + const p = values["list-style-position"]; + const i = values["list-style-image"]; + if (!t || !p || !i) return null; + const st = serialize(t).trim(); + const sp = serialize(p).trim(); + const si = serialize(i).trim(); + const wide = checkUniformCssWide([ + st, + sp, + si + ]); + if (wide !== void 0) return wide; + const parts = []; + if (sp.toLowerCase() !== "outside") parts.push(sp); + if (si.toLowerCase() !== "none") parts.push(si); + if (st.toLowerCase() !== "disc") parts.push(st); + return parts.length === 0 ? "disc" : parts.join(" "); +} +const FLEX_LONGHANDS = [ + "flex-grow", + "flex-shrink", + "flex-basis" +]; +function expandFlex(values) { + const filtered = filterSignificantTokens(values); + if (filtered.length === 0 || filtered.length > 3) return null; + const wide = tryExpandCssWide(filtered, FLEX_LONGHANDS); + if (wide) return wide; + if (filtered.length === 1 && filtered[0].type === "ident") { + const v = filtered[0].value.toLowerCase(); + if (v === "none") return { + "flex-grow": [{ + type: "number", + value: 0, + sign: null, + numberType: "integer" + }], + "flex-shrink": [{ + type: "number", + value: 0, + sign: null, + numberType: "integer" + }], + "flex-basis": [{ + type: "ident", + value: "auto" + }] + }; + if (v === "auto") return { + "flex-grow": [{ + type: "number", + value: 1, + sign: null, + numberType: "integer" + }], + "flex-shrink": [{ + type: "number", + value: 1, + sign: null, + numberType: "integer" + }], + "flex-basis": [{ + type: "ident", + value: "auto" + }] + }; + } + let grow = null; + let shrink = null; + let basis = null; + for (const token of filtered) if (token.type === "number") { + if (grow === null) grow = [token]; + else if (shrink === null) shrink = [token]; + else return null; + } else if (isValidLengthOrPercentage(token) || token.type === "ident" && FLEX_BASIS_KEYWORDS.has(token.value.toLowerCase())) { + if (basis === null) basis = [token]; + else return null; + } else return null; + if (grow === null && basis === null) return null; + const one = [{ + type: "number", + value: 1, + sign: null, + numberType: "integer" + }]; + return { + "flex-grow": grow ?? one, + "flex-shrink": shrink ?? one, + "flex-basis": basis ?? (grow !== null ? [{ + type: "dimension", + value: 0, + unit: "px", + sign: null, + numberType: "integer" + }] : [{ + type: "ident", + value: "auto" + }]) + }; +} +function contractFlex(values) { + const g = values["flex-grow"]; + const s = values["flex-shrink"]; + const b = values["flex-basis"]; + if (!g || !s || !b) return null; + const sg = serialize(g).trim(); + const ss = serialize(s).trim(); + const sb = serialize(b).trim(); + const wide = checkUniformCssWide([ + sg, + ss, + sb + ]); + if (wide !== void 0) return wide; + if (sg.includes("var(") || ss.includes("var(") || sb.includes("var(")) return sg === ss && sg === sb ? sg : null; + if (sb.toLowerCase() === "auto") { + if (sg === "0" && ss === "1") return "initial"; + if (sg === "1" && ss === "1") return "auto"; + if (sg === "0" && ss === "0") return "none"; + } + if ((sb === "0px" || sb === "0%" || sb === "0") && ss === "1") return `${sg} 1 0px`; + return `${sg} ${ss} ${sb}`; +} +function contractOverflow(values) { + const x = values["overflow-x"]; + const y = values["overflow-y"]; + if (!x || !y) return null; + const sx = serialize(x).trim(); + const sy = serialize(y).trim(); + const wide = checkUniformCssWide([sx, sy]); + if (wide !== void 0) return wide; + if (sx.includes("var(") || sy.includes("var(")) return sx === sy ? sx : null; + return sx === sy ? sx : `${sx} ${sy}`; +} +const LINE_CLAMP_LONGHANDS = [ + "max-lines", + "block-ellipsis", + "continue" +]; +function expandLineClamp(values) { + const filtered = filterSignificantTokens(values); + if (filtered.length === 0) return null; + const wide = tryExpandCssWide(filtered, LINE_CLAMP_LONGHANDS); + if (wide) return wide; + const autoToken = [{ + type: "ident", + value: "auto" + }]; + if (filtered.length === 1 && filtered[0].type === "ident" && filtered[0].value.toLowerCase() === "none") return { + "max-lines": [{ + type: "ident", + value: "none" + }], + "block-ellipsis": autoToken, + "continue": autoToken + }; + return { + "max-lines": filtered, + "block-ellipsis": autoToken, + "continue": autoToken + }; +} +function contractLineClamp(values) { + const lines = values["max-lines"]; + if (!lines) return null; + const sLines = serialize(lines).trim(); + return sLines.toLowerCase() === "none" ? "none" : sLines; +} +const BORDER_RADIUS_PHYSICAL = [ + "border-top-left-radius", + "border-top-right-radius", + "border-bottom-right-radius", + "border-bottom-left-radius" +]; +function expandBorderRadius(values) { + const filtered = filterSignificantTokens(values); + if (filtered.length === 0) return null; + if (filtered[0].type === "ident" && filtered[0].value.toLowerCase() === "logical") return null; + const slashIndex = filtered.findIndex((v) => v.type === "delim" && v.value === "/"); + let hValues; + let vValues; + if (slashIndex !== -1) { + hValues = filtered.slice(0, slashIndex); + vValues = filtered.slice(slashIndex + 1); + if (hValues.length === 0 || hValues.length > 4 || vValues.length === 0 || vValues.length > 4) return null; + if (vValues.some((v) => v.type === "delim" && v.value === "/")) return null; + } else { + if (filtered.length > 4) return null; + hValues = filtered; + vValues = filtered; + } + const hExpanded = expandFourSides(hValues); + const vExpanded = expandFourSides(vValues); + const result = {}; + for (let i = 0; i < 4; i++) { + const h = hExpanded[i]; + const v = vExpanded[i]; + result[BORDER_RADIUS_PHYSICAL[i]] = serialize(h) === serialize(v) ? h : [ + ...h, + { + type: "whitespace", + value: " " + }, + ...v + ]; + } + return result; +} +function contractBorderRadius(values) { + if (!BORDER_RADIUS_PHYSICAL.every((prop) => values[prop] !== void 0)) return null; + const longhands = BORDER_RADIUS_PHYSICAL.map((prop) => values[prop]); + const wide = checkUniformCssWide(longhands.map((lh) => serialize(lh).trim())); + if (wide !== void 0) return wide; + const parsed = longhands.map((lh) => { + const filtered = filterSignificantTokens(lh); + return { + h: serialize([filtered[0]]).trim(), + v: serialize([filtered.length > 1 ? filtered[1] : filtered[0]]).trim() + }; + }); + const hStr = formatFourSides(parsed[0].h, parsed[1].h, parsed[2].h, parsed[3].h); + const vStr = formatFourSides(parsed[0].v, parsed[1].v, parsed[2].v, parsed[3].v); + return hStr === vStr ? hStr : `${hStr} / ${vStr}`; +} +const logicalLonghands = []; +const physicalLonghands = []; +for (const prop of SUPPORTED_PROPERTIES) if (prop !== "all" && prop !== "direction" && prop !== "unicode-bidi" && !prop.startsWith("--") && !(prop in SHORTHANDS_DATA)) { + if (prop in LOGICAL_MAPPING) logicalLonghands.push(prop); + else physicalLonghands.push(prop); +} +const ALL_SHORTHAND_LONGHANDS = Object.freeze([...logicalLonghands, ...physicalLonghands]); +function isCSSWideKeywordOrVar(tokens) { + const nonWs = filterSignificantTokens(tokens); + if (nonWs.length === 1 && nonWs[0].type === "ident" && CSS_WIDE_KEYWORDS$2.has(nonWs[0].value.toLowerCase())) return true; + return nonWs.some((t) => t.type === "function" && getFunctionName(t) === "var"); +} +function expandAll(value) { + if (!value || value.length === 0 || !isCSSWideKeywordOrVar(value)) return null; + return expandUniformLonghands(ALL_SHORTHAND_LONGHANDS, value); +} +function contractAll(longhands) { + let firstVal = null; + for (const lh of ALL_SHORTHAND_LONGHANDS) { + const valTokens = longhands[lh]; + if (!valTokens || valTokens.length === 0) return null; + const serialized = serialize(valTokens).trim(); + if (firstVal === null) firstVal = serialized; + else if (serialized !== firstVal) return null; + } + if (!firstVal) return null; + const lower = firstVal.toLowerCase(); + return CSS_WIDE_KEYWORDS$2.has(lower) || lower.startsWith("var(") ? firstVal : null; +} +function createBoxShorthand(name, physical, logical) { + return { + longhands: SHORTHANDS_DATA[name], + logicalLonghands: logical, + expand: expandBox(physical, logical), + contract: contractBox(physical, logical) + }; +} +function createTwoValueShorthand(name, physicalLonghands) { + const longhands = SHORTHANDS_DATA[name]; + return { + longhands, + ...physicalLonghands ? { physicalLonghands } : {}, + expand: expandTwoValue(longhands), + contract: contractTwoValue(longhands) + }; +} +function createBorderSideShorthand(prefix, longhands, physicalLonghands) { + return { + longhands, + ...physicalLonghands ? { physicalLonghands } : {}, + expand: expandBorderSide(prefix), + contract: contractBorderSide(prefix) + }; +} +function createBorderAxisShorthand(name, startProp, endProp, physicalLonghands, logicalLonghands) { + return { + longhands: SHORTHANDS_DATA[name], + physicalLonghands, + logicalLonghands, + expand: (values) => { + if (filterSignificantTokens(values).length === 0) return null; + return { + [startProp]: values, + [endProp]: values + }; + }, + contract: (values) => { + const sVal = values[startProp]; + const eVal = values[endProp]; + const start = sVal ? serialize(sVal).trim() : contractBorderSide(startProp)(values); + const end = eVal ? serialize(eVal).trim() : contractBorderSide(endProp)(values); + return start && end && start === end ? start : null; + } + }; +} +const SHORTHANDS = { + "border-block": createBorderAxisShorthand("border-block", "border-block-start", "border-block-end", [ + "border-top-width", + "border-top-style", + "border-top-color", + "border-bottom-width", + "border-bottom-style", + "border-bottom-color" + ], [ + "border-block-start-width", + "border-block-start-style", + "border-block-start-color", + "border-block-end-width", + "border-block-end-style", + "border-block-end-color" + ]), + "border-block-color": createTwoValueShorthand("border-block-color", ["border-top-color", "border-bottom-color"]), + "border-block-end": createBorderSideShorthand("border-block-end", SHORTHANDS_DATA["border-block-end"], [ + "border-bottom-width", + "border-bottom-style", + "border-bottom-color" + ]), + "border-block-start": createBorderSideShorthand("border-block-start", SHORTHANDS_DATA["border-block-start"], [ + "border-top-width", + "border-top-style", + "border-top-color" + ]), + "border-block-style": createTwoValueShorthand("border-block-style", ["border-top-style", "border-bottom-style"]), + "border-block-width": createTwoValueShorthand("border-block-width", ["border-top-width", "border-bottom-width"]), + "border": { + longhands: BORDER_ALL_LONGHANDS, + expand: expandBorder, + contract: contractBorder + }, + "border-top": createBorderSideShorthand("border-top", [ + "border-top-width", + "border-top-style", + "border-top-color" + ]), + "border-right": createBorderSideShorthand("border-right", [ + "border-right-width", + "border-right-style", + "border-right-color" + ]), + "border-bottom": createBorderSideShorthand("border-bottom", [ + "border-bottom-width", + "border-bottom-style", + "border-bottom-color" + ]), + "border-left": createBorderSideShorthand("border-left", [ + "border-left-width", + "border-left-style", + "border-left-color" + ]), + "border-image": { + longhands: BORDER_IMAGE_LONGHANDS, + expand: expandBorderImage, + contract: contractBorderImage + }, + "outline": { + longhands: [ + "outline-color", + "outline-style", + "outline-width" + ], + expand: expandOutline, + contract: contractOutline + }, + "font-variant": { + longhands: FONT_VARIANT_LONGHANDS, + expand: expandFontVariant, + contract: contractFontVariant + }, + "font": { + longhands: FONT_LONGHANDS, + expand: expandFont, + contract: contractFont + }, + "list-style": { + longhands: LIST_STYLE_LONGHANDS, + expand: expandListStyle, + contract: contractListStyle + }, + "overflow": { + longhands: SHORTHANDS_DATA["overflow"], + expand: expandTwoValue(SHORTHANDS_DATA["overflow"]), + contract: contractOverflow + }, + "flex": { + longhands: FLEX_LONGHANDS, + expand: expandFlex, + contract: contractFlex + }, + "-webkit-flex": { + longhands: FLEX_LONGHANDS, + expand: expandFlex, + contract: contractFlex + }, + "line-clamp": { + longhands: LINE_CLAMP_LONGHANDS, + expand: expandLineClamp, + contract: contractLineClamp + }, + "-webkit-line-clamp": { + longhands: LINE_CLAMP_LONGHANDS, + expand: expandLineClamp, + contract: contractLineClamp + }, + "border-color": createBoxShorthand("border-color", [ + "border-top-color", + "border-right-color", + "border-bottom-color", + "border-left-color" + ], [ + "border-block-start-color", + "border-inline-start-color", + "border-block-end-color", + "border-inline-end-color" + ]), + "border-inline": createBorderAxisShorthand("border-inline", "border-inline-start", "border-inline-end", [ + "border-left-width", + "border-left-style", + "border-left-color", + "border-right-width", + "border-right-style", + "border-right-color" + ], [ + "border-inline-start-width", + "border-inline-start-style", + "border-inline-start-color", + "border-inline-end-width", + "border-inline-end-style", + "border-inline-end-color" + ]), + "border-inline-color": createTwoValueShorthand("border-inline-color", ["border-left-color", "border-right-color"]), + "border-inline-end": createBorderSideShorthand("border-inline-end", SHORTHANDS_DATA["border-inline-end"], [ + "border-right-width", + "border-right-style", + "border-right-color" + ]), + "border-inline-start": createBorderSideShorthand("border-inline-start", SHORTHANDS_DATA["border-inline-start"], [ + "border-left-width", + "border-left-style", + "border-left-color" + ]), + "border-inline-style": createTwoValueShorthand("border-inline-style", ["border-left-style", "border-right-style"]), + "border-inline-width": createTwoValueShorthand("border-inline-width", ["border-left-width", "border-right-width"]), + "border-radius": { + longhands: SHORTHANDS_DATA["border-radius"], + logicalLonghands: [ + "border-start-start-radius", + "border-start-end-radius", + "border-end-end-radius", + "border-end-start-radius" + ], + expand: expandBorderRadius, + contract: contractBorderRadius + }, + "border-style": createBoxShorthand("border-style", [ + "border-top-style", + "border-right-style", + "border-bottom-style", + "border-left-style" + ], [ + "border-block-start-style", + "border-inline-start-style", + "border-block-end-style", + "border-inline-end-style" + ]), + "border-width": createBoxShorthand("border-width", [ + "border-top-width", + "border-right-width", + "border-bottom-width", + "border-left-width" + ], [ + "border-block-start-width", + "border-inline-start-width", + "border-block-end-width", + "border-inline-end-width" + ]), + "inset": createBoxShorthand("inset", [ + "top", + "right", + "bottom", + "left" + ], [ + "inset-block-start", + "inset-inline-start", + "inset-block-end", + "inset-inline-end" + ]), + "inset-block": createTwoValueShorthand("inset-block", ["top", "bottom"]), + "inset-inline": createTwoValueShorthand("inset-inline", ["left", "right"]), + "margin": createBoxShorthand("margin", [ + "margin-top", + "margin-right", + "margin-bottom", + "margin-left" + ], [ + "margin-block-start", + "margin-inline-start", + "margin-block-end", + "margin-inline-end" + ]), + "margin-block": createTwoValueShorthand("margin-block", ["margin-top", "margin-bottom"]), + "margin-inline": createTwoValueShorthand("margin-inline", ["margin-left", "margin-right"]), + "padding": createBoxShorthand("padding", [ + "padding-top", + "padding-right", + "padding-bottom", + "padding-left" + ], [ + "padding-block-start", + "padding-inline-start", + "padding-block-end", + "padding-inline-end" + ]), + "padding-block": createTwoValueShorthand("padding-block", ["padding-top", "padding-bottom"]), + "padding-inline": createTwoValueShorthand("padding-inline", ["padding-left", "padding-right"]), + "scroll-margin": createBoxShorthand("scroll-margin", [ + "scroll-margin-top", + "scroll-margin-right", + "scroll-margin-bottom", + "scroll-margin-left" + ], [ + "scroll-margin-block-start", + "scroll-margin-inline-start", + "scroll-margin-block-end", + "scroll-margin-inline-end" + ]), + "scroll-padding": createBoxShorthand("scroll-padding", [ + "scroll-padding-top", + "scroll-padding-right", + "scroll-padding-bottom", + "scroll-padding-left" + ], [ + "scroll-padding-block-start", + "scroll-padding-inline-start", + "scroll-padding-block-end", + "scroll-padding-inline-end" + ]), + "background": { + longhands: SHORTHANDS_DATA["background"], + expand: expandBackground, + contract: contractBackground + }, + "all": { + longhands: ALL_SHORTHAND_LONGHANDS, + expand: expandAll, + contract: contractAll + } +}; +function getShorthandAllLonghands(shorthand) { + if (shorthand.allLonghands) return shorthand.allLonghands; + if (!shorthand.logicalLonghands && !shorthand.physicalLonghands) return shorthand.longhands; + const combined = Object.freeze([ + ...shorthand.longhands, + ...shorthand.logicalLonghands || [], + ...shorthand.physicalLonghands || [] + ]); + shorthand.allLonghands = combined; + return combined; +} +const LONGHAND_TO_SHORTHAND = {}; +for (const [shorthand, def] of Object.entries(SHORTHANDS)) { + def.allLonghands = Object.freeze([ + ...def.longhands, + ...def.logicalLonghands || [], + ...def.physicalLonghands || [] + ]); + if (shorthand === "all") continue; + for (const longhand of def.longhands) { + if (!LONGHAND_TO_SHORTHAND[longhand]) LONGHAND_TO_SHORTHAND[longhand] = []; + LONGHAND_TO_SHORTHAND[longhand].push(shorthand); + } + if (def.logicalLonghands) for (const longhand of def.logicalLonghands) { + if (!LONGHAND_TO_SHORTHAND[longhand]) LONGHAND_TO_SHORTHAND[longhand] = []; + LONGHAND_TO_SHORTHAND[longhand].push(shorthand); + } + if (def.physicalLonghands) for (const longhand of def.physicalLonghands) { + if (!LONGHAND_TO_SHORTHAND[longhand]) LONGHAND_TO_SHORTHAND[longhand] = []; + if (!LONGHAND_TO_SHORTHAND[longhand].includes(shorthand)) LONGHAND_TO_SHORTHAND[longhand].push(shorthand); + } +} +// src/utils/format.ts +/** @license Copyright 2026 Google LLC. SPDX-License-Identifier: Apache-2.0 */ +const formatter = new Intl.NumberFormat("en-US", { + useGrouping: false, + minimumFractionDigits: 0, + maximumFractionDigits: 6 +}); +function formatNumber(val) { + if (val === 0) return "0"; + if (!Number.isFinite(val)) { + if (val === Infinity) return "infinity"; + if (val === -Infinity) return "-infinity"; + return "nan"; + } + const formatted = formatter.format(val); + return formatted === "-0" ? "0" : formatted; +} +// src/data/gen/selectors.ts +/** +* @license +* Copyright 2026 Google LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +const PSEUDO_CLASSES = /* @__PURE__ */ new Set("active,active-view-transition,active-view-transition-type,after,animated-image,any-link,autofill,before,blank,buffering,checked,current,default,defined,dir,disabled,empty,enabled,first,first-child,first-letter,first-line,first-of-page,first-of-type,focus,focus-visible,focus-within,fullscreen,future,has,has-slotted,heading,high-value,host,host-context,hover,in-range,indeterminate,interest-source,interest-target,invalid,is,lang,last-child,last-of-page,last-of-type,left,link,link-to,local-link,low-value,matches,modal,muted,navigation-source,not,nth,nth-child,nth-col,nth-last-child,nth-last-col,nth-last-of-type,nth-of-page,nth-of-type,only-child,only-of-type,open,optimal-value,optional,out-of-range,past,paused,picture-in-picture,placeholder-shown,playing,popover-open,read-only,read-write,required,right,root,scope,seeking,snapped,snapped-block,snapped-inline,snapped-x,snapped-y,stalled,start-of-page,state,target,target-after,target-before,target-current,target-within,unchecked,user-invalid,user-valid,valid,visited,volume-locked,where,xr-overlay".split(",")); +const PSEUDO_ELEMENTS = /* @__PURE__ */ new Set("-moz-progress-bar,-moz-range-progress,-moz-range-thumb,-moz-range-track,-ms-browse,-ms-check,-ms-clear,-ms-expand,-ms-fill,-ms-fill-lower,-ms-fill-upper,-ms-reveal,-ms-thumb,-ms-ticks-after,-ms-ticks-before,-ms-tooltip,-ms-track,-ms-value,-webkit-progress-bar,-webkit-progress-inner-value,-webkit-progress-value,-webkit-slider-runnable-track,-webkit-slider-thumb,after,backdrop,before,checkmark,clear-icon,color-swatch,column,cue,cue-region,details-content,field-component,field-content,field-separator,file-selector-button,first-letter,first-line,grammar-error,highlight,interest-button,marker,nth-fragment,part,picker,picker-icon,placeholder,reveal-icon,scroll-button,scroll-marker,scroll-marker-group,search-text,selection,slider-fill,slider-thumb,slider-track,slotted,spelling-error,step-control,step-down,step-up,target-text,view-transition,view-transition-group,view-transition-group-children,view-transition-image-pair,view-transition-new,view-transition-old".split(",")); +// src/SelectorParser.ts +/** +* @license +* Copyright 2026 Google LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +function isIdentToken(val) { + return val !== void 0 && val.type === "ident"; +} +function isDelimToken(val, char) { + return val !== void 0 && val.type === "delim" && (char === void 0 || val.value === char); +} +function isHashToken(val) { + return val !== void 0 && val.type === "hash"; +} +function isStringToken(val) { + return val !== void 0 && (val.type === "string" || val.type === "bad-string"); +} +function isNumberToken(val) { + return val !== void 0 && val.type === "number"; +} +function isDimensionToken(val) { + return val !== void 0 && val.type === "dimension"; +} +function isSimpleBlock(val, associatedType) { + return val !== void 0 && val.type === "simple-block" && (associatedType === void 0 || val.associatedToken.type === associatedType); +} +function isCSSFunction(val, name) { + return val !== void 0 && val.type === "function" && (name === void 0); +} +var ComponentValueCursor = class { + values; + _i = 0; + constructor(values) { + this.values = values; + } + get hasNext() { + return this._i < this.values.length; + } + get i() { + return this._i; + } + set i(pos) { + this._i = pos; + } + get length() { + return this.values.length; + } + get next() { + return this.values[this._i]; + } + peek(offset = 1) { + return this.values[this._i + offset]; + } + consume() { + return this.values[this._i++] || { type: "EOF" }; + } + skipWhitespace() { + while (this._i < this.values.length && this.values[this._i].type === "whitespace") this._i++; + } + skipToNextComma() { + const commaOffset = this.values.slice(this._i).findIndex((v) => v.type === "comma"); + this._i = commaOffset === -1 ? this.values.length : this._i + commaOffset; + } + slice(start, end) { + return this.values.slice(start, end ?? this._i); + } +}; +const LEGACY_PSEUDO_CLASS_ALIASES = { "-webkit-autofill": "autofill" }; +/** +* Selector Parser according to Selectors Level 4. +* +* selectors-4 #grammar +* selectors-4 #parse-selector +* selectors-4 #parse-relative-selector +*/ +var SelectorParser = class SelectorParser { + static PSEUDO_CLASSES = PSEUDO_CLASSES; + static PSEUDO_ELEMENTS = PSEUDO_ELEMENTS; + cursor; + allowRelative; + forgiving; + insideHas; + forbidPseudo; + declaredNamespaces; + strictSupports; + allowVendorPseudos; + constructor(values, options = {}) { + this.cursor = new ComponentValueCursor(values); + this.allowRelative = options.allowRelative ?? false; + this.forgiving = options.forgiving ?? false; + this.insideHas = options.insideHas ?? false; + this.forbidPseudo = options.forbidPseudo ?? false; + this.declaredNamespaces = options.declaredNamespaces; + this.strictSupports = options.strictSupports ?? false; + this.allowVendorPseudos = options.allowVendorPseudos ?? false; + } + hasAmpersand(values) { + return values.some((val) => { + if (isDelimToken(val, "&")) return true; + if (isSimpleBlock(val)) return this.hasAmpersand(val.value); + if (isCSSFunction(val)) return this.hasAmpersand(val.value); + return false; + }); + } + /** + * Parses component values into a SelectorList. + * + * selectors-4 #grouping () + * selectors-4 #grammar () + * selectors-4 #forgiving-selector (parsing forgiving selector list) + * selectors-4 #parse-selector + */ + parse() { + const selectors = []; + while (this.cursor.hasNext) { + this.cursor.skipWhitespace(); + if (!this.cursor.hasNext || this.cursor.next?.type === "EOF") break; + const start = this.cursor.i; + try { + const selector = this.consumeComplexSelector(); + this.cursor.skipWhitespace(); + const next = this.cursor.next; + if (!next || next.type === "comma") selectors.push(selector); + else throw new DOMException("Unexpected token in selector", "SyntaxError"); + } catch (e) { + if (this.forgiving) { + this.cursor.skipToNextComma(); + const rawSlice = this.cursor.slice(start, this.cursor.i); + let trimmedStart = 0; + while (trimmedStart < rawSlice.length && rawSlice[trimmedStart].type === "whitespace") trimmedStart++; + let trimmedEnd = rawSlice.length - 1; + while (trimmedEnd >= trimmedStart && rawSlice[trimmedEnd].type === "whitespace") trimmedEnd--; + const failedTokens = rawSlice.slice(trimmedStart, trimmedEnd + 1); + if (failedTokens.length > 0) selectors.push({ + type: "invalid-selector", + tokens: failedTokens + }); + } else throw e; + } + if (this.cursor.next?.type === "comma") this.cursor.consume(); + } + if (!this.forgiving && selectors.length === 0) throw new DOMException("Selector list cannot be empty", "SyntaxError"); + return { + type: "selector-list", + selectors + }; + } + /** + * Validates namespace prefixes against declared namespaces. + * + * selectors-4 #namespaces + * selectors-4 #parse-selector step 2 + */ + validateNamespace(namespace) { + if (this.declaredNamespaces !== void 0 && namespace !== void 0 && namespace !== "*" && namespace !== "") { + if (!this.declaredNamespaces.has(namespace)) throw new DOMException(`Undeclared namespace prefix: "${namespace}"`, "SyntaxError"); + } + } + /** + * Consumes a complex selector. + * + * selectors-4 #structure () + * selectors-4 #grammar + */ + consumeComplexSelector() { + const items = []; + const start = this.cursor.i; + let seenPseudoElement = false; + while (this.cursor.hasNext) { + this.cursor.skipWhitespace(); + if (!this.cursor.hasNext || this.cursor.next?.type === "comma") break; + const combinator = this.tryConsumeCombinator(); + if (combinator) { + if (items.length === 0 && !this.allowRelative) throw new DOMException("Relative selector not allowed in this context", "SyntaxError"); + if (seenPseudoElement) throw new DOMException("Pseudo-element must be at the end of the selector", "SyntaxError"); + if (items.length > 0 && items[items.length - 1].type === "combinator") throw new DOMException("Consecutive combinators are not allowed", "SyntaxError"); + items.push(combinator); + continue; + } + const compound = this.consumeCompoundSelector(); + if (compound.selectors.length > 0) { + if (seenPseudoElement) throw new DOMException("Pseudo-element must be at the end of the selector", "SyntaxError"); + const hasPseudo = compound.selectors.some((s) => s.type === "pseudo-element-selector"); + if (items.length > 0 && items[items.length - 1].type === "compound-selector") items.push({ + type: "combinator", + value: " " + }); + items.push(compound); + if (hasPseudo) seenPseudoElement = true; + } else break; + } + if (items.length > 0 && items[items.length - 1].type === "combinator") throw new DOMException("Trailing combinator is not allowed", "SyntaxError"); + if (items.length === 0) throw new DOMException("Complex selector cannot be empty", "SyntaxError"); + const end = this.cursor.i; + return { + type: "complex-selector", + items, + tokens: this.cursor.slice(start, end) + }; + } + /** + * selectors-4 #combinators + * selectors-5 #the-column-combinator (||) + */ + tryConsumeCombinator() { + const token = this.cursor.next; + if (!token) return null; + if (isDelimToken(token)) { + const val = token.value; + if (val === ">" || val === "+" || val === "~") { + this.cursor.consume(); + return { + type: "combinator", + value: val + }; + } + if (val === "|" && isDelimToken(this.cursor.peek(1), "|")) { + this.cursor.consume(); + this.cursor.consume(); + return { + type: "combinator", + value: "||" + }; + } + } + return null; + } + isUserActionPseudoClass(name) { + const lower = name.toLowerCase(); + return [ + "hover", + "active", + "focus", + "focus-visible", + "focus-within" + ].includes(lower); + } + validateSimpleSelectorAfterPseudo(selector) { + if (selector.type === "pseudo-class-selector") { + const lowerName = selector.name.toLowerCase(); + if ([ + "not", + "is", + "where", + "has" + ].includes(lowerName)) return; + else if (!this.isUserActionPseudoClass(selector.name)) throw new DOMException("Only user-action pseudo-classes are allowed after a pseudo-element", "SyntaxError"); + } else throw new DOMException("Only user-action pseudo-classes are allowed after a pseudo-element", "SyntaxError"); + } + consumeCompoundSelector() { + const selectors = []; + let lastPseudoElement = null; + while (this.cursor.hasNext) { + const token = this.cursor.next; + if (!token || token.type === "whitespace" || token.type === "comma") break; + if (isDelimToken(token)) { + const val = token.value; + if (val === ">" || val === "+" || val === "~") break; + if (val === "|") { + if (lastPseudoElement) break; + if (isDelimToken(this.cursor.peek(1), "|")) break; + if (selectors.length > 0) throw new DOMException("Type selector must be first in compound selector", "SyntaxError"); + selectors.push(this.consumeTypeOrUniversalSelector()); + continue; + } + if (val === "*") { + if (lastPseudoElement) break; + if (selectors.length > 0) throw new DOMException("Universal selector must be first in compound selector", "SyntaxError"); + selectors.push(this.consumeTypeOrUniversalSelector()); + continue; + } + if (val === ".") { + if (lastPseudoElement) break; + selectors.push(this.consumeClassSelector()); + continue; + } + if (val === "&") { + if (lastPseudoElement) break; + this.cursor.consume(); + selectors.push({ type: "nesting-selector" }); + continue; + } + } + if (isHashToken(token)) { + if (token.hashType !== "id") throw new DOMException("ID selector must be an identifier", "SyntaxError"); + if (lastPseudoElement) break; + selectors.push({ + type: "id-selector", + name: token.value + }); + this.cursor.consume(); + continue; + } + if (isIdentToken(token)) { + if (lastPseudoElement) break; + if (selectors.length > 0) throw new DOMException("Type selector must be first in compound selector", "SyntaxError"); + selectors.push(this.consumeTypeOrUniversalSelector()); + continue; + } + if (isSimpleBlock(token, "[")) { + if (lastPseudoElement) break; + selectors.push(this.consumeAttributeSelector()); + continue; + } + if (token.type === "colon") { + const selector = this.consumePseudoSelector(); + if (lastPseudoElement) { + const isSlottedOrPart = ["slotted", "part"].includes(lastPseudoElement.toLowerCase()); + if (selector.type === "pseudo-element-selector") { + if (!isSlottedOrPart) throw new DOMException("Pseudo-elements cannot be nested", "SyntaxError"); + } else if (selector.type === "pseudo-class-selector") { + if (!isSlottedOrPart) this.validateSimpleSelectorAfterPseudo(selector); + } else throw new DOMException("Unexpected selector after pseudo-element", "SyntaxError"); + } + if (selector.type === "pseudo-element-selector") lastPseudoElement = selector.name; + selectors.push(selector); + continue; + } + break; + } + return { + type: "compound-selector", + selectors + }; + } + consumeTypeOrUniversalSelector() { + let namespace = void 0; + const token = this.cursor.next; + if (!token) throw new DOMException("Unexpected EOF in type selector", "SyntaxError"); + const isNextPipe = isDelimToken(this.cursor.peek(1), "|"); + const isNextNextPipe = isDelimToken(this.cursor.peek(2), "|"); + const isColumnCombinator = isNextPipe && isNextNextPipe; + if (isIdentToken(token) && isNextPipe && !isColumnCombinator) { + namespace = token.value; + this.cursor.i += 2; + } else if (isDelimToken(token, "*") && isNextPipe && !isColumnCombinator) { + namespace = "*"; + this.cursor.i += 2; + } else if (isDelimToken(token, "|") && !isNextPipe) { + namespace = ""; + this.cursor.i += 1; + } + const next = this.cursor.consume(); + if (isDelimToken(next, "*")) { + this.validateNamespace(namespace); + return { + type: "universal-selector", + namespace + }; + } + if (!isIdentToken(next)) throw new DOMException("Expected identifier or * after namespace pipe", "SyntaxError"); + this.validateNamespace(namespace); + return { + type: "type-selector", + name: next.value, + namespace + }; + } + consumeClassSelector() { + this.cursor.consume(); + const ident = this.cursor.consume(); + if (!isIdentToken(ident)) return { + type: "class-selector", + name: "" + }; + return { + type: "class-selector", + name: ident.value + }; + } + consumeAttributeSelector() { + const block = this.cursor.consume(); + if (!isSimpleBlock(block, "[")) throw new DOMException("Expected attribute selector block", "SyntaxError"); + const subCursor = new ComponentValueCursor(block.value); + let name = ""; + let namespace = void 0; + let operator = ""; + let value = ""; + let flags = ""; + subCursor.skipWhitespace(); + if (subCursor.hasNext) { + const v1 = subCursor.next; + const v2 = subCursor.peek(1); + const v3 = subCursor.peek(2); + const isPipeFollowedByEquals = isDelimToken(v2, "|") && isDelimToken(v3, "="); + if (isIdentToken(v1) && isDelimToken(v2, "|") && !isPipeFollowedByEquals) { + namespace = v1.value; + subCursor.i += 2; + } else if (isDelimToken(v1, "*") && isDelimToken(v2, "|") && !isPipeFollowedByEquals) { + namespace = "*"; + subCursor.i += 2; + } else if (isDelimToken(v1, "|") && !isDelimToken(v2, "=")) { + namespace = ""; + subCursor.i += 1; + } + } + const valName = subCursor.next; + if (isIdentToken(valName)) { + name = valName.value; + subCursor.consume(); + } + subCursor.skipWhitespace(); + const valOp = subCursor.next; + if (isDelimToken(valOp)) { + operator = valOp.value; + subCursor.consume(); + const valEq = subCursor.next; + if (isDelimToken(valEq, "=")) { + operator += valEq.value; + subCursor.consume(); + } + } + subCursor.skipWhitespace(); + const valVal = subCursor.next; + if (isStringToken(valVal) || isIdentToken(valVal)) { + value = valVal.value; + subCursor.consume(); + } + subCursor.skipWhitespace(); + const valFlag = subCursor.next; + if (isIdentToken(valFlag)) { + const flagValue = valFlag.value; + const lowerFlag = flagValue.toLowerCase(); + if (lowerFlag !== "i" && lowerFlag !== "s") throw new DOMException(`Invalid attribute selector flag: ${flagValue}`, "SyntaxError"); + flags = flagValue; + subCursor.consume(); + } + subCursor.skipWhitespace(); + if (subCursor.hasNext) throw new DOMException("Unexpected content in attribute selector", "SyntaxError"); + if (!name) throw new DOMException("Expected attribute name in attribute selector", "SyntaxError"); + this.validateNamespace(namespace); + return { + type: "attribute-selector", + name, + namespace, + operator, + value, + flags + }; + } + consumePseudoSelector() { + this.cursor.consume(); + let isPseudoElement = false; + if (this.cursor.next?.type === "colon") { + this.cursor.consume(); + isPseudoElement = true; + } + const token = this.cursor.consume(); + if (!token) return { + type: "pseudo-class-selector", + name: "" + }; + if (isIdentToken(token)) { + const originalName = token.value; + const lowerName = originalName.toLowerCase(); + const name = LEGACY_PSEUDO_CLASS_ALIASES[lowerName] || originalName; + const effectiveLowerName = name.toLowerCase(); + const isVendorPrefixed = /^-[a-zA-Z0-9]+-/.test(effectiveLowerName); + if (isPseudoElement) { + if (this.forbidPseudo || this.insideHas) throw new DOMException("Pseudo-elements are not allowed in this context", "SyntaxError"); + const isAllowedVendor = this.allowVendorPseudos && isVendorPrefixed || !this.strictSupports && effectiveLowerName.startsWith("-webkit-"); + if (!PSEUDO_ELEMENTS.has(effectiveLowerName) && !isAllowedVendor) throw new DOMException(`Unknown pseudo-element ::${name}`, "SyntaxError"); + return { + type: "pseudo-element-selector", + name + }; + } + if ([ + "before", + "after", + "first-line", + "first-letter" + ].includes(effectiveLowerName)) { + if (this.forbidPseudo || this.insideHas) throw new DOMException("Pseudo-elements are not allowed in this context", "SyntaxError"); + return { + type: "pseudo-element-selector", + name + }; + } + const isAllowedVendor = this.allowVendorPseudos && isVendorPrefixed || !this.strictSupports && effectiveLowerName.startsWith("-webkit-"); + if (!PSEUDO_CLASSES.has(effectiveLowerName) && !isAllowedVendor) throw new DOMException(`Unknown pseudo-class :${name}`, "SyntaxError"); + return { + type: "pseudo-class-selector", + name + }; + } else if (isCSSFunction(token)) { + const func = token; + const name = func.name; + const lowerName = name.toLowerCase(); + const isVendorPrefixed = /^-[a-zA-Z0-9]+-/.test(lowerName); + if (isPseudoElement) { + if (this.forbidPseudo || this.insideHas) throw new DOMException("Pseudo-elements are not allowed in this context", "SyntaxError"); + if (!PSEUDO_ELEMENTS.has(lowerName) && !(this.allowVendorPseudos && isVendorPrefixed)) throw new DOMException(`Unknown pseudo-element ::${name}()`, "SyntaxError"); + if (lowerName === "slotted") { + const subParser = new SelectorParser(func.value, { + insideHas: this.insideHas, + forbidPseudo: true, + declaredNamespaces: this.declaredNamespaces, + strictSupports: this.strictSupports, + allowVendorPseudos: this.allowVendorPseudos + }); + subParser.cursor.skipWhitespace(); + const compound = subParser.consumeCompoundSelector(); + subParser.cursor.skipWhitespace(); + if (subParser.cursor.i !== func.value.length || compound.selectors.length === 0) throw new DOMException("Argument to ::slotted() must be a compound selector", "SyntaxError"); + return { + type: "pseudo-element-selector", + name, + argument: { + type: "selector-list", + selectors: [{ + type: "complex-selector", + items: [compound], + tokens: func.value + }] + } + }; + } + return { + type: "pseudo-element-selector", + name, + argument: func.value + }; + } + if (!PSEUDO_CLASSES.has(lowerName) && lowerName !== "matches" && !(this.allowVendorPseudos && isVendorPrefixed)) throw new DOMException(`Unknown pseudo-class :${name}()`, "SyntaxError"); + if ([ + "is", + "not", + "has", + "where", + "matches" + ].includes(lowerName)) { + const isHas = lowerName === "has"; + if (isHas && this.insideHas) throw new DOMException(":has() cannot be nested", "SyntaxError"); + const isForgiving = !this.strictSupports && [ + "is", + "where", + "matches" + ].includes(lowerName); + const isLogicalPseudo = [ + "is", + "where", + "not", + "matches" + ].includes(lowerName); + return { + type: "pseudo-class-selector", + name, + argument: new SelectorParser(func.value, { + allowRelative: isHas, + forgiving: isForgiving, + insideHas: isHas || this.insideHas, + forbidPseudo: isLogicalPseudo || isHas || this.forbidPseudo, + declaredNamespaces: this.declaredNamespaces, + strictSupports: this.strictSupports, + allowVendorPseudos: this.allowVendorPseudos + }).parse() + }; + } + if (["host", "host-context"].includes(lowerName)) { + const subParser = new SelectorParser(func.value, { + insideHas: this.insideHas, + forbidPseudo: true, + declaredNamespaces: this.declaredNamespaces, + strictSupports: this.strictSupports, + allowVendorPseudos: this.allowVendorPseudos + }); + subParser.cursor.skipWhitespace(); + const compound = subParser.consumeCompoundSelector(); + subParser.cursor.skipWhitespace(); + if (subParser.cursor.i !== func.value.length || compound.selectors.length === 0) throw new DOMException(`Argument to :${name}() must be a compound selector`, "SyntaxError"); + return { + type: "pseudo-class-selector", + name, + argument: { + type: "selector-list", + selectors: [{ + type: "complex-selector", + items: [compound], + tokens: func.value + }] + } + }; + } + if ([ + "nth-child", + "nth-last-child", + "nth-of-type", + "nth-last-of-type" + ].includes(lowerName)) { + let ofIdx = -1; + for (let k = 0; k < func.value.length; k++) { + const v = func.value[k]; + if (isIdentToken(v) && v.value.toLowerCase() === "of") { + ofIdx = k; + break; + } + } + if (ofIdx !== -1) { + if (["nth-of-type", "nth-last-of-type"].includes(lowerName)) throw new DOMException(`'of' is not allowed in :${name}()`, "SyntaxError"); + const nth = func.value.slice(0, ofIdx); + this.validateAnPlusB(nth); + return { + type: "pseudo-class-selector", + name, + argument: new SelectorParser(func.value.slice(ofIdx + 1), { + insideHas: this.insideHas, + forbidPseudo: true, + allowRelative: false, + declaredNamespaces: this.declaredNamespaces, + strictSupports: this.strictSupports, + allowVendorPseudos: this.allowVendorPseudos + }).parse(), + nth + }; + } else { + this.validateAnPlusB(func.value); + return { + type: "pseudo-class-selector", + name, + argument: func.value, + nth: func.value + }; + } + } + if (lowerName === "dir") this.validateDir(func.value); + if (lowerName === "heading") this.validateHeading(func.value); + if (lowerName === "lang") this.validateLang(func.value); + return { + type: "pseudo-class-selector", + name, + argument: func.value + }; + } + throw new DOMException("Expected identifier or function after colon in pseudo-selector", "SyntaxError"); + } + validateAnPlusB(values) { + const res = parseAnPlusB(values); + if (!res) { + const text = getOriginalText(values).trim(); + throw new DOMException(`Invalid An+B expression: ${text}`, "SyntaxError"); + } + return res; + } + validateHeading(values) { + const nonComment = values.filter((v) => v.type !== "comment"); + let start = 0; + while (start < nonComment.length && nonComment[start].type === "whitespace") start++; + let end = nonComment.length - 1; + while (end >= start && nonComment[end].type === "whitespace") end--; + if (start > end) throw new DOMException("Argument to :heading() cannot be empty", "SyntaxError"); + const tokens = nonComment.slice(start, end + 1); + let expectInteger = true; + for (let i = 0; i < tokens.length; i++) { + const t = tokens[i]; + if (t.type === "whitespace") continue; + if (expectInteger) { + if (t.type === "number" && t.numberType === "integer") expectInteger = false; + else throw new DOMException("Argument to :heading() must be comma-separated integers", "SyntaxError"); + } else if (t.type === "comma" || t.type === "delim" && t.value === ",") expectInteger = true; + else throw new DOMException("Expected comma in :heading() arguments", "SyntaxError"); + } + if (expectInteger) throw new DOMException("Trailing comma in :heading() arguments", "SyntaxError"); + } + validateDir(values) { + const nonWs = values.filter((v) => v.type !== "whitespace" && v.type !== "comment"); + const firstToken = nonWs[0]; + if (nonWs.length !== 1 || !isIdentToken(firstToken)) throw new DOMException("Argument to :dir() must be a single identifier", "SyntaxError"); + const val = firstToken.value.toLowerCase(); + if (val !== "ltr" && val !== "rtl" && val !== "auto") throw new DOMException("Argument to :dir() must be ltr, rtl, or auto", "SyntaxError"); + } + validateLang(values) { + const nonWs = values.filter((v) => v.type !== "whitespace" && v.type !== "comment"); + if (nonWs.length === 0) throw new DOMException("Argument to :lang() cannot be empty", "SyntaxError"); + let expectItem = true; + for (const v of nonWs) if (expectItem) { + if (!isIdentToken(v) && !isStringToken(v)) throw new DOMException("Argument to :lang() must be identifiers or strings", "SyntaxError"); + expectItem = false; + } else { + if (v.type !== "comma") throw new DOMException("Expected comma in :lang() argument", "SyntaxError"); + expectItem = true; + } + if (expectItem) throw new DOMException("Trailing comma in :lang() argument", "SyntaxError"); + } +}; +/** +* Parses An+B microsyntax into { a, b }. +* +* selectors-4 #child-index +* css-syntax-3 #anb-microsyntax +* css-syntax-3 #anb-syntax +* css-syntax-3 #the-anb-type ( production) +*/ +function parseAnPlusB(values) { + const nonComment = values.filter((v) => v.type !== "comment"); + let start = 0; + while (start < nonComment.length && nonComment[start].type === "whitespace") start++; + let end = nonComment.length - 1; + while (end >= start && nonComment[end].type === "whitespace") end--; + if (start > end) return null; + const tokens = nonComment.slice(start, end + 1); + const isIntegerNumber = (t) => { + return isNumberToken(t) && t.numberType === "integer"; + }; + if (tokens.length === 1) { + const t = tokens[0]; + if (isIdentToken(t)) { + const lower = t.value.toLowerCase(); + if (lower === "odd") return { + a: 2, + b: 1 + }; + if (lower === "even") return { + a: 2, + b: 0 + }; + if (lower === "n") return { + a: 1, + b: 0 + }; + if (lower === "-n") return { + a: -1, + b: 0 + }; + const matchNdashDigit = lower.match(/^n-(\d+)$/); + if (matchNdashDigit) return { + a: 1, + b: -parseInt(matchNdashDigit[1], 10) + }; + const matchDashNdashDigit = lower.match(/^-n-(\d+)$/); + if (matchDashNdashDigit) return { + a: -1, + b: -parseInt(matchDashNdashDigit[1], 10) + }; + return null; + } + if (isIntegerNumber(t)) return { + a: 0, + b: Number(t.value) + }; + if (isDimensionToken(t) && t.numberType === "integer") { + const unit = (t.unit || "").toLowerCase(); + if (unit === "n") return { + a: Number(t.value), + b: 0 + }; + const matchDim = unit.match(/^n-(\d+)$/); + if (matchDim) return { + a: Number(t.value), + b: -parseInt(matchDim[1], 10) + }; + return null; + } + return null; + } + let idx = 0; + let plusPrefix = false; + if (isDelimToken(tokens[0], "+")) { + plusPrefix = true; + idx = 1; + if (tokens[1]?.type === "whitespace") return null; + } + const nextNonWs = (from) => { + let p = from; + while (p < tokens.length && tokens[p].type === "whitespace") p++; + return p; + }; + const t1 = tokens[idx]; + if (!t1) return null; + let a = null; + let hasDashAfterN = false; + if (isIdentToken(t1)) { + const lower = t1.value.toLowerCase(); + if (lower === "n") a = 1; + else if (lower === "-n" && !plusPrefix) a = -1; + else if (lower === "n-") { + a = 1; + hasDashAfterN = true; + } else if (lower === "-n-" && !plusPrefix) { + a = -1; + hasDashAfterN = true; + } else if (lower.startsWith("n-")) { + const match = lower.match(/^n-(\d+)$/); + if (match && idx === tokens.length - 1) return { + a: 1, + b: -parseInt(match[1], 10) + }; + } + } else if (isDimensionToken(t1) && t1.numberType === "integer" && !plusPrefix) { + const unit = (t1.unit || "").toLowerCase(); + if (unit === "n") a = Number(t1.value); + else if (unit === "n-") { + a = Number(t1.value); + hasDashAfterN = true; + } + } + if (a === null) return null; + const afterT1 = nextNonWs(idx + 1); + if (afterT1 >= tokens.length) { + if (!hasDashAfterN) return { + a, + b: 0 + }; + return null; + } + const t2 = tokens[afterT1]; + if (hasDashAfterN) { + if (isIntegerNumber(t2) && !t2.sign) { + if (nextNonWs(afterT1 + 1) < tokens.length) return null; + return { + a, + b: -Number(t2.value) + }; + } + return null; + } + if (isIntegerNumber(t2) && t2.sign) { + if (nextNonWs(afterT1 + 1) < tokens.length) return null; + return { + a, + b: Number(t2.value) + }; + } + if (isDelimToken(t2, "+") || isDelimToken(t2, "-")) { + const sign = t2.value === "+" ? 1 : -1; + const afterT2 = nextNonWs(afterT1 + 1); + if (afterT2 >= tokens.length) return null; + const t3 = tokens[afterT2]; + if (isIntegerNumber(t3) && !t3.sign) { + if (nextNonWs(afterT2 + 1) < tokens.length) return null; + return { + a, + b: sign * Number(t3.value) + }; + } + return null; + } + return null; } - -const tagName$h = 'template'; - +// src/serializer.ts /** - * @implements globalThis.HTMLTemplateElement - */ -class HTMLTemplateElement extends HTMLElement { - constructor(ownerDocument) { - super(ownerDocument, tagName$h); - const content = this.ownerDocument.createDocumentFragment(); - (this[CONTENT] = content)[PRIVATE] = this; - } - - get content() { - if (this.hasChildNodes() && !this[CONTENT].hasChildNodes()) { - for (const node of this.childNodes) - this[CONTENT].appendChild(node.cloneNode(true)); - } - return this[CONTENT]; - } -} - -registerHTMLClass(tagName$h, HTMLTemplateElement); - +* @license +* Copyright 2026 Google LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ /** - * @implements globalThis.HTMLHtmlElement - */ -class HTMLHtmlElement extends HTMLElement { - constructor(ownerDocument, localName = 'html') { - super(ownerDocument, localName); - } +* Determines whether two consecutive tokens require an empty comment separator +* between them to prevent coalescing per css-syntax-3 #serialization. +* // css-syntax-3 #serialization-tables +*/ +function requiresTokenSeparator(t1, t2) { + const isIdent1 = t1.type === "ident"; + const isAtKeyword1 = t1.type === "at-keyword"; + const isHash1 = t1.type === "hash"; + const isDimension1 = t1.type === "dimension"; + const isDelimHash1 = t1.type === "delim" && t1.value === "#"; + const isDelimDash1 = t1.type === "delim" && t1.value === "-"; + const isNumber1 = t1.type === "number"; + const isDelimAt1 = t1.type === "delim" && t1.value === "@"; + const isDelimDot1 = t1.type === "delim" && t1.value === "."; + const isDelimPlus1 = t1.type === "delim" && t1.value === "+"; + const isDelimSlash1 = t1.type === "delim" && t1.value === "/"; + if (!isIdent1 && !isAtKeyword1 && !isHash1 && !isDimension1 && !isDelimHash1 && !isDelimDash1 && !isNumber1 && !isDelimAt1 && !isDelimDot1 && !isDelimPlus1 && !isDelimSlash1) return false; + const isIdent2 = t2.type === "ident"; + const isFunction2 = t2.type === "function"; + const isUrl2 = t2.type === "url"; + const isBadUrl2 = t2.type === "bad-url"; + const isDelimDash2 = t2.type === "delim" && t2.value === "-"; + const isNumber2 = t2.type === "number"; + const isPercentage2 = t2.type === "percentage"; + const isDimension2 = t2.type === "dimension"; + const isCDC2 = t2.type === "CDC"; + const isOpenParen2 = t2.type === "(" || t2.type === "delim" && t2.value === "("; + const isDelimStar2 = t2.type === "delim" && t2.value === "*"; + const isDelimPercent2 = t2.type === "delim" && t2.value === "%"; + const inGroupA = isIdent2 || isFunction2 || isUrl2 || isBadUrl2 || isDelimDash2 || isNumber2 || isPercentage2 || isDimension2 || isCDC2; + if (isIdent1) return inGroupA || isOpenParen2; + if (isAtKeyword1 || isHash1 || isDimension1 || isDelimHash1 || isDelimDash1) return inGroupA; + if (isNumber1) return isIdent2 || isFunction2 || isUrl2 || isBadUrl2 || isNumber2 || isPercentage2 || isDimension2 || isCDC2 || isDelimPercent2; + if (isDelimAt1) return isIdent2 || isFunction2 || isUrl2 || isBadUrl2 || isDelimDash2 || isCDC2; + if (isDelimDot1 || isDelimPlus1) return isNumber2 || isPercentage2 || isDimension2; + if (isDelimSlash1) return isDelimStar2; + return false; } - -const {toString} = HTMLElement.prototype; - -class TextElement extends HTMLElement { - - get innerHTML() { return this.textContent; } - set innerHTML(html) { this.textContent = html; } - - toString() { - const outerHTML = toString.call(this.cloneNode()); - return outerHTML.replace('><', () => `>${this.textContent}<`); - } +function getFirstToken(node) { + if (typeof node !== "object" || node === null) return null; + if (node.type === "simple-block") return node.associatedToken; + if (node.type === "function" && "name" in node) return { + type: "function", + value: node.name + }; + const t = node; + if (t.type === "EOF") return null; + return t; } - -const tagName$g = 'script'; - -/** - * @implements globalThis.HTMLScriptElement - */ -class HTMLScriptElement extends TextElement { - constructor(ownerDocument, localName = tagName$g) { - super(ownerDocument, localName); - } - - get type() { - return stringAttribute.get(this, 'type'); - } - set type(value) { - stringAttribute.set(this, 'type', value); - } - - get src() { - return stringAttribute.get(this, 'src'); - } - set src(value) { - stringAttribute.set(this, 'src', value); - } - - get defer() { - return booleanAttribute.get(this, 'defer'); - } - - set defer(value) { - booleanAttribute.set(this, 'defer', value); - } - - get crossOrigin() { - return stringAttribute.get(this, 'crossorigin'); - } - set crossOrigin(value) { - stringAttribute.set(this, 'crossorigin', value); - } - - get nomodule() { - return booleanAttribute.get(this, 'nomodule'); - } - set nomodule(value) { - booleanAttribute.set(this, 'nomodule', value); - } - - get referrerPolicy() { - return stringAttribute.get(this, 'referrerpolicy'); - } - set referrerPolicy(value) { - stringAttribute.set(this, 'referrerpolicy', value); - } - - get nonce() { - return stringAttribute.get(this, 'nonce'); - } - set nonce(value) { - stringAttribute.set(this, 'nonce', value); - } - - get async() { - return booleanAttribute.get(this, 'async'); - } - set async(value) { - booleanAttribute.set(this, 'async', value); - } - - get text() { return this.textContent; } - set text(content) { this.textContent = content; } +function getLastToken(node) { + if (typeof node !== "object" || node === null) return null; + if (node.type === "simple-block") { + const start = node.associatedToken?.value; + const end = getMirrorToken(start); + return { + type: end || ")", + value: end + }; + } + if (node.type === "function" && "name" in node) return { + type: ")", + value: ")" + }; + const t = node; + if (t.type === "EOF") return null; + return t; } - -registerHTMLClass(tagName$g, HTMLScriptElement); - -/** - * @implements globalThis.HTMLFrameElement - */ -class HTMLFrameElement extends HTMLElement { - constructor(ownerDocument, localName = 'frame') { - super(ownerDocument, localName); - } +function serialize(nodes, preserveCase = false, propertyName) { + if (propertyName === "font-family") return serializeFontFamily(nodes); + let result = ""; + let prevLastToken = null; + for (const node of nodes) { + if (node.type === "EOF") continue; + const firstToken = getFirstToken(node); + if (prevLastToken && firstToken && requiresTokenSeparator(prevLastToken, firstToken)) result += "/**/"; + result += serializeNode(node, preserveCase); + const last = getLastToken(node); + if (last) prevLastToken = last; + } + return result; } - -const tagName$f = 'iframe'; - -/** - * @implements globalThis.HTMLIFrameElement - */ -class HTMLIFrameElement extends HTMLElement { - constructor(ownerDocument, localName = tagName$f) { - super(ownerDocument, localName); - } - - /* c8 ignore start */ - get src() { return stringAttribute.get(this, 'src'); } - set src(value) { stringAttribute.set(this, 'src', value); } - - get srcdoc() { return stringAttribute.get(this, "srcdoc"); } - set srcdoc(value) { stringAttribute.set(this, "srcdoc", value); } - - get name() { return stringAttribute.get(this, "name"); } - set name(value) { stringAttribute.set(this, "name", value); } - - get allow() { return stringAttribute.get(this, "allow"); } - set allow(value) { stringAttribute.set(this, "allow", value); } - - get allowFullscreen() { return booleanAttribute.get(this, "allowfullscreen"); } - set allowFullscreen(value) { booleanAttribute.set(this, "allowfullscreen", value); } - - get referrerPolicy() { return stringAttribute.get(this, "referrerpolicy"); } - set referrerPolicy(value) { stringAttribute.set(this, "referrerpolicy", value); } - - get loading() { return stringAttribute.get(this, "loading"); } - set loading(value) { stringAttribute.set(this, "loading", value); } - /* c8 ignore stop */ +function trimWhitespaceTokens(tokens) { + let start = 0; + while (start < tokens.length && tokens[start].type === "whitespace") start++; + let end = tokens.length - 1; + while (end >= start && tokens[end].type === "whitespace") end--; + return start <= end ? tokens.slice(start, end + 1) : []; } - -registerHTMLClass(tagName$f, HTMLIFrameElement); - -/** - * @implements globalThis.HTMLObjectElement - */ -class HTMLObjectElement extends HTMLElement { - constructor(ownerDocument, localName = 'object') { - super(ownerDocument, localName); - } +function serializeNode(node, preserveCase) { + if (typeof node !== "object" || node === null || !("type" in node)) return ""; + if (node.type === "simple-block") { + const start = node.associatedToken.value; + return start + serialize(node.value, preserveCase) + getMirrorToken(start); + } + if (node.type === "function" && "name" in node) { + let args = node.value; + const funcName = preserveCase ? node.name : node.name.toLowerCase(); + if (funcName === "counter") { + let i = args.length - 1; + while (i >= 0 && args[i].type === "whitespace") i--; + if (i >= 0 && args[i].type === "ident" && args[i].value === "decimal") { + let j = i - 1; + while (j >= 0 && args[j].type === "whitespace") j--; + if (j >= 0 && args[j].type === "comma") args = args.slice(0, j); + } + } else if (funcName === "url") args = trimWhitespaceTokens(args); + else if (funcName === "attr") { + let i = 0; + while (i < args.length && args[i].type === "whitespace") i++; + if (i < args.length && args[i].type === "delim" && args[i].value === "|") { + args = args.slice(i + 1); + let k = args.length - 1; + while (k >= 0 && args[k].type === "whitespace") k--; + if (k >= 0 && args[k].type === "string" && args[k].value === "") { + let l = k - 1; + while (l >= 0 && args[l].type === "whitespace") l--; + if (l >= 0 && args[l].type === "comma") args = args.slice(0, l); + } + } + args = trimWhitespaceTokens(args); + } + return `${funcName}(${serialize(args, preserveCase)})`; + } + return serializeToken(node, preserveCase); +} +function serializeToken(token, preserveCase = false) { + switch (token.type) { + case "ident": return serializeIdentifier(token.value); + case "at-keyword": return "@" + serializeIdentifier(token.value); + case "hash": return "#" + token.value; + case "string": return preserveCase && token.originalText && !token.originalText.endsWith("\\") ? token.originalText : serializeString(token.value); + case "url": return preserveCase ? serializeUrlToken(token.value, token.originalText) : serializeUrl(token.value); + case "delim": return token.value === "\\" ? "\\\n" : token.value; + case "number": return formatNumber(token.value); + case "percentage": return formatNumber(token.value) + "%"; + case "dimension": return formatNumber(token.value) + (token.unit ? serializeIdentifier(token.unit) : ""); + case "whitespace": return preserveCase && token.originalText ? token.originalText : token.value; + case "comment": return token.value || "/**/"; + case "CDO": return ""; + case "colon": return ":"; + case "semicolon": return ";"; + case "comma": return ","; + case "[": + case "]": + case "{": + case "}": + case "(": + case ")": return token.value; + case "function": return serializeIdentifier(preserveCase ? token.value : token.value.toLowerCase()) + "("; + case "unicode-range": return token.value; + case "EOF": return ""; + default: return token.value || ""; + } +} +function getMirrorToken(start) { + if (start === "{") return "}"; + if (start === "[") return "]"; + if (start === "(") return ")"; + return ""; +} +function getOriginalText(values) { + let text = ""; + for (const val of values) if (val.type === "simple-block") text += (val.associatedToken.originalText || "") + getOriginalText(val.value) + getMirrorToken(val.associatedToken.value); + else if (val.type === "function") { + const func = val; + text += `${func.name}(${getOriginalText(func.value)})`; + } else text += val.originalText || val.value; + return text; } - /** - * @implements globalThis.HTMLHeadElement - */ -class HTMLHeadElement extends HTMLElement { - constructor(ownerDocument, localName = 'head') { - super(ownerDocument, localName); - } +* // cssom-1 #serialize-an-identifier +*/ +function serializeIdentifier(id) { + let result = ""; + for (let i = 0; i < id.length; i++) { + const charCode = id.charCodeAt(i); + const char = id[i]; + if (charCode === 0) { + result += "�"; + continue; + } + if (charCode >= 1 && charCode <= 31 || charCode === 127) { + result += escapeAsCodePoint(charCode); + continue; + } + if (i === 0 && charCode >= 48 && charCode <= 57) { + result += escapeAsCodePoint(charCode); + continue; + } + if (i === 1 && charCode >= 48 && charCode <= 57 && id.charCodeAt(0) === 45) { + result += escapeAsCodePoint(charCode); + continue; + } + if (i === 0 && charCode === 45 && id.length === 1) { + result += "\\-"; + continue; + } + if (charCode >= 128 || charCode === 45 || charCode === 95 || charCode >= 48 && charCode <= 57 || charCode >= 65 && charCode <= 90 || charCode >= 97 && charCode <= 122) { + result += char; + continue; + } + result += "\\" + char; + } + return result; } - /** - * @implements globalThis.HTMLBodyElement - */ -class HTMLBodyElement extends HTMLElement { - constructor(ownerDocument, localName = 'body') { - super(ownerDocument, localName); - } +* // cssom-1 #serialize-a-string +*/ +function serializeString(s) { + let result = "\""; + for (let i = 0; i < s.length; i++) { + const charCode = s.charCodeAt(i); + const char = s[i]; + if (charCode === 0) { + result += "�"; + continue; + } + if (charCode >= 1 && charCode <= 31 || charCode === 127) { + result += escapeAsCodePoint(charCode); + continue; + } + if (charCode === 34 || charCode === 92) { + result += "\\" + char; + continue; + } + result += char; + } + result += "\""; + return result; } - -var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; - -var lib = {}; - -var CSSOM_1; -var hasRequiredCSSOM; - -function requireCSSOM () { - if (hasRequiredCSSOM) return CSSOM_1; - hasRequiredCSSOM = 1; - var CSSOM = { - /** - * Creates and configures a new CSSOM instance with the specified options. - * - * @param {Object} opts - Configuration options for the CSSOM instance - * @param {Object} [opts.globalObject] - Optional global object to be assigned to CSSOM objects prototype - * @returns {Object} A new CSSOM instance with the applied configuration - * @description - * This method creates a new instance of CSSOM and optionally - * configures CSSStyleSheet with a global object reference. When a globalObject is provided - * and CSSStyleSheet exists on the instance, it creates a new CSSStyleSheet constructor - * using a factory function and assigns the globalObject to its prototype's __globalObject property. - */ - setup: function (opts) { - var instance = Object.create(this); - if (opts.globalObject) { - if (instance.CSSStyleSheet) { - var factoryCSSStyleSheet = createFunctionFactory(instance.CSSStyleSheet); - var CSSStyleSheet = factoryCSSStyleSheet(); - CSSStyleSheet.prototype.__globalObject = opts.globalObject; - - instance.CSSStyleSheet = CSSStyleSheet; - } - } - return instance; - } - }; - - function createFunctionFactory(fn) { - return function() { - // Create a new function that delegates to the original - var newFn = function() { - return fn.apply(this, arguments); - }; - - // Copy prototype chain - Object.setPrototypeOf(newFn, Object.getPrototypeOf(fn)); - - // Copy own properties - for (var key in fn) { - if (Object.prototype.hasOwnProperty.call(fn, key)) { - newFn[key] = fn[key]; - } - } - - // Clone the .prototype object for constructor-like behavior - if (fn.prototype) { - newFn.prototype = Object.create(fn.prototype); - } - - return newFn; - }; +function serializeUrl(val) { + return `url(${serializeString(val)})`; +} +function serializeUrlToken(val, originalText) { + if (originalText && !val.includes("�") && originalText.endsWith(")")) return originalText; + let result = ""; + for (let i = 0; i < val.length; i++) { + const charCode = val.charCodeAt(i); + const char = val[i]; + if (charCode === 34 || charCode === 39 || charCode === 40 || charCode === 41 || charCode === 92 || charCode <= 32 || charCode === 127) result += "\\" + char; + else result += char; + } + return `url(${result})`; +} +function escapeAsCodePoint(charCode) { + return "\\" + charCode.toString(16) + " "; +} +const logicalShorthandsEntries = Object.entries({ + "margin-inline": { + start: "margin-inline-start", + end: "margin-inline-end", + allowDifferent: true + }, + "padding-inline": { + start: "padding-inline-start", + end: "padding-inline-end", + allowDifferent: true + }, + "margin-block": { + start: "margin-block-start", + end: "margin-block-end", + allowDifferent: true + }, + "padding-block": { + start: "padding-block-start", + end: "padding-block-end", + allowDifferent: true + }, + "inset-inline": { + start: "inset-inline-start", + end: "inset-inline-end", + allowDifferent: true + }, + "inset-block": { + start: "inset-block-start", + end: "inset-block-end", + allowDifferent: true + }, + "border-inline-width": { + start: "border-inline-start-width", + end: "border-inline-end-width", + allowDifferent: true + }, + "border-block-width": { + start: "border-block-start-width", + end: "border-block-end-width", + allowDifferent: true + }, + "border-inline-style": { + start: "border-inline-start-style", + end: "border-inline-end-style", + allowDifferent: true + }, + "border-block-style": { + start: "border-block-start-style", + end: "border-block-end-style", + allowDifferent: true + }, + "border-inline-color": { + start: "border-inline-start-color", + end: "border-inline-end-color", + allowDifferent: true + }, + "border-block-color": { + start: "border-block-start-color", + end: "border-block-end-color", + allowDifferent: true + }, + "border-inline": { + start: "border-inline-start", + end: "border-inline-end", + allowDifferent: false + }, + "border-block": { + start: "border-block-start", + end: "border-block-end", + allowDifferent: false + }, + "overflow": { + start: "overflow-x", + end: "overflow-y", + allowDifferent: true + }, + "overscroll-behavior": { + start: "overscroll-behavior-x", + end: "overscroll-behavior-y", + allowDifferent: true + } +}); +const propertyToGroup = { + "margin-top": "margin", + "margin-right": "margin", + "margin-bottom": "margin", + "margin-left": "margin", + "margin-inline-start": "margin", + "margin-inline-end": "margin", + "margin-block-start": "margin", + "margin-block-end": "margin", + "padding-top": "padding", + "padding-right": "padding", + "padding-bottom": "padding", + "padding-left": "padding", + "padding-inline-start": "padding", + "padding-inline-end": "padding", + "padding-block-start": "padding", + "padding-block-end": "padding", + "top": "inset", + "right": "inset", + "bottom": "inset", + "left": "inset", + "inset-inline-start": "inset", + "inset-inline-end": "inset", + "inset-block-start": "inset", + "inset-block-end": "inset", + "border-top-width": "border-width", + "border-right-width": "border-width", + "border-bottom-width": "border-width", + "border-left-width": "border-width", + "border-inline-start-width": "border-width", + "border-inline-end-width": "border-width", + "border-block-start-width": "border-width", + "border-block-end-width": "border-width", + "border-top-style": "border-style", + "border-right-style": "border-style", + "border-bottom-style": "border-style", + "border-left-style": "border-style", + "border-inline-start-style": "border-style", + "border-inline-end-style": "border-style", + "border-block-start-style": "border-style", + "border-block-end-style": "border-style", + "border-top-color": "border-color", + "border-right-color": "border-color", + "border-bottom-color": "border-color", + "border-left-color": "border-color", + "border-inline-start-color": "border-color", + "border-inline-end-color": "border-color", + "border-block-start-color": "border-color", + "border-block-end-color": "border-color", + "width": "size", + "height": "size", + "inline-size": "size", + "block-size": "size", + "min-width": "min-size", + "min-height": "min-size", + "min-inline-size": "min-size", + "min-block-size": "min-size", + "max-width": "max-size", + "max-height": "max-size", + "max-inline-size": "max-size", + "max-block-size": "max-size", + "border-top-left-radius": "border-radius", + "border-top-right-radius": "border-radius", + "border-bottom-right-radius": "border-radius", + "border-bottom-left-radius": "border-radius", + "border-start-start-radius": "border-radius", + "border-start-end-radius": "border-radius", + "border-end-start-radius": "border-radius", + "border-end-end-radius": "border-radius", + "border-top": "border", + "border-right": "border", + "border-bottom": "border", + "border-left": "border", + "border-block-start": "border", + "border-block-end": "border", + "border-inline-start": "border", + "border-inline-end": "border", + "overflow-x": "overflow", + "overflow-y": "overflow", + "overflow-inline": "overflow", + "overflow-block": "overflow", + "overscroll-behavior-x": "overscroll-behavior", + "overscroll-behavior-y": "overscroll-behavior", + "overscroll-behavior-inline": "overscroll-behavior", + "overscroll-behavior-block": "overscroll-behavior", + "outline-color": "outline", + "outline-style": "outline", + "outline-width": "outline", + "list-style-type": "list-style", + "list-style-position": "list-style", + "list-style-image": "list-style", + "flex-grow": "flex", + "flex-shrink": "flex", + "flex-basis": "flex", + "font-style": "font", + "font-variant-caps": "font", + "font-weight": "font", + "font-stretch": "font", + "font-size": "font", + "line-height": "font", + "font-family": "font", + "font-variant-ligatures": "font-variant", + "font-variant-alternates": "font-variant", + "font-variant-numeric": "font-variant", + "font-variant-east-asian": "font-variant", + "font-variant-position": "font-variant", + "font-variant-emoji": "font-variant" +}; +const genericShorthands = { + "border-top": [ + "border-top-width", + "border-top-style", + "border-top-color" + ], + "border-right": [ + "border-right-width", + "border-right-style", + "border-right-color" + ], + "border-bottom": [ + "border-bottom-width", + "border-bottom-style", + "border-bottom-color" + ], + "border-left": [ + "border-left-width", + "border-left-style", + "border-left-color" + ], + "border-block-start": [ + "border-block-start-width", + "border-block-start-style", + "border-block-start-color" + ], + "border-block-end": [ + "border-block-end-width", + "border-block-end-style", + "border-block-end-color" + ], + "border-inline-start": [ + "border-inline-start-width", + "border-inline-start-style", + "border-inline-start-color" + ], + "border-inline-end": [ + "border-inline-end-width", + "border-inline-end-style", + "border-inline-end-color" + ], + "outline": [ + "outline-color", + "outline-style", + "outline-width" + ], + "list-style": [ + "list-style-type", + "list-style-position", + "list-style-image" + ], + "flex": [ + "flex-grow", + "flex-shrink", + "flex-basis" + ], + "border-image": [ + "border-image-source", + "border-image-slice", + "border-image-width", + "border-image-outset", + "border-image-repeat" + ], + "line-clamp": [ + "max-lines", + "block-ellipsis", + "continue" + ] +}; +const genericShorthandsEntries = Object.entries(genericShorthands); +function checkIntervening(decls, allDecls, declIndices) { + const indices = decls.map((d) => declIndices.get(d)); + const startIdx = Math.min(...indices); + const endIdx = Math.max(...indices); + const names = new Set(decls.map((d) => d.name)); + const groups = new Set(decls.map((d) => propertyToGroup[d.name]).filter(Boolean)); + const isSideShorthand = groups.size > 1; + for (let i = startIdx + 1; i < endIdx; i++) { + const intervening = allDecls[i]; + if (names.has(intervening.name)) continue; + const interveningGroup = propertyToGroup[intervening.name]; + if (!interveningGroup) continue; + if (isSideShorthand) { + const sidePrefix = decls[0].name.replace(/-(width|style|color)$/, ""); + if (intervening.name.startsWith(sidePrefix + "-") || ["all", "border"].includes(intervening.name)) return true; + } else { + if (groups.has(interveningGroup)) return true; + if (["all", "border"].includes(intervening.name)) return true; + } + } + return false; +} +const BOX_SHORTHAND_NAMES = /* @__PURE__ */ new Set("margin,padding,border-width,border-style,border-color,scroll-margin,scroll-padding,inset,overflow-clip-margin,border-radius".split(",")); +const BOX_SHORTHAND_ENTRIES = Object.entries(SHORTHANDS).filter(([shorthand, def]) => BOX_SHORTHAND_NAMES.has(shorthand) && def.logicalLonghands?.length === 4); +function tryCombineBoxShorthand(d, declMap, processed, declarations, declIndices) { + for (const [shorthand, def] of BOX_SHORTHAND_ENTRIES) { + const physical = def.longhands; + const logical = def.logicalLonghands; + const isPhysical = physical.includes(d.name); + const isLogical = logical.includes(d.name); + if (!isPhysical && !isLogical) continue; + const allDecls = (isPhysical ? physical : logical).map((name) => declMap.get(name)); + if (!allDecls.every((other) => other && !processed.has(other) && other.important === d.important)) continue; + if (checkIntervening(allDecls, declarations, declIndices)) continue; + const valuesForContract = {}; + for (const other of allDecls) valuesForContract[other.name] = other.value; + const value = def.contract(valuesForContract); + if (value !== null) { + for (const other of allDecls) processed.add(other); + return `${shorthand}: ${value}${d.important ? " !important" : ""}`; + } + } + return null; +} +function tryCombineLogicalShorthand(d, declMap, processed, declarations, declIndices) { + for (const [shorthand, longhands] of logicalShorthandsEntries) { + if (d.name !== longhands.start && d.name !== longhands.end) continue; + const otherName = d.name === longhands.start ? longhands.end : longhands.start; + const otherDecl = declMap.get(otherName); + if (!otherDecl || processed.has(otherDecl) || d.important !== otherDecl.important) continue; + if (checkIntervening([d, otherDecl], declarations, declIndices)) continue; + const startDecl = d.name === longhands.start ? d : otherDecl; + const endDecl = d.name === longhands.end ? d : otherDecl; + const valS = serialize(startDecl.value).trim(); + const valE = serialize(endDecl.value).trim(); + if (valS === valE) { + processed.add(startDecl); + processed.add(endDecl); + return `${shorthand}: ${valS}${d.important ? " !important" : ""}`; + } + if (longhands.allowDifferent && !valS.includes("var(") && !valE.includes("var(")) { + processed.add(startDecl); + processed.add(endDecl); + return `${shorthand}: ${valS} ${valE}${d.important ? " !important" : ""}`; + } + } + return null; +} +function tryCombineGenericShorthand(d, declMap, processed, declarations, declIndices) { + for (const [shorthand, longhands] of genericShorthandsEntries) { + if (!longhands.includes(d.name)) continue; + const allDecls = longhands.map((name) => declMap.get(name)); + if (!allDecls.every((other) => other && !processed.has(other) && other.important === d.important)) continue; + if (checkIntervening(allDecls, declarations, declIndices)) continue; + const record = {}; + for (const other of allDecls) record[other.name] = other.value; + const contracted = SHORTHANDS[shorthand]?.contract(record); + if (contracted !== null && contracted !== void 0) { + for (const other of allDecls) processed.add(other); + return { + name: shorthand, + value: contracted, + important: d.important + }; + } + } + return null; +} +const BORDER_PHYSICAL_SIDE_LONGHANDS = [ + "border-top-width", + "border-top-style", + "border-top-color", + "border-right-width", + "border-right-style", + "border-right-color", + "border-bottom-width", + "border-bottom-style", + "border-bottom-color", + "border-left-width", + "border-left-style", + "border-left-color" +]; +const BORDER_IMAGE_LONGHAND_NAMES = [ + "border-image-source", + "border-image-slice", + "border-image-width", + "border-image-outset", + "border-image-repeat" +]; +function tryCombineBorderFull(d, declMap, processed, declarations, declIndices) { + if (!BORDER_PHYSICAL_SIDE_LONGHANDS.includes(d.name)) return null; + const allDecls = BORDER_PHYSICAL_SIDE_LONGHANDS.map((name) => declMap.get(name)); + if (!allDecls.every((other) => other && !processed.has(other) && other.important === d.important)) return null; + if (checkIntervening(allDecls, declarations, declIndices)) return null; + const imageDecls = BORDER_IMAGE_LONGHAND_NAMES.map((name) => declMap.get(name)).filter((img) => Boolean(img && !processed.has(img))); + if (imageDecls.length === 0) return null; + const imageRecord = { + "border-image-source": [{ + type: "ident", + value: "none" + }], + "border-image-slice": [{ + type: "percentage", + value: 100, + sign: null + }], + "border-image-width": [{ + type: "number", + value: 1, + sign: null, + numberType: "integer" + }], + "border-image-outset": [{ + type: "number", + value: 0, + sign: null, + numberType: "integer" + }], + "border-image-repeat": [{ + type: "ident", + value: "stretch" + }] + }; + for (const img of imageDecls) imageRecord[img.name] = img.value; + if (!isInitialBorderImage(imageRecord)) return null; + const record = { ...imageRecord }; + for (const other of allDecls) record[other.name] = other.value; + const contracted = SHORTHANDS["border"]?.contract(record); + if (contracted !== null && contracted !== void 0) { + for (const other of allDecls) processed.add(other); + for (const img of imageDecls) processed.add(img); + return `border: ${contracted}${d.important ? " !important" : ""}`; } - - //.CommonJS - CSSOM_1 = CSSOM; - ///CommonJS - return CSSOM_1; + return null; } - -var errorUtils = {}; - -var hasRequiredErrorUtils; - -function requireErrorUtils () { - if (hasRequiredErrorUtils) return errorUtils; - hasRequiredErrorUtils = 1; - // Utility functions for CSSOM error handling - - /** - * Gets the appropriate error constructor from the global object context. - * Tries to find the error constructor from parentStyleSheet.__globalObject, - * then from __globalObject, then falls back to the native constructor. - * - * @param {Object} context - The CSSOM object (rule, stylesheet, etc.) - * @param {string} errorType - The error type ('TypeError', 'RangeError', 'DOMException', etc.) - * @return {Function} The error constructor - */ - function getErrorConstructor(context, errorType) { - // Try parentStyleSheet.__globalObject first - if (context.parentStyleSheet && context.parentStyleSheet.__globalObject && context.parentStyleSheet.__globalObject[errorType]) { - return context.parentStyleSheet.__globalObject[errorType]; - } - - // Try __parentStyleSheet (alternative naming) - if (context.__parentStyleSheet && context.__parentStyleSheet.__globalObject && context.__parentStyleSheet.__globalObject[errorType]) { - return context.__parentStyleSheet.__globalObject[errorType]; - } - - // Try __globalObject on the context itself - if (context.__globalObject && context.__globalObject[errorType]) { - return context.__globalObject[errorType]; - } - - // Fall back to native constructor - return (typeof commonjsGlobal !== 'undefined' && commonjsGlobal[errorType]) || - (typeof window !== 'undefined' && window[errorType]) || - eval(errorType); +const ORDERED_SHORTHAND_COMBINERS = [ + { + name: "font", + longhands: SHORTHANDS["font"].longhands + }, + { + name: "font-variant", + longhands: SHORTHANDS["font-variant"].longhands + }, + { + name: "background", + longhands: [ + "background-image", + "background-position", + "background-size", + "background-repeat", + "background-attachment", + "background-origin", + "background-clip", + "background-color" + ] + }, + { + name: "border-block", + longhands: [ + "border-block-start-width", + "border-block-start-style", + "border-block-start-color", + "border-block-end-width", + "border-block-end-style", + "border-block-end-color" + ] + }, + { + name: "border-inline", + longhands: [ + "border-inline-start-width", + "border-inline-start-style", + "border-inline-start-color", + "border-inline-end-width", + "border-inline-end-style", + "border-inline-end-color" + ] } - - /** - * Creates an appropriate error with context-aware constructor. - * - * @param {Object} context - The CSSOM object (rule, stylesheet, etc.) - * @param {string} errorType - The error type ('TypeError', 'RangeError', 'DOMException', etc.) - * @param {string} message - The error message - * @param {string} [name] - Optional name for DOMException - */ - function createError(context, errorType, message, name) { - var ErrorConstructor = getErrorConstructor(context, errorType); - return new ErrorConstructor(message, name); +]; +function tryCombineNamedShorthand(shorthandName, longhands, d, declMap, processed, declarations, declIndices) { + if (!longhands.includes(d.name)) return null; + const allDecls = longhands.map((name) => declMap.get(name)); + if (!allDecls.every((other) => other && !processed.has(other) && other.important === d.important)) return null; + if (checkIntervening(allDecls, declarations, declIndices)) return null; + const record = {}; + for (const other of allDecls) record[other.name] = other.value; + const contracted = SHORTHANDS[shorthandName]?.contract(record); + if (contracted !== null && contracted !== void 0) { + for (const other of allDecls) processed.add(other); + return `${shorthandName}: ${contracted}${d.important ? " !important" : ""}`; } - - /** - * Creates and throws an appropriate error with context-aware constructor. - * - * @param {Object} context - The CSSOM object (rule, stylesheet, etc.) - * @param {string} errorType - The error type ('TypeError', 'RangeError', 'DOMException', etc.) - * @param {string} message - The error message - * @param {string} [name] - Optional name for DOMException - */ - function throwError(context, errorType, message, name) { - throw createError(context, errorType, message, name); + return null; +} +const CSS_WIDE_AND_DEFAULT = /* @__PURE__ */ new Set("initial,inherit,unset,revert,revert-layer,default".split(",")); +function serializeFontFamilyItem(tokens) { + const nonWs = tokens.filter((t) => t.type !== "whitespace" && t.type !== "comment" && t.type !== "EOF"); + if (nonWs.length === 1 && nonWs[0].type === "string") { + let strVal = nonWs[0].value; + if (strVal.startsWith("'") && strVal.endsWith("'") || strVal.startsWith("\"") && strVal.endsWith("\"")) strVal = strVal.slice(1, -1); + const lower = strVal.toLowerCase(); + if (GENERIC_FONT_FAMILIES.has(lower) || CSS_WIDE_AND_DEFAULT.has(lower)) return `"${strVal}"`; + if (strVal !== strVal.trim() || /\s{2,}|\t|\n|\r/.test(strVal)) return `"${strVal}"`; + const words = strVal.split(" "); + return words.length > 0 && words.every((word) => word.length > 0 && !/^[0-9]|^--|^-[0-9]/.test(word) && /^[a-zA-Z_-][a-zA-Z0-9_-]*$/.test(word)) ? strVal : `"${strVal}"`; } - - /** - * Throws a TypeError for missing required arguments. - * - * @param {Object} context - The CSSOM object - * @param {string} methodName - The method name (e.g., 'appendRule') - * @param {string} objectName - The object name (e.g., 'CSSKeyframesRule') - * @param {number} [required=1] - Number of required arguments - * @param {number} [provided=0] - Number of provided arguments - */ - function throwMissingArguments(context, methodName, objectName, required, provided) { - required = required || 1; - provided = provided || 0; - var message = "Failed to execute '" + methodName + "' on '" + objectName + "': " + - required + " argument" + (required > 1 ? "s" : "") + " required, but only " + - provided + " present."; - throwError(context, 'TypeError', message); + if (nonWs.every((t) => t.type === "ident")) return nonWs.map((t) => serializeNode(t, false)).join(" "); + return tokens.map((t) => serializeNode(t, false)).join(""); +} +function serializeFontFamily(values) { + const groups = []; + let current = []; + for (const token of values) if (token.type === "comma") { + groups.push(current); + current = []; + } else current.push(token); + if (current.length > 0) groups.push(current); + return groups.map((g) => serializeFontFamilyItem(g).trim()).filter((s) => s.length > 0).join(", "); +} +function serializeDeclarations(declarations) { + if (declarations.length === 0) return ""; + const declMap = /* @__PURE__ */ new Map(); + const declIndices = /* @__PURE__ */ new Map(); + for (let i = 0; i < declarations.length; i++) { + const d = declarations[i]; + declMap.set(d.name, d); + declIndices.set(d, i); } - - /** - * Throws a DOMException for parse errors. - * - * @param {Object} context - The CSSOM object - * @param {string} methodName - The method name - * @param {string} objectName - The object name - * @param {string} rule - The rule that failed to parse - * @param {string} [name='SyntaxError'] - The DOMException name - */ - function throwParseError(context, methodName, objectName, rule, name) { - var message = "Failed to execute '" + methodName + "' on '" + objectName + "': " + - "Failed to parse the rule '" + rule + "'."; - throwError(context, 'DOMException', message, name || 'SyntaxError'); + if (declarations.length >= ALL_SHORTHAND_LONGHANDS.length) { + const firstDecl = declMap.get(ALL_SHORTHAND_LONGHANDS[0]); + if (firstDecl) { + const firstValLower = serialize(firstDecl.value).trim().toLowerCase(); + const firstImportant = firstDecl.important; + if (CSS_WIDE_AND_DEFAULT.has(firstValLower) || firstValLower.startsWith("var(")) { + if (ALL_SHORTHAND_LONGHANDS.every((lh) => { + const d = declMap.get(lh); + return d && serialize(d.value).trim().toLowerCase() === firstValLower && d.important === firstImportant; + })) { + const allLonghandsSet = new Set(ALL_SHORTHAND_LONGHANDS); + const allDecl = { + type: "declaration", + name: "all", + value: firstDecl.value, + important: firstImportant + }; + const newDecls = []; + let inserted = false; + for (const d of declarations) if (allLonghandsSet.has(d.name)) { + if (!inserted) { + newDecls.push(allDecl); + inserted = true; + } + } else newDecls.push(d); + return serializeDeclarations(newDecls); + } + } + } } - - /** - * Throws a DOMException for index errors. - * - * @param {Object} context - The CSSOM object - * @param {string} methodName - The method name - * @param {string} objectName - The object name - * @param {number} index - The invalid index - * @param {number} maxIndex - The maximum valid index - * @param {string} [name='IndexSizeError'] - The DOMException name - */ - function throwIndexError(context, methodName, objectName, index, maxIndex, name) { - var message = "Failed to execute '" + methodName + "' on '" + objectName + "': " + - "The index provided (" + index + ") is larger than the maximum index (" + maxIndex + ")."; - throwError(context, 'DOMException', message, name || 'IndexSizeError'); - } - - var errorUtils$1 = { - createError: createError, - getErrorConstructor: getErrorConstructor, - throwError: throwError, - throwMissingArguments: throwMissingArguments, - throwParseError: throwParseError, - throwIndexError: throwIndexError - }; - - //.CommonJS - errorUtils.errorUtils = errorUtils$1; - ///CommonJS - return errorUtils; + const processed = /* @__PURE__ */ new Set(); + const result = []; + for (const d of declarations) { + if (processed.has(d)) continue; + let combined = tryCombineBorderFull(d, declMap, processed, declarations, declIndices); + if (!combined) for (const spec of ORDERED_SHORTHAND_COMBINERS) { + combined = tryCombineNamedShorthand(spec.name, spec.longhands, d, declMap, processed, declarations, declIndices); + if (combined) break; + } + if (!combined) combined = tryCombineBoxShorthand(d, declMap, processed, declarations, declIndices); + if (!combined) { + const generic = tryCombineGenericShorthand(d, declMap, processed, declarations, declIndices); + if (generic) { + const sides = [ + "border-top", + "border-right", + "border-bottom", + "border-left" + ]; + if (sides.includes(generic.name)) { + const sideResults = sides.map((side) => { + if (side === generic.name) return generic; + const existing = declMap.get(side); + if (existing && !processed.has(existing)) return { + name: side, + value: serialize(existing.value).trim(), + important: existing.important, + decl: existing + }; + const longhands = genericShorthands[side]; + if (!longhands) return null; + const sideLonghands = longhands.map((lh) => declMap.get(lh)); + if (sideLonghands.every((lh) => lh && !processed.has(lh) && lh.important === generic.important)) { + if (checkIntervening(sideLonghands, declarations, declIndices)) return null; + return { + name: side, + value: sideLonghands.map((lh) => serialize(lh.value).trim()).filter((v) => v !== "").join(" "), + important: generic.important, + longhands: sideLonghands + }; + } + return null; + }); + if (sideResults.every((r) => r !== null && r.value === generic.value && r.important === generic.important)) { + for (const r of sideResults) if (r && "longhands" in r) r.longhands.forEach((lh) => processed.add(lh)); + else if (r && "decl" in r) processed.add(r.decl); + combined = `border: ${generic.value}${generic.important ? " !important" : ""}`; + } else combined = `${generic.name}: ${generic.value}${generic.important ? " !important" : ""}`; + } else combined = `${generic.name}: ${generic.value}${generic.important ? " !important" : ""}`; + } + } + if (!combined) combined = tryCombineLogicalShorthand(d, declMap, processed, declarations, declIndices); + if (combined) result.push(combined); + else { + const isCustom = d.name.startsWith("--"); + let val; + if (d.name === "font-family") val = serializeFontFamily(d.value); + else if (d.name === "flex-basis" && serialize(d.value).trim() === "0") val = "0px"; + else val = d.raw && !d.raw.includes("var(") ? d.raw : serialize(d.value, isCustom, d.name).trim(); + result.push(`${serializeIdentifier(d.name)}: ${val}${d.important ? " !important" : ""}`); + processed.add(d); + } + } + return result.join("; ") + ";"; } - -var regexPatterns = {}; - -var hasRequiredRegexPatterns; - -function requireRegexPatterns () { - if (hasRequiredRegexPatterns) return regexPatterns; - hasRequiredRegexPatterns = 1; - // Shared regex patterns for CSS parsing and validation - // These patterns are compiled once and reused across multiple files for better performance - - // Regex patterns for CSS parsing - var atKeyframesRegExp = /@(-(?:\w+-)+)?keyframes/g; // Match @keyframes and vendor-prefixed @keyframes - var beforeRulePortionRegExp = /{(?!.*{)|}(?!.*})|;(?!.*;)|\*\/(?!.*\*\/)/g; // Match the closest allowed character (a opening or closing brace, a semicolon or a comment ending) before the rule - var beforeRuleValidationRegExp = /^[\s{};]*(\*\/\s*)?$/; // Match that the portion before the rule is empty or contains only whitespace, semicolons, opening/closing braces, and optionally a comment ending (*/) followed by whitespace - var forwardRuleValidationRegExp = /(?:\s|\/\*|\{|\()/; // Match that the rule is followed by any whitespace, a opening comment, a condition opening parenthesis or a opening brace - var forwardImportRuleValidationRegExp = /(?:\s|\/\*|'|")/; // Match that the rule is followed by any whitespace, an opening comment, a single quote or double quote - var forwardRuleClosingBraceRegExp = /{[^{}]*}|}/; // Finds the next closing brace of a rule block - var forwardRuleSemicolonAndOpeningBraceRegExp = /^.*?({|;)/; // Finds the next semicolon or opening brace after the at-rule - - // Regex patterns for CSS selector validation and parsing - var cssCustomIdentifierRegExp = /^(-?[_a-zA-Z]+(\.[_a-zA-Z]+)*[_a-zA-Z0-9-]*)$/; // Validates a css custom identifier - var startsWithCombinatorRegExp = /^\s*[>+~]/; // Checks if a selector starts with a CSS combinator (>, +, ~) - - /** - * Parse `@page` selectorText for page name and pseudo-pages - * Valid formats: - * - (empty - no name, no pseudo-page) - * - `:left`, `:right`, `:first`, `:blank` (pseudo-page only) - * - `named` (named page only) - * - `named:first` (named page with single pseudo-page) - * - `named:first:left` (named page with multiple pseudo-pages) - */ - var atPageRuleSelectorRegExp = /^([^\s:]+)?((?::\w+)*)$/; // Validates @page rule selectors - - // Regex patterns for CSSImportRule parsing - var layerRegExp = /layer\(([^)]*)\)/; // Matches layer() function in @import - var layerRuleNameRegExp = /^(-?[_a-zA-Z]+(\.[_a-zA-Z]+)*[_a-zA-Z0-9-]*)$/; // Validates layer name (same as custom identifier) - var doubleOrMoreSpacesRegExp = /\s{2,}/g; // Matches two or more consecutive whitespace characters - - - // Regex patterns for CSS escape sequences and identifiers - var startsWithHexEscapeRegExp = /^\\[0-9a-fA-F]/; // Checks if escape sequence starts with hex escape - var identStartCharRegExp = /[a-zA-Z_\u00A0-\uFFFF]/; // Valid identifier start character - var identCharRegExp = /^[a-zA-Z0-9_\-\u00A0-\uFFFF\\]/; // Valid identifier character - var specialCharsNeedEscapeRegExp = /[!"#$%&'()*+,./:;<=>?@\[\\\]^`{|}~\s]/; // Characters that need escaping - var combinatorOrSeparatorRegExp = /[\s>+~,()]/; // Selector boundaries and combinators - var afterHexEscapeSeparatorRegExp = /[\s>+~,(){}\[\]]/; // Characters that separate after hex escape - var trailingSpaceSeparatorRegExp = /[\s>+~,(){}]/; // Characters that allow trailing space - var endsWithHexEscapeRegExp = /\\[0-9a-fA-F]{1,6}\s+$/; // Matches selector ending with hex escape + space(s) - - /** - * Regular expression to detect invalid characters in the value portion of a CSS style declaration. - * - * This regex matches a colon (:) that is not inside parentheses and not inside single or double quotes. - * It is used to ensure that the value part of a CSS property does not contain unexpected colons, - * which would indicate a malformed declaration (e.g., "color: foo:bar;" is invalid). - * - * The negative lookahead `(?![^(]*\))` ensures that the colon is not followed by a closing - * parenthesis without encountering an opening parenthesis, effectively ignoring colons inside - * function-like values (e.g., `url(data:image/png;base64,...)`). - * - * The lookahead `(?=(?:[^'"]|'[^']*'|"[^"]*")*$)` ensures that the colon is not inside single or double quotes, - * allowing colons within quoted strings (e.g., `content: ":";` or `background: url("foo:bar.png");`). - * - * Example: - * - `color: red;` // valid, does not match - * - `background: url(data:image/png;base64,...);` // valid, does not match - * - `content: ':';` // valid, does not match - * - `color: foo:bar;` // invalid, matches - */ - var basicStylePropertyValueValidationRegExp = /:(?![^(]*\))(?=(?:[^'"]|'[^']*'|"[^"]*")*$)/; - - // Attribute selector pattern: matches attribute-name operator value - // Operators: =, ~=, |=, ^=, $=, *= - // Rewritten to avoid ReDoS by using greedy match and trimming in JavaScript - var attributeSelectorContentRegExp = /^([^\s=~|^$*]+)\s*(~=|\|=|\^=|\$=|\*=|=)\s*(.+)$/; - - // Selector validation patterns - var pseudoElementRegExp = /::[a-zA-Z][\w-]*|:(before|after|first-line|first-letter)(?![a-zA-Z0-9_-])/; // Matches pseudo-elements - var invalidCombinatorLtGtRegExp = /<>/; // Invalid <> combinator - var invalidCombinatorDoubleGtRegExp = />>/; // Invalid >> combinator - var consecutiveCombinatorsRegExp = /[>+~]\s*[>+~]/; // Invalid consecutive combinators - var invalidSlottedRegExp = /(?:^|[\s>+~,\[])slotted\s*\(/i; // Invalid slotted() without :: - var invalidPartRegExp = /(?:^|[\s>+~,\[])part\s*\(/i; // Invalid part() without :: - var invalidCueRegExp = /(?:^|[\s>+~,\[])cue\s*\(/i; // Invalid cue() without :: - var invalidCueRegionRegExp = /(?:^|[\s>+~,\[])cue-region\s*\(/i; // Invalid cue-region() without :: - var invalidNestingPattern = /&(?![.\#\[:>\+~\s])[a-zA-Z]/; // Invalid & followed by type selector - var emptyPseudoClassRegExp = /:(?:is|not|where|has)\(\s*\)/; // Empty pseudo-class like :is() - var whitespaceNormalizationRegExp = /(['"])(?:\\.|[^\\])*?\1|(\r\n|\r|\n)/g; // Normalize newlines outside quotes - var newlineRemovalRegExp = /\n/g; // Remove all newlines - var whitespaceAndDotRegExp = /[\s.]/; // Matches whitespace or dot - var declarationOrOpenBraceRegExp = /[{;}]/; // Matches declaration separator or open brace - var ampersandRegExp = /&/; // Matches nesting selector - var hexEscapeSequenceRegExp = /^([0-9a-fA-F]{1,6})[ \t\r\n\f]?/; // Matches hex escape sequence (1-6 hex digits optionally followed by whitespace) - var attributeCaseFlagRegExp = /^(.+?)\s+([is])$/i; // Matches case-sensitivity flag at end of attribute value - var prependedAmpersandRegExp = /^&\s+[:\\.]/; // Matches prepended ampersand pattern (& followed by space and : or .) - var openBraceGlobalRegExp = /{/g; // Matches opening braces (global) - var closeBraceGlobalRegExp = /}/g; // Matches closing braces (global) - var scopePreludeSplitRegExp = /\s*\)\s*to\s+\(/; // Splits scope prelude by ") to (" - var leadingWhitespaceRegExp = /^\s+/; // Matches leading whitespace (used to implement a ES5-compliant alternative to trimStart()) - var doubleQuoteRegExp = /"/g; // Match all double quotes (for escaping in attribute values) - var backslashRegExp = /\\/g; // Match all backslashes (for escaping in attribute values) - - // Media query validation patterns - var invalidMediaOperatorWithoutSpaceRegExp = /\)(and|or)\(/i; // )and( or )or( - missing space after operator - var invalidMediaNotAfterOperatorRegExp = /\b(and|or)\s+not\b/i; // and not or or not - not after operator - var invalidMediaNotIdentifierRegExp = /\)\s+not\(/i; // ) not( - not() identifier after media feature - var invalidMediaNotAfterFeatureRegExp = /\)\s+not\s+/i; // ) not - not operator after media feature (only valid at start or after comma) - var invalidMediaOperatorBetweenParensRegExp = /\)\s+(?!and\b|or\b)[a-z]+\s+\(/i; // ) xx ( - invalid operator between ) and ( - var invalidMediaNotWithMediaTypeRegExp = /\bnot\s+[a-z][a-z0-9_-]*\s+\(/i; // not ( - missing 'and' between media type and feature - - // Media query normalization patterns - var mediaFeatureColonRegExp = /(\(\s*)([^():]+):(\s*)([^()]*\))/g; // Match colon in media features - var identifierBeforeParenRegExp = /[a-z0-9_-]/i; // Match identifier character before opening paren - var mediaAndOperatorRegExp = /\s*\band\b\s*/gi; // Match 'and' operator with word boundaries - var mediaOrOperatorRegExp = /\s*\bor\b\s*/gi; // Match 'or' operator with word boundaries - var mediaNotOperatorStartRegExp = /^\s*not\s+(?=\S)/i; // Match 'not' operator at start - var mediaNotOperatorRegExp = /\s+\bnot\s+(?=\S)/gi; // Match 'not' operator after space - - var regexPatterns$1 = { - // Parsing patterns - atKeyframesRegExp: atKeyframesRegExp, - beforeRulePortionRegExp: beforeRulePortionRegExp, - beforeRuleValidationRegExp: beforeRuleValidationRegExp, - forwardRuleValidationRegExp: forwardRuleValidationRegExp, - forwardImportRuleValidationRegExp: forwardImportRuleValidationRegExp, - forwardRuleClosingBraceRegExp: forwardRuleClosingBraceRegExp, - forwardRuleSemicolonAndOpeningBraceRegExp: forwardRuleSemicolonAndOpeningBraceRegExp, - - // Selector validation patterns - cssCustomIdentifierRegExp: cssCustomIdentifierRegExp, - startsWithCombinatorRegExp: startsWithCombinatorRegExp, - atPageRuleSelectorRegExp: atPageRuleSelectorRegExp, - - // Parsing patterns used in CSSImportRule - layerRegExp: layerRegExp, - layerRuleNameRegExp: layerRuleNameRegExp, - doubleOrMoreSpacesRegExp: doubleOrMoreSpacesRegExp, - - // Escape sequence and identifier patterns - startsWithHexEscapeRegExp: startsWithHexEscapeRegExp, - identStartCharRegExp: identStartCharRegExp, - identCharRegExp: identCharRegExp, - specialCharsNeedEscapeRegExp: specialCharsNeedEscapeRegExp, - combinatorOrSeparatorRegExp: combinatorOrSeparatorRegExp, - afterHexEscapeSeparatorRegExp: afterHexEscapeSeparatorRegExp, - trailingSpaceSeparatorRegExp: trailingSpaceSeparatorRegExp, - endsWithHexEscapeRegExp: endsWithHexEscapeRegExp, - - // Basic style property value validation - basicStylePropertyValueValidationRegExp: basicStylePropertyValueValidationRegExp, - - // Attribute selector patterns - attributeSelectorContentRegExp: attributeSelectorContentRegExp, - - // Selector validation patterns - pseudoElementRegExp: pseudoElementRegExp, - invalidCombinatorLtGtRegExp: invalidCombinatorLtGtRegExp, - invalidCombinatorDoubleGtRegExp: invalidCombinatorDoubleGtRegExp, - consecutiveCombinatorsRegExp: consecutiveCombinatorsRegExp, - invalidSlottedRegExp: invalidSlottedRegExp, - invalidPartRegExp: invalidPartRegExp, - invalidCueRegExp: invalidCueRegExp, - invalidCueRegionRegExp: invalidCueRegionRegExp, - invalidNestingPattern: invalidNestingPattern, - emptyPseudoClassRegExp: emptyPseudoClassRegExp, - whitespaceNormalizationRegExp: whitespaceNormalizationRegExp, - newlineRemovalRegExp: newlineRemovalRegExp, - whitespaceAndDotRegExp: whitespaceAndDotRegExp, - declarationOrOpenBraceRegExp: declarationOrOpenBraceRegExp, - ampersandRegExp: ampersandRegExp, - hexEscapeSequenceRegExp: hexEscapeSequenceRegExp, - attributeCaseFlagRegExp: attributeCaseFlagRegExp, - prependedAmpersandRegExp: prependedAmpersandRegExp, - openBraceGlobalRegExp: openBraceGlobalRegExp, - closeBraceGlobalRegExp: closeBraceGlobalRegExp, - scopePreludeSplitRegExp: scopePreludeSplitRegExp, - leadingWhitespaceRegExp: leadingWhitespaceRegExp, - doubleQuoteRegExp: doubleQuoteRegExp, - backslashRegExp: backslashRegExp, - - // Media query validation patterns - invalidMediaOperatorWithoutSpaceRegExp: invalidMediaOperatorWithoutSpaceRegExp, - invalidMediaNotAfterOperatorRegExp: invalidMediaNotAfterOperatorRegExp, - invalidMediaNotIdentifierRegExp: invalidMediaNotIdentifierRegExp, - invalidMediaNotAfterFeatureRegExp: invalidMediaNotAfterFeatureRegExp, - invalidMediaOperatorBetweenParensRegExp: invalidMediaOperatorBetweenParensRegExp, - invalidMediaNotWithMediaTypeRegExp: invalidMediaNotWithMediaTypeRegExp, - - // Media query normalization patterns - mediaFeatureColonRegExp: mediaFeatureColonRegExp, - identifierBeforeParenRegExp: identifierBeforeParenRegExp, - mediaAndOperatorRegExp: mediaAndOperatorRegExp, - mediaOrOperatorRegExp: mediaOrOperatorRegExp, - mediaNotOperatorStartRegExp: mediaNotOperatorStartRegExp, - mediaNotOperatorRegExp: mediaNotOperatorRegExp +function serializeSelectorList(list, nsContext) { + const context = { + hasDefaultNamespace: typeof nsContext === "boolean" ? nsContext : Boolean(nsContext?.hasDefaultNamespace), + defaultNamespacePrefixes: typeof nsContext === "object" && nsContext !== null ? nsContext.defaultNamespacePrefixes : void 0 }; - - //.CommonJS - regexPatterns.regexPatterns = regexPatterns$1; - ///CommonJS - return regexPatterns; + return list.selectors.map((s) => s.type === "invalid-selector" ? serialize(s.tokens) : serializeComplexSelector(s, context)).join(", "); } - -var CSSStyleDeclaration = {}; - -var parse$1 = {}; - -var CSSStyleSheet = {}; - -var MediaList = {}; - -var hasRequiredMediaList; - -function requireMediaList () { - if (hasRequiredMediaList) return MediaList; - hasRequiredMediaList = 1; - //.CommonJS - var CSSOM = {}; - ///CommonJS - - - /** - * @constructor - * @see http://dev.w3.org/csswg/cssom/#the-medialist-interface - */ - CSSOM.MediaList = function MediaList(){ - this.length = 0; - }; - - CSSOM.MediaList.prototype = { - - constructor: CSSOM.MediaList, - - /** - * @return {string} - */ - get mediaText() { - return Array.prototype.join.call(this, ", "); - }, - - /** - * @param {string} value - */ - set mediaText(value) { - if (typeof value === "string") { - var values = value.split(",").filter(function(text){ - return !!text; - }); - var length = this.length = values.length; - for (var i=0; i { + if (item.type === "combinator") { + if (item.value === " ") return " "; + return idx === 0 ? `${item.value} ` : ` ${item.value} `; + } + return item.selectors.filter((s, sIdx) => { + if (s.type === "universal-selector" && item.selectors.length > 1 && sIdx === 0) { + const isDefaultNs = s.namespace !== void 0 && s.namespace !== "" && defaultNamespacePrefixes?.has(s.namespace); + if (s.namespace === void 0 || isDefaultNs || s.namespace === "*" && !hasDefaultNamespace) return false; + } + return true; + }).map((s) => serializeSimpleSelector(s, nsContext)).join(""); + }).join(""); +} +function formatAnPlusB(tokens) { + const parsed = parseAnPlusB(tokens); + if (parsed === null) return serialize(tokens).trim(); + const { a, b } = parsed; + if (a === 0) return b.toString(); + const partA = a === 1 ? "n" : a === -1 ? "-n" : `${a}n`; + if (b === 0) return partA; + return b > 0 ? `${partA}+${b}` : `${partA}${b}`; +} +function formatSelectorNamespacePrefix(namespace, nsContext, isElementSelector) { + if (namespace === void 0) return ""; + if (isElementSelector && namespace !== "" && nsContext.defaultNamespacePrefixes?.has(namespace)) return ""; + if (namespace === "*") return !isElementSelector || nsContext.hasDefaultNamespace ? "*|" : ""; + if (namespace === "") return isElementSelector ? "|" : ""; + return `${serializeIdentifier(namespace)}|`; +} +const NTH_PSEUDO_CLASSES = /* @__PURE__ */ new Set("nth-child,nth-last-child,nth-of-type,nth-last-of-type".split(",")); +function serializeSimpleSelector(simple, nsContext) { + switch (simple.type) { + case "type-selector": return formatSelectorNamespacePrefix(simple.namespace, nsContext, true) + serializeIdentifier(simple.name); + case "universal-selector": return formatSelectorNamespacePrefix(simple.namespace, nsContext, true) + "*"; + case "id-selector": return "#" + serializeIdentifier(simple.name); + case "class-selector": return "." + serializeIdentifier(simple.name); + case "attribute-selector": { + let attr = "[" + formatSelectorNamespacePrefix(simple.namespace, nsContext, false) + serializeIdentifier(simple.name); + if (simple.operator) attr += simple.operator + serializeString(simple.value || ""); + if (simple.flags) attr += " " + simple.flags; + return attr + "]"; + } + case "pseudo-class-selector": { + let pc = `:${simple.name}`; + const isNth = NTH_PSEUDO_CLASSES.has(simple.name.toLowerCase()); + if (simple.argument) { + if ("type" in simple.argument && simple.argument.type === "selector-list") { + const selStr = serializeSelectorList(simple.argument, nsContext); + pc += isNth && simple.nth ? `(${formatAnPlusB(simple.nth)} of ${selStr})` : `(${selStr})`; + } else { + const tokens = simple.argument; + pc += `(${isNth ? formatAnPlusB(tokens) : serialize(tokens).trim()})`; } - } else if (value === null) { - var length = this.length; - for (var i = 0; i < length; i++) { - delete this[i]; + } + return pc; + } + case "pseudo-element-selector": { + let pe = `::${simple.name}`; + if (simple.argument) pe += "type" in simple.argument && simple.argument.type === "selector-list" ? `(${serializeSelectorList(simple.argument, nsContext)})` : `(${serialize(simple.argument).trim()})`; + return pe; + } + case "nesting-selector": return "&"; + default: return ""; + } +} +// src/AbstractTokenizer.ts +/** +* @license +* Copyright 2026 Google LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +var AbstractTokenizer = class { + unicodeRangesAllowed = false; + errors = []; + parseError(message) { + this.errors.push({ message }); + console.warn(`CSS Parse Error: ${message}`); + } + consumeToken() { + while (true) { + this.consumeComments(); + const cp = this.consume(); + if (cp === -1) return { + type: "EOF", + value: "" + }; + if (this.isWhitespace(cp)) { + while (this.isWhitespace(this.cp)) this.consume(); + return { + type: "whitespace", + value: " " + }; + } + switch (cp) { + case 34: return this.consumeStringToken(34); + case 35: + if (this.isIdentCodePoint(this.cp) || this.isValidEscape(this.cp, this.peek(1))) { + const token = { + type: "hash", + value: "", + hashType: "unrestricted" + }; + if (this.wouldStartIdentSequence(this.cp, this.peek(1), this.peek(2))) token.hashType = "id"; + token.value = this.consumeIdentSequence(); + return token; + } + return { + type: "delim", + value: String.fromCodePoint(cp) + }; + case 39: return this.consumeStringToken(39); + case 40: return { + type: "(", + value: "(" + }; + case 41: return { + type: ")", + value: ")" + }; + case 43: + if (this.wouldStartNumber(cp, this.cp, this.peek(1))) { + this.reconsume(); + return this.consumeNumericToken(); + } + return { + type: "delim", + value: "+" + }; + case 44: return { + type: "comma", + value: "," + }; + case 45: + if (this.wouldStartNumber(cp, this.cp, this.peek(1))) { + this.reconsume(); + return this.consumeNumericToken(); + } + if (this.cp === 45 && this.peek(1) === 62) { + this.consume(); + this.consume(); + return { + type: "CDC", + value: "-->" + }; + } + if (this.wouldStartIdentSequence(cp, this.cp, this.peek(1))) { + this.reconsume(); + return this.consumeIdentLikeToken(); + } + return { + type: "delim", + value: "-" + }; + case 46: + if (this.wouldStartNumber(cp, this.cp, this.peek(1))) { + this.reconsume(); + return this.consumeNumericToken(); + } + return { + type: "delim", + value: "." + }; + case 47: return { + type: "delim", + value: "/" + }; + case 58: return { + type: "colon", + value: ":" + }; + case 59: return { + type: "semicolon", + value: ";" + }; + case 60: + if (this.cp === 33 && this.peek(1) === 45 && this.peek(2) === 45) { + this.consume(); + this.consume(); + this.consume(); + return { + type: "CDO", + value: ""; - case "colon": return ":"; - case "semicolon": return ";"; - case "comma": return ","; - case "[": - case "]": - case "{": - case "}": - case "(": - case ")": return token.value; - case "function": return serializeIdentifier(preserveCase ? token.value : token.value.toLowerCase()) + "("; - case "unicode-range": return token.value; - case "EOF": return ""; - default: return token.value || ""; - } -} -function getMirrorToken(start) { - if (start === "{") return "}"; - if (start === "[") return "]"; - if (start === "(") return ")"; - return ""; -} -function getOriginalText(values) { - let text = ""; - for (const val of values) if (val.type === "simple-block") text += (val.associatedToken.originalText || "") + getOriginalText(val.value) + getMirrorToken(val.associatedToken.value); - else if (val.type === "function") { - const func = val; - text += `${func.name}(${getOriginalText(func.value)})`; - } else text += val.originalText || val.value; - return text; -} -/** -* // cssom-1 #serialize-an-identifier -*/ -function serializeIdentifier(id) { - let result = ""; - for (let i = 0; i < id.length; i++) { - const charCode = id.charCodeAt(i); - const char = id[i]; - if (charCode === 0) { - result += "�"; - continue; - } - if (charCode >= 1 && charCode <= 31 || charCode === 127) { - result += escapeAsCodePoint(charCode); - continue; - } - if (i === 0 && charCode >= 48 && charCode <= 57) { - result += escapeAsCodePoint(charCode); - continue; - } - if (i === 1 && charCode >= 48 && charCode <= 57 && id.charCodeAt(0) === 45) { - result += escapeAsCodePoint(charCode); - continue; - } - if (i === 0 && charCode === 45 && id.length === 1) { - result += "\\-"; - continue; - } - if (charCode >= 128 || charCode === 45 || charCode === 95 || charCode >= 48 && charCode <= 57 || charCode >= 65 && charCode <= 90 || charCode >= 97 && charCode <= 122) { - result += char; - continue; - } - result += "\\" + char; - } - return result; -} -/** -* // cssom-1 #serialize-a-string -*/ -function serializeString(s) { - let result = "\""; - for (let i = 0; i < s.length; i++) { - const charCode = s.charCodeAt(i); - const char = s[i]; - if (charCode === 0) { - result += "�"; - continue; - } - if (charCode >= 1 && charCode <= 31 || charCode === 127) { - result += escapeAsCodePoint(charCode); - continue; - } - if (charCode === 34 || charCode === 92) { - result += "\\" + char; - continue; - } - result += char; - } - result += "\""; - return result; -} -function serializeUrl(val) { - return `url(${serializeString(val)})`; -} -function serializeUrlToken(val, originalText) { - if (originalText && !val.includes("�") && originalText.endsWith(")")) return originalText; - let result = ""; - for (let i = 0; i < val.length; i++) { - const charCode = val.charCodeAt(i); - const char = val[i]; - if (charCode === 34 || charCode === 39 || charCode === 40 || charCode === 41 || charCode === 92 || charCode <= 32 || charCode === 127) result += "\\" + char; - else result += char; - } - return `url(${result})`; -} -function escapeAsCodePoint(charCode) { - return "\\" + charCode.toString(16) + " "; -} -const logicalShorthandsEntries = Object.entries({ - "margin-inline": { - start: "margin-inline-start", - end: "margin-inline-end", - allowDifferent: true - }, - "padding-inline": { - start: "padding-inline-start", - end: "padding-inline-end", - allowDifferent: true - }, - "margin-block": { - start: "margin-block-start", - end: "margin-block-end", - allowDifferent: true - }, - "padding-block": { - start: "padding-block-start", - end: "padding-block-end", - allowDifferent: true - }, - "inset-inline": { - start: "inset-inline-start", - end: "inset-inline-end", - allowDifferent: true - }, - "inset-block": { - start: "inset-block-start", - end: "inset-block-end", - allowDifferent: true - }, - "border-inline-width": { - start: "border-inline-start-width", - end: "border-inline-end-width", - allowDifferent: true - }, - "border-block-width": { - start: "border-block-start-width", - end: "border-block-end-width", - allowDifferent: true - }, - "border-inline-style": { - start: "border-inline-start-style", - end: "border-inline-end-style", - allowDifferent: true - }, - "border-block-style": { - start: "border-block-start-style", - end: "border-block-end-style", - allowDifferent: true - }, - "border-inline-color": { - start: "border-inline-start-color", - end: "border-inline-end-color", - allowDifferent: true - }, - "border-block-color": { - start: "border-block-start-color", - end: "border-block-end-color", - allowDifferent: true - }, - "border-inline": { - start: "border-inline-start", - end: "border-inline-end", - allowDifferent: false - }, - "border-block": { - start: "border-block-start", - end: "border-block-end", - allowDifferent: false - }, - "overflow": { - start: "overflow-x", - end: "overflow-y", - allowDifferent: true - }, - "overscroll-behavior": { - start: "overscroll-behavior-x", - end: "overscroll-behavior-y", - allowDifferent: true - } -}); -const propertyToGroup = { - "margin-top": "margin", - "margin-right": "margin", - "margin-bottom": "margin", - "margin-left": "margin", - "margin-inline-start": "margin", - "margin-inline-end": "margin", - "margin-block-start": "margin", - "margin-block-end": "margin", - "padding-top": "padding", - "padding-right": "padding", - "padding-bottom": "padding", - "padding-left": "padding", - "padding-inline-start": "padding", - "padding-inline-end": "padding", - "padding-block-start": "padding", - "padding-block-end": "padding", - "top": "inset", - "right": "inset", - "bottom": "inset", - "left": "inset", - "inset-inline-start": "inset", - "inset-inline-end": "inset", - "inset-block-start": "inset", - "inset-block-end": "inset", - "border-top-width": "border-width", - "border-right-width": "border-width", - "border-bottom-width": "border-width", - "border-left-width": "border-width", - "border-inline-start-width": "border-width", - "border-inline-end-width": "border-width", - "border-block-start-width": "border-width", - "border-block-end-width": "border-width", - "border-top-style": "border-style", - "border-right-style": "border-style", - "border-bottom-style": "border-style", - "border-left-style": "border-style", - "border-inline-start-style": "border-style", - "border-inline-end-style": "border-style", - "border-block-start-style": "border-style", - "border-block-end-style": "border-style", - "border-top-color": "border-color", - "border-right-color": "border-color", - "border-bottom-color": "border-color", - "border-left-color": "border-color", - "border-inline-start-color": "border-color", - "border-inline-end-color": "border-color", - "border-block-start-color": "border-color", - "border-block-end-color": "border-color", - "width": "size", - "height": "size", - "inline-size": "size", - "block-size": "size", - "min-width": "min-size", - "min-height": "min-size", - "min-inline-size": "min-size", - "min-block-size": "min-size", - "max-width": "max-size", - "max-height": "max-size", - "max-inline-size": "max-size", - "max-block-size": "max-size", - "border-top-left-radius": "border-radius", - "border-top-right-radius": "border-radius", - "border-bottom-right-radius": "border-radius", - "border-bottom-left-radius": "border-radius", - "border-start-start-radius": "border-radius", - "border-start-end-radius": "border-radius", - "border-end-start-radius": "border-radius", - "border-end-end-radius": "border-radius", - "border-top": "border", - "border-right": "border", - "border-bottom": "border", - "border-left": "border", - "border-block-start": "border", - "border-block-end": "border", - "border-inline-start": "border", - "border-inline-end": "border", - "overflow-x": "overflow", - "overflow-y": "overflow", - "overflow-inline": "overflow", - "overflow-block": "overflow", - "overscroll-behavior-x": "overscroll-behavior", - "overscroll-behavior-y": "overscroll-behavior", - "overscroll-behavior-inline": "overscroll-behavior", - "overscroll-behavior-block": "overscroll-behavior", - "outline-color": "outline", - "outline-style": "outline", - "outline-width": "outline", - "list-style-type": "list-style", - "list-style-position": "list-style", - "list-style-image": "list-style", - "flex-grow": "flex", - "flex-shrink": "flex", - "flex-basis": "flex", - "font-style": "font", - "font-variant-caps": "font", - "font-weight": "font", - "font-stretch": "font", - "font-size": "font", - "line-height": "font", - "font-family": "font", - "font-variant-ligatures": "font-variant", - "font-variant-alternates": "font-variant", - "font-variant-numeric": "font-variant", - "font-variant-east-asian": "font-variant", - "font-variant-position": "font-variant", - "font-variant-emoji": "font-variant" -}; -const genericShorthands = { - "border-top": [ - "border-top-width", - "border-top-style", - "border-top-color" - ], - "border-right": [ - "border-right-width", - "border-right-style", - "border-right-color" - ], - "border-bottom": [ - "border-bottom-width", - "border-bottom-style", - "border-bottom-color" - ], - "border-left": [ - "border-left-width", - "border-left-style", - "border-left-color" - ], - "border-block-start": [ - "border-block-start-width", - "border-block-start-style", - "border-block-start-color" - ], - "border-block-end": [ - "border-block-end-width", - "border-block-end-style", - "border-block-end-color" - ], - "border-inline-start": [ - "border-inline-start-width", - "border-inline-start-style", - "border-inline-start-color" - ], - "border-inline-end": [ - "border-inline-end-width", - "border-inline-end-style", - "border-inline-end-color" - ], - "outline": [ - "outline-color", - "outline-style", - "outline-width" - ], - "list-style": [ - "list-style-type", - "list-style-position", - "list-style-image" - ], - "flex": [ - "flex-grow", - "flex-shrink", - "flex-basis" - ], - "border-image": [ - "border-image-source", - "border-image-slice", - "border-image-width", - "border-image-outset", - "border-image-repeat" - ], - "line-clamp": [ - "max-lines", - "block-ellipsis", - "continue" - ] -}; -const genericShorthandsEntries = Object.entries(genericShorthands); -function checkIntervening(decls, allDecls, declIndices) { - const indices = decls.map((d) => declIndices.get(d)); - const startIdx = Math.min(...indices); - const endIdx = Math.max(...indices); - const names = new Set(decls.map((d) => d.name)); - const groups = new Set(decls.map((d) => propertyToGroup[d.name]).filter(Boolean)); - const isSideShorthand = groups.size > 1; - for (let i = startIdx + 1; i < endIdx; i++) { - const intervening = allDecls[i]; - if (names.has(intervening.name)) continue; - const interveningGroup = propertyToGroup[intervening.name]; - if (!interveningGroup) continue; - if (isSideShorthand) { - const sidePrefix = decls[0].name.replace(/-(width|style|color)$/, ""); - if (intervening.name.startsWith(sidePrefix + "-") || ["all", "border"].includes(intervening.name)) return true; - } else { - if (groups.has(interveningGroup)) return true; - if (["all", "border"].includes(intervening.name)) return true; - } - } - return false; -} -const BOX_SHORTHAND_NAMES = /* @__PURE__ */ new Set("margin,padding,border-width,border-style,border-color,scroll-margin,scroll-padding,inset,overflow-clip-margin,border-radius".split(",")); -const BOX_SHORTHAND_ENTRIES = Object.entries(SHORTHANDS).filter(([shorthand, def]) => BOX_SHORTHAND_NAMES.has(shorthand) && def.logicalLonghands?.length === 4); -function tryCombineBoxShorthand(d, declMap, processed, declarations, declIndices) { - for (const [shorthand, def] of BOX_SHORTHAND_ENTRIES) { - const physical = def.longhands; - const logical = def.logicalLonghands; - const isPhysical = physical.includes(d.name); - const isLogical = logical.includes(d.name); - if (!isPhysical && !isLogical) continue; - const allDecls = (isPhysical ? physical : logical).map((name) => declMap.get(name)); - if (!allDecls.every((other) => other && !processed.has(other) && other.important === d.important)) continue; - if (checkIntervening(allDecls, declarations, declIndices)) continue; - const valuesForContract = {}; - for (const other of allDecls) valuesForContract[other.name] = other.value; - const value = def.contract(valuesForContract); - if (value !== null) { - for (const other of allDecls) processed.add(other); - return `${shorthand}: ${value}${d.important ? " !important" : ""}`; - } - } - return null; -} -function tryCombineLogicalShorthand(d, declMap, processed, declarations, declIndices) { - for (const [shorthand, longhands] of logicalShorthandsEntries) { - if (d.name !== longhands.start && d.name !== longhands.end) continue; - const otherName = d.name === longhands.start ? longhands.end : longhands.start; - const otherDecl = declMap.get(otherName); - if (!otherDecl || processed.has(otherDecl) || d.important !== otherDecl.important) continue; - if (checkIntervening([d, otherDecl], declarations, declIndices)) continue; - const startDecl = d.name === longhands.start ? d : otherDecl; - const endDecl = d.name === longhands.end ? d : otherDecl; - const valS = serialize(startDecl.value).trim(); - const valE = serialize(endDecl.value).trim(); - if (valS === valE) { - processed.add(startDecl); - processed.add(endDecl); - return `${shorthand}: ${valS}${d.important ? " !important" : ""}`; - } - if (longhands.allowDifferent && !valS.includes("var(") && !valE.includes("var(")) { - processed.add(startDecl); - processed.add(endDecl); - return `${shorthand}: ${valS} ${valE}${d.important ? " !important" : ""}`; - } - } - return null; -} -function tryCombineGenericShorthand(d, declMap, processed, declarations, declIndices) { - for (const [shorthand, longhands] of genericShorthandsEntries) { - if (!longhands.includes(d.name)) continue; - const allDecls = longhands.map((name) => declMap.get(name)); - if (!allDecls.every((other) => other && !processed.has(other) && other.important === d.important)) continue; - if (checkIntervening(allDecls, declarations, declIndices)) continue; - const record = {}; - for (const other of allDecls) record[other.name] = other.value; - const contracted = SHORTHANDS[shorthand]?.contract(record); - if (contracted !== null && contracted !== void 0) { - for (const other of allDecls) processed.add(other); - return { - name: shorthand, - value: contracted, - important: d.important - }; - } - } - return null; -} -const BORDER_PHYSICAL_SIDE_LONGHANDS = [ - "border-top-width", - "border-top-style", - "border-top-color", - "border-right-width", - "border-right-style", - "border-right-color", - "border-bottom-width", - "border-bottom-style", - "border-bottom-color", - "border-left-width", - "border-left-style", - "border-left-color" -]; -const BORDER_IMAGE_LONGHAND_NAMES = [ - "border-image-source", - "border-image-slice", - "border-image-width", - "border-image-outset", - "border-image-repeat" -]; -function tryCombineBorderFull(d, declMap, processed, declarations, declIndices) { - if (!BORDER_PHYSICAL_SIDE_LONGHANDS.includes(d.name)) return null; - const allDecls = BORDER_PHYSICAL_SIDE_LONGHANDS.map((name) => declMap.get(name)); - if (!allDecls.every((other) => other && !processed.has(other) && other.important === d.important)) return null; - if (checkIntervening(allDecls, declarations, declIndices)) return null; - const imageDecls = BORDER_IMAGE_LONGHAND_NAMES.map((name) => declMap.get(name)).filter((img) => Boolean(img && !processed.has(img))); - if (imageDecls.length === 0) return null; - const imageRecord = { - "border-image-source": [{ - type: "ident", - value: "none" - }], - "border-image-slice": [{ - type: "percentage", - value: 100, - sign: null - }], - "border-image-width": [{ - type: "number", - value: 1, - sign: null, - numberType: "integer" - }], - "border-image-outset": [{ - type: "number", - value: 0, - sign: null, - numberType: "integer" - }], - "border-image-repeat": [{ - type: "ident", - value: "stretch" - }] - }; - for (const img of imageDecls) imageRecord[img.name] = img.value; - if (!isInitialBorderImage(imageRecord)) return null; - const record = { ...imageRecord }; - for (const other of allDecls) record[other.name] = other.value; - const contracted = SHORTHANDS["border"]?.contract(record); - if (contracted !== null && contracted !== void 0) { - for (const other of allDecls) processed.add(other); - for (const img of imageDecls) processed.add(img); - return `border: ${contracted}${d.important ? " !important" : ""}`; - } - return null; -} -const ORDERED_SHORTHAND_COMBINERS = [ - { - name: "font", - longhands: SHORTHANDS["font"].longhands - }, - { - name: "font-variant", - longhands: SHORTHANDS["font-variant"].longhands - }, - { - name: "background", - longhands: [ - "background-image", - "background-position", - "background-size", - "background-repeat", - "background-attachment", - "background-origin", - "background-clip", - "background-color" - ] - }, - { - name: "border-block", - longhands: [ - "border-block-start-width", - "border-block-start-style", - "border-block-start-color", - "border-block-end-width", - "border-block-end-style", - "border-block-end-color" - ] - }, - { - name: "border-inline", - longhands: [ - "border-inline-start-width", - "border-inline-start-style", - "border-inline-start-color", - "border-inline-end-width", - "border-inline-end-style", - "border-inline-end-color" - ] - } -]; -function tryCombineNamedShorthand(shorthandName, longhands, d, declMap, processed, declarations, declIndices) { - if (!longhands.includes(d.name)) return null; - const allDecls = longhands.map((name) => declMap.get(name)); - if (!allDecls.every((other) => other && !processed.has(other) && other.important === d.important)) return null; - if (checkIntervening(allDecls, declarations, declIndices)) return null; - const record = {}; - for (const other of allDecls) record[other.name] = other.value; - const contracted = SHORTHANDS[shorthandName]?.contract(record); - if (contracted !== null && contracted !== void 0) { - for (const other of allDecls) processed.add(other); - return `${shorthandName}: ${contracted}${d.important ? " !important" : ""}`; - } - return null; -} -const CSS_WIDE_AND_DEFAULT = /* @__PURE__ */ new Set("initial,inherit,unset,revert,revert-layer,default".split(",")); -function serializeFontFamilyItem(tokens) { - const nonWs = tokens.filter((t) => t.type !== "whitespace" && t.type !== "comment" && t.type !== "EOF"); - if (nonWs.length === 1 && nonWs[0].type === "string") { - let strVal = nonWs[0].value; - if (strVal.startsWith("'") && strVal.endsWith("'") || strVal.startsWith("\"") && strVal.endsWith("\"")) strVal = strVal.slice(1, -1); - const lower = strVal.toLowerCase(); - if (GENERIC_FONT_FAMILIES.has(lower) || CSS_WIDE_AND_DEFAULT.has(lower)) return `"${strVal}"`; - if (strVal !== strVal.trim() || /\s{2,}|\t|\n|\r/.test(strVal)) return `"${strVal}"`; - const words = strVal.split(" "); - return words.length > 0 && words.every((word) => word.length > 0 && !/^[0-9]|^--|^-[0-9]/.test(word) && /^[a-zA-Z_-][a-zA-Z0-9_-]*$/.test(word)) ? strVal : `"${strVal}"`; - } - if (nonWs.every((t) => t.type === "ident")) return nonWs.map((t) => serializeNode(t, false)).join(" "); - return tokens.map((t) => serializeNode(t, false)).join(""); -} -function serializeFontFamily(values) { - const groups = []; - let current = []; - for (const token of values) if (token.type === "comma") { - groups.push(current); - current = []; - } else current.push(token); - if (current.length > 0) groups.push(current); - return groups.map((g) => serializeFontFamilyItem(g).trim()).filter((s) => s.length > 0).join(", "); -} -function serializeDeclarations(declarations) { - if (declarations.length === 0) return ""; - const declMap = /* @__PURE__ */ new Map(); - const declIndices = /* @__PURE__ */ new Map(); - for (let i = 0; i < declarations.length; i++) { - const d = declarations[i]; - declMap.set(d.name, d); - declIndices.set(d, i); - } - if (declarations.length >= ALL_SHORTHAND_LONGHANDS.length) { - const firstDecl = declMap.get(ALL_SHORTHAND_LONGHANDS[0]); - if (firstDecl) { - const firstValLower = serialize(firstDecl.value).trim().toLowerCase(); - const firstImportant = firstDecl.important; - if (CSS_WIDE_AND_DEFAULT.has(firstValLower) || firstValLower.startsWith("var(")) { - if (ALL_SHORTHAND_LONGHANDS.every((lh) => { - const d = declMap.get(lh); - return d && serialize(d.value).trim().toLowerCase() === firstValLower && d.important === firstImportant; - })) { - const allLonghandsSet = new Set(ALL_SHORTHAND_LONGHANDS); - const allDecl = { - type: "declaration", - name: "all", - value: firstDecl.value, - important: firstImportant - }; - const newDecls = []; - let inserted = false; - for (const d of declarations) if (allLonghandsSet.has(d.name)) { - if (!inserted) { - newDecls.push(allDecl); - inserted = true; - } - } else newDecls.push(d); - return serializeDeclarations(newDecls); - } - } - } - } - const processed = /* @__PURE__ */ new Set(); - const result = []; - for (const d of declarations) { - if (processed.has(d)) continue; - let combined = tryCombineBorderFull(d, declMap, processed, declarations, declIndices); - if (!combined) for (const spec of ORDERED_SHORTHAND_COMBINERS) { - combined = tryCombineNamedShorthand(spec.name, spec.longhands, d, declMap, processed, declarations, declIndices); - if (combined) break; - } - if (!combined) combined = tryCombineBoxShorthand(d, declMap, processed, declarations, declIndices); - if (!combined) { - const generic = tryCombineGenericShorthand(d, declMap, processed, declarations, declIndices); - if (generic) { - const sides = [ - "border-top", - "border-right", - "border-bottom", - "border-left" - ]; - if (sides.includes(generic.name)) { - const sideResults = sides.map((side) => { - if (side === generic.name) return generic; - const existing = declMap.get(side); - if (existing && !processed.has(existing)) return { - name: side, - value: serialize(existing.value).trim(), - important: existing.important, - decl: existing - }; - const longhands = genericShorthands[side]; - if (!longhands) return null; - const sideLonghands = longhands.map((lh) => declMap.get(lh)); - if (sideLonghands.every((lh) => lh && !processed.has(lh) && lh.important === generic.important)) { - if (checkIntervening(sideLonghands, declarations, declIndices)) return null; - return { - name: side, - value: sideLonghands.map((lh) => serialize(lh.value).trim()).filter((v) => v !== "").join(" "), - important: generic.important, - longhands: sideLonghands - }; - } - return null; - }); - if (sideResults.every((r) => r !== null && r.value === generic.value && r.important === generic.important)) { - for (const r of sideResults) if (r && "longhands" in r) r.longhands.forEach((lh) => processed.add(lh)); - else if (r && "decl" in r) processed.add(r.decl); - combined = `border: ${generic.value}${generic.important ? " !important" : ""}`; - } else combined = `${generic.name}: ${generic.value}${generic.important ? " !important" : ""}`; - } else combined = `${generic.name}: ${generic.value}${generic.important ? " !important" : ""}`; - } - } - if (!combined) combined = tryCombineLogicalShorthand(d, declMap, processed, declarations, declIndices); - if (combined) result.push(combined); - else { - const isCustom = d.name.startsWith("--"); - let val; - if (d.name === "font-family") val = serializeFontFamily(d.value); - else if (d.name === "flex-basis" && serialize(d.value).trim() === "0") val = "0px"; - else val = d.raw && !d.raw.includes("var(") ? d.raw : serialize(d.value, isCustom, d.name).trim(); - result.push(`${serializeIdentifier(d.name)}: ${val}${d.important ? " !important" : ""}`); - processed.add(d); - } - } - return result.join("; ") + ";"; -} -function serializeSelectorList(list, nsContext) { - const context = { - hasDefaultNamespace: typeof nsContext === "boolean" ? nsContext : Boolean(nsContext?.hasDefaultNamespace), - defaultNamespacePrefixes: typeof nsContext === "object" && nsContext !== null ? nsContext.defaultNamespacePrefixes : void 0 - }; - return list.selectors.map((s) => s.type === "invalid-selector" ? serialize(s.tokens) : serializeComplexSelector(s, context)).join(", "); -} -function serializeComplexSelector(complex, nsContext) { - const { hasDefaultNamespace, defaultNamespacePrefixes } = nsContext; - return complex.items.map((item, idx) => { - if (item.type === "combinator") { - if (item.value === " ") return " "; - return idx === 0 ? `${item.value} ` : ` ${item.value} `; - } - return item.selectors.filter((s, sIdx) => { - if (s.type === "universal-selector" && item.selectors.length > 1 && sIdx === 0) { - const isDefaultNs = s.namespace !== void 0 && s.namespace !== "" && defaultNamespacePrefixes?.has(s.namespace); - if (s.namespace === void 0 || isDefaultNs || s.namespace === "*" && !hasDefaultNamespace) return false; - } - return true; - }).map((s) => serializeSimpleSelector(s, nsContext)).join(""); - }).join(""); -} -function formatAnPlusB(tokens) { - const parsed = parseAnPlusB(tokens); - if (parsed === null) return serialize(tokens).trim(); - const { a, b } = parsed; - if (a === 0) return b.toString(); - const partA = a === 1 ? "n" : a === -1 ? "-n" : `${a}n`; - if (b === 0) return partA; - return b > 0 ? `${partA}+${b}` : `${partA}${b}`; -} -function formatSelectorNamespacePrefix(namespace, nsContext, isElementSelector) { - if (namespace === void 0) return ""; - if (isElementSelector && namespace !== "" && nsContext.defaultNamespacePrefixes?.has(namespace)) return ""; - if (namespace === "*") return !isElementSelector || nsContext.hasDefaultNamespace ? "*|" : ""; - if (namespace === "") return isElementSelector ? "|" : ""; - return `${serializeIdentifier(namespace)}|`; -} -const NTH_PSEUDO_CLASSES = /* @__PURE__ */ new Set("nth-child,nth-last-child,nth-of-type,nth-last-of-type".split(",")); -function serializeSimpleSelector(simple, nsContext) { - switch (simple.type) { - case "type-selector": return formatSelectorNamespacePrefix(simple.namespace, nsContext, true) + serializeIdentifier(simple.name); - case "universal-selector": return formatSelectorNamespacePrefix(simple.namespace, nsContext, true) + "*"; - case "id-selector": return "#" + serializeIdentifier(simple.name); - case "class-selector": return "." + serializeIdentifier(simple.name); - case "attribute-selector": { - let attr = "[" + formatSelectorNamespacePrefix(simple.namespace, nsContext, false) + serializeIdentifier(simple.name); - if (simple.operator) attr += simple.operator + serializeString(simple.value || ""); - if (simple.flags) attr += " " + simple.flags; - return attr + "]"; - } - case "pseudo-class-selector": { - let pc = `:${simple.name}`; - const isNth = NTH_PSEUDO_CLASSES.has(simple.name.toLowerCase()); - if (simple.argument) { - if ("type" in simple.argument && simple.argument.type === "selector-list") { - const selStr = serializeSelectorList(simple.argument, nsContext); - pc += isNth && simple.nth ? `(${formatAnPlusB(simple.nth)} of ${selStr})` : `(${selStr})`; - } else { - const tokens = simple.argument; - pc += `(${isNth ? formatAnPlusB(tokens) : serialize(tokens).trim()})`; - } - } - return pc; - } - case "pseudo-element-selector": { - let pe = `::${simple.name}`; - if (simple.argument) pe += "type" in simple.argument && simple.argument.type === "selector-list" ? `(${serializeSelectorList(simple.argument, nsContext)})` : `(${serialize(simple.argument).trim()})`; - return pe; - } - case "nesting-selector": return "&"; - default: return ""; - } -} -// src/AbstractTokenizer.ts -/** -* @license -* Copyright 2026 Google LLC -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* https://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ -var AbstractTokenizer = class { - unicodeRangesAllowed = false; - errors = []; - parseError(message) { - this.errors.push({ message }); - console.warn(`CSS Parse Error: ${message}`); - } - consumeToken() { - while (true) { - this.consumeComments(); - const cp = this.consume(); - if (cp === -1) return { - type: "EOF", - value: "" - }; - if (this.isWhitespace(cp)) { - while (this.isWhitespace(this.cp)) this.consume(); - return { - type: "whitespace", - value: " " - }; - } - switch (cp) { - case 34: return this.consumeStringToken(34); - case 35: - if (this.isIdentCodePoint(this.cp) || this.isValidEscape(this.cp, this.peek(1))) { - const token = { - type: "hash", - value: "", - hashType: "unrestricted" - }; - if (this.wouldStartIdentSequence(this.cp, this.peek(1), this.peek(2))) token.hashType = "id"; - token.value = this.consumeIdentSequence(); - return token; - } - return { - type: "delim", - value: String.fromCodePoint(cp) - }; - case 39: return this.consumeStringToken(39); - case 40: return { - type: "(", - value: "(" - }; - case 41: return { - type: ")", - value: ")" - }; - case 43: - if (this.wouldStartNumber(cp, this.cp, this.peek(1))) { - this.reconsume(); - return this.consumeNumericToken(); - } - return { - type: "delim", - value: "+" - }; - case 44: return { - type: "comma", - value: "," - }; - case 45: - if (this.wouldStartNumber(cp, this.cp, this.peek(1))) { - this.reconsume(); - return this.consumeNumericToken(); - } - if (this.cp === 45 && this.peek(1) === 62) { - this.consume(); - this.consume(); - return { - type: "CDC", - value: "-->" - }; - } - if (this.wouldStartIdentSequence(cp, this.cp, this.peek(1))) { - this.reconsume(); - return this.consumeIdentLikeToken(); - } - return { - type: "delim", - value: "-" - }; - case 46: - if (this.wouldStartNumber(cp, this.cp, this.peek(1))) { - this.reconsume(); - return this.consumeNumericToken(); - } - return { - type: "delim", - value: "." - }; - case 47: return { - type: "delim", - value: "/" - }; - case 58: return { - type: "colon", - value: ":" - }; - case 59: return { - type: "semicolon", - value: ";" - }; - case 60: - if (this.cp === 33 && this.peek(1) === 45 && this.peek(2) === 45) { - this.consume(); - this.consume(); - this.consume(); - return { - type: "CDO", - value: ""; + case "colon": return ":"; + case "semicolon": return ";"; + case "comma": return ","; + case "[": + case "]": + case "{": + case "}": + case "(": + case ")": return token.value; + case "function": return serializeIdentifier(preserveCase ? token.value : token.value.toLowerCase()) + "("; + case "unicode-range": return token.value; + case "EOF": return ""; + default: return token.value || ""; + } +} +function getMirrorToken(start) { + if (start === "{") return "}"; + if (start === "[") return "]"; + if (start === "(") return ")"; + return ""; +} +function getOriginalText(values) { + let text = ""; + for (const val of values) if (val.type === "simple-block") text += (val.associatedToken.originalText || "") + getOriginalText(val.value) + getMirrorToken(val.associatedToken.value); + else if (val.type === "function") { + const func = val; + text += `${func.name}(${getOriginalText(func.value)})`; + } else text += val.originalText || val.value; + return text; +} +/** +* // cssom-1 #serialize-an-identifier +*/ +function serializeIdentifier(id) { + let result = ""; + for (let i = 0; i < id.length; i++) { + const charCode = id.charCodeAt(i); + const char = id[i]; + if (charCode === 0) { + result += "�"; + continue; + } + if (charCode >= 1 && charCode <= 31 || charCode === 127) { + result += escapeAsCodePoint(charCode); + continue; + } + if (i === 0 && charCode >= 48 && charCode <= 57) { + result += escapeAsCodePoint(charCode); + continue; + } + if (i === 1 && charCode >= 48 && charCode <= 57 && id.charCodeAt(0) === 45) { + result += escapeAsCodePoint(charCode); + continue; + } + if (i === 0 && charCode === 45 && id.length === 1) { + result += "\\-"; + continue; + } + if (charCode >= 128 || charCode === 45 || charCode === 95 || charCode >= 48 && charCode <= 57 || charCode >= 65 && charCode <= 90 || charCode >= 97 && charCode <= 122) { + result += char; + continue; + } + result += "\\" + char; + } + return result; +} +/** +* // cssom-1 #serialize-a-string +*/ +function serializeString(s) { + let result = "\""; + for (let i = 0; i < s.length; i++) { + const charCode = s.charCodeAt(i); + const char = s[i]; + if (charCode === 0) { + result += "�"; + continue; + } + if (charCode >= 1 && charCode <= 31 || charCode === 127) { + result += escapeAsCodePoint(charCode); + continue; + } + if (charCode === 34 || charCode === 92) { + result += "\\" + char; + continue; + } + result += char; + } + result += "\""; + return result; +} +function serializeUrl(val) { + return `url(${serializeString(val)})`; +} +function serializeUrlToken(val, originalText) { + if (originalText && !val.includes("�") && originalText.endsWith(")")) return originalText; + let result = ""; + for (let i = 0; i < val.length; i++) { + const charCode = val.charCodeAt(i); + const char = val[i]; + if (charCode === 34 || charCode === 39 || charCode === 40 || charCode === 41 || charCode === 92 || charCode <= 32 || charCode === 127) result += "\\" + char; + else result += char; + } + return `url(${result})`; +} +function escapeAsCodePoint(charCode) { + return "\\" + charCode.toString(16) + " "; +} +const logicalShorthandsEntries = Object.entries({ + "margin-inline": { + start: "margin-inline-start", + end: "margin-inline-end", + allowDifferent: true + }, + "padding-inline": { + start: "padding-inline-start", + end: "padding-inline-end", + allowDifferent: true + }, + "margin-block": { + start: "margin-block-start", + end: "margin-block-end", + allowDifferent: true + }, + "padding-block": { + start: "padding-block-start", + end: "padding-block-end", + allowDifferent: true + }, + "inset-inline": { + start: "inset-inline-start", + end: "inset-inline-end", + allowDifferent: true + }, + "inset-block": { + start: "inset-block-start", + end: "inset-block-end", + allowDifferent: true + }, + "border-inline-width": { + start: "border-inline-start-width", + end: "border-inline-end-width", + allowDifferent: true + }, + "border-block-width": { + start: "border-block-start-width", + end: "border-block-end-width", + allowDifferent: true + }, + "border-inline-style": { + start: "border-inline-start-style", + end: "border-inline-end-style", + allowDifferent: true + }, + "border-block-style": { + start: "border-block-start-style", + end: "border-block-end-style", + allowDifferent: true + }, + "border-inline-color": { + start: "border-inline-start-color", + end: "border-inline-end-color", + allowDifferent: true + }, + "border-block-color": { + start: "border-block-start-color", + end: "border-block-end-color", + allowDifferent: true + }, + "border-inline": { + start: "border-inline-start", + end: "border-inline-end", + allowDifferent: false + }, + "border-block": { + start: "border-block-start", + end: "border-block-end", + allowDifferent: false + }, + "overflow": { + start: "overflow-x", + end: "overflow-y", + allowDifferent: true + }, + "overscroll-behavior": { + start: "overscroll-behavior-x", + end: "overscroll-behavior-y", + allowDifferent: true + } +}); +const propertyToGroup = { + "margin-top": "margin", + "margin-right": "margin", + "margin-bottom": "margin", + "margin-left": "margin", + "margin-inline-start": "margin", + "margin-inline-end": "margin", + "margin-block-start": "margin", + "margin-block-end": "margin", + "padding-top": "padding", + "padding-right": "padding", + "padding-bottom": "padding", + "padding-left": "padding", + "padding-inline-start": "padding", + "padding-inline-end": "padding", + "padding-block-start": "padding", + "padding-block-end": "padding", + "top": "inset", + "right": "inset", + "bottom": "inset", + "left": "inset", + "inset-inline-start": "inset", + "inset-inline-end": "inset", + "inset-block-start": "inset", + "inset-block-end": "inset", + "border-top-width": "border-width", + "border-right-width": "border-width", + "border-bottom-width": "border-width", + "border-left-width": "border-width", + "border-inline-start-width": "border-width", + "border-inline-end-width": "border-width", + "border-block-start-width": "border-width", + "border-block-end-width": "border-width", + "border-top-style": "border-style", + "border-right-style": "border-style", + "border-bottom-style": "border-style", + "border-left-style": "border-style", + "border-inline-start-style": "border-style", + "border-inline-end-style": "border-style", + "border-block-start-style": "border-style", + "border-block-end-style": "border-style", + "border-top-color": "border-color", + "border-right-color": "border-color", + "border-bottom-color": "border-color", + "border-left-color": "border-color", + "border-inline-start-color": "border-color", + "border-inline-end-color": "border-color", + "border-block-start-color": "border-color", + "border-block-end-color": "border-color", + "width": "size", + "height": "size", + "inline-size": "size", + "block-size": "size", + "min-width": "min-size", + "min-height": "min-size", + "min-inline-size": "min-size", + "min-block-size": "min-size", + "max-width": "max-size", + "max-height": "max-size", + "max-inline-size": "max-size", + "max-block-size": "max-size", + "border-top-left-radius": "border-radius", + "border-top-right-radius": "border-radius", + "border-bottom-right-radius": "border-radius", + "border-bottom-left-radius": "border-radius", + "border-start-start-radius": "border-radius", + "border-start-end-radius": "border-radius", + "border-end-start-radius": "border-radius", + "border-end-end-radius": "border-radius", + "border-top": "border", + "border-right": "border", + "border-bottom": "border", + "border-left": "border", + "border-block-start": "border", + "border-block-end": "border", + "border-inline-start": "border", + "border-inline-end": "border", + "overflow-x": "overflow", + "overflow-y": "overflow", + "overflow-inline": "overflow", + "overflow-block": "overflow", + "overscroll-behavior-x": "overscroll-behavior", + "overscroll-behavior-y": "overscroll-behavior", + "overscroll-behavior-inline": "overscroll-behavior", + "overscroll-behavior-block": "overscroll-behavior", + "outline-color": "outline", + "outline-style": "outline", + "outline-width": "outline", + "list-style-type": "list-style", + "list-style-position": "list-style", + "list-style-image": "list-style", + "flex-grow": "flex", + "flex-shrink": "flex", + "flex-basis": "flex", + "font-style": "font", + "font-variant-caps": "font", + "font-weight": "font", + "font-stretch": "font", + "font-size": "font", + "line-height": "font", + "font-family": "font", + "font-variant-ligatures": "font-variant", + "font-variant-alternates": "font-variant", + "font-variant-numeric": "font-variant", + "font-variant-east-asian": "font-variant", + "font-variant-position": "font-variant", + "font-variant-emoji": "font-variant" +}; +const genericShorthands = { + "border-top": [ + "border-top-width", + "border-top-style", + "border-top-color" + ], + "border-right": [ + "border-right-width", + "border-right-style", + "border-right-color" + ], + "border-bottom": [ + "border-bottom-width", + "border-bottom-style", + "border-bottom-color" + ], + "border-left": [ + "border-left-width", + "border-left-style", + "border-left-color" + ], + "border-block-start": [ + "border-block-start-width", + "border-block-start-style", + "border-block-start-color" + ], + "border-block-end": [ + "border-block-end-width", + "border-block-end-style", + "border-block-end-color" + ], + "border-inline-start": [ + "border-inline-start-width", + "border-inline-start-style", + "border-inline-start-color" + ], + "border-inline-end": [ + "border-inline-end-width", + "border-inline-end-style", + "border-inline-end-color" + ], + "outline": [ + "outline-color", + "outline-style", + "outline-width" + ], + "list-style": [ + "list-style-type", + "list-style-position", + "list-style-image" + ], + "flex": [ + "flex-grow", + "flex-shrink", + "flex-basis" + ], + "border-image": [ + "border-image-source", + "border-image-slice", + "border-image-width", + "border-image-outset", + "border-image-repeat" + ], + "line-clamp": [ + "max-lines", + "block-ellipsis", + "continue" + ] +}; +const genericShorthandsEntries = Object.entries(genericShorthands); +function checkIntervening(decls, allDecls, declIndices) { + const indices = decls.map((d) => declIndices.get(d)); + const startIdx = Math.min(...indices); + const endIdx = Math.max(...indices); + const names = new Set(decls.map((d) => d.name)); + const groups = new Set(decls.map((d) => propertyToGroup[d.name]).filter(Boolean)); + const isSideShorthand = groups.size > 1; + for (let i = startIdx + 1; i < endIdx; i++) { + const intervening = allDecls[i]; + if (names.has(intervening.name)) continue; + const interveningGroup = propertyToGroup[intervening.name]; + if (!interveningGroup) continue; + if (isSideShorthand) { + const sidePrefix = decls[0].name.replace(/-(width|style|color)$/, ""); + if (intervening.name.startsWith(sidePrefix + "-") || ["all", "border"].includes(intervening.name)) return true; + } else { + if (groups.has(interveningGroup)) return true; + if (["all", "border"].includes(intervening.name)) return true; + } + } + return false; +} +const BOX_SHORTHAND_NAMES = /* @__PURE__ */ new Set("margin,padding,border-width,border-style,border-color,scroll-margin,scroll-padding,inset,overflow-clip-margin,border-radius".split(",")); +const BOX_SHORTHAND_ENTRIES = Object.entries(SHORTHANDS).filter(([shorthand, def]) => BOX_SHORTHAND_NAMES.has(shorthand) && def.logicalLonghands?.length === 4); +function tryCombineBoxShorthand(d, declMap, processed, declarations, declIndices) { + for (const [shorthand, def] of BOX_SHORTHAND_ENTRIES) { + const physical = def.longhands; + const logical = def.logicalLonghands; + const isPhysical = physical.includes(d.name); + const isLogical = logical.includes(d.name); + if (!isPhysical && !isLogical) continue; + const allDecls = (isPhysical ? physical : logical).map((name) => declMap.get(name)); + if (!allDecls.every((other) => other && !processed.has(other) && other.important === d.important)) continue; + if (checkIntervening(allDecls, declarations, declIndices)) continue; + const valuesForContract = {}; + for (const other of allDecls) valuesForContract[other.name] = other.value; + const value = def.contract(valuesForContract); + if (value !== null) { + for (const other of allDecls) processed.add(other); + return `${shorthand}: ${value}${d.important ? " !important" : ""}`; + } + } + return null; +} +function tryCombineLogicalShorthand(d, declMap, processed, declarations, declIndices) { + for (const [shorthand, longhands] of logicalShorthandsEntries) { + if (d.name !== longhands.start && d.name !== longhands.end) continue; + const otherName = d.name === longhands.start ? longhands.end : longhands.start; + const otherDecl = declMap.get(otherName); + if (!otherDecl || processed.has(otherDecl) || d.important !== otherDecl.important) continue; + if (checkIntervening([d, otherDecl], declarations, declIndices)) continue; + const startDecl = d.name === longhands.start ? d : otherDecl; + const endDecl = d.name === longhands.end ? d : otherDecl; + const valS = serialize(startDecl.value).trim(); + const valE = serialize(endDecl.value).trim(); + if (valS === valE) { + processed.add(startDecl); + processed.add(endDecl); + return `${shorthand}: ${valS}${d.important ? " !important" : ""}`; + } + if (longhands.allowDifferent && !valS.includes("var(") && !valE.includes("var(")) { + processed.add(startDecl); + processed.add(endDecl); + return `${shorthand}: ${valS} ${valE}${d.important ? " !important" : ""}`; + } + } + return null; +} +function tryCombineGenericShorthand(d, declMap, processed, declarations, declIndices) { + for (const [shorthand, longhands] of genericShorthandsEntries) { + if (!longhands.includes(d.name)) continue; + const allDecls = longhands.map((name) => declMap.get(name)); + if (!allDecls.every((other) => other && !processed.has(other) && other.important === d.important)) continue; + if (checkIntervening(allDecls, declarations, declIndices)) continue; + const record = {}; + for (const other of allDecls) record[other.name] = other.value; + const contracted = SHORTHANDS[shorthand]?.contract(record); + if (contracted !== null && contracted !== void 0) { + for (const other of allDecls) processed.add(other); + return { + name: shorthand, + value: contracted, + important: d.important + }; + } + } + return null; +} +const BORDER_PHYSICAL_SIDE_LONGHANDS = [ + "border-top-width", + "border-top-style", + "border-top-color", + "border-right-width", + "border-right-style", + "border-right-color", + "border-bottom-width", + "border-bottom-style", + "border-bottom-color", + "border-left-width", + "border-left-style", + "border-left-color" +]; +const BORDER_IMAGE_LONGHAND_NAMES = [ + "border-image-source", + "border-image-slice", + "border-image-width", + "border-image-outset", + "border-image-repeat" +]; +function tryCombineBorderFull(d, declMap, processed, declarations, declIndices) { + if (!BORDER_PHYSICAL_SIDE_LONGHANDS.includes(d.name)) return null; + const allDecls = BORDER_PHYSICAL_SIDE_LONGHANDS.map((name) => declMap.get(name)); + if (!allDecls.every((other) => other && !processed.has(other) && other.important === d.important)) return null; + if (checkIntervening(allDecls, declarations, declIndices)) return null; + const imageDecls = BORDER_IMAGE_LONGHAND_NAMES.map((name) => declMap.get(name)).filter((img) => Boolean(img && !processed.has(img))); + if (imageDecls.length === 0) return null; + const imageRecord = { + "border-image-source": [{ + type: "ident", + value: "none" + }], + "border-image-slice": [{ + type: "percentage", + value: 100, + sign: null + }], + "border-image-width": [{ + type: "number", + value: 1, + sign: null, + numberType: "integer" + }], + "border-image-outset": [{ + type: "number", + value: 0, + sign: null, + numberType: "integer" + }], + "border-image-repeat": [{ + type: "ident", + value: "stretch" + }] + }; + for (const img of imageDecls) imageRecord[img.name] = img.value; + if (!isInitialBorderImage(imageRecord)) return null; + const record = { ...imageRecord }; + for (const other of allDecls) record[other.name] = other.value; + const contracted = SHORTHANDS["border"]?.contract(record); + if (contracted !== null && contracted !== void 0) { + for (const other of allDecls) processed.add(other); + for (const img of imageDecls) processed.add(img); + return `border: ${contracted}${d.important ? " !important" : ""}`; + } + return null; +} +const ORDERED_SHORTHAND_COMBINERS = [ + { + name: "font", + longhands: SHORTHANDS["font"].longhands + }, + { + name: "font-variant", + longhands: SHORTHANDS["font-variant"].longhands + }, + { + name: "background", + longhands: [ + "background-image", + "background-position", + "background-size", + "background-repeat", + "background-attachment", + "background-origin", + "background-clip", + "background-color" + ] + }, + { + name: "border-block", + longhands: [ + "border-block-start-width", + "border-block-start-style", + "border-block-start-color", + "border-block-end-width", + "border-block-end-style", + "border-block-end-color" + ] + }, + { + name: "border-inline", + longhands: [ + "border-inline-start-width", + "border-inline-start-style", + "border-inline-start-color", + "border-inline-end-width", + "border-inline-end-style", + "border-inline-end-color" + ] + } +]; +function tryCombineNamedShorthand(shorthandName, longhands, d, declMap, processed, declarations, declIndices) { + if (!longhands.includes(d.name)) return null; + const allDecls = longhands.map((name) => declMap.get(name)); + if (!allDecls.every((other) => other && !processed.has(other) && other.important === d.important)) return null; + if (checkIntervening(allDecls, declarations, declIndices)) return null; + const record = {}; + for (const other of allDecls) record[other.name] = other.value; + const contracted = SHORTHANDS[shorthandName]?.contract(record); + if (contracted !== null && contracted !== void 0) { + for (const other of allDecls) processed.add(other); + return `${shorthandName}: ${contracted}${d.important ? " !important" : ""}`; + } + return null; +} +const CSS_WIDE_AND_DEFAULT = /* @__PURE__ */ new Set("initial,inherit,unset,revert,revert-layer,default".split(",")); +function serializeFontFamilyItem(tokens) { + const nonWs = tokens.filter((t) => t.type !== "whitespace" && t.type !== "comment" && t.type !== "EOF"); + if (nonWs.length === 1 && nonWs[0].type === "string") { + let strVal = nonWs[0].value; + if (strVal.startsWith("'") && strVal.endsWith("'") || strVal.startsWith("\"") && strVal.endsWith("\"")) strVal = strVal.slice(1, -1); + const lower = strVal.toLowerCase(); + if (GENERIC_FONT_FAMILIES.has(lower) || CSS_WIDE_AND_DEFAULT.has(lower)) return `"${strVal}"`; + if (strVal !== strVal.trim() || /\s{2,}|\t|\n|\r/.test(strVal)) return `"${strVal}"`; + const words = strVal.split(" "); + return words.length > 0 && words.every((word) => word.length > 0 && !/^[0-9]|^--|^-[0-9]/.test(word) && /^[a-zA-Z_-][a-zA-Z0-9_-]*$/.test(word)) ? strVal : `"${strVal}"`; + } + if (nonWs.every((t) => t.type === "ident")) return nonWs.map((t) => serializeNode(t, false)).join(" "); + return tokens.map((t) => serializeNode(t, false)).join(""); +} +function serializeFontFamily(values) { + const groups = []; + let current = []; + for (const token of values) if (token.type === "comma") { + groups.push(current); + current = []; + } else current.push(token); + if (current.length > 0) groups.push(current); + return groups.map((g) => serializeFontFamilyItem(g).trim()).filter((s) => s.length > 0).join(", "); +} +function serializeDeclarations(declarations) { + if (declarations.length === 0) return ""; + const declMap = /* @__PURE__ */ new Map(); + const declIndices = /* @__PURE__ */ new Map(); + for (let i = 0; i < declarations.length; i++) { + const d = declarations[i]; + declMap.set(d.name, d); + declIndices.set(d, i); + } + if (declarations.length >= ALL_SHORTHAND_LONGHANDS.length) { + const firstDecl = declMap.get(ALL_SHORTHAND_LONGHANDS[0]); + if (firstDecl) { + const firstValLower = serialize(firstDecl.value).trim().toLowerCase(); + const firstImportant = firstDecl.important; + if (CSS_WIDE_AND_DEFAULT.has(firstValLower) || firstValLower.startsWith("var(")) { + if (ALL_SHORTHAND_LONGHANDS.every((lh) => { + const d = declMap.get(lh); + return d && serialize(d.value).trim().toLowerCase() === firstValLower && d.important === firstImportant; + })) { + const allLonghandsSet = new Set(ALL_SHORTHAND_LONGHANDS); + const allDecl = { + type: "declaration", + name: "all", + value: firstDecl.value, + important: firstImportant + }; + const newDecls = []; + let inserted = false; + for (const d of declarations) if (allLonghandsSet.has(d.name)) { + if (!inserted) { + newDecls.push(allDecl); + inserted = true; + } + } else newDecls.push(d); + return serializeDeclarations(newDecls); + } + } + } + } + const processed = /* @__PURE__ */ new Set(); + const result = []; + for (const d of declarations) { + if (processed.has(d)) continue; + let combined = tryCombineBorderFull(d, declMap, processed, declarations, declIndices); + if (!combined) for (const spec of ORDERED_SHORTHAND_COMBINERS) { + combined = tryCombineNamedShorthand(spec.name, spec.longhands, d, declMap, processed, declarations, declIndices); + if (combined) break; + } + if (!combined) combined = tryCombineBoxShorthand(d, declMap, processed, declarations, declIndices); + if (!combined) { + const generic = tryCombineGenericShorthand(d, declMap, processed, declarations, declIndices); + if (generic) { + const sides = [ + "border-top", + "border-right", + "border-bottom", + "border-left" + ]; + if (sides.includes(generic.name)) { + const sideResults = sides.map((side) => { + if (side === generic.name) return generic; + const existing = declMap.get(side); + if (existing && !processed.has(existing)) return { + name: side, + value: serialize(existing.value).trim(), + important: existing.important, + decl: existing + }; + const longhands = genericShorthands[side]; + if (!longhands) return null; + const sideLonghands = longhands.map((lh) => declMap.get(lh)); + if (sideLonghands.every((lh) => lh && !processed.has(lh) && lh.important === generic.important)) { + if (checkIntervening(sideLonghands, declarations, declIndices)) return null; + return { + name: side, + value: sideLonghands.map((lh) => serialize(lh.value).trim()).filter((v) => v !== "").join(" "), + important: generic.important, + longhands: sideLonghands + }; + } + return null; + }); + if (sideResults.every((r) => r !== null && r.value === generic.value && r.important === generic.important)) { + for (const r of sideResults) if (r && "longhands" in r) r.longhands.forEach((lh) => processed.add(lh)); + else if (r && "decl" in r) processed.add(r.decl); + combined = `border: ${generic.value}${generic.important ? " !important" : ""}`; + } else combined = `${generic.name}: ${generic.value}${generic.important ? " !important" : ""}`; + } else combined = `${generic.name}: ${generic.value}${generic.important ? " !important" : ""}`; + } + } + if (!combined) combined = tryCombineLogicalShorthand(d, declMap, processed, declarations, declIndices); + if (combined) result.push(combined); + else { + const isCustom = d.name.startsWith("--"); + let val; + if (d.name === "font-family") val = serializeFontFamily(d.value); + else if (d.name === "flex-basis" && serialize(d.value).trim() === "0") val = "0px"; + else val = d.raw && !d.raw.includes("var(") ? d.raw : serialize(d.value, isCustom, d.name).trim(); + result.push(`${serializeIdentifier(d.name)}: ${val}${d.important ? " !important" : ""}`); + processed.add(d); + } + } + return result.join("; ") + ";"; +} +function serializeSelectorList(list, nsContext) { + const context = { + hasDefaultNamespace: typeof nsContext === "boolean" ? nsContext : Boolean(nsContext?.hasDefaultNamespace), + defaultNamespacePrefixes: typeof nsContext === "object" && nsContext !== null ? nsContext.defaultNamespacePrefixes : void 0 + }; + return list.selectors.map((s) => s.type === "invalid-selector" ? serialize(s.tokens) : serializeComplexSelector(s, context)).join(", "); +} +function serializeComplexSelector(complex, nsContext) { + const { hasDefaultNamespace, defaultNamespacePrefixes } = nsContext; + return complex.items.map((item, idx) => { + if (item.type === "combinator") { + if (item.value === " ") return " "; + return idx === 0 ? `${item.value} ` : ` ${item.value} `; + } + return item.selectors.filter((s, sIdx) => { + if (s.type === "universal-selector" && item.selectors.length > 1 && sIdx === 0) { + const isDefaultNs = s.namespace !== void 0 && s.namespace !== "" && defaultNamespacePrefixes?.has(s.namespace); + if (s.namespace === void 0 || isDefaultNs || s.namespace === "*" && !hasDefaultNamespace) return false; + } + return true; + }).map((s) => serializeSimpleSelector(s, nsContext)).join(""); + }).join(""); +} +function formatAnPlusB(tokens) { + const parsed = parseAnPlusB(tokens); + if (parsed === null) return serialize(tokens).trim(); + const { a, b } = parsed; + if (a === 0) return b.toString(); + const partA = a === 1 ? "n" : a === -1 ? "-n" : `${a}n`; + if (b === 0) return partA; + return b > 0 ? `${partA}+${b}` : `${partA}${b}`; +} +function formatSelectorNamespacePrefix(namespace, nsContext, isElementSelector) { + if (namespace === void 0) return ""; + if (isElementSelector && namespace !== "" && nsContext.defaultNamespacePrefixes?.has(namespace)) return ""; + if (namespace === "*") return !isElementSelector || nsContext.hasDefaultNamespace ? "*|" : ""; + if (namespace === "") return isElementSelector ? "|" : ""; + return `${serializeIdentifier(namespace)}|`; +} +const NTH_PSEUDO_CLASSES = /* @__PURE__ */ new Set("nth-child,nth-last-child,nth-of-type,nth-last-of-type".split(",")); +function serializeSimpleSelector(simple, nsContext) { + switch (simple.type) { + case "type-selector": return formatSelectorNamespacePrefix(simple.namespace, nsContext, true) + serializeIdentifier(simple.name); + case "universal-selector": return formatSelectorNamespacePrefix(simple.namespace, nsContext, true) + "*"; + case "id-selector": return "#" + serializeIdentifier(simple.name); + case "class-selector": return "." + serializeIdentifier(simple.name); + case "attribute-selector": { + let attr = "[" + formatSelectorNamespacePrefix(simple.namespace, nsContext, false) + serializeIdentifier(simple.name); + if (simple.operator) attr += simple.operator + serializeString(simple.value || ""); + if (simple.flags) attr += " " + simple.flags; + return attr + "]"; + } + case "pseudo-class-selector": { + let pc = `:${simple.name}`; + const isNth = NTH_PSEUDO_CLASSES.has(simple.name.toLowerCase()); + if (simple.argument) { + if ("type" in simple.argument && simple.argument.type === "selector-list") { + const selStr = serializeSelectorList(simple.argument, nsContext); + pc += isNth && simple.nth ? `(${formatAnPlusB(simple.nth)} of ${selStr})` : `(${selStr})`; + } else { + const tokens = simple.argument; + pc += `(${isNth ? formatAnPlusB(tokens) : serialize(tokens).trim()})`; + } + } + return pc; + } + case "pseudo-element-selector": { + let pe = `::${simple.name}`; + if (simple.argument) pe += "type" in simple.argument && simple.argument.type === "selector-list" ? `(${serializeSelectorList(simple.argument, nsContext)})` : `(${serialize(simple.argument).trim()})`; + return pe; + } + case "nesting-selector": return "&"; + default: return ""; + } +} +// src/AbstractTokenizer.ts +/** +* @license +* Copyright 2026 Google LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +var AbstractTokenizer = class { + unicodeRangesAllowed = false; + errors = []; + parseError(message) { + this.errors.push({ message }); + console.warn(`CSS Parse Error: ${message}`); + } + consumeToken() { + while (true) { + this.consumeComments(); + const cp = this.consume(); + if (cp === -1) return { + type: "EOF", + value: "" + }; + if (this.isWhitespace(cp)) { + while (this.isWhitespace(this.cp)) this.consume(); + return { + type: "whitespace", + value: " " + }; + } + switch (cp) { + case 34: return this.consumeStringToken(34); + case 35: + if (this.isIdentCodePoint(this.cp) || this.isValidEscape(this.cp, this.peek(1))) { + const token = { + type: "hash", + value: "", + hashType: "unrestricted" + }; + if (this.wouldStartIdentSequence(this.cp, this.peek(1), this.peek(2))) token.hashType = "id"; + token.value = this.consumeIdentSequence(); + return token; + } + return { + type: "delim", + value: String.fromCodePoint(cp) + }; + case 39: return this.consumeStringToken(39); + case 40: return { + type: "(", + value: "(" + }; + case 41: return { + type: ")", + value: ")" + }; + case 43: + if (this.wouldStartNumber(cp, this.cp, this.peek(1))) { + this.reconsume(); + return this.consumeNumericToken(); + } + return { + type: "delim", + value: "+" + }; + case 44: return { + type: "comma", + value: "," + }; + case 45: + if (this.wouldStartNumber(cp, this.cp, this.peek(1))) { + this.reconsume(); + return this.consumeNumericToken(); + } + if (this.cp === 45 && this.peek(1) === 62) { + this.consume(); + this.consume(); + return { + type: "CDC", + value: "-->" + }; + } + if (this.wouldStartIdentSequence(cp, this.cp, this.peek(1))) { + this.reconsume(); + return this.consumeIdentLikeToken(); + } + return { + type: "delim", + value: "-" + }; + case 46: + if (this.wouldStartNumber(cp, this.cp, this.peek(1))) { + this.reconsume(); + return this.consumeNumericToken(); + } + return { + type: "delim", + value: "." + }; + case 47: return { + type: "delim", + value: "/" + }; + case 58: return { + type: "colon", + value: ":" + }; + case 59: return { + type: "semicolon", + value: ";" + }; + case 60: + if (this.cp === 33 && this.peek(1) === 45 && this.peek(2) === 45) { + this.consume(); + this.consume(); + this.consume(); + return { + type: "CDO", + value: ""; - case "colon": return ":"; - case "semicolon": return ";"; - case "comma": return ","; - case "[": - case "]": - case "{": - case "}": - case "(": - case ")": return token.value; - case "function": return serializeIdentifier(preserveCase ? token.value : token.value.toLowerCase()) + "("; - case "unicode-range": return token.value; - case "EOF": return ""; - default: return token.value || ""; - } -} -function getMirrorToken(start) { - if (start === "{") return "}"; - if (start === "[") return "]"; - if (start === "(") return ")"; - return ""; -} -function getOriginalText(values) { - let text = ""; - for (const val of values) if (val.type === "simple-block") text += (val.associatedToken.originalText || "") + getOriginalText(val.value) + getMirrorToken(val.associatedToken.value); - else if (val.type === "function") { - const func = val; - text += `${func.name}(${getOriginalText(func.value)})`; - } else text += val.originalText || val.value; - return text; -} -/** -* // cssom-1 #serialize-an-identifier -*/ -function serializeIdentifier(id) { - let result = ""; - for (let i = 0; i < id.length; i++) { - const charCode = id.charCodeAt(i); - const char = id[i]; - if (charCode === 0) { - result += "�"; - continue; - } - if (charCode >= 1 && charCode <= 31 || charCode === 127) { - result += escapeAsCodePoint(charCode); - continue; - } - if (i === 0 && charCode >= 48 && charCode <= 57) { - result += escapeAsCodePoint(charCode); - continue; - } - if (i === 1 && charCode >= 48 && charCode <= 57 && id.charCodeAt(0) === 45) { - result += escapeAsCodePoint(charCode); - continue; - } - if (i === 0 && charCode === 45 && id.length === 1) { - result += "\\-"; - continue; - } - if (charCode >= 128 || charCode === 45 || charCode === 95 || charCode >= 48 && charCode <= 57 || charCode >= 65 && charCode <= 90 || charCode >= 97 && charCode <= 122) { - result += char; - continue; - } - result += "\\" + char; - } - return result; -} -/** -* // cssom-1 #serialize-a-string -*/ -function serializeString(s) { - let result = "\""; - for (let i = 0; i < s.length; i++) { - const charCode = s.charCodeAt(i); - const char = s[i]; - if (charCode === 0) { - result += "�"; - continue; - } - if (charCode >= 1 && charCode <= 31 || charCode === 127) { - result += escapeAsCodePoint(charCode); - continue; - } - if (charCode === 34 || charCode === 92) { - result += "\\" + char; - continue; - } - result += char; - } - result += "\""; - return result; -} -function serializeUrl(val) { - return `url(${serializeString(val)})`; -} -function serializeUrlToken(val, originalText) { - if (originalText && !val.includes("�") && originalText.endsWith(")")) return originalText; - let result = ""; - for (let i = 0; i < val.length; i++) { - const charCode = val.charCodeAt(i); - const char = val[i]; - if (charCode === 34 || charCode === 39 || charCode === 40 || charCode === 41 || charCode === 92 || charCode <= 32 || charCode === 127) result += "\\" + char; - else result += char; - } - return `url(${result})`; -} -function escapeAsCodePoint(charCode) { - return "\\" + charCode.toString(16) + " "; -} -const logicalShorthandsEntries = Object.entries({ - "margin-inline": { - start: "margin-inline-start", - end: "margin-inline-end", - allowDifferent: true - }, - "padding-inline": { - start: "padding-inline-start", - end: "padding-inline-end", - allowDifferent: true - }, - "margin-block": { - start: "margin-block-start", - end: "margin-block-end", - allowDifferent: true - }, - "padding-block": { - start: "padding-block-start", - end: "padding-block-end", - allowDifferent: true - }, - "inset-inline": { - start: "inset-inline-start", - end: "inset-inline-end", - allowDifferent: true - }, - "inset-block": { - start: "inset-block-start", - end: "inset-block-end", - allowDifferent: true - }, - "border-inline-width": { - start: "border-inline-start-width", - end: "border-inline-end-width", - allowDifferent: true - }, - "border-block-width": { - start: "border-block-start-width", - end: "border-block-end-width", - allowDifferent: true - }, - "border-inline-style": { - start: "border-inline-start-style", - end: "border-inline-end-style", - allowDifferent: true - }, - "border-block-style": { - start: "border-block-start-style", - end: "border-block-end-style", - allowDifferent: true - }, - "border-inline-color": { - start: "border-inline-start-color", - end: "border-inline-end-color", - allowDifferent: true - }, - "border-block-color": { - start: "border-block-start-color", - end: "border-block-end-color", - allowDifferent: true - }, - "border-inline": { - start: "border-inline-start", - end: "border-inline-end", - allowDifferent: false - }, - "border-block": { - start: "border-block-start", - end: "border-block-end", - allowDifferent: false - }, - "overflow": { - start: "overflow-x", - end: "overflow-y", - allowDifferent: true - }, - "overscroll-behavior": { - start: "overscroll-behavior-x", - end: "overscroll-behavior-y", - allowDifferent: true - } -}); -const propertyToGroup = { - "margin-top": "margin", - "margin-right": "margin", - "margin-bottom": "margin", - "margin-left": "margin", - "margin-inline-start": "margin", - "margin-inline-end": "margin", - "margin-block-start": "margin", - "margin-block-end": "margin", - "padding-top": "padding", - "padding-right": "padding", - "padding-bottom": "padding", - "padding-left": "padding", - "padding-inline-start": "padding", - "padding-inline-end": "padding", - "padding-block-start": "padding", - "padding-block-end": "padding", - "top": "inset", - "right": "inset", - "bottom": "inset", - "left": "inset", - "inset-inline-start": "inset", - "inset-inline-end": "inset", - "inset-block-start": "inset", - "inset-block-end": "inset", - "border-top-width": "border-width", - "border-right-width": "border-width", - "border-bottom-width": "border-width", - "border-left-width": "border-width", - "border-inline-start-width": "border-width", - "border-inline-end-width": "border-width", - "border-block-start-width": "border-width", - "border-block-end-width": "border-width", - "border-top-style": "border-style", - "border-right-style": "border-style", - "border-bottom-style": "border-style", - "border-left-style": "border-style", - "border-inline-start-style": "border-style", - "border-inline-end-style": "border-style", - "border-block-start-style": "border-style", - "border-block-end-style": "border-style", - "border-top-color": "border-color", - "border-right-color": "border-color", - "border-bottom-color": "border-color", - "border-left-color": "border-color", - "border-inline-start-color": "border-color", - "border-inline-end-color": "border-color", - "border-block-start-color": "border-color", - "border-block-end-color": "border-color", - "width": "size", - "height": "size", - "inline-size": "size", - "block-size": "size", - "min-width": "min-size", - "min-height": "min-size", - "min-inline-size": "min-size", - "min-block-size": "min-size", - "max-width": "max-size", - "max-height": "max-size", - "max-inline-size": "max-size", - "max-block-size": "max-size", - "border-top-left-radius": "border-radius", - "border-top-right-radius": "border-radius", - "border-bottom-right-radius": "border-radius", - "border-bottom-left-radius": "border-radius", - "border-start-start-radius": "border-radius", - "border-start-end-radius": "border-radius", - "border-end-start-radius": "border-radius", - "border-end-end-radius": "border-radius", - "border-top": "border", - "border-right": "border", - "border-bottom": "border", - "border-left": "border", - "border-block-start": "border", - "border-block-end": "border", - "border-inline-start": "border", - "border-inline-end": "border", - "overflow-x": "overflow", - "overflow-y": "overflow", - "overflow-inline": "overflow", - "overflow-block": "overflow", - "overscroll-behavior-x": "overscroll-behavior", - "overscroll-behavior-y": "overscroll-behavior", - "overscroll-behavior-inline": "overscroll-behavior", - "overscroll-behavior-block": "overscroll-behavior", - "outline-color": "outline", - "outline-style": "outline", - "outline-width": "outline", - "list-style-type": "list-style", - "list-style-position": "list-style", - "list-style-image": "list-style", - "flex-grow": "flex", - "flex-shrink": "flex", - "flex-basis": "flex", - "font-style": "font", - "font-variant-caps": "font", - "font-weight": "font", - "font-stretch": "font", - "font-size": "font", - "line-height": "font", - "font-family": "font", - "font-variant-ligatures": "font-variant", - "font-variant-alternates": "font-variant", - "font-variant-numeric": "font-variant", - "font-variant-east-asian": "font-variant", - "font-variant-position": "font-variant", - "font-variant-emoji": "font-variant" -}; -const genericShorthands = { - "border-top": [ - "border-top-width", - "border-top-style", - "border-top-color" - ], - "border-right": [ - "border-right-width", - "border-right-style", - "border-right-color" - ], - "border-bottom": [ - "border-bottom-width", - "border-bottom-style", - "border-bottom-color" - ], - "border-left": [ - "border-left-width", - "border-left-style", - "border-left-color" - ], - "border-block-start": [ - "border-block-start-width", - "border-block-start-style", - "border-block-start-color" - ], - "border-block-end": [ - "border-block-end-width", - "border-block-end-style", - "border-block-end-color" - ], - "border-inline-start": [ - "border-inline-start-width", - "border-inline-start-style", - "border-inline-start-color" - ], - "border-inline-end": [ - "border-inline-end-width", - "border-inline-end-style", - "border-inline-end-color" - ], - "outline": [ - "outline-color", - "outline-style", - "outline-width" - ], - "list-style": [ - "list-style-type", - "list-style-position", - "list-style-image" - ], - "flex": [ - "flex-grow", - "flex-shrink", - "flex-basis" - ], - "border-image": [ - "border-image-source", - "border-image-slice", - "border-image-width", - "border-image-outset", - "border-image-repeat" - ], - "line-clamp": [ - "max-lines", - "block-ellipsis", - "continue" - ] -}; -const genericShorthandsEntries = Object.entries(genericShorthands); -function checkIntervening(decls, allDecls, declIndices) { - const indices = decls.map((d) => declIndices.get(d)); - const startIdx = Math.min(...indices); - const endIdx = Math.max(...indices); - const names = new Set(decls.map((d) => d.name)); - const groups = new Set(decls.map((d) => propertyToGroup[d.name]).filter(Boolean)); - const isSideShorthand = groups.size > 1; - for (let i = startIdx + 1; i < endIdx; i++) { - const intervening = allDecls[i]; - if (names.has(intervening.name)) continue; - const interveningGroup = propertyToGroup[intervening.name]; - if (!interveningGroup) continue; - if (isSideShorthand) { - const sidePrefix = decls[0].name.replace(/-(width|style|color)$/, ""); - if (intervening.name.startsWith(sidePrefix + "-") || ["all", "border"].includes(intervening.name)) return true; - } else { - if (groups.has(interveningGroup)) return true; - if (["all", "border"].includes(intervening.name)) return true; - } - } - return false; -} -const BOX_SHORTHAND_NAMES = /* @__PURE__ */ new Set("margin,padding,border-width,border-style,border-color,scroll-margin,scroll-padding,inset,overflow-clip-margin,border-radius".split(",")); -const BOX_SHORTHAND_ENTRIES = Object.entries(SHORTHANDS).filter(([shorthand, def]) => BOX_SHORTHAND_NAMES.has(shorthand) && def.logicalLonghands?.length === 4); -function tryCombineBoxShorthand(d, declMap, processed, declarations, declIndices) { - for (const [shorthand, def] of BOX_SHORTHAND_ENTRIES) { - const physical = def.longhands; - const logical = def.logicalLonghands; - const isPhysical = physical.includes(d.name); - const isLogical = logical.includes(d.name); - if (!isPhysical && !isLogical) continue; - const allDecls = (isPhysical ? physical : logical).map((name) => declMap.get(name)); - if (!allDecls.every((other) => other && !processed.has(other) && other.important === d.important)) continue; - if (checkIntervening(allDecls, declarations, declIndices)) continue; - const valuesForContract = {}; - for (const other of allDecls) valuesForContract[other.name] = other.value; - const value = def.contract(valuesForContract); - if (value !== null) { - for (const other of allDecls) processed.add(other); - return `${shorthand}: ${value}${d.important ? " !important" : ""}`; - } - } - return null; -} -function tryCombineLogicalShorthand(d, declMap, processed, declarations, declIndices) { - for (const [shorthand, longhands] of logicalShorthandsEntries) { - if (d.name !== longhands.start && d.name !== longhands.end) continue; - const otherName = d.name === longhands.start ? longhands.end : longhands.start; - const otherDecl = declMap.get(otherName); - if (!otherDecl || processed.has(otherDecl) || d.important !== otherDecl.important) continue; - if (checkIntervening([d, otherDecl], declarations, declIndices)) continue; - const startDecl = d.name === longhands.start ? d : otherDecl; - const endDecl = d.name === longhands.end ? d : otherDecl; - const valS = serialize(startDecl.value).trim(); - const valE = serialize(endDecl.value).trim(); - if (valS === valE) { - processed.add(startDecl); - processed.add(endDecl); - return `${shorthand}: ${valS}${d.important ? " !important" : ""}`; - } - if (longhands.allowDifferent && !valS.includes("var(") && !valE.includes("var(")) { - processed.add(startDecl); - processed.add(endDecl); - return `${shorthand}: ${valS} ${valE}${d.important ? " !important" : ""}`; - } - } - return null; -} -function tryCombineGenericShorthand(d, declMap, processed, declarations, declIndices) { - for (const [shorthand, longhands] of genericShorthandsEntries) { - if (!longhands.includes(d.name)) continue; - const allDecls = longhands.map((name) => declMap.get(name)); - if (!allDecls.every((other) => other && !processed.has(other) && other.important === d.important)) continue; - if (checkIntervening(allDecls, declarations, declIndices)) continue; - const record = {}; - for (const other of allDecls) record[other.name] = other.value; - const contracted = SHORTHANDS[shorthand]?.contract(record); - if (contracted !== null && contracted !== void 0) { - for (const other of allDecls) processed.add(other); - return { - name: shorthand, - value: contracted, - important: d.important - }; - } - } - return null; -} -const BORDER_PHYSICAL_SIDE_LONGHANDS = [ - "border-top-width", - "border-top-style", - "border-top-color", - "border-right-width", - "border-right-style", - "border-right-color", - "border-bottom-width", - "border-bottom-style", - "border-bottom-color", - "border-left-width", - "border-left-style", - "border-left-color" -]; -const BORDER_IMAGE_LONGHAND_NAMES = [ - "border-image-source", - "border-image-slice", - "border-image-width", - "border-image-outset", - "border-image-repeat" -]; -function tryCombineBorderFull(d, declMap, processed, declarations, declIndices) { - if (!BORDER_PHYSICAL_SIDE_LONGHANDS.includes(d.name)) return null; - const allDecls = BORDER_PHYSICAL_SIDE_LONGHANDS.map((name) => declMap.get(name)); - if (!allDecls.every((other) => other && !processed.has(other) && other.important === d.important)) return null; - if (checkIntervening(allDecls, declarations, declIndices)) return null; - const imageDecls = BORDER_IMAGE_LONGHAND_NAMES.map((name) => declMap.get(name)).filter((img) => Boolean(img && !processed.has(img))); - if (imageDecls.length === 0) return null; - const imageRecord = { - "border-image-source": [{ - type: "ident", - value: "none" - }], - "border-image-slice": [{ - type: "percentage", - value: 100, - sign: null - }], - "border-image-width": [{ - type: "number", - value: 1, - sign: null, - numberType: "integer" - }], - "border-image-outset": [{ - type: "number", - value: 0, - sign: null, - numberType: "integer" - }], - "border-image-repeat": [{ - type: "ident", - value: "stretch" - }] - }; - for (const img of imageDecls) imageRecord[img.name] = img.value; - if (!isInitialBorderImage(imageRecord)) return null; - const record = { ...imageRecord }; - for (const other of allDecls) record[other.name] = other.value; - const contracted = SHORTHANDS["border"]?.contract(record); - if (contracted !== null && contracted !== void 0) { - for (const other of allDecls) processed.add(other); - for (const img of imageDecls) processed.add(img); - return `border: ${contracted}${d.important ? " !important" : ""}`; - } - return null; -} -const ORDERED_SHORTHAND_COMBINERS = [ - { - name: "font", - longhands: SHORTHANDS["font"].longhands - }, - { - name: "font-variant", - longhands: SHORTHANDS["font-variant"].longhands - }, - { - name: "background", - longhands: [ - "background-image", - "background-position", - "background-size", - "background-repeat", - "background-attachment", - "background-origin", - "background-clip", - "background-color" - ] - }, - { - name: "border-block", - longhands: [ - "border-block-start-width", - "border-block-start-style", - "border-block-start-color", - "border-block-end-width", - "border-block-end-style", - "border-block-end-color" - ] - }, - { - name: "border-inline", - longhands: [ - "border-inline-start-width", - "border-inline-start-style", - "border-inline-start-color", - "border-inline-end-width", - "border-inline-end-style", - "border-inline-end-color" - ] - } -]; -function tryCombineNamedShorthand(shorthandName, longhands, d, declMap, processed, declarations, declIndices) { - if (!longhands.includes(d.name)) return null; - const allDecls = longhands.map((name) => declMap.get(name)); - if (!allDecls.every((other) => other && !processed.has(other) && other.important === d.important)) return null; - if (checkIntervening(allDecls, declarations, declIndices)) return null; - const record = {}; - for (const other of allDecls) record[other.name] = other.value; - const contracted = SHORTHANDS[shorthandName]?.contract(record); - if (contracted !== null && contracted !== void 0) { - for (const other of allDecls) processed.add(other); - return `${shorthandName}: ${contracted}${d.important ? " !important" : ""}`; - } - return null; -} -const CSS_WIDE_AND_DEFAULT = /* @__PURE__ */ new Set("initial,inherit,unset,revert,revert-layer,default".split(",")); -function serializeFontFamilyItem(tokens) { - const nonWs = tokens.filter((t) => t.type !== "whitespace" && t.type !== "comment" && t.type !== "EOF"); - if (nonWs.length === 1 && nonWs[0].type === "string") { - let strVal = nonWs[0].value; - if (strVal.startsWith("'") && strVal.endsWith("'") || strVal.startsWith("\"") && strVal.endsWith("\"")) strVal = strVal.slice(1, -1); - const lower = strVal.toLowerCase(); - if (GENERIC_FONT_FAMILIES.has(lower) || CSS_WIDE_AND_DEFAULT.has(lower)) return `"${strVal}"`; - if (strVal !== strVal.trim() || /\s{2,}|\t|\n|\r/.test(strVal)) return `"${strVal}"`; - const words = strVal.split(" "); - return words.length > 0 && words.every((word) => word.length > 0 && !/^[0-9]|^--|^-[0-9]/.test(word) && /^[a-zA-Z_-][a-zA-Z0-9_-]*$/.test(word)) ? strVal : `"${strVal}"`; - } - if (nonWs.every((t) => t.type === "ident")) return nonWs.map((t) => serializeNode(t, false)).join(" "); - return tokens.map((t) => serializeNode(t, false)).join(""); -} -function serializeFontFamily(values) { - const groups = []; - let current = []; - for (const token of values) if (token.type === "comma") { - groups.push(current); - current = []; - } else current.push(token); - if (current.length > 0) groups.push(current); - return groups.map((g) => serializeFontFamilyItem(g).trim()).filter((s) => s.length > 0).join(", "); -} -function serializeDeclarations(declarations) { - if (declarations.length === 0) return ""; - const declMap = /* @__PURE__ */ new Map(); - const declIndices = /* @__PURE__ */ new Map(); - for (let i = 0; i < declarations.length; i++) { - const d = declarations[i]; - declMap.set(d.name, d); - declIndices.set(d, i); - } - if (declarations.length >= ALL_SHORTHAND_LONGHANDS.length) { - const firstDecl = declMap.get(ALL_SHORTHAND_LONGHANDS[0]); - if (firstDecl) { - const firstValLower = serialize(firstDecl.value).trim().toLowerCase(); - const firstImportant = firstDecl.important; - if (CSS_WIDE_AND_DEFAULT.has(firstValLower) || firstValLower.startsWith("var(")) { - if (ALL_SHORTHAND_LONGHANDS.every((lh) => { - const d = declMap.get(lh); - return d && serialize(d.value).trim().toLowerCase() === firstValLower && d.important === firstImportant; - })) { - const allLonghandsSet = new Set(ALL_SHORTHAND_LONGHANDS); - const allDecl = { - type: "declaration", - name: "all", - value: firstDecl.value, - important: firstImportant - }; - const newDecls = []; - let inserted = false; - for (const d of declarations) if (allLonghandsSet.has(d.name)) { - if (!inserted) { - newDecls.push(allDecl); - inserted = true; - } - } else newDecls.push(d); - return serializeDeclarations(newDecls); - } - } - } - } - const processed = /* @__PURE__ */ new Set(); - const result = []; - for (const d of declarations) { - if (processed.has(d)) continue; - let combined = tryCombineBorderFull(d, declMap, processed, declarations, declIndices); - if (!combined) for (const spec of ORDERED_SHORTHAND_COMBINERS) { - combined = tryCombineNamedShorthand(spec.name, spec.longhands, d, declMap, processed, declarations, declIndices); - if (combined) break; - } - if (!combined) combined = tryCombineBoxShorthand(d, declMap, processed, declarations, declIndices); - if (!combined) { - const generic = tryCombineGenericShorthand(d, declMap, processed, declarations, declIndices); - if (generic) { - const sides = [ - "border-top", - "border-right", - "border-bottom", - "border-left" - ]; - if (sides.includes(generic.name)) { - const sideResults = sides.map((side) => { - if (side === generic.name) return generic; - const existing = declMap.get(side); - if (existing && !processed.has(existing)) return { - name: side, - value: serialize(existing.value).trim(), - important: existing.important, - decl: existing - }; - const longhands = genericShorthands[side]; - if (!longhands) return null; - const sideLonghands = longhands.map((lh) => declMap.get(lh)); - if (sideLonghands.every((lh) => lh && !processed.has(lh) && lh.important === generic.important)) { - if (checkIntervening(sideLonghands, declarations, declIndices)) return null; - return { - name: side, - value: sideLonghands.map((lh) => serialize(lh.value).trim()).filter((v) => v !== "").join(" "), - important: generic.important, - longhands: sideLonghands - }; - } - return null; - }); - if (sideResults.every((r) => r !== null && r.value === generic.value && r.important === generic.important)) { - for (const r of sideResults) if (r && "longhands" in r) r.longhands.forEach((lh) => processed.add(lh)); - else if (r && "decl" in r) processed.add(r.decl); - combined = `border: ${generic.value}${generic.important ? " !important" : ""}`; - } else combined = `${generic.name}: ${generic.value}${generic.important ? " !important" : ""}`; - } else combined = `${generic.name}: ${generic.value}${generic.important ? " !important" : ""}`; - } - } - if (!combined) combined = tryCombineLogicalShorthand(d, declMap, processed, declarations, declIndices); - if (combined) result.push(combined); - else { - const isCustom = d.name.startsWith("--"); - let val; - if (d.name === "font-family") val = serializeFontFamily(d.value); - else if (d.name === "flex-basis" && serialize(d.value).trim() === "0") val = "0px"; - else val = d.raw && !d.raw.includes("var(") ? d.raw : serialize(d.value, isCustom, d.name).trim(); - result.push(`${serializeIdentifier(d.name)}: ${val}${d.important ? " !important" : ""}`); - processed.add(d); - } - } - return result.join("; ") + ";"; -} -function serializeSelectorList(list, nsContext) { - const context = { - hasDefaultNamespace: typeof nsContext === "boolean" ? nsContext : Boolean(nsContext?.hasDefaultNamespace), - defaultNamespacePrefixes: typeof nsContext === "object" && nsContext !== null ? nsContext.defaultNamespacePrefixes : void 0 - }; - return list.selectors.map((s) => s.type === "invalid-selector" ? serialize(s.tokens) : serializeComplexSelector(s, context)).join(", "); -} -function serializeComplexSelector(complex, nsContext) { - const { hasDefaultNamespace, defaultNamespacePrefixes } = nsContext; - return complex.items.map((item, idx) => { - if (item.type === "combinator") { - if (item.value === " ") return " "; - return idx === 0 ? `${item.value} ` : ` ${item.value} `; - } - return item.selectors.filter((s, sIdx) => { - if (s.type === "universal-selector" && item.selectors.length > 1 && sIdx === 0) { - const isDefaultNs = s.namespace !== void 0 && s.namespace !== "" && defaultNamespacePrefixes?.has(s.namespace); - if (s.namespace === void 0 || isDefaultNs || s.namespace === "*" && !hasDefaultNamespace) return false; - } - return true; - }).map((s) => serializeSimpleSelector(s, nsContext)).join(""); - }).join(""); -} -function formatAnPlusB(tokens) { - const parsed = parseAnPlusB(tokens); - if (parsed === null) return serialize(tokens).trim(); - const { a, b } = parsed; - if (a === 0) return b.toString(); - const partA = a === 1 ? "n" : a === -1 ? "-n" : `${a}n`; - if (b === 0) return partA; - return b > 0 ? `${partA}+${b}` : `${partA}${b}`; -} -function formatSelectorNamespacePrefix(namespace, nsContext, isElementSelector) { - if (namespace === void 0) return ""; - if (isElementSelector && namespace !== "" && nsContext.defaultNamespacePrefixes?.has(namespace)) return ""; - if (namespace === "*") return !isElementSelector || nsContext.hasDefaultNamespace ? "*|" : ""; - if (namespace === "") return isElementSelector ? "|" : ""; - return `${serializeIdentifier(namespace)}|`; -} -const NTH_PSEUDO_CLASSES = /* @__PURE__ */ new Set("nth-child,nth-last-child,nth-of-type,nth-last-of-type".split(",")); -function serializeSimpleSelector(simple, nsContext) { - switch (simple.type) { - case "type-selector": return formatSelectorNamespacePrefix(simple.namespace, nsContext, true) + serializeIdentifier(simple.name); - case "universal-selector": return formatSelectorNamespacePrefix(simple.namespace, nsContext, true) + "*"; - case "id-selector": return "#" + serializeIdentifier(simple.name); - case "class-selector": return "." + serializeIdentifier(simple.name); - case "attribute-selector": { - let attr = "[" + formatSelectorNamespacePrefix(simple.namespace, nsContext, false) + serializeIdentifier(simple.name); - if (simple.operator) attr += simple.operator + serializeString(simple.value || ""); - if (simple.flags) attr += " " + simple.flags; - return attr + "]"; - } - case "pseudo-class-selector": { - let pc = `:${simple.name}`; - const isNth = NTH_PSEUDO_CLASSES.has(simple.name.toLowerCase()); - if (simple.argument) { - if ("type" in simple.argument && simple.argument.type === "selector-list") { - const selStr = serializeSelectorList(simple.argument, nsContext); - pc += isNth && simple.nth ? `(${formatAnPlusB(simple.nth)} of ${selStr})` : `(${selStr})`; - } else { - const tokens = simple.argument; - pc += `(${isNth ? formatAnPlusB(tokens) : serialize(tokens).trim()})`; - } - } - return pc; - } - case "pseudo-element-selector": { - let pe = `::${simple.name}`; - if (simple.argument) pe += "type" in simple.argument && simple.argument.type === "selector-list" ? `(${serializeSelectorList(simple.argument, nsContext)})` : `(${serialize(simple.argument).trim()})`; - return pe; - } - case "nesting-selector": return "&"; - default: return ""; - } -} -// src/AbstractTokenizer.ts -/** -* @license -* Copyright 2026 Google LLC -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* https://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ -var AbstractTokenizer = class { - unicodeRangesAllowed = false; - errors = []; - parseError(message) { - this.errors.push({ message }); - console.warn(`CSS Parse Error: ${message}`); - } - consumeToken() { - while (true) { - this.consumeComments(); - const cp = this.consume(); - if (cp === -1) return { - type: "EOF", - value: "" - }; - if (this.isWhitespace(cp)) { - while (this.isWhitespace(this.cp)) this.consume(); - return { - type: "whitespace", - value: " " - }; - } - switch (cp) { - case 34: return this.consumeStringToken(34); - case 35: - if (this.isIdentCodePoint(this.cp) || this.isValidEscape(this.cp, this.peek(1))) { - const token = { - type: "hash", - value: "", - hashType: "unrestricted" - }; - if (this.wouldStartIdentSequence(this.cp, this.peek(1), this.peek(2))) token.hashType = "id"; - token.value = this.consumeIdentSequence(); - return token; - } - return { - type: "delim", - value: String.fromCodePoint(cp) - }; - case 39: return this.consumeStringToken(39); - case 40: return { - type: "(", - value: "(" - }; - case 41: return { - type: ")", - value: ")" - }; - case 43: - if (this.wouldStartNumber(cp, this.cp, this.peek(1))) { - this.reconsume(); - return this.consumeNumericToken(); - } - return { - type: "delim", - value: "+" - }; - case 44: return { - type: "comma", - value: "," - }; - case 45: - if (this.wouldStartNumber(cp, this.cp, this.peek(1))) { - this.reconsume(); - return this.consumeNumericToken(); - } - if (this.cp === 45 && this.peek(1) === 62) { - this.consume(); - this.consume(); - return { - type: "CDC", - value: "-->" - }; - } - if (this.wouldStartIdentSequence(cp, this.cp, this.peek(1))) { - this.reconsume(); - return this.consumeIdentLikeToken(); - } - return { - type: "delim", - value: "-" - }; - case 46: - if (this.wouldStartNumber(cp, this.cp, this.peek(1))) { - this.reconsume(); - return this.consumeNumericToken(); - } - return { - type: "delim", - value: "." - }; - case 47: return { - type: "delim", - value: "/" - }; - case 58: return { - type: "colon", - value: ":" - }; - case 59: return { - type: "semicolon", - value: ";" - }; - case 60: - if (this.cp === 33 && this.peek(1) === 45 && this.peek(2) === 45) { - this.consume(); - this.consume(); - this.consume(); - return { - type: "CDO", - value: ""; + case "colon": return ":"; + case "semicolon": return ";"; + case "comma": return ","; + case "[": + case "]": + case "{": + case "}": + case "(": + case ")": return token.value; + case "function": return serializeIdentifier(preserveCase ? token.value : token.value.toLowerCase()) + "("; + case "unicode-range": return token.value; + case "EOF": return ""; + default: return token.value || ""; + } +} +function getMirrorToken(start) { + if (start === "{") return "}"; + if (start === "[") return "]"; + if (start === "(") return ")"; + return ""; +} +function getOriginalText(values) { + let text = ""; + for (const val of values) if (val.type === "simple-block") text += (val.associatedToken.originalText || "") + getOriginalText(val.value) + getMirrorToken(val.associatedToken.value); + else if (val.type === "function") { + const func = val; + text += `${func.name}(${getOriginalText(func.value)})`; + } else text += val.originalText || val.value; + return text; +} +/** +* // cssom-1 #serialize-an-identifier +*/ +function serializeIdentifier(id) { + let result = ""; + for (let i = 0; i < id.length; i++) { + const charCode = id.charCodeAt(i); + const char = id[i]; + if (charCode === 0) { + result += "�"; + continue; + } + if (charCode >= 1 && charCode <= 31 || charCode === 127) { + result += escapeAsCodePoint(charCode); + continue; + } + if (i === 0 && charCode >= 48 && charCode <= 57) { + result += escapeAsCodePoint(charCode); + continue; + } + if (i === 1 && charCode >= 48 && charCode <= 57 && id.charCodeAt(0) === 45) { + result += escapeAsCodePoint(charCode); + continue; + } + if (i === 0 && charCode === 45 && id.length === 1) { + result += "\\-"; + continue; + } + if (charCode >= 128 || charCode === 45 || charCode === 95 || charCode >= 48 && charCode <= 57 || charCode >= 65 && charCode <= 90 || charCode >= 97 && charCode <= 122) { + result += char; + continue; + } + result += "\\" + char; + } + return result; +} +/** +* // cssom-1 #serialize-a-string +*/ +function serializeString(s) { + let result = "\""; + for (let i = 0; i < s.length; i++) { + const charCode = s.charCodeAt(i); + const char = s[i]; + if (charCode === 0) { + result += "�"; + continue; + } + if (charCode >= 1 && charCode <= 31 || charCode === 127) { + result += escapeAsCodePoint(charCode); + continue; + } + if (charCode === 34 || charCode === 92) { + result += "\\" + char; + continue; + } + result += char; + } + result += "\""; + return result; +} +function serializeUrl(val) { + return `url(${serializeString(val)})`; +} +function serializeUrlToken(val, originalText) { + if (originalText && !val.includes("�") && originalText.endsWith(")")) return originalText; + let result = ""; + for (let i = 0; i < val.length; i++) { + const charCode = val.charCodeAt(i); + const char = val[i]; + if (charCode === 34 || charCode === 39 || charCode === 40 || charCode === 41 || charCode === 92 || charCode <= 32 || charCode === 127) result += "\\" + char; + else result += char; + } + return `url(${result})`; +} +function escapeAsCodePoint(charCode) { + return "\\" + charCode.toString(16) + " "; +} +const logicalShorthandsEntries = Object.entries({ + "margin-inline": { + start: "margin-inline-start", + end: "margin-inline-end", + allowDifferent: true + }, + "padding-inline": { + start: "padding-inline-start", + end: "padding-inline-end", + allowDifferent: true + }, + "margin-block": { + start: "margin-block-start", + end: "margin-block-end", + allowDifferent: true + }, + "padding-block": { + start: "padding-block-start", + end: "padding-block-end", + allowDifferent: true + }, + "inset-inline": { + start: "inset-inline-start", + end: "inset-inline-end", + allowDifferent: true + }, + "inset-block": { + start: "inset-block-start", + end: "inset-block-end", + allowDifferent: true + }, + "border-inline-width": { + start: "border-inline-start-width", + end: "border-inline-end-width", + allowDifferent: true + }, + "border-block-width": { + start: "border-block-start-width", + end: "border-block-end-width", + allowDifferent: true + }, + "border-inline-style": { + start: "border-inline-start-style", + end: "border-inline-end-style", + allowDifferent: true + }, + "border-block-style": { + start: "border-block-start-style", + end: "border-block-end-style", + allowDifferent: true + }, + "border-inline-color": { + start: "border-inline-start-color", + end: "border-inline-end-color", + allowDifferent: true + }, + "border-block-color": { + start: "border-block-start-color", + end: "border-block-end-color", + allowDifferent: true + }, + "border-inline": { + start: "border-inline-start", + end: "border-inline-end", + allowDifferent: false + }, + "border-block": { + start: "border-block-start", + end: "border-block-end", + allowDifferent: false + }, + "overflow": { + start: "overflow-x", + end: "overflow-y", + allowDifferent: true + }, + "overscroll-behavior": { + start: "overscroll-behavior-x", + end: "overscroll-behavior-y", + allowDifferent: true + } +}); +const propertyToGroup = { + "margin-top": "margin", + "margin-right": "margin", + "margin-bottom": "margin", + "margin-left": "margin", + "margin-inline-start": "margin", + "margin-inline-end": "margin", + "margin-block-start": "margin", + "margin-block-end": "margin", + "padding-top": "padding", + "padding-right": "padding", + "padding-bottom": "padding", + "padding-left": "padding", + "padding-inline-start": "padding", + "padding-inline-end": "padding", + "padding-block-start": "padding", + "padding-block-end": "padding", + "top": "inset", + "right": "inset", + "bottom": "inset", + "left": "inset", + "inset-inline-start": "inset", + "inset-inline-end": "inset", + "inset-block-start": "inset", + "inset-block-end": "inset", + "border-top-width": "border-width", + "border-right-width": "border-width", + "border-bottom-width": "border-width", + "border-left-width": "border-width", + "border-inline-start-width": "border-width", + "border-inline-end-width": "border-width", + "border-block-start-width": "border-width", + "border-block-end-width": "border-width", + "border-top-style": "border-style", + "border-right-style": "border-style", + "border-bottom-style": "border-style", + "border-left-style": "border-style", + "border-inline-start-style": "border-style", + "border-inline-end-style": "border-style", + "border-block-start-style": "border-style", + "border-block-end-style": "border-style", + "border-top-color": "border-color", + "border-right-color": "border-color", + "border-bottom-color": "border-color", + "border-left-color": "border-color", + "border-inline-start-color": "border-color", + "border-inline-end-color": "border-color", + "border-block-start-color": "border-color", + "border-block-end-color": "border-color", + "width": "size", + "height": "size", + "inline-size": "size", + "block-size": "size", + "min-width": "min-size", + "min-height": "min-size", + "min-inline-size": "min-size", + "min-block-size": "min-size", + "max-width": "max-size", + "max-height": "max-size", + "max-inline-size": "max-size", + "max-block-size": "max-size", + "border-top-left-radius": "border-radius", + "border-top-right-radius": "border-radius", + "border-bottom-right-radius": "border-radius", + "border-bottom-left-radius": "border-radius", + "border-start-start-radius": "border-radius", + "border-start-end-radius": "border-radius", + "border-end-start-radius": "border-radius", + "border-end-end-radius": "border-radius", + "border-top": "border", + "border-right": "border", + "border-bottom": "border", + "border-left": "border", + "border-block-start": "border", + "border-block-end": "border", + "border-inline-start": "border", + "border-inline-end": "border", + "overflow-x": "overflow", + "overflow-y": "overflow", + "overflow-inline": "overflow", + "overflow-block": "overflow", + "overscroll-behavior-x": "overscroll-behavior", + "overscroll-behavior-y": "overscroll-behavior", + "overscroll-behavior-inline": "overscroll-behavior", + "overscroll-behavior-block": "overscroll-behavior", + "outline-color": "outline", + "outline-style": "outline", + "outline-width": "outline", + "list-style-type": "list-style", + "list-style-position": "list-style", + "list-style-image": "list-style", + "flex-grow": "flex", + "flex-shrink": "flex", + "flex-basis": "flex", + "font-style": "font", + "font-variant-caps": "font", + "font-weight": "font", + "font-stretch": "font", + "font-size": "font", + "line-height": "font", + "font-family": "font", + "font-variant-ligatures": "font-variant", + "font-variant-alternates": "font-variant", + "font-variant-numeric": "font-variant", + "font-variant-east-asian": "font-variant", + "font-variant-position": "font-variant", + "font-variant-emoji": "font-variant" +}; +const genericShorthands = { + "border-top": [ + "border-top-width", + "border-top-style", + "border-top-color" + ], + "border-right": [ + "border-right-width", + "border-right-style", + "border-right-color" + ], + "border-bottom": [ + "border-bottom-width", + "border-bottom-style", + "border-bottom-color" + ], + "border-left": [ + "border-left-width", + "border-left-style", + "border-left-color" + ], + "border-block-start": [ + "border-block-start-width", + "border-block-start-style", + "border-block-start-color" + ], + "border-block-end": [ + "border-block-end-width", + "border-block-end-style", + "border-block-end-color" + ], + "border-inline-start": [ + "border-inline-start-width", + "border-inline-start-style", + "border-inline-start-color" + ], + "border-inline-end": [ + "border-inline-end-width", + "border-inline-end-style", + "border-inline-end-color" + ], + "outline": [ + "outline-color", + "outline-style", + "outline-width" + ], + "list-style": [ + "list-style-type", + "list-style-position", + "list-style-image" + ], + "flex": [ + "flex-grow", + "flex-shrink", + "flex-basis" + ], + "border-image": [ + "border-image-source", + "border-image-slice", + "border-image-width", + "border-image-outset", + "border-image-repeat" + ], + "line-clamp": [ + "max-lines", + "block-ellipsis", + "continue" + ] +}; +const genericShorthandsEntries = Object.entries(genericShorthands); +function checkIntervening(decls, allDecls, declIndices) { + const indices = decls.map((d) => declIndices.get(d)); + const startIdx = Math.min(...indices); + const endIdx = Math.max(...indices); + const names = new Set(decls.map((d) => d.name)); + const groups = new Set(decls.map((d) => propertyToGroup[d.name]).filter(Boolean)); + const isSideShorthand = groups.size > 1; + for (let i = startIdx + 1; i < endIdx; i++) { + const intervening = allDecls[i]; + if (names.has(intervening.name)) continue; + const interveningGroup = propertyToGroup[intervening.name]; + if (!interveningGroup) continue; + if (isSideShorthand) { + const sidePrefix = decls[0].name.replace(/-(width|style|color)$/, ""); + if (intervening.name.startsWith(sidePrefix + "-") || ["all", "border"].includes(intervening.name)) return true; + } else { + if (groups.has(interveningGroup)) return true; + if (["all", "border"].includes(intervening.name)) return true; + } + } + return false; +} +const BOX_SHORTHAND_NAMES = /* @__PURE__ */ new Set("margin,padding,border-width,border-style,border-color,scroll-margin,scroll-padding,inset,overflow-clip-margin,border-radius".split(",")); +const BOX_SHORTHAND_ENTRIES = Object.entries(SHORTHANDS).filter(([shorthand, def]) => BOX_SHORTHAND_NAMES.has(shorthand) && def.logicalLonghands?.length === 4); +function tryCombineBoxShorthand(d, declMap, processed, declarations, declIndices) { + for (const [shorthand, def] of BOX_SHORTHAND_ENTRIES) { + const physical = def.longhands; + const logical = def.logicalLonghands; + const isPhysical = physical.includes(d.name); + const isLogical = logical.includes(d.name); + if (!isPhysical && !isLogical) continue; + const allDecls = (isPhysical ? physical : logical).map((name) => declMap.get(name)); + if (!allDecls.every((other) => other && !processed.has(other) && other.important === d.important)) continue; + if (checkIntervening(allDecls, declarations, declIndices)) continue; + const valuesForContract = {}; + for (const other of allDecls) valuesForContract[other.name] = other.value; + const value = def.contract(valuesForContract); + if (value !== null) { + for (const other of allDecls) processed.add(other); + return `${shorthand}: ${value}${d.important ? " !important" : ""}`; + } + } + return null; +} +function tryCombineLogicalShorthand(d, declMap, processed, declarations, declIndices) { + for (const [shorthand, longhands] of logicalShorthandsEntries) { + if (d.name !== longhands.start && d.name !== longhands.end) continue; + const otherName = d.name === longhands.start ? longhands.end : longhands.start; + const otherDecl = declMap.get(otherName); + if (!otherDecl || processed.has(otherDecl) || d.important !== otherDecl.important) continue; + if (checkIntervening([d, otherDecl], declarations, declIndices)) continue; + const startDecl = d.name === longhands.start ? d : otherDecl; + const endDecl = d.name === longhands.end ? d : otherDecl; + const valS = serialize(startDecl.value).trim(); + const valE = serialize(endDecl.value).trim(); + if (valS === valE) { + processed.add(startDecl); + processed.add(endDecl); + return `${shorthand}: ${valS}${d.important ? " !important" : ""}`; + } + if (longhands.allowDifferent && !valS.includes("var(") && !valE.includes("var(")) { + processed.add(startDecl); + processed.add(endDecl); + return `${shorthand}: ${valS} ${valE}${d.important ? " !important" : ""}`; + } + } + return null; +} +function tryCombineGenericShorthand(d, declMap, processed, declarations, declIndices) { + for (const [shorthand, longhands] of genericShorthandsEntries) { + if (!longhands.includes(d.name)) continue; + const allDecls = longhands.map((name) => declMap.get(name)); + if (!allDecls.every((other) => other && !processed.has(other) && other.important === d.important)) continue; + if (checkIntervening(allDecls, declarations, declIndices)) continue; + const record = {}; + for (const other of allDecls) record[other.name] = other.value; + const contracted = SHORTHANDS[shorthand]?.contract(record); + if (contracted !== null && contracted !== void 0) { + for (const other of allDecls) processed.add(other); + return { + name: shorthand, + value: contracted, + important: d.important + }; + } + } + return null; +} +const BORDER_PHYSICAL_SIDE_LONGHANDS = [ + "border-top-width", + "border-top-style", + "border-top-color", + "border-right-width", + "border-right-style", + "border-right-color", + "border-bottom-width", + "border-bottom-style", + "border-bottom-color", + "border-left-width", + "border-left-style", + "border-left-color" +]; +const BORDER_IMAGE_LONGHAND_NAMES = [ + "border-image-source", + "border-image-slice", + "border-image-width", + "border-image-outset", + "border-image-repeat" +]; +function tryCombineBorderFull(d, declMap, processed, declarations, declIndices) { + if (!BORDER_PHYSICAL_SIDE_LONGHANDS.includes(d.name)) return null; + const allDecls = BORDER_PHYSICAL_SIDE_LONGHANDS.map((name) => declMap.get(name)); + if (!allDecls.every((other) => other && !processed.has(other) && other.important === d.important)) return null; + if (checkIntervening(allDecls, declarations, declIndices)) return null; + const imageDecls = BORDER_IMAGE_LONGHAND_NAMES.map((name) => declMap.get(name)).filter((img) => Boolean(img && !processed.has(img))); + if (imageDecls.length === 0) return null; + const imageRecord = { + "border-image-source": [{ + type: "ident", + value: "none" + }], + "border-image-slice": [{ + type: "percentage", + value: 100, + sign: null + }], + "border-image-width": [{ + type: "number", + value: 1, + sign: null, + numberType: "integer" + }], + "border-image-outset": [{ + type: "number", + value: 0, + sign: null, + numberType: "integer" + }], + "border-image-repeat": [{ + type: "ident", + value: "stretch" + }] + }; + for (const img of imageDecls) imageRecord[img.name] = img.value; + if (!isInitialBorderImage(imageRecord)) return null; + const record = { ...imageRecord }; + for (const other of allDecls) record[other.name] = other.value; + const contracted = SHORTHANDS["border"]?.contract(record); + if (contracted !== null && contracted !== void 0) { + for (const other of allDecls) processed.add(other); + for (const img of imageDecls) processed.add(img); + return `border: ${contracted}${d.important ? " !important" : ""}`; + } + return null; +} +const ORDERED_SHORTHAND_COMBINERS = [ + { + name: "font", + longhands: SHORTHANDS["font"].longhands + }, + { + name: "font-variant", + longhands: SHORTHANDS["font-variant"].longhands + }, + { + name: "background", + longhands: [ + "background-image", + "background-position", + "background-size", + "background-repeat", + "background-attachment", + "background-origin", + "background-clip", + "background-color" + ] + }, + { + name: "border-block", + longhands: [ + "border-block-start-width", + "border-block-start-style", + "border-block-start-color", + "border-block-end-width", + "border-block-end-style", + "border-block-end-color" + ] + }, + { + name: "border-inline", + longhands: [ + "border-inline-start-width", + "border-inline-start-style", + "border-inline-start-color", + "border-inline-end-width", + "border-inline-end-style", + "border-inline-end-color" + ] + } +]; +function tryCombineNamedShorthand(shorthandName, longhands, d, declMap, processed, declarations, declIndices) { + if (!longhands.includes(d.name)) return null; + const allDecls = longhands.map((name) => declMap.get(name)); + if (!allDecls.every((other) => other && !processed.has(other) && other.important === d.important)) return null; + if (checkIntervening(allDecls, declarations, declIndices)) return null; + const record = {}; + for (const other of allDecls) record[other.name] = other.value; + const contracted = SHORTHANDS[shorthandName]?.contract(record); + if (contracted !== null && contracted !== void 0) { + for (const other of allDecls) processed.add(other); + return `${shorthandName}: ${contracted}${d.important ? " !important" : ""}`; + } + return null; +} +const CSS_WIDE_AND_DEFAULT = /* @__PURE__ */ new Set("initial,inherit,unset,revert,revert-layer,default".split(",")); +function serializeFontFamilyItem(tokens) { + const nonWs = tokens.filter((t) => t.type !== "whitespace" && t.type !== "comment" && t.type !== "EOF"); + if (nonWs.length === 1 && nonWs[0].type === "string") { + let strVal = nonWs[0].value; + if (strVal.startsWith("'") && strVal.endsWith("'") || strVal.startsWith("\"") && strVal.endsWith("\"")) strVal = strVal.slice(1, -1); + const lower = strVal.toLowerCase(); + if (GENERIC_FONT_FAMILIES.has(lower) || CSS_WIDE_AND_DEFAULT.has(lower)) return `"${strVal}"`; + if (strVal !== strVal.trim() || /\s{2,}|\t|\n|\r/.test(strVal)) return `"${strVal}"`; + const words = strVal.split(" "); + return words.length > 0 && words.every((word) => word.length > 0 && !/^[0-9]|^--|^-[0-9]/.test(word) && /^[a-zA-Z_-][a-zA-Z0-9_-]*$/.test(word)) ? strVal : `"${strVal}"`; + } + if (nonWs.every((t) => t.type === "ident")) return nonWs.map((t) => serializeNode(t, false)).join(" "); + return tokens.map((t) => serializeNode(t, false)).join(""); +} +function serializeFontFamily(values) { + const groups = []; + let current = []; + for (const token of values) if (token.type === "comma") { + groups.push(current); + current = []; + } else current.push(token); + if (current.length > 0) groups.push(current); + return groups.map((g) => serializeFontFamilyItem(g).trim()).filter((s) => s.length > 0).join(", "); +} +function serializeDeclarations(declarations) { + if (declarations.length === 0) return ""; + const declMap = /* @__PURE__ */ new Map(); + const declIndices = /* @__PURE__ */ new Map(); + for (let i = 0; i < declarations.length; i++) { + const d = declarations[i]; + declMap.set(d.name, d); + declIndices.set(d, i); + } + if (declarations.length >= ALL_SHORTHAND_LONGHANDS.length) { + const firstDecl = declMap.get(ALL_SHORTHAND_LONGHANDS[0]); + if (firstDecl) { + const firstValLower = serialize(firstDecl.value).trim().toLowerCase(); + const firstImportant = firstDecl.important; + if (CSS_WIDE_AND_DEFAULT.has(firstValLower) || firstValLower.startsWith("var(")) { + if (ALL_SHORTHAND_LONGHANDS.every((lh) => { + const d = declMap.get(lh); + return d && serialize(d.value).trim().toLowerCase() === firstValLower && d.important === firstImportant; + })) { + const allLonghandsSet = new Set(ALL_SHORTHAND_LONGHANDS); + const allDecl = { + type: "declaration", + name: "all", + value: firstDecl.value, + important: firstImportant + }; + const newDecls = []; + let inserted = false; + for (const d of declarations) if (allLonghandsSet.has(d.name)) { + if (!inserted) { + newDecls.push(allDecl); + inserted = true; + } + } else newDecls.push(d); + return serializeDeclarations(newDecls); + } + } + } + } + const processed = /* @__PURE__ */ new Set(); + const result = []; + for (const d of declarations) { + if (processed.has(d)) continue; + let combined = tryCombineBorderFull(d, declMap, processed, declarations, declIndices); + if (!combined) for (const spec of ORDERED_SHORTHAND_COMBINERS) { + combined = tryCombineNamedShorthand(spec.name, spec.longhands, d, declMap, processed, declarations, declIndices); + if (combined) break; + } + if (!combined) combined = tryCombineBoxShorthand(d, declMap, processed, declarations, declIndices); + if (!combined) { + const generic = tryCombineGenericShorthand(d, declMap, processed, declarations, declIndices); + if (generic) { + const sides = [ + "border-top", + "border-right", + "border-bottom", + "border-left" + ]; + if (sides.includes(generic.name)) { + const sideResults = sides.map((side) => { + if (side === generic.name) return generic; + const existing = declMap.get(side); + if (existing && !processed.has(existing)) return { + name: side, + value: serialize(existing.value).trim(), + important: existing.important, + decl: existing + }; + const longhands = genericShorthands[side]; + if (!longhands) return null; + const sideLonghands = longhands.map((lh) => declMap.get(lh)); + if (sideLonghands.every((lh) => lh && !processed.has(lh) && lh.important === generic.important)) { + if (checkIntervening(sideLonghands, declarations, declIndices)) return null; + return { + name: side, + value: sideLonghands.map((lh) => serialize(lh.value).trim()).filter((v) => v !== "").join(" "), + important: generic.important, + longhands: sideLonghands + }; + } + return null; + }); + if (sideResults.every((r) => r !== null && r.value === generic.value && r.important === generic.important)) { + for (const r of sideResults) if (r && "longhands" in r) r.longhands.forEach((lh) => processed.add(lh)); + else if (r && "decl" in r) processed.add(r.decl); + combined = `border: ${generic.value}${generic.important ? " !important" : ""}`; + } else combined = `${generic.name}: ${generic.value}${generic.important ? " !important" : ""}`; + } else combined = `${generic.name}: ${generic.value}${generic.important ? " !important" : ""}`; + } + } + if (!combined) combined = tryCombineLogicalShorthand(d, declMap, processed, declarations, declIndices); + if (combined) result.push(combined); + else { + const isCustom = d.name.startsWith("--"); + let val; + if (d.name === "font-family") val = serializeFontFamily(d.value); + else if (d.name === "flex-basis" && serialize(d.value).trim() === "0") val = "0px"; + else val = d.raw && !d.raw.includes("var(") ? d.raw : serialize(d.value, isCustom, d.name).trim(); + result.push(`${serializeIdentifier(d.name)}: ${val}${d.important ? " !important" : ""}`); + processed.add(d); + } + } + return result.join("; ") + ";"; +} +function serializeSelectorList(list, nsContext) { + const context = { + hasDefaultNamespace: typeof nsContext === "boolean" ? nsContext : Boolean(nsContext?.hasDefaultNamespace), + defaultNamespacePrefixes: typeof nsContext === "object" && nsContext !== null ? nsContext.defaultNamespacePrefixes : void 0 + }; + return list.selectors.map((s) => s.type === "invalid-selector" ? serialize(s.tokens) : serializeComplexSelector(s, context)).join(", "); +} +function serializeComplexSelector(complex, nsContext) { + const { hasDefaultNamespace, defaultNamespacePrefixes } = nsContext; + return complex.items.map((item, idx) => { + if (item.type === "combinator") { + if (item.value === " ") return " "; + return idx === 0 ? `${item.value} ` : ` ${item.value} `; + } + return item.selectors.filter((s, sIdx) => { + if (s.type === "universal-selector" && item.selectors.length > 1 && sIdx === 0) { + const isDefaultNs = s.namespace !== void 0 && s.namespace !== "" && defaultNamespacePrefixes?.has(s.namespace); + if (s.namespace === void 0 || isDefaultNs || s.namespace === "*" && !hasDefaultNamespace) return false; + } + return true; + }).map((s) => serializeSimpleSelector(s, nsContext)).join(""); + }).join(""); +} +function formatAnPlusB(tokens) { + const parsed = parseAnPlusB(tokens); + if (parsed === null) return serialize(tokens).trim(); + const { a, b } = parsed; + if (a === 0) return b.toString(); + const partA = a === 1 ? "n" : a === -1 ? "-n" : `${a}n`; + if (b === 0) return partA; + return b > 0 ? `${partA}+${b}` : `${partA}${b}`; +} +function formatSelectorNamespacePrefix(namespace, nsContext, isElementSelector) { + if (namespace === void 0) return ""; + if (isElementSelector && namespace !== "" && nsContext.defaultNamespacePrefixes?.has(namespace)) return ""; + if (namespace === "*") return !isElementSelector || nsContext.hasDefaultNamespace ? "*|" : ""; + if (namespace === "") return isElementSelector ? "|" : ""; + return `${serializeIdentifier(namespace)}|`; +} +const NTH_PSEUDO_CLASSES = /* @__PURE__ */ new Set("nth-child,nth-last-child,nth-of-type,nth-last-of-type".split(",")); +function serializeSimpleSelector(simple, nsContext) { + switch (simple.type) { + case "type-selector": return formatSelectorNamespacePrefix(simple.namespace, nsContext, true) + serializeIdentifier(simple.name); + case "universal-selector": return formatSelectorNamespacePrefix(simple.namespace, nsContext, true) + "*"; + case "id-selector": return "#" + serializeIdentifier(simple.name); + case "class-selector": return "." + serializeIdentifier(simple.name); + case "attribute-selector": { + let attr = "[" + formatSelectorNamespacePrefix(simple.namespace, nsContext, false) + serializeIdentifier(simple.name); + if (simple.operator) attr += simple.operator + serializeString(simple.value || ""); + if (simple.flags) attr += " " + simple.flags; + return attr + "]"; + } + case "pseudo-class-selector": { + let pc = `:${simple.name}`; + const isNth = NTH_PSEUDO_CLASSES.has(simple.name.toLowerCase()); + if (simple.argument) { + if ("type" in simple.argument && simple.argument.type === "selector-list") { + const selStr = serializeSelectorList(simple.argument, nsContext); + pc += isNth && simple.nth ? `(${formatAnPlusB(simple.nth)} of ${selStr})` : `(${selStr})`; + } else { + const tokens = simple.argument; + pc += `(${isNth ? formatAnPlusB(tokens) : serialize(tokens).trim()})`; + } + } + return pc; + } + case "pseudo-element-selector": { + let pe = `::${simple.name}`; + if (simple.argument) pe += "type" in simple.argument && simple.argument.type === "selector-list" ? `(${serializeSelectorList(simple.argument, nsContext)})` : `(${serialize(simple.argument).trim()})`; + return pe; + } + case "nesting-selector": return "&"; + default: return ""; + } +} +// src/AbstractTokenizer.ts +/** +* @license +* Copyright 2026 Google LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +var AbstractTokenizer = class { + unicodeRangesAllowed = false; + errors = []; + parseError(message) { + this.errors.push({ message }); + console.warn(`CSS Parse Error: ${message}`); + } + consumeToken() { + while (true) { + this.consumeComments(); + const cp = this.consume(); + if (cp === -1) return { + type: "EOF", + value: "" + }; + if (this.isWhitespace(cp)) { + while (this.isWhitespace(this.cp)) this.consume(); + return { + type: "whitespace", + value: " " + }; + } + switch (cp) { + case 34: return this.consumeStringToken(34); + case 35: + if (this.isIdentCodePoint(this.cp) || this.isValidEscape(this.cp, this.peek(1))) { + const token = { + type: "hash", + value: "", + hashType: "unrestricted" + }; + if (this.wouldStartIdentSequence(this.cp, this.peek(1), this.peek(2))) token.hashType = "id"; + token.value = this.consumeIdentSequence(); + return token; + } + return { + type: "delim", + value: String.fromCodePoint(cp) + }; + case 39: return this.consumeStringToken(39); + case 40: return { + type: "(", + value: "(" + }; + case 41: return { + type: ")", + value: ")" + }; + case 43: + if (this.wouldStartNumber(cp, this.cp, this.peek(1))) { + this.reconsume(); + return this.consumeNumericToken(); + } + return { + type: "delim", + value: "+" + }; + case 44: return { + type: "comma", + value: "," + }; + case 45: + if (this.wouldStartNumber(cp, this.cp, this.peek(1))) { + this.reconsume(); + return this.consumeNumericToken(); + } + if (this.cp === 45 && this.peek(1) === 62) { + this.consume(); + this.consume(); + return { + type: "CDC", + value: "-->" + }; + } + if (this.wouldStartIdentSequence(cp, this.cp, this.peek(1))) { + this.reconsume(); + return this.consumeIdentLikeToken(); + } + return { + type: "delim", + value: "-" + }; + case 46: + if (this.wouldStartNumber(cp, this.cp, this.peek(1))) { + this.reconsume(); + return this.consumeNumericToken(); + } + return { + type: "delim", + value: "." + }; + case 47: return { + type: "delim", + value: "/" + }; + case 58: return { + type: "colon", + value: ":" + }; + case 59: return { + type: "semicolon", + value: ";" + }; + case 60: + if (this.cp === 33 && this.peek(1) === 45 && this.peek(2) === 45) { + this.consume(); + this.consume(); + this.consume(); + return { + type: "CDO", + value: "