This commit is contained in:
CHEVALLIER Abel
2025-11-13 16:23:22 +01:00
parent de9c515a47
commit cb235644dc
34924 changed files with 3811102 additions and 0 deletions

11
node_modules/css-what/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,11 @@
Copyright (c) Felix Böhm
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

4
node_modules/css-what/dist/commonjs/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,4 @@
export * from "./types.js";
export { isTraversal, parse } from "./parse.js";
export { stringify } from "./stringify.js";
//# sourceMappingURL=index.d.ts.map

1
node_modules/css-what/dist/commonjs/index.d.ts.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC"}

23
node_modules/css-what/dist/commonjs/index.js generated vendored Normal file
View File

@@ -0,0 +1,23 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.stringify = exports.parse = exports.isTraversal = void 0;
__exportStar(require("./types.js"), exports);
var parse_js_1 = require("./parse.js");
Object.defineProperty(exports, "isTraversal", { enumerable: true, get: function () { return parse_js_1.isTraversal; } });
Object.defineProperty(exports, "parse", { enumerable: true, get: function () { return parse_js_1.parse; } });
var stringify_js_1 = require("./stringify.js");
Object.defineProperty(exports, "stringify", { enumerable: true, get: function () { return stringify_js_1.stringify; } });

3
node_modules/css-what/dist/commonjs/package.json generated vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"type": "commonjs"
}

19
node_modules/css-what/dist/commonjs/parse.d.ts generated vendored Normal file
View File

@@ -0,0 +1,19 @@
import { type Selector, type Traversal } from "./types.js";
/**
* Checks whether a specific selector is a traversal.
* This is useful eg. in swapping the order of elements that
* are not traversals.
*
* @param selector Selector to check.
*/
export declare function isTraversal(selector: Selector): selector is Traversal;
/**
* Parses `selector`.
*
* @param selector Selector to parse.
* @returns Returns a two-dimensional array.
* The first dimension represents selectors separated by commas (eg. `sub1, sub2`),
* the second contains the relevant tokens for that selector.
*/
export declare function parse(selector: string): Selector[][];
//# sourceMappingURL=parse.d.ts.map

1
node_modules/css-what/dist/commonjs/parse.d.ts.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/parse.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,KAAK,QAAQ,EAGb,KAAK,SAAS,EAIjB,MAAM,YAAY,CAAC;AAyEpB;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,QAAQ,GAAG,QAAQ,IAAI,SAAS,CAcrE;AAuCD;;;;;;;GAOG;AACH,wBAAgB,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,EAAE,EAAE,CAUpD"}

489
node_modules/css-what/dist/commonjs/parse.js generated vendored Normal file
View File

@@ -0,0 +1,489 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isTraversal = isTraversal;
exports.parse = parse;
const types_js_1 = require("./types.js");
const reName = /^[^#\\]?(?:\\(?:[\da-f]{1,6}\s?|.)|[\w\u00B0-\uFFFF-])+/;
const reEscape = /\\([\da-f]{1,6}\s?|(\s)|.)/gi;
var CharCode;
(function (CharCode) {
CharCode[CharCode["LeftParenthesis"] = 40] = "LeftParenthesis";
CharCode[CharCode["RightParenthesis"] = 41] = "RightParenthesis";
CharCode[CharCode["LeftSquareBracket"] = 91] = "LeftSquareBracket";
CharCode[CharCode["RightSquareBracket"] = 93] = "RightSquareBracket";
CharCode[CharCode["Comma"] = 44] = "Comma";
CharCode[CharCode["Period"] = 46] = "Period";
CharCode[CharCode["Colon"] = 58] = "Colon";
CharCode[CharCode["SingleQuote"] = 39] = "SingleQuote";
CharCode[CharCode["DoubleQuote"] = 34] = "DoubleQuote";
CharCode[CharCode["Plus"] = 43] = "Plus";
CharCode[CharCode["Tilde"] = 126] = "Tilde";
CharCode[CharCode["QuestionMark"] = 63] = "QuestionMark";
CharCode[CharCode["ExclamationMark"] = 33] = "ExclamationMark";
CharCode[CharCode["Slash"] = 47] = "Slash";
CharCode[CharCode["Equal"] = 61] = "Equal";
CharCode[CharCode["Dollar"] = 36] = "Dollar";
CharCode[CharCode["Pipe"] = 124] = "Pipe";
CharCode[CharCode["Circumflex"] = 94] = "Circumflex";
CharCode[CharCode["Asterisk"] = 42] = "Asterisk";
CharCode[CharCode["GreaterThan"] = 62] = "GreaterThan";
CharCode[CharCode["LessThan"] = 60] = "LessThan";
CharCode[CharCode["Hash"] = 35] = "Hash";
CharCode[CharCode["LowerI"] = 105] = "LowerI";
CharCode[CharCode["LowerS"] = 115] = "LowerS";
CharCode[CharCode["BackSlash"] = 92] = "BackSlash";
// Whitespace
CharCode[CharCode["Space"] = 32] = "Space";
CharCode[CharCode["Tab"] = 9] = "Tab";
CharCode[CharCode["NewLine"] = 10] = "NewLine";
CharCode[CharCode["FormFeed"] = 12] = "FormFeed";
CharCode[CharCode["CarriageReturn"] = 13] = "CarriageReturn";
})(CharCode || (CharCode = {}));
const actionTypes = new Map([
[CharCode.Tilde, types_js_1.AttributeAction.Element],
[CharCode.Circumflex, types_js_1.AttributeAction.Start],
[CharCode.Dollar, types_js_1.AttributeAction.End],
[CharCode.Asterisk, types_js_1.AttributeAction.Any],
[CharCode.ExclamationMark, types_js_1.AttributeAction.Not],
[CharCode.Pipe, types_js_1.AttributeAction.Hyphen],
]);
// Pseudos, whose data property is parsed as well.
const unpackPseudos = new Set([
"has",
"not",
"matches",
"is",
"where",
"host",
"host-context",
]);
/**
* Pseudo elements defined in CSS Level 1 and CSS Level 2 can be written with
* a single colon; eg. :before will turn into ::before.
*
* @see {@link https://www.w3.org/TR/2018/WD-selectors-4-20181121/#pseudo-element-syntax}
*/
const pseudosToPseudoElements = new Set([
"before",
"after",
"first-line",
"first-letter",
]);
/**
* Checks whether a specific selector is a traversal.
* This is useful eg. in swapping the order of elements that
* are not traversals.
*
* @param selector Selector to check.
*/
function isTraversal(selector) {
switch (selector.type) {
case types_js_1.SelectorType.Adjacent:
case types_js_1.SelectorType.Child:
case types_js_1.SelectorType.Descendant:
case types_js_1.SelectorType.Parent:
case types_js_1.SelectorType.Sibling:
case types_js_1.SelectorType.ColumnCombinator: {
return true;
}
default: {
return false;
}
}
}
const stripQuotesFromPseudos = new Set(["contains", "icontains"]);
// Unescape function taken from https://github.com/jquery/sizzle/blob/master/src/sizzle.js#L152
function funescape(_, escaped, escapedWhitespace) {
const high = Number.parseInt(escaped, 16) - 65536;
// NaN means non-codepoint
return high !== high || escapedWhitespace
? escaped
: high < 0
? // BMP codepoint
String.fromCharCode(high + 65536)
: // Supplemental Plane codepoint (surrogate pair)
String.fromCharCode((high >> 10) | 55296, (high & 1023) | 56320);
}
function unescapeCSS(cssString) {
return cssString.replace(reEscape, funescape);
}
function isQuote(c) {
return c === CharCode.SingleQuote || c === CharCode.DoubleQuote;
}
function isWhitespace(c) {
return (c === CharCode.Space ||
c === CharCode.Tab ||
c === CharCode.NewLine ||
c === CharCode.FormFeed ||
c === CharCode.CarriageReturn);
}
/**
* Parses `selector`.
*
* @param selector Selector to parse.
* @returns Returns a two-dimensional array.
* The first dimension represents selectors separated by commas (eg. `sub1, sub2`),
* the second contains the relevant tokens for that selector.
*/
function parse(selector) {
const subselects = [];
const endIndex = parseSelector(subselects, `${selector}`, 0);
if (endIndex < selector.length) {
throw new Error(`Unmatched selector: ${selector.slice(endIndex)}`);
}
return subselects;
}
function parseSelector(subselects, selector, selectorIndex) {
let tokens = [];
function getName(offset) {
const match = selector.slice(selectorIndex + offset).match(reName);
if (!match) {
throw new Error(`Expected name, found ${selector.slice(selectorIndex)}`);
}
const [name] = match;
selectorIndex += offset + name.length;
return unescapeCSS(name);
}
function stripWhitespace(offset) {
selectorIndex += offset;
while (selectorIndex < selector.length &&
isWhitespace(selector.charCodeAt(selectorIndex))) {
selectorIndex++;
}
}
function readValueWithParenthesis() {
selectorIndex += 1;
const start = selectorIndex;
for (let counter = 1; selectorIndex < selector.length; selectorIndex++) {
switch (selector.charCodeAt(selectorIndex)) {
case CharCode.BackSlash: {
// Skip next character
selectorIndex += 1;
break;
}
case CharCode.LeftParenthesis: {
counter += 1;
break;
}
case CharCode.RightParenthesis: {
counter -= 1;
if (counter === 0) {
return unescapeCSS(selector.slice(start, selectorIndex++));
}
break;
}
}
}
throw new Error("Parenthesis not matched");
}
function ensureNotTraversal() {
if (tokens.length > 0 && isTraversal(tokens[tokens.length - 1])) {
throw new Error("Did not expect successive traversals.");
}
}
function addTraversal(type) {
if (tokens.length > 0 &&
tokens[tokens.length - 1].type === types_js_1.SelectorType.Descendant) {
tokens[tokens.length - 1].type = type;
return;
}
ensureNotTraversal();
tokens.push({ type });
}
function addSpecialAttribute(name, action) {
tokens.push({
type: types_js_1.SelectorType.Attribute,
name,
action,
value: getName(1),
namespace: null,
ignoreCase: "quirks",
});
}
/**
* We have finished parsing the current part of the selector.
*
* Remove descendant tokens at the end if they exist,
* and return the last index, so that parsing can be
* picked up from here.
*/
function finalizeSubselector() {
if (tokens.length > 0 &&
tokens[tokens.length - 1].type === types_js_1.SelectorType.Descendant) {
tokens.pop();
}
if (tokens.length === 0) {
throw new Error("Empty sub-selector");
}
subselects.push(tokens);
}
stripWhitespace(0);
if (selector.length === selectorIndex) {
return selectorIndex;
}
loop: while (selectorIndex < selector.length) {
const firstChar = selector.charCodeAt(selectorIndex);
switch (firstChar) {
// Whitespace
case CharCode.Space:
case CharCode.Tab:
case CharCode.NewLine:
case CharCode.FormFeed:
case CharCode.CarriageReturn: {
if (tokens.length === 0 ||
tokens[0].type !== types_js_1.SelectorType.Descendant) {
ensureNotTraversal();
tokens.push({ type: types_js_1.SelectorType.Descendant });
}
stripWhitespace(1);
break;
}
// Traversals
case CharCode.GreaterThan: {
addTraversal(types_js_1.SelectorType.Child);
stripWhitespace(1);
break;
}
case CharCode.LessThan: {
addTraversal(types_js_1.SelectorType.Parent);
stripWhitespace(1);
break;
}
case CharCode.Tilde: {
addTraversal(types_js_1.SelectorType.Sibling);
stripWhitespace(1);
break;
}
case CharCode.Plus: {
addTraversal(types_js_1.SelectorType.Adjacent);
stripWhitespace(1);
break;
}
// Special attribute selectors: .class, #id
case CharCode.Period: {
addSpecialAttribute("class", types_js_1.AttributeAction.Element);
break;
}
case CharCode.Hash: {
addSpecialAttribute("id", types_js_1.AttributeAction.Equals);
break;
}
case CharCode.LeftSquareBracket: {
stripWhitespace(1);
// Determine attribute name and namespace
let name;
let namespace = null;
if (selector.charCodeAt(selectorIndex) === CharCode.Pipe) {
// Equivalent to no namespace
name = getName(1);
}
else if (selector.startsWith("*|", selectorIndex)) {
namespace = "*";
name = getName(2);
}
else {
name = getName(0);
if (selector.charCodeAt(selectorIndex) === CharCode.Pipe &&
selector.charCodeAt(selectorIndex + 1) !==
CharCode.Equal) {
namespace = name;
name = getName(1);
}
}
stripWhitespace(0);
// Determine comparison operation
let action = types_js_1.AttributeAction.Exists;
const possibleAction = actionTypes.get(selector.charCodeAt(selectorIndex));
if (possibleAction) {
action = possibleAction;
if (selector.charCodeAt(selectorIndex + 1) !==
CharCode.Equal) {
throw new Error("Expected `=`");
}
stripWhitespace(2);
}
else if (selector.charCodeAt(selectorIndex) === CharCode.Equal) {
action = types_js_1.AttributeAction.Equals;
stripWhitespace(1);
}
// Determine value
let value = "";
let ignoreCase = null;
if (action !== "exists") {
if (isQuote(selector.charCodeAt(selectorIndex))) {
const quote = selector.charCodeAt(selectorIndex);
selectorIndex += 1;
const sectionStart = selectorIndex;
while (selectorIndex < selector.length &&
selector.charCodeAt(selectorIndex) !== quote) {
selectorIndex +=
// Skip next character if it is escaped
selector.charCodeAt(selectorIndex) ===
CharCode.BackSlash
? 2
: 1;
}
if (selector.charCodeAt(selectorIndex) !== quote) {
throw new Error("Attribute value didn't end");
}
value = unescapeCSS(selector.slice(sectionStart, selectorIndex));
selectorIndex += 1;
}
else {
const valueStart = selectorIndex;
while (selectorIndex < selector.length &&
!isWhitespace(selector.charCodeAt(selectorIndex)) &&
selector.charCodeAt(selectorIndex) !==
CharCode.RightSquareBracket) {
selectorIndex +=
// Skip next character if it is escaped
selector.charCodeAt(selectorIndex) ===
CharCode.BackSlash
? 2
: 1;
}
value = unescapeCSS(selector.slice(valueStart, selectorIndex));
}
stripWhitespace(0);
// See if we have a force ignore flag
switch (selector.charCodeAt(selectorIndex) | 0x20) {
// If the forceIgnore flag is set (either `i` or `s`), use that value
case CharCode.LowerI: {
ignoreCase = true;
stripWhitespace(1);
break;
}
case CharCode.LowerS: {
ignoreCase = false;
stripWhitespace(1);
break;
}
}
}
if (selector.charCodeAt(selectorIndex) !==
CharCode.RightSquareBracket) {
throw new Error("Attribute selector didn't terminate");
}
selectorIndex += 1;
const attributeSelector = {
type: types_js_1.SelectorType.Attribute,
name,
action,
value,
namespace,
ignoreCase,
};
tokens.push(attributeSelector);
break;
}
case CharCode.Colon: {
if (selector.charCodeAt(selectorIndex + 1) === CharCode.Colon) {
tokens.push({
type: types_js_1.SelectorType.PseudoElement,
name: getName(2).toLowerCase(),
data: selector.charCodeAt(selectorIndex) ===
CharCode.LeftParenthesis
? readValueWithParenthesis()
: null,
});
break;
}
const name = getName(1).toLowerCase();
if (pseudosToPseudoElements.has(name)) {
tokens.push({
type: types_js_1.SelectorType.PseudoElement,
name,
data: null,
});
break;
}
let data = null;
if (selector.charCodeAt(selectorIndex) ===
CharCode.LeftParenthesis) {
if (unpackPseudos.has(name)) {
if (isQuote(selector.charCodeAt(selectorIndex + 1))) {
throw new Error(`Pseudo-selector ${name} cannot be quoted`);
}
data = [];
selectorIndex = parseSelector(data, selector, selectorIndex + 1);
if (selector.charCodeAt(selectorIndex) !==
CharCode.RightParenthesis) {
throw new Error(`Missing closing parenthesis in :${name} (${selector})`);
}
selectorIndex += 1;
}
else {
data = readValueWithParenthesis();
if (stripQuotesFromPseudos.has(name)) {
const quot = data.charCodeAt(0);
if (quot === data.charCodeAt(data.length - 1) &&
isQuote(quot)) {
data = data.slice(1, -1);
}
}
data = unescapeCSS(data);
}
}
tokens.push({ type: types_js_1.SelectorType.Pseudo, name, data });
break;
}
case CharCode.Comma: {
finalizeSubselector();
tokens = [];
stripWhitespace(1);
break;
}
default: {
if (selector.startsWith("/*", selectorIndex)) {
const endIndex = selector.indexOf("*/", selectorIndex + 2);
if (endIndex < 0) {
throw new Error("Comment was not terminated");
}
selectorIndex = endIndex + 2;
// Remove leading whitespace
if (tokens.length === 0) {
stripWhitespace(0);
}
break;
}
let namespace = null;
let name;
if (firstChar === CharCode.Asterisk) {
selectorIndex += 1;
name = "*";
}
else if (firstChar === CharCode.Pipe) {
name = "";
if (selector.charCodeAt(selectorIndex + 1) === CharCode.Pipe) {
addTraversal(types_js_1.SelectorType.ColumnCombinator);
stripWhitespace(2);
break;
}
}
else if (reName.test(selector.slice(selectorIndex))) {
name = getName(0);
}
else {
break loop;
}
if (selector.charCodeAt(selectorIndex) === CharCode.Pipe &&
selector.charCodeAt(selectorIndex + 1) !== CharCode.Pipe) {
namespace = name;
if (selector.charCodeAt(selectorIndex + 1) ===
CharCode.Asterisk) {
name = "*";
selectorIndex += 2;
}
else {
name = getName(1);
}
}
tokens.push(name === "*"
? { type: types_js_1.SelectorType.Universal, namespace }
: { type: types_js_1.SelectorType.Tag, name, namespace });
}
}
}
finalizeSubselector();
return selectorIndex;
}

8
node_modules/css-what/dist/commonjs/stringify.d.ts generated vendored Normal file
View File

@@ -0,0 +1,8 @@
import { type Selector } from "./types.js";
/**
* Turns `selector` back into a string.
*
* @param selector Selector to stringify.
*/
export declare function stringify(selector: Selector[][]): string;
//# sourceMappingURL=stringify.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"stringify.d.ts","sourceRoot":"","sources":["../../src/stringify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAiC,MAAM,YAAY,CAAC;AA8B1E;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE,GAAG,MAAM,CAUxD"}

150
node_modules/css-what/dist/commonjs/stringify.js generated vendored Normal file
View File

@@ -0,0 +1,150 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.stringify = stringify;
const types_js_1 = require("./types.js");
const attribValueChars = ["\\", '"'];
const pseudoValueChars = [...attribValueChars, "(", ")"];
const charsToEscapeInAttributeValue = new Set(attribValueChars.map((c) => c.charCodeAt(0)));
const charsToEscapeInPseudoValue = new Set(pseudoValueChars.map((c) => c.charCodeAt(0)));
const charsToEscapeInName = new Set([
...pseudoValueChars,
"~",
"^",
"$",
"*",
"+",
"!",
"|",
":",
"[",
"]",
" ",
".",
"%",
].map((c) => c.charCodeAt(0)));
/**
* Turns `selector` back into a string.
*
* @param selector Selector to stringify.
*/
function stringify(selector) {
return selector
.map((token) => token
.map((token, index, array) => stringifyToken(token, index, array))
.join(""))
.join(", ");
}
function stringifyToken(token, index, array) {
switch (token.type) {
// Simple types
case types_js_1.SelectorType.Child: {
return index === 0 ? "> " : " > ";
}
case types_js_1.SelectorType.Parent: {
return index === 0 ? "< " : " < ";
}
case types_js_1.SelectorType.Sibling: {
return index === 0 ? "~ " : " ~ ";
}
case types_js_1.SelectorType.Adjacent: {
return index === 0 ? "+ " : " + ";
}
case types_js_1.SelectorType.Descendant: {
return " ";
}
case types_js_1.SelectorType.ColumnCombinator: {
return index === 0 ? "|| " : " || ";
}
case types_js_1.SelectorType.Universal: {
// Return an empty string if the selector isn't needed.
return token.namespace === "*" &&
index + 1 < array.length &&
"name" in array[index + 1]
? ""
: `${getNamespace(token.namespace)}*`;
}
case types_js_1.SelectorType.Tag: {
return getNamespacedName(token);
}
case types_js_1.SelectorType.PseudoElement: {
return `::${escapeName(token.name, charsToEscapeInName)}${token.data === null
? ""
: `(${escapeName(token.data, charsToEscapeInPseudoValue)})`}`;
}
case types_js_1.SelectorType.Pseudo: {
return `:${escapeName(token.name, charsToEscapeInName)}${token.data === null
? ""
: `(${typeof token.data === "string"
? escapeName(token.data, charsToEscapeInPseudoValue)
: stringify(token.data)})`}`;
}
case types_js_1.SelectorType.Attribute: {
if (token.name === "id" &&
token.action === types_js_1.AttributeAction.Equals &&
token.ignoreCase === "quirks" &&
!token.namespace) {
return `#${escapeName(token.value, charsToEscapeInName)}`;
}
if (token.name === "class" &&
token.action === types_js_1.AttributeAction.Element &&
token.ignoreCase === "quirks" &&
!token.namespace) {
return `.${escapeName(token.value, charsToEscapeInName)}`;
}
const name = getNamespacedName(token);
if (token.action === types_js_1.AttributeAction.Exists) {
return `[${name}]`;
}
return `[${name}${getActionValue(token.action)}="${escapeName(token.value, charsToEscapeInAttributeValue)}"${token.ignoreCase === null ? "" : token.ignoreCase ? " i" : " s"}]`;
}
}
}
function getActionValue(action) {
switch (action) {
case types_js_1.AttributeAction.Equals: {
return "";
}
case types_js_1.AttributeAction.Element: {
return "~";
}
case types_js_1.AttributeAction.Start: {
return "^";
}
case types_js_1.AttributeAction.End: {
return "$";
}
case types_js_1.AttributeAction.Any: {
return "*";
}
case types_js_1.AttributeAction.Not: {
return "!";
}
case types_js_1.AttributeAction.Hyphen: {
return "|";
}
default: {
throw new Error("Shouldn't be here");
}
}
}
function getNamespacedName(token) {
return `${getNamespace(token.namespace)}${escapeName(token.name, charsToEscapeInName)}`;
}
function getNamespace(namespace) {
return namespace === null
? ""
: `${namespace === "*"
? "*"
: escapeName(namespace, charsToEscapeInName)}|`;
}
function escapeName(name, charsToEscape) {
let lastIndex = 0;
let escapedName = "";
for (let index = 0; index < name.length; index++) {
if (charsToEscape.has(name.charCodeAt(index))) {
escapedName += `${name.slice(lastIndex, index)}\\${name.charAt(index)}`;
lastIndex = index + 1;
}
}
return escapedName.length > 0 ? escapedName + name.slice(lastIndex) : name;
}

70
node_modules/css-what/dist/commonjs/types.d.ts generated vendored Normal file
View File

@@ -0,0 +1,70 @@
export type Selector = PseudoSelector | PseudoElement | AttributeSelector | TagSelector | UniversalSelector | Traversal;
export declare enum SelectorType {
Attribute = "attribute",
Pseudo = "pseudo",
PseudoElement = "pseudo-element",
Tag = "tag",
Universal = "universal",
Adjacent = "adjacent",
Child = "child",
Descendant = "descendant",
Parent = "parent",
Sibling = "sibling",
ColumnCombinator = "column-combinator"
}
/**
* Modes for ignore case.
*
* This could be updated to an enum, and the object is
* the current stand-in that will allow code to be updated
* without big changes.
*/
export declare const IgnoreCaseMode: {
readonly Unknown: null;
readonly QuirksMode: "quirks";
readonly IgnoreCase: true;
readonly CaseSensitive: false;
};
export interface AttributeSelector {
type: SelectorType.Attribute;
name: string;
action: AttributeAction;
value: string;
ignoreCase: "quirks" | boolean | null;
namespace: string | null;
}
export type DataType = Selector[][] | null | string;
export interface PseudoSelector {
type: SelectorType.Pseudo;
name: string;
data: DataType;
}
export interface PseudoElement {
type: SelectorType.PseudoElement;
name: string;
data: string | null;
}
export interface TagSelector {
type: SelectorType.Tag;
name: string;
namespace: string | null;
}
export interface UniversalSelector {
type: SelectorType.Universal;
namespace: string | null;
}
export interface Traversal {
type: TraversalType;
}
export declare enum AttributeAction {
Any = "any",
Element = "element",
End = "end",
Equals = "equals",
Exists = "exists",
Hyphen = "hyphen",
Not = "not",
Start = "start"
}
export type TraversalType = SelectorType.Adjacent | SelectorType.Child | SelectorType.Descendant | SelectorType.Parent | SelectorType.Sibling | SelectorType.ColumnCombinator;
//# sourceMappingURL=types.d.ts.map

1
node_modules/css-what/dist/commonjs/types.d.ts.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GACd,cAAc,GACd,aAAa,GACb,iBAAiB,GACjB,WAAW,GACX,iBAAiB,GACjB,SAAS,CAAC;AAEhB,oBAAY,YAAY;IACpB,SAAS,cAAc;IACvB,MAAM,WAAW;IACjB,aAAa,mBAAmB;IAChC,GAAG,QAAQ;IACX,SAAS,cAAc;IAGvB,QAAQ,aAAa;IACrB,KAAK,UAAU;IACf,UAAU,eAAe;IACzB,MAAM,WAAW;IACjB,OAAO,YAAY;IACnB,gBAAgB,sBAAsB;CACzC;AAED;;;;;;GAMG;AACH,eAAO,MAAM,cAAc;;;;;CAKjB,CAAC;AAEX,MAAM,WAAW,iBAAiB;IAC9B,IAAI,EAAE,YAAY,CAAC,SAAS,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,eAAe,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,QAAQ,GAAG,OAAO,GAAG,IAAI,CAAC;IACtC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,MAAM,QAAQ,GAAG,QAAQ,EAAE,EAAE,GAAG,IAAI,GAAG,MAAM,CAAC;AAEpD,MAAM,WAAW,cAAc;IAC3B,IAAI,EAAE,YAAY,CAAC,MAAM,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,QAAQ,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC1B,IAAI,EAAE,YAAY,CAAC,aAAa,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,YAAY,CAAC,GAAG,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,iBAAiB;IAC9B,IAAI,EAAE,YAAY,CAAC,SAAS,CAAC;IAC7B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,SAAS;IACtB,IAAI,EAAE,aAAa,CAAC;CACvB;AAED,oBAAY,eAAe;IACvB,GAAG,QAAQ;IACX,OAAO,YAAY;IACnB,GAAG,QAAQ;IACX,MAAM,WAAW;IACjB,MAAM,WAAW;IACjB,MAAM,WAAW;IACjB,GAAG,QAAQ;IACX,KAAK,UAAU;CAClB;AAED,MAAM,MAAM,aAAa,GACnB,YAAY,CAAC,QAAQ,GACrB,YAAY,CAAC,KAAK,GAClB,YAAY,CAAC,UAAU,GACvB,YAAY,CAAC,MAAM,GACnB,YAAY,CAAC,OAAO,GACpB,YAAY,CAAC,gBAAgB,CAAC"}

42
node_modules/css-what/dist/commonjs/types.js generated vendored Normal file
View File

@@ -0,0 +1,42 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AttributeAction = exports.IgnoreCaseMode = exports.SelectorType = void 0;
var SelectorType;
(function (SelectorType) {
SelectorType["Attribute"] = "attribute";
SelectorType["Pseudo"] = "pseudo";
SelectorType["PseudoElement"] = "pseudo-element";
SelectorType["Tag"] = "tag";
SelectorType["Universal"] = "universal";
// Traversals
SelectorType["Adjacent"] = "adjacent";
SelectorType["Child"] = "child";
SelectorType["Descendant"] = "descendant";
SelectorType["Parent"] = "parent";
SelectorType["Sibling"] = "sibling";
SelectorType["ColumnCombinator"] = "column-combinator";
})(SelectorType || (exports.SelectorType = SelectorType = {}));
/**
* Modes for ignore case.
*
* This could be updated to an enum, and the object is
* the current stand-in that will allow code to be updated
* without big changes.
*/
exports.IgnoreCaseMode = {
Unknown: null,
QuirksMode: "quirks",
IgnoreCase: true,
CaseSensitive: false,
};
var AttributeAction;
(function (AttributeAction) {
AttributeAction["Any"] = "any";
AttributeAction["Element"] = "element";
AttributeAction["End"] = "end";
AttributeAction["Equals"] = "equals";
AttributeAction["Exists"] = "exists";
AttributeAction["Hyphen"] = "hyphen";
AttributeAction["Not"] = "not";
AttributeAction["Start"] = "start";
})(AttributeAction || (exports.AttributeAction = AttributeAction = {}));

4
node_modules/css-what/dist/esm/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,4 @@
export * from "./types.js";
export { isTraversal, parse } from "./parse.js";
export { stringify } from "./stringify.js";
//# sourceMappingURL=index.d.ts.map

1
node_modules/css-what/dist/esm/index.d.ts.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC"}

3
node_modules/css-what/dist/esm/index.js generated vendored Normal file
View File

@@ -0,0 +1,3 @@
export * from "./types.js";
export { isTraversal, parse } from "./parse.js";
export { stringify } from "./stringify.js";

3
node_modules/css-what/dist/esm/package.json generated vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"type": "module"
}

19
node_modules/css-what/dist/esm/parse.d.ts generated vendored Normal file
View File

@@ -0,0 +1,19 @@
import { type Selector, type Traversal } from "./types.js";
/**
* Checks whether a specific selector is a traversal.
* This is useful eg. in swapping the order of elements that
* are not traversals.
*
* @param selector Selector to check.
*/
export declare function isTraversal(selector: Selector): selector is Traversal;
/**
* Parses `selector`.
*
* @param selector Selector to parse.
* @returns Returns a two-dimensional array.
* The first dimension represents selectors separated by commas (eg. `sub1, sub2`),
* the second contains the relevant tokens for that selector.
*/
export declare function parse(selector: string): Selector[][];
//# sourceMappingURL=parse.d.ts.map

1
node_modules/css-what/dist/esm/parse.d.ts.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/parse.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,KAAK,QAAQ,EAGb,KAAK,SAAS,EAIjB,MAAM,YAAY,CAAC;AAyEpB;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,QAAQ,GAAG,QAAQ,IAAI,SAAS,CAcrE;AAuCD;;;;;;;GAOG;AACH,wBAAgB,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,EAAE,EAAE,CAUpD"}

485
node_modules/css-what/dist/esm/parse.js generated vendored Normal file
View File

@@ -0,0 +1,485 @@
import { SelectorType, AttributeAction, } from "./types.js";
const reName = /^[^#\\]?(?:\\(?:[\da-f]{1,6}\s?|.)|[\w\u00B0-\uFFFF-])+/;
const reEscape = /\\([\da-f]{1,6}\s?|(\s)|.)/gi;
var CharCode;
(function (CharCode) {
CharCode[CharCode["LeftParenthesis"] = 40] = "LeftParenthesis";
CharCode[CharCode["RightParenthesis"] = 41] = "RightParenthesis";
CharCode[CharCode["LeftSquareBracket"] = 91] = "LeftSquareBracket";
CharCode[CharCode["RightSquareBracket"] = 93] = "RightSquareBracket";
CharCode[CharCode["Comma"] = 44] = "Comma";
CharCode[CharCode["Period"] = 46] = "Period";
CharCode[CharCode["Colon"] = 58] = "Colon";
CharCode[CharCode["SingleQuote"] = 39] = "SingleQuote";
CharCode[CharCode["DoubleQuote"] = 34] = "DoubleQuote";
CharCode[CharCode["Plus"] = 43] = "Plus";
CharCode[CharCode["Tilde"] = 126] = "Tilde";
CharCode[CharCode["QuestionMark"] = 63] = "QuestionMark";
CharCode[CharCode["ExclamationMark"] = 33] = "ExclamationMark";
CharCode[CharCode["Slash"] = 47] = "Slash";
CharCode[CharCode["Equal"] = 61] = "Equal";
CharCode[CharCode["Dollar"] = 36] = "Dollar";
CharCode[CharCode["Pipe"] = 124] = "Pipe";
CharCode[CharCode["Circumflex"] = 94] = "Circumflex";
CharCode[CharCode["Asterisk"] = 42] = "Asterisk";
CharCode[CharCode["GreaterThan"] = 62] = "GreaterThan";
CharCode[CharCode["LessThan"] = 60] = "LessThan";
CharCode[CharCode["Hash"] = 35] = "Hash";
CharCode[CharCode["LowerI"] = 105] = "LowerI";
CharCode[CharCode["LowerS"] = 115] = "LowerS";
CharCode[CharCode["BackSlash"] = 92] = "BackSlash";
// Whitespace
CharCode[CharCode["Space"] = 32] = "Space";
CharCode[CharCode["Tab"] = 9] = "Tab";
CharCode[CharCode["NewLine"] = 10] = "NewLine";
CharCode[CharCode["FormFeed"] = 12] = "FormFeed";
CharCode[CharCode["CarriageReturn"] = 13] = "CarriageReturn";
})(CharCode || (CharCode = {}));
const actionTypes = new Map([
[CharCode.Tilde, AttributeAction.Element],
[CharCode.Circumflex, AttributeAction.Start],
[CharCode.Dollar, AttributeAction.End],
[CharCode.Asterisk, AttributeAction.Any],
[CharCode.ExclamationMark, AttributeAction.Not],
[CharCode.Pipe, AttributeAction.Hyphen],
]);
// Pseudos, whose data property is parsed as well.
const unpackPseudos = new Set([
"has",
"not",
"matches",
"is",
"where",
"host",
"host-context",
]);
/**
* Pseudo elements defined in CSS Level 1 and CSS Level 2 can be written with
* a single colon; eg. :before will turn into ::before.
*
* @see {@link https://www.w3.org/TR/2018/WD-selectors-4-20181121/#pseudo-element-syntax}
*/
const pseudosToPseudoElements = new Set([
"before",
"after",
"first-line",
"first-letter",
]);
/**
* Checks whether a specific selector is a traversal.
* This is useful eg. in swapping the order of elements that
* are not traversals.
*
* @param selector Selector to check.
*/
export function isTraversal(selector) {
switch (selector.type) {
case SelectorType.Adjacent:
case SelectorType.Child:
case SelectorType.Descendant:
case SelectorType.Parent:
case SelectorType.Sibling:
case SelectorType.ColumnCombinator: {
return true;
}
default: {
return false;
}
}
}
const stripQuotesFromPseudos = new Set(["contains", "icontains"]);
// Unescape function taken from https://github.com/jquery/sizzle/blob/master/src/sizzle.js#L152
function funescape(_, escaped, escapedWhitespace) {
const high = Number.parseInt(escaped, 16) - 65536;
// NaN means non-codepoint
return high !== high || escapedWhitespace
? escaped
: high < 0
? // BMP codepoint
String.fromCharCode(high + 65536)
: // Supplemental Plane codepoint (surrogate pair)
String.fromCharCode((high >> 10) | 55296, (high & 1023) | 56320);
}
function unescapeCSS(cssString) {
return cssString.replace(reEscape, funescape);
}
function isQuote(c) {
return c === CharCode.SingleQuote || c === CharCode.DoubleQuote;
}
function isWhitespace(c) {
return (c === CharCode.Space ||
c === CharCode.Tab ||
c === CharCode.NewLine ||
c === CharCode.FormFeed ||
c === CharCode.CarriageReturn);
}
/**
* Parses `selector`.
*
* @param selector Selector to parse.
* @returns Returns a two-dimensional array.
* The first dimension represents selectors separated by commas (eg. `sub1, sub2`),
* the second contains the relevant tokens for that selector.
*/
export function parse(selector) {
const subselects = [];
const endIndex = parseSelector(subselects, `${selector}`, 0);
if (endIndex < selector.length) {
throw new Error(`Unmatched selector: ${selector.slice(endIndex)}`);
}
return subselects;
}
function parseSelector(subselects, selector, selectorIndex) {
let tokens = [];
function getName(offset) {
const match = selector.slice(selectorIndex + offset).match(reName);
if (!match) {
throw new Error(`Expected name, found ${selector.slice(selectorIndex)}`);
}
const [name] = match;
selectorIndex += offset + name.length;
return unescapeCSS(name);
}
function stripWhitespace(offset) {
selectorIndex += offset;
while (selectorIndex < selector.length &&
isWhitespace(selector.charCodeAt(selectorIndex))) {
selectorIndex++;
}
}
function readValueWithParenthesis() {
selectorIndex += 1;
const start = selectorIndex;
for (let counter = 1; selectorIndex < selector.length; selectorIndex++) {
switch (selector.charCodeAt(selectorIndex)) {
case CharCode.BackSlash: {
// Skip next character
selectorIndex += 1;
break;
}
case CharCode.LeftParenthesis: {
counter += 1;
break;
}
case CharCode.RightParenthesis: {
counter -= 1;
if (counter === 0) {
return unescapeCSS(selector.slice(start, selectorIndex++));
}
break;
}
}
}
throw new Error("Parenthesis not matched");
}
function ensureNotTraversal() {
if (tokens.length > 0 && isTraversal(tokens[tokens.length - 1])) {
throw new Error("Did not expect successive traversals.");
}
}
function addTraversal(type) {
if (tokens.length > 0 &&
tokens[tokens.length - 1].type === SelectorType.Descendant) {
tokens[tokens.length - 1].type = type;
return;
}
ensureNotTraversal();
tokens.push({ type });
}
function addSpecialAttribute(name, action) {
tokens.push({
type: SelectorType.Attribute,
name,
action,
value: getName(1),
namespace: null,
ignoreCase: "quirks",
});
}
/**
* We have finished parsing the current part of the selector.
*
* Remove descendant tokens at the end if they exist,
* and return the last index, so that parsing can be
* picked up from here.
*/
function finalizeSubselector() {
if (tokens.length > 0 &&
tokens[tokens.length - 1].type === SelectorType.Descendant) {
tokens.pop();
}
if (tokens.length === 0) {
throw new Error("Empty sub-selector");
}
subselects.push(tokens);
}
stripWhitespace(0);
if (selector.length === selectorIndex) {
return selectorIndex;
}
loop: while (selectorIndex < selector.length) {
const firstChar = selector.charCodeAt(selectorIndex);
switch (firstChar) {
// Whitespace
case CharCode.Space:
case CharCode.Tab:
case CharCode.NewLine:
case CharCode.FormFeed:
case CharCode.CarriageReturn: {
if (tokens.length === 0 ||
tokens[0].type !== SelectorType.Descendant) {
ensureNotTraversal();
tokens.push({ type: SelectorType.Descendant });
}
stripWhitespace(1);
break;
}
// Traversals
case CharCode.GreaterThan: {
addTraversal(SelectorType.Child);
stripWhitespace(1);
break;
}
case CharCode.LessThan: {
addTraversal(SelectorType.Parent);
stripWhitespace(1);
break;
}
case CharCode.Tilde: {
addTraversal(SelectorType.Sibling);
stripWhitespace(1);
break;
}
case CharCode.Plus: {
addTraversal(SelectorType.Adjacent);
stripWhitespace(1);
break;
}
// Special attribute selectors: .class, #id
case CharCode.Period: {
addSpecialAttribute("class", AttributeAction.Element);
break;
}
case CharCode.Hash: {
addSpecialAttribute("id", AttributeAction.Equals);
break;
}
case CharCode.LeftSquareBracket: {
stripWhitespace(1);
// Determine attribute name and namespace
let name;
let namespace = null;
if (selector.charCodeAt(selectorIndex) === CharCode.Pipe) {
// Equivalent to no namespace
name = getName(1);
}
else if (selector.startsWith("*|", selectorIndex)) {
namespace = "*";
name = getName(2);
}
else {
name = getName(0);
if (selector.charCodeAt(selectorIndex) === CharCode.Pipe &&
selector.charCodeAt(selectorIndex + 1) !==
CharCode.Equal) {
namespace = name;
name = getName(1);
}
}
stripWhitespace(0);
// Determine comparison operation
let action = AttributeAction.Exists;
const possibleAction = actionTypes.get(selector.charCodeAt(selectorIndex));
if (possibleAction) {
action = possibleAction;
if (selector.charCodeAt(selectorIndex + 1) !==
CharCode.Equal) {
throw new Error("Expected `=`");
}
stripWhitespace(2);
}
else if (selector.charCodeAt(selectorIndex) === CharCode.Equal) {
action = AttributeAction.Equals;
stripWhitespace(1);
}
// Determine value
let value = "";
let ignoreCase = null;
if (action !== "exists") {
if (isQuote(selector.charCodeAt(selectorIndex))) {
const quote = selector.charCodeAt(selectorIndex);
selectorIndex += 1;
const sectionStart = selectorIndex;
while (selectorIndex < selector.length &&
selector.charCodeAt(selectorIndex) !== quote) {
selectorIndex +=
// Skip next character if it is escaped
selector.charCodeAt(selectorIndex) ===
CharCode.BackSlash
? 2
: 1;
}
if (selector.charCodeAt(selectorIndex) !== quote) {
throw new Error("Attribute value didn't end");
}
value = unescapeCSS(selector.slice(sectionStart, selectorIndex));
selectorIndex += 1;
}
else {
const valueStart = selectorIndex;
while (selectorIndex < selector.length &&
!isWhitespace(selector.charCodeAt(selectorIndex)) &&
selector.charCodeAt(selectorIndex) !==
CharCode.RightSquareBracket) {
selectorIndex +=
// Skip next character if it is escaped
selector.charCodeAt(selectorIndex) ===
CharCode.BackSlash
? 2
: 1;
}
value = unescapeCSS(selector.slice(valueStart, selectorIndex));
}
stripWhitespace(0);
// See if we have a force ignore flag
switch (selector.charCodeAt(selectorIndex) | 0x20) {
// If the forceIgnore flag is set (either `i` or `s`), use that value
case CharCode.LowerI: {
ignoreCase = true;
stripWhitespace(1);
break;
}
case CharCode.LowerS: {
ignoreCase = false;
stripWhitespace(1);
break;
}
}
}
if (selector.charCodeAt(selectorIndex) !==
CharCode.RightSquareBracket) {
throw new Error("Attribute selector didn't terminate");
}
selectorIndex += 1;
const attributeSelector = {
type: SelectorType.Attribute,
name,
action,
value,
namespace,
ignoreCase,
};
tokens.push(attributeSelector);
break;
}
case CharCode.Colon: {
if (selector.charCodeAt(selectorIndex + 1) === CharCode.Colon) {
tokens.push({
type: SelectorType.PseudoElement,
name: getName(2).toLowerCase(),
data: selector.charCodeAt(selectorIndex) ===
CharCode.LeftParenthesis
? readValueWithParenthesis()
: null,
});
break;
}
const name = getName(1).toLowerCase();
if (pseudosToPseudoElements.has(name)) {
tokens.push({
type: SelectorType.PseudoElement,
name,
data: null,
});
break;
}
let data = null;
if (selector.charCodeAt(selectorIndex) ===
CharCode.LeftParenthesis) {
if (unpackPseudos.has(name)) {
if (isQuote(selector.charCodeAt(selectorIndex + 1))) {
throw new Error(`Pseudo-selector ${name} cannot be quoted`);
}
data = [];
selectorIndex = parseSelector(data, selector, selectorIndex + 1);
if (selector.charCodeAt(selectorIndex) !==
CharCode.RightParenthesis) {
throw new Error(`Missing closing parenthesis in :${name} (${selector})`);
}
selectorIndex += 1;
}
else {
data = readValueWithParenthesis();
if (stripQuotesFromPseudos.has(name)) {
const quot = data.charCodeAt(0);
if (quot === data.charCodeAt(data.length - 1) &&
isQuote(quot)) {
data = data.slice(1, -1);
}
}
data = unescapeCSS(data);
}
}
tokens.push({ type: SelectorType.Pseudo, name, data });
break;
}
case CharCode.Comma: {
finalizeSubselector();
tokens = [];
stripWhitespace(1);
break;
}
default: {
if (selector.startsWith("/*", selectorIndex)) {
const endIndex = selector.indexOf("*/", selectorIndex + 2);
if (endIndex < 0) {
throw new Error("Comment was not terminated");
}
selectorIndex = endIndex + 2;
// Remove leading whitespace
if (tokens.length === 0) {
stripWhitespace(0);
}
break;
}
let namespace = null;
let name;
if (firstChar === CharCode.Asterisk) {
selectorIndex += 1;
name = "*";
}
else if (firstChar === CharCode.Pipe) {
name = "";
if (selector.charCodeAt(selectorIndex + 1) === CharCode.Pipe) {
addTraversal(SelectorType.ColumnCombinator);
stripWhitespace(2);
break;
}
}
else if (reName.test(selector.slice(selectorIndex))) {
name = getName(0);
}
else {
break loop;
}
if (selector.charCodeAt(selectorIndex) === CharCode.Pipe &&
selector.charCodeAt(selectorIndex + 1) !== CharCode.Pipe) {
namespace = name;
if (selector.charCodeAt(selectorIndex + 1) ===
CharCode.Asterisk) {
name = "*";
selectorIndex += 2;
}
else {
name = getName(1);
}
}
tokens.push(name === "*"
? { type: SelectorType.Universal, namespace }
: { type: SelectorType.Tag, name, namespace });
}
}
}
finalizeSubselector();
return selectorIndex;
}

8
node_modules/css-what/dist/esm/stringify.d.ts generated vendored Normal file
View File

@@ -0,0 +1,8 @@
import { type Selector } from "./types.js";
/**
* Turns `selector` back into a string.
*
* @param selector Selector to stringify.
*/
export declare function stringify(selector: Selector[][]): string;
//# sourceMappingURL=stringify.d.ts.map

1
node_modules/css-what/dist/esm/stringify.d.ts.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"stringify.d.ts","sourceRoot":"","sources":["../../src/stringify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAiC,MAAM,YAAY,CAAC;AA8B1E;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE,GAAG,MAAM,CAUxD"}

147
node_modules/css-what/dist/esm/stringify.js generated vendored Normal file
View File

@@ -0,0 +1,147 @@
import { SelectorType, AttributeAction } from "./types.js";
const attribValueChars = ["\\", '"'];
const pseudoValueChars = [...attribValueChars, "(", ")"];
const charsToEscapeInAttributeValue = new Set(attribValueChars.map((c) => c.charCodeAt(0)));
const charsToEscapeInPseudoValue = new Set(pseudoValueChars.map((c) => c.charCodeAt(0)));
const charsToEscapeInName = new Set([
...pseudoValueChars,
"~",
"^",
"$",
"*",
"+",
"!",
"|",
":",
"[",
"]",
" ",
".",
"%",
].map((c) => c.charCodeAt(0)));
/**
* Turns `selector` back into a string.
*
* @param selector Selector to stringify.
*/
export function stringify(selector) {
return selector
.map((token) => token
.map((token, index, array) => stringifyToken(token, index, array))
.join(""))
.join(", ");
}
function stringifyToken(token, index, array) {
switch (token.type) {
// Simple types
case SelectorType.Child: {
return index === 0 ? "> " : " > ";
}
case SelectorType.Parent: {
return index === 0 ? "< " : " < ";
}
case SelectorType.Sibling: {
return index === 0 ? "~ " : " ~ ";
}
case SelectorType.Adjacent: {
return index === 0 ? "+ " : " + ";
}
case SelectorType.Descendant: {
return " ";
}
case SelectorType.ColumnCombinator: {
return index === 0 ? "|| " : " || ";
}
case SelectorType.Universal: {
// Return an empty string if the selector isn't needed.
return token.namespace === "*" &&
index + 1 < array.length &&
"name" in array[index + 1]
? ""
: `${getNamespace(token.namespace)}*`;
}
case SelectorType.Tag: {
return getNamespacedName(token);
}
case SelectorType.PseudoElement: {
return `::${escapeName(token.name, charsToEscapeInName)}${token.data === null
? ""
: `(${escapeName(token.data, charsToEscapeInPseudoValue)})`}`;
}
case SelectorType.Pseudo: {
return `:${escapeName(token.name, charsToEscapeInName)}${token.data === null
? ""
: `(${typeof token.data === "string"
? escapeName(token.data, charsToEscapeInPseudoValue)
: stringify(token.data)})`}`;
}
case SelectorType.Attribute: {
if (token.name === "id" &&
token.action === AttributeAction.Equals &&
token.ignoreCase === "quirks" &&
!token.namespace) {
return `#${escapeName(token.value, charsToEscapeInName)}`;
}
if (token.name === "class" &&
token.action === AttributeAction.Element &&
token.ignoreCase === "quirks" &&
!token.namespace) {
return `.${escapeName(token.value, charsToEscapeInName)}`;
}
const name = getNamespacedName(token);
if (token.action === AttributeAction.Exists) {
return `[${name}]`;
}
return `[${name}${getActionValue(token.action)}="${escapeName(token.value, charsToEscapeInAttributeValue)}"${token.ignoreCase === null ? "" : token.ignoreCase ? " i" : " s"}]`;
}
}
}
function getActionValue(action) {
switch (action) {
case AttributeAction.Equals: {
return "";
}
case AttributeAction.Element: {
return "~";
}
case AttributeAction.Start: {
return "^";
}
case AttributeAction.End: {
return "$";
}
case AttributeAction.Any: {
return "*";
}
case AttributeAction.Not: {
return "!";
}
case AttributeAction.Hyphen: {
return "|";
}
default: {
throw new Error("Shouldn't be here");
}
}
}
function getNamespacedName(token) {
return `${getNamespace(token.namespace)}${escapeName(token.name, charsToEscapeInName)}`;
}
function getNamespace(namespace) {
return namespace === null
? ""
: `${namespace === "*"
? "*"
: escapeName(namespace, charsToEscapeInName)}|`;
}
function escapeName(name, charsToEscape) {
let lastIndex = 0;
let escapedName = "";
for (let index = 0; index < name.length; index++) {
if (charsToEscape.has(name.charCodeAt(index))) {
escapedName += `${name.slice(lastIndex, index)}\\${name.charAt(index)}`;
lastIndex = index + 1;
}
}
return escapedName.length > 0 ? escapedName + name.slice(lastIndex) : name;
}

70
node_modules/css-what/dist/esm/types.d.ts generated vendored Normal file
View File

@@ -0,0 +1,70 @@
export type Selector = PseudoSelector | PseudoElement | AttributeSelector | TagSelector | UniversalSelector | Traversal;
export declare enum SelectorType {
Attribute = "attribute",
Pseudo = "pseudo",
PseudoElement = "pseudo-element",
Tag = "tag",
Universal = "universal",
Adjacent = "adjacent",
Child = "child",
Descendant = "descendant",
Parent = "parent",
Sibling = "sibling",
ColumnCombinator = "column-combinator"
}
/**
* Modes for ignore case.
*
* This could be updated to an enum, and the object is
* the current stand-in that will allow code to be updated
* without big changes.
*/
export declare const IgnoreCaseMode: {
readonly Unknown: null;
readonly QuirksMode: "quirks";
readonly IgnoreCase: true;
readonly CaseSensitive: false;
};
export interface AttributeSelector {
type: SelectorType.Attribute;
name: string;
action: AttributeAction;
value: string;
ignoreCase: "quirks" | boolean | null;
namespace: string | null;
}
export type DataType = Selector[][] | null | string;
export interface PseudoSelector {
type: SelectorType.Pseudo;
name: string;
data: DataType;
}
export interface PseudoElement {
type: SelectorType.PseudoElement;
name: string;
data: string | null;
}
export interface TagSelector {
type: SelectorType.Tag;
name: string;
namespace: string | null;
}
export interface UniversalSelector {
type: SelectorType.Universal;
namespace: string | null;
}
export interface Traversal {
type: TraversalType;
}
export declare enum AttributeAction {
Any = "any",
Element = "element",
End = "end",
Equals = "equals",
Exists = "exists",
Hyphen = "hyphen",
Not = "not",
Start = "start"
}
export type TraversalType = SelectorType.Adjacent | SelectorType.Child | SelectorType.Descendant | SelectorType.Parent | SelectorType.Sibling | SelectorType.ColumnCombinator;
//# sourceMappingURL=types.d.ts.map

1
node_modules/css-what/dist/esm/types.d.ts.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GACd,cAAc,GACd,aAAa,GACb,iBAAiB,GACjB,WAAW,GACX,iBAAiB,GACjB,SAAS,CAAC;AAEhB,oBAAY,YAAY;IACpB,SAAS,cAAc;IACvB,MAAM,WAAW;IACjB,aAAa,mBAAmB;IAChC,GAAG,QAAQ;IACX,SAAS,cAAc;IAGvB,QAAQ,aAAa;IACrB,KAAK,UAAU;IACf,UAAU,eAAe;IACzB,MAAM,WAAW;IACjB,OAAO,YAAY;IACnB,gBAAgB,sBAAsB;CACzC;AAED;;;;;;GAMG;AACH,eAAO,MAAM,cAAc;;;;;CAKjB,CAAC;AAEX,MAAM,WAAW,iBAAiB;IAC9B,IAAI,EAAE,YAAY,CAAC,SAAS,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,eAAe,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,QAAQ,GAAG,OAAO,GAAG,IAAI,CAAC;IACtC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,MAAM,QAAQ,GAAG,QAAQ,EAAE,EAAE,GAAG,IAAI,GAAG,MAAM,CAAC;AAEpD,MAAM,WAAW,cAAc;IAC3B,IAAI,EAAE,YAAY,CAAC,MAAM,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,QAAQ,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC1B,IAAI,EAAE,YAAY,CAAC,aAAa,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,YAAY,CAAC,GAAG,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,iBAAiB;IAC9B,IAAI,EAAE,YAAY,CAAC,SAAS,CAAC;IAC7B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,SAAS;IACtB,IAAI,EAAE,aAAa,CAAC;CACvB;AAED,oBAAY,eAAe;IACvB,GAAG,QAAQ;IACX,OAAO,YAAY;IACnB,GAAG,QAAQ;IACX,MAAM,WAAW;IACjB,MAAM,WAAW;IACjB,MAAM,WAAW;IACjB,GAAG,QAAQ;IACX,KAAK,UAAU;CAClB;AAED,MAAM,MAAM,aAAa,GACnB,YAAY,CAAC,QAAQ,GACrB,YAAY,CAAC,KAAK,GAClB,YAAY,CAAC,UAAU,GACvB,YAAY,CAAC,MAAM,GACnB,YAAY,CAAC,OAAO,GACpB,YAAY,CAAC,gBAAgB,CAAC"}

39
node_modules/css-what/dist/esm/types.js generated vendored Normal file
View File

@@ -0,0 +1,39 @@
export var SelectorType;
(function (SelectorType) {
SelectorType["Attribute"] = "attribute";
SelectorType["Pseudo"] = "pseudo";
SelectorType["PseudoElement"] = "pseudo-element";
SelectorType["Tag"] = "tag";
SelectorType["Universal"] = "universal";
// Traversals
SelectorType["Adjacent"] = "adjacent";
SelectorType["Child"] = "child";
SelectorType["Descendant"] = "descendant";
SelectorType["Parent"] = "parent";
SelectorType["Sibling"] = "sibling";
SelectorType["ColumnCombinator"] = "column-combinator";
})(SelectorType || (SelectorType = {}));
/**
* Modes for ignore case.
*
* This could be updated to an enum, and the object is
* the current stand-in that will allow code to be updated
* without big changes.
*/
export const IgnoreCaseMode = {
Unknown: null,
QuirksMode: "quirks",
IgnoreCase: true,
CaseSensitive: false,
};
export var AttributeAction;
(function (AttributeAction) {
AttributeAction["Any"] = "any";
AttributeAction["Element"] = "element";
AttributeAction["End"] = "end";
AttributeAction["Equals"] = "equals";
AttributeAction["Exists"] = "exists";
AttributeAction["Hyphen"] = "hyphen";
AttributeAction["Not"] = "not";
AttributeAction["Start"] = "start";
})(AttributeAction || (AttributeAction = {}));

81
node_modules/css-what/package.json generated vendored Normal file
View File

@@ -0,0 +1,81 @@
{
"name": "css-what",
"version": "7.0.0",
"description": "a CSS selector parser",
"repository": {
"type": "git",
"url": "https://github.com/fb55/css-what"
},
"funding": {
"url": "https://github.com/sponsors/fb55"
},
"license": "BSD-2-Clause",
"author": "Felix Böhm <me@feedic.com> (http://feedic.com)",
"sideEffects": false,
"type": "module",
"exports": {
"./package.json": "./package.json",
".": {
"import": {
"types": "./dist/esm/index.d.ts",
"default": "./dist/esm/index.js"
},
"require": {
"types": "./dist/commonjs/index.d.ts",
"default": "./dist/commonjs/index.js"
}
}
},
"main": "./dist/commonjs/index.js",
"module": "./dist/esm/index.js",
"types": "./dist/commonjs/index.d.ts",
"files": [
"dist",
"src"
],
"scripts": {
"format": "npm run format:es && npm run format:prettier",
"format:es": "npm run lint:es -- --fix",
"format:prettier": "npm run prettier -- --write",
"lint": "npm run lint:tsc && npm run lint:es && npm run lint:prettier",
"lint:es": "eslint src",
"lint:prettier": "npm run prettier -- --check",
"lint:tsc": "tsc --noEmit",
"prepublishOnly": "tshy",
"prettier": "prettier '**/*.{ts,md,json,yml}'",
"test": "npm run test:vi && npm run lint",
"test:vi": "vitest run"
},
"prettier": {
"tabWidth": 4
},
"devDependencies": {
"@types/node": "^22.15.30",
"@typescript-eslint/eslint-plugin": "^7.18.0",
"@typescript-eslint/parser": "^7.18.0",
"@vitest/coverage-v8": "^3.2.4",
"eslint": "^8.57.1",
"eslint-config-prettier": "^10.1.5",
"eslint-plugin-n": "^17.20.0",
"eslint-plugin-unicorn": "^55.0.0",
"prettier": "^3.6.2",
"tshy": "^3.0.2",
"typescript": "^5.8.3",
"vitest": "^3.2.4"
},
"engines": {
"node": ">= 6"
},
"tshy": {
"exclude": [
"**/*.spec.ts",
"**/__fixtures__/*",
"**/__tests__/*",
"**/__snapshots__/*"
],
"exports": {
"./package.json": "./package.json",
".": "./src/index.ts"
}
}
}

69
node_modules/css-what/readme.md generated vendored Normal file
View File

@@ -0,0 +1,69 @@
# css-what
[![Node.js CI](https://github.com/fb55/css-what/actions/workflows/nodejs-test.yml/badge.svg)](https://github.com/fb55/css-what/actions/workflows/nodejs-test.yml)
[![Coverage](https://img.shields.io/coveralls/github/fb55/css-what/master)](https://coveralls.io/github/fb55/css-what?branch=master)
A CSS selector parser.
## Example
```js
import * as CSSwhat from "css-what";
CSSwhat.parse("foo[bar]:baz")
~> [
[
{ type: "tag", name: "foo" },
{
type: "attribute",
name: "bar",
action: "exists",
value: "",
ignoreCase: null
},
{ type: "pseudo", name: "baz", data: null }
]
]
```
## API
**`CSSwhat.parse(selector)` - Parses `selector`.**
The function returns a two-dimensional array. The first array represents selectors separated by commas (eg. `sub1, sub2`), the second contains the relevant tokens for that selector. Possible token types are:
| name | properties | example | output |
| ------------------- | --------------------------------------- | ------------- | ---------------------------------------------------------------------------------------- |
| `tag` | `name` | `div` | `{ type: 'tag', name: 'div' }` |
| `universal` | - | `*` | `{ type: 'universal' }` |
| `pseudo` | `name`, `data` | `:name(data)` | `{ type: 'pseudo', name: 'name', data: 'data' }` |
| `pseudo` | `name`, `data` | `:name` | `{ type: 'pseudo', name: 'name', data: null }` |
| `pseudo-element` | `name` | `::name` | `{ type: 'pseudo-element', name: 'name' }` |
| `attribute` | `name`, `action`, `value`, `ignoreCase` | `[attr]` | `{ type: 'attribute', name: 'attr', action: 'exists', value: '', ignoreCase: false }` |
| `attribute` | `name`, `action`, `value`, `ignoreCase` | `[attr=val]` | `{ type: 'attribute', name: 'attr', action: 'equals', value: 'val', ignoreCase: false }` |
| `attribute` | `name`, `action`, `value`, `ignoreCase` | `[attr^=val]` | `{ type: 'attribute', name: 'attr', action: 'start', value: 'val', ignoreCase: false }` |
| `attribute` | `name`, `action`, `value`, `ignoreCase` | `[attr$=val]` | `{ type: 'attribute', name: 'attr', action: 'end', value: 'val', ignoreCase: false }` |
| `child` | - | `>` | `{ type: 'child' }` |
| `parent` | - | `<` | `{ type: 'parent' }` |
| `sibling` | - | `~` | `{ type: 'sibling' }` |
| `adjacent` | - | `+` | `{ type: 'adjacent' }` |
| `descendant` | - | | `{ type: 'descendant' }` |
| `column-combinator` | - | `\|\|` | `{ type: 'column-combinator' }` |
**`CSSwhat.stringify(selector)` - Turns `selector` back into a string.**
---
License: BSD-2-Clause
## Security contact information
To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security).
Tidelift will coordinate the fix and disclosure.
## `css-what` for enterprise
Available as part of the Tidelift Subscription
The maintainers of `css-what` and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/npm-css-what?utm_source=npm-css-what&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)

17296
node_modules/css-what/src/__fixtures__/out.json generated vendored Normal file

File diff suppressed because it is too large Load Diff

1028
node_modules/css-what/src/__fixtures__/tests.ts generated vendored Normal file

File diff suppressed because it is too large Load Diff

3
node_modules/css-what/src/index.ts generated vendored Normal file
View File

@@ -0,0 +1,3 @@
export * from "./types.js";
export { isTraversal, parse } from "./parse.js";
export { stringify } from "./stringify.js";

69
node_modules/css-what/src/parse.spec.ts generated vendored Normal file
View File

@@ -0,0 +1,69 @@
import { readFileSync } from "node:fs";
import { describe, it, expect } from "vitest";
import { parse } from "./parse.js";
import { tests } from "./__fixtures__/tests.js";
const broken = [
"[",
"(",
"{",
"()",
"<>",
"{}",
",",
",a",
"a,",
"[id=012345678901234567890123456789",
"input[name=foo b]",
"input[name!foo]",
"input[name|]",
"input[name=']",
"input[name=foo[baz]]",
':has("p")',
":has(p",
":foo(p()",
"#",
"##foo",
"/*",
];
describe("Parse", () => {
describe("Own tests", () => {
for (const [selector, expected, message] of tests) {
it(message, () => expect(parse(selector)).toStrictEqual(expected));
}
});
describe("Collected selectors (qwery, sizzle, nwmatcher)", () => {
const out = JSON.parse(
readFileSync(`${__dirname}/__fixtures__/out.json`, "utf8"),
);
for (const s of Object.keys(out)) {
it(s, () => {
expect(parse(s)).toStrictEqual(out[s]);
});
}
});
describe("Broken selectors", () => {
for (const selector of broken) {
it(`should not parse — ${selector}`, () => {
expect(() => parse(selector)).toThrow(Error);
});
}
});
it("should ignore comments", () => {
expect(parse("/* comment1 */ /**/ foo /*comment2*/")).toEqual([
[{ name: "foo", namespace: null, type: "tag" }],
]);
expect(() => parse("/*/")).toThrowError("Comment was not terminated");
});
it("should support legacy pseudo-elements with single colon", () => {
expect(parse(":before")).toEqual([
[{ name: "before", data: null, type: "pseudo-element" }],
]);
});
});

634
node_modules/css-what/src/parse.ts generated vendored Normal file
View File

@@ -0,0 +1,634 @@
import {
type Selector,
SelectorType,
type AttributeSelector,
type Traversal,
AttributeAction,
type TraversalType,
type DataType,
} from "./types.js";
const reName = /^[^#\\]?(?:\\(?:[\da-f]{1,6}\s?|.)|[\w\u00B0-\uFFFF-])+/;
const reEscape = /\\([\da-f]{1,6}\s?|(\s)|.)/gi;
const enum CharCode {
LeftParenthesis = 40,
RightParenthesis = 41,
LeftSquareBracket = 91,
RightSquareBracket = 93,
Comma = 44,
Period = 46,
Colon = 58,
SingleQuote = 39,
DoubleQuote = 34,
Plus = 43,
Tilde = 126,
QuestionMark = 63,
ExclamationMark = 33,
Slash = 47,
Equal = 61,
Dollar = 36,
Pipe = 124,
Circumflex = 94,
Asterisk = 42,
GreaterThan = 62,
LessThan = 60,
Hash = 35,
LowerI = 105,
LowerS = 115,
BackSlash = 92,
// Whitespace
Space = 32,
Tab = 9,
NewLine = 10,
FormFeed = 12,
CarriageReturn = 13,
}
const actionTypes = new Map<number, AttributeAction>([
[CharCode.Tilde, AttributeAction.Element],
[CharCode.Circumflex, AttributeAction.Start],
[CharCode.Dollar, AttributeAction.End],
[CharCode.Asterisk, AttributeAction.Any],
[CharCode.ExclamationMark, AttributeAction.Not],
[CharCode.Pipe, AttributeAction.Hyphen],
]);
// Pseudos, whose data property is parsed as well.
const unpackPseudos = new Set([
"has",
"not",
"matches",
"is",
"where",
"host",
"host-context",
]);
/**
* Pseudo elements defined in CSS Level 1 and CSS Level 2 can be written with
* a single colon; eg. :before will turn into ::before.
*
* @see {@link https://www.w3.org/TR/2018/WD-selectors-4-20181121/#pseudo-element-syntax}
*/
const pseudosToPseudoElements = new Set([
"before",
"after",
"first-line",
"first-letter",
]);
/**
* Checks whether a specific selector is a traversal.
* This is useful eg. in swapping the order of elements that
* are not traversals.
*
* @param selector Selector to check.
*/
export function isTraversal(selector: Selector): selector is Traversal {
switch (selector.type) {
case SelectorType.Adjacent:
case SelectorType.Child:
case SelectorType.Descendant:
case SelectorType.Parent:
case SelectorType.Sibling:
case SelectorType.ColumnCombinator: {
return true;
}
default: {
return false;
}
}
}
const stripQuotesFromPseudos = new Set(["contains", "icontains"]);
// Unescape function taken from https://github.com/jquery/sizzle/blob/master/src/sizzle.js#L152
function funescape(_: string, escaped: string, escapedWhitespace?: string) {
const high = Number.parseInt(escaped, 16) - 0x1_00_00;
// NaN means non-codepoint
return high !== high || escapedWhitespace
? escaped
: high < 0
? // BMP codepoint
String.fromCharCode(high + 0x1_00_00)
: // Supplemental Plane codepoint (surrogate pair)
String.fromCharCode(
(high >> 10) | 0xd8_00,
(high & 0x3_ff) | 0xdc_00,
);
}
function unescapeCSS(cssString: string) {
return cssString.replace(reEscape, funescape);
}
function isQuote(c: number): boolean {
return c === CharCode.SingleQuote || c === CharCode.DoubleQuote;
}
function isWhitespace(c: number): boolean {
return (
c === CharCode.Space ||
c === CharCode.Tab ||
c === CharCode.NewLine ||
c === CharCode.FormFeed ||
c === CharCode.CarriageReturn
);
}
/**
* Parses `selector`.
*
* @param selector Selector to parse.
* @returns Returns a two-dimensional array.
* The first dimension represents selectors separated by commas (eg. `sub1, sub2`),
* the second contains the relevant tokens for that selector.
*/
export function parse(selector: string): Selector[][] {
const subselects: Selector[][] = [];
const endIndex = parseSelector(subselects, `${selector}`, 0);
if (endIndex < selector.length) {
throw new Error(`Unmatched selector: ${selector.slice(endIndex)}`);
}
return subselects;
}
function parseSelector(
subselects: Selector[][],
selector: string,
selectorIndex: number,
): number {
let tokens: Selector[] = [];
function getName(offset: number): string {
const match = selector.slice(selectorIndex + offset).match(reName);
if (!match) {
throw new Error(
`Expected name, found ${selector.slice(selectorIndex)}`,
);
}
const [name] = match;
selectorIndex += offset + name.length;
return unescapeCSS(name);
}
function stripWhitespace(offset: number) {
selectorIndex += offset;
while (
selectorIndex < selector.length &&
isWhitespace(selector.charCodeAt(selectorIndex))
) {
selectorIndex++;
}
}
function readValueWithParenthesis(): string {
selectorIndex += 1;
const start = selectorIndex;
for (
let counter = 1;
selectorIndex < selector.length;
selectorIndex++
) {
switch (selector.charCodeAt(selectorIndex)) {
case CharCode.BackSlash: {
// Skip next character
selectorIndex += 1;
break;
}
case CharCode.LeftParenthesis: {
counter += 1;
break;
}
case CharCode.RightParenthesis: {
counter -= 1;
if (counter === 0) {
return unescapeCSS(
selector.slice(start, selectorIndex++),
);
}
break;
}
}
}
throw new Error("Parenthesis not matched");
}
function ensureNotTraversal() {
if (tokens.length > 0 && isTraversal(tokens[tokens.length - 1])) {
throw new Error("Did not expect successive traversals.");
}
}
function addTraversal(type: TraversalType) {
if (
tokens.length > 0 &&
tokens[tokens.length - 1].type === SelectorType.Descendant
) {
tokens[tokens.length - 1].type = type;
return;
}
ensureNotTraversal();
tokens.push({ type });
}
function addSpecialAttribute(name: string, action: AttributeAction) {
tokens.push({
type: SelectorType.Attribute,
name,
action,
value: getName(1),
namespace: null,
ignoreCase: "quirks",
});
}
/**
* We have finished parsing the current part of the selector.
*
* Remove descendant tokens at the end if they exist,
* and return the last index, so that parsing can be
* picked up from here.
*/
function finalizeSubselector() {
if (
tokens.length > 0 &&
tokens[tokens.length - 1].type === SelectorType.Descendant
) {
tokens.pop();
}
if (tokens.length === 0) {
throw new Error("Empty sub-selector");
}
subselects.push(tokens);
}
stripWhitespace(0);
if (selector.length === selectorIndex) {
return selectorIndex;
}
loop: while (selectorIndex < selector.length) {
const firstChar = selector.charCodeAt(selectorIndex);
switch (firstChar) {
// Whitespace
case CharCode.Space:
case CharCode.Tab:
case CharCode.NewLine:
case CharCode.FormFeed:
case CharCode.CarriageReturn: {
if (
tokens.length === 0 ||
tokens[0].type !== SelectorType.Descendant
) {
ensureNotTraversal();
tokens.push({ type: SelectorType.Descendant });
}
stripWhitespace(1);
break;
}
// Traversals
case CharCode.GreaterThan: {
addTraversal(SelectorType.Child);
stripWhitespace(1);
break;
}
case CharCode.LessThan: {
addTraversal(SelectorType.Parent);
stripWhitespace(1);
break;
}
case CharCode.Tilde: {
addTraversal(SelectorType.Sibling);
stripWhitespace(1);
break;
}
case CharCode.Plus: {
addTraversal(SelectorType.Adjacent);
stripWhitespace(1);
break;
}
// Special attribute selectors: .class, #id
case CharCode.Period: {
addSpecialAttribute("class", AttributeAction.Element);
break;
}
case CharCode.Hash: {
addSpecialAttribute("id", AttributeAction.Equals);
break;
}
case CharCode.LeftSquareBracket: {
stripWhitespace(1);
// Determine attribute name and namespace
let name: string;
let namespace: string | null = null;
if (selector.charCodeAt(selectorIndex) === CharCode.Pipe) {
// Equivalent to no namespace
name = getName(1);
} else if (selector.startsWith("*|", selectorIndex)) {
namespace = "*";
name = getName(2);
} else {
name = getName(0);
if (
selector.charCodeAt(selectorIndex) === CharCode.Pipe &&
selector.charCodeAt(selectorIndex + 1) !==
CharCode.Equal
) {
namespace = name;
name = getName(1);
}
}
stripWhitespace(0);
// Determine comparison operation
let action: AttributeAction = AttributeAction.Exists;
const possibleAction = actionTypes.get(
selector.charCodeAt(selectorIndex),
);
if (possibleAction) {
action = possibleAction;
if (
selector.charCodeAt(selectorIndex + 1) !==
CharCode.Equal
) {
throw new Error("Expected `=`");
}
stripWhitespace(2);
} else if (
selector.charCodeAt(selectorIndex) === CharCode.Equal
) {
action = AttributeAction.Equals;
stripWhitespace(1);
}
// Determine value
let value = "";
let ignoreCase: boolean | null = null;
if (action !== "exists") {
if (isQuote(selector.charCodeAt(selectorIndex))) {
const quote = selector.charCodeAt(selectorIndex);
selectorIndex += 1;
const sectionStart = selectorIndex;
while (
selectorIndex < selector.length &&
selector.charCodeAt(selectorIndex) !== quote
) {
selectorIndex +=
// Skip next character if it is escaped
selector.charCodeAt(selectorIndex) ===
CharCode.BackSlash
? 2
: 1;
}
if (selector.charCodeAt(selectorIndex) !== quote) {
throw new Error("Attribute value didn't end");
}
value = unescapeCSS(
selector.slice(sectionStart, selectorIndex),
);
selectorIndex += 1;
} else {
const valueStart = selectorIndex;
while (
selectorIndex < selector.length &&
!isWhitespace(selector.charCodeAt(selectorIndex)) &&
selector.charCodeAt(selectorIndex) !==
CharCode.RightSquareBracket
) {
selectorIndex +=
// Skip next character if it is escaped
selector.charCodeAt(selectorIndex) ===
CharCode.BackSlash
? 2
: 1;
}
value = unescapeCSS(
selector.slice(valueStart, selectorIndex),
);
}
stripWhitespace(0);
// See if we have a force ignore flag
switch (selector.charCodeAt(selectorIndex) | 0x20) {
// If the forceIgnore flag is set (either `i` or `s`), use that value
case CharCode.LowerI: {
ignoreCase = true;
stripWhitespace(1);
break;
}
case CharCode.LowerS: {
ignoreCase = false;
stripWhitespace(1);
break;
}
}
}
if (
selector.charCodeAt(selectorIndex) !==
CharCode.RightSquareBracket
) {
throw new Error("Attribute selector didn't terminate");
}
selectorIndex += 1;
const attributeSelector: AttributeSelector = {
type: SelectorType.Attribute,
name,
action,
value,
namespace,
ignoreCase,
};
tokens.push(attributeSelector);
break;
}
case CharCode.Colon: {
if (selector.charCodeAt(selectorIndex + 1) === CharCode.Colon) {
tokens.push({
type: SelectorType.PseudoElement,
name: getName(2).toLowerCase(),
data:
selector.charCodeAt(selectorIndex) ===
CharCode.LeftParenthesis
? readValueWithParenthesis()
: null,
});
break;
}
const name = getName(1).toLowerCase();
if (pseudosToPseudoElements.has(name)) {
tokens.push({
type: SelectorType.PseudoElement,
name,
data: null,
});
break;
}
let data: DataType = null;
if (
selector.charCodeAt(selectorIndex) ===
CharCode.LeftParenthesis
) {
if (unpackPseudos.has(name)) {
if (isQuote(selector.charCodeAt(selectorIndex + 1))) {
throw new Error(
`Pseudo-selector ${name} cannot be quoted`,
);
}
data = [];
selectorIndex = parseSelector(
data,
selector,
selectorIndex + 1,
);
if (
selector.charCodeAt(selectorIndex) !==
CharCode.RightParenthesis
) {
throw new Error(
`Missing closing parenthesis in :${name} (${selector})`,
);
}
selectorIndex += 1;
} else {
data = readValueWithParenthesis();
if (stripQuotesFromPseudos.has(name)) {
const quot = data.charCodeAt(0);
if (
quot === data.charCodeAt(data.length - 1) &&
isQuote(quot)
) {
data = data.slice(1, -1);
}
}
data = unescapeCSS(data);
}
}
tokens.push({ type: SelectorType.Pseudo, name, data });
break;
}
case CharCode.Comma: {
finalizeSubselector();
tokens = [];
stripWhitespace(1);
break;
}
default: {
if (selector.startsWith("/*", selectorIndex)) {
const endIndex = selector.indexOf("*/", selectorIndex + 2);
if (endIndex < 0) {
throw new Error("Comment was not terminated");
}
selectorIndex = endIndex + 2;
// Remove leading whitespace
if (tokens.length === 0) {
stripWhitespace(0);
}
break;
}
let namespace = null;
let name: string;
if (firstChar === CharCode.Asterisk) {
selectorIndex += 1;
name = "*";
} else if (firstChar === CharCode.Pipe) {
name = "";
if (
selector.charCodeAt(selectorIndex + 1) === CharCode.Pipe
) {
addTraversal(SelectorType.ColumnCombinator);
stripWhitespace(2);
break;
}
} else if (reName.test(selector.slice(selectorIndex))) {
name = getName(0);
} else {
break loop;
}
if (
selector.charCodeAt(selectorIndex) === CharCode.Pipe &&
selector.charCodeAt(selectorIndex + 1) !== CharCode.Pipe
) {
namespace = name;
if (
selector.charCodeAt(selectorIndex + 1) ===
CharCode.Asterisk
) {
name = "*";
selectorIndex += 2;
} else {
name = getName(1);
}
}
tokens.push(
name === "*"
? { type: SelectorType.Universal, namespace }
: { type: SelectorType.Tag, name, namespace },
);
}
}
}
finalizeSubselector();
return selectorIndex;
}

23
node_modules/css-what/src/stringify.spec.ts generated vendored Normal file
View File

@@ -0,0 +1,23 @@
import { readFileSync } from "node:fs";
import { describe, it, expect } from "vitest";
import { parse, stringify } from "./index.js";
import { tests } from "./__fixtures__/tests.js";
describe("Stringify & re-parse", () => {
describe("Own tests", () => {
for (const [selector, expected, message] of tests) {
it(`${message} (${selector})`, () => {
expect(parse(stringify(expected))).toStrictEqual(expected);
});
}
});
it("Collected Selectors (qwery, sizzle, nwmatcher)", () => {
const out = JSON.parse(
readFileSync(`${__dirname}/__fixtures__/out.json`, "utf8"),
);
for (const s of Object.keys(out)) {
expect(parse(stringify(out[s]))).toStrictEqual(out[s]);
}
});
});

204
node_modules/css-what/src/stringify.ts generated vendored Normal file
View File

@@ -0,0 +1,204 @@
import { type Selector, SelectorType, AttributeAction } from "./types.js";
const attribValueChars = ["\\", '"'];
const pseudoValueChars = [...attribValueChars, "(", ")"];
const charsToEscapeInAttributeValue = new Set(
attribValueChars.map((c) => c.charCodeAt(0)),
);
const charsToEscapeInPseudoValue = new Set(
pseudoValueChars.map((c) => c.charCodeAt(0)),
);
const charsToEscapeInName = new Set(
[
...pseudoValueChars,
"~",
"^",
"$",
"*",
"+",
"!",
"|",
":",
"[",
"]",
" ",
".",
"%",
].map((c) => c.charCodeAt(0)),
);
/**
* Turns `selector` back into a string.
*
* @param selector Selector to stringify.
*/
export function stringify(selector: Selector[][]): string {
return selector
.map((token) =>
token
.map((token, index, array) =>
stringifyToken(token, index, array),
)
.join(""),
)
.join(", ");
}
function stringifyToken(
token: Selector,
index: number,
array: Selector[],
): string {
switch (token.type) {
// Simple types
case SelectorType.Child: {
return index === 0 ? "> " : " > ";
}
case SelectorType.Parent: {
return index === 0 ? "< " : " < ";
}
case SelectorType.Sibling: {
return index === 0 ? "~ " : " ~ ";
}
case SelectorType.Adjacent: {
return index === 0 ? "+ " : " + ";
}
case SelectorType.Descendant: {
return " ";
}
case SelectorType.ColumnCombinator: {
return index === 0 ? "|| " : " || ";
}
case SelectorType.Universal: {
// Return an empty string if the selector isn't needed.
return token.namespace === "*" &&
index + 1 < array.length &&
"name" in array[index + 1]
? ""
: `${getNamespace(token.namespace)}*`;
}
case SelectorType.Tag: {
return getNamespacedName(token);
}
case SelectorType.PseudoElement: {
return `::${escapeName(token.name, charsToEscapeInName)}${
token.data === null
? ""
: `(${escapeName(token.data, charsToEscapeInPseudoValue)})`
}`;
}
case SelectorType.Pseudo: {
return `:${escapeName(token.name, charsToEscapeInName)}${
token.data === null
? ""
: `(${
typeof token.data === "string"
? escapeName(
token.data,
charsToEscapeInPseudoValue,
)
: stringify(token.data)
})`
}`;
}
case SelectorType.Attribute: {
if (
token.name === "id" &&
token.action === AttributeAction.Equals &&
token.ignoreCase === "quirks" &&
!token.namespace
) {
return `#${escapeName(token.value, charsToEscapeInName)}`;
}
if (
token.name === "class" &&
token.action === AttributeAction.Element &&
token.ignoreCase === "quirks" &&
!token.namespace
) {
return `.${escapeName(token.value, charsToEscapeInName)}`;
}
const name = getNamespacedName(token);
if (token.action === AttributeAction.Exists) {
return `[${name}]`;
}
return `[${name}${getActionValue(token.action)}="${escapeName(
token.value,
charsToEscapeInAttributeValue,
)}"${
token.ignoreCase === null ? "" : token.ignoreCase ? " i" : " s"
}]`;
}
}
}
function getActionValue(action: AttributeAction): string {
switch (action) {
case AttributeAction.Equals: {
return "";
}
case AttributeAction.Element: {
return "~";
}
case AttributeAction.Start: {
return "^";
}
case AttributeAction.End: {
return "$";
}
case AttributeAction.Any: {
return "*";
}
case AttributeAction.Not: {
return "!";
}
case AttributeAction.Hyphen: {
return "|";
}
default: {
throw new Error("Shouldn't be here");
}
}
}
function getNamespacedName(token: {
name: string;
namespace: string | null;
}): string {
return `${getNamespace(token.namespace)}${escapeName(
token.name,
charsToEscapeInName,
)}`;
}
function getNamespace(namespace: string | null): string {
return namespace === null
? ""
: `${
namespace === "*"
? "*"
: escapeName(namespace, charsToEscapeInName)
}|`;
}
function escapeName(name: string, charsToEscape: Set<number>): string {
let lastIndex = 0;
let escapedName = "";
for (let index = 0; index < name.length; index++) {
if (charsToEscape.has(name.charCodeAt(index))) {
escapedName += `${name.slice(lastIndex, index)}\\${name.charAt(index)}`;
lastIndex = index + 1;
}
}
return escapedName.length > 0 ? escapedName + name.slice(lastIndex) : name;
}

94
node_modules/css-what/src/types.ts generated vendored Normal file
View File

@@ -0,0 +1,94 @@
export type Selector =
| PseudoSelector
| PseudoElement
| AttributeSelector
| TagSelector
| UniversalSelector
| Traversal;
export enum SelectorType {
Attribute = "attribute",
Pseudo = "pseudo",
PseudoElement = "pseudo-element",
Tag = "tag",
Universal = "universal",
// Traversals
Adjacent = "adjacent",
Child = "child",
Descendant = "descendant",
Parent = "parent",
Sibling = "sibling",
ColumnCombinator = "column-combinator",
}
/**
* Modes for ignore case.
*
* This could be updated to an enum, and the object is
* the current stand-in that will allow code to be updated
* without big changes.
*/
export const IgnoreCaseMode = {
Unknown: null,
QuirksMode: "quirks",
IgnoreCase: true,
CaseSensitive: false,
} as const;
export interface AttributeSelector {
type: SelectorType.Attribute;
name: string;
action: AttributeAction;
value: string;
ignoreCase: "quirks" | boolean | null;
namespace: string | null;
}
export type DataType = Selector[][] | null | string;
export interface PseudoSelector {
type: SelectorType.Pseudo;
name: string;
data: DataType;
}
export interface PseudoElement {
type: SelectorType.PseudoElement;
name: string;
data: string | null;
}
export interface TagSelector {
type: SelectorType.Tag;
name: string;
namespace: string | null;
}
export interface UniversalSelector {
type: SelectorType.Universal;
namespace: string | null;
}
export interface Traversal {
type: TraversalType;
}
export enum AttributeAction {
Any = "any",
Element = "element",
End = "end",
Equals = "equals",
Exists = "exists",
Hyphen = "hyphen",
Not = "not",
Start = "start",
}
export type TraversalType =
| SelectorType.Adjacent
| SelectorType.Child
| SelectorType.Descendant
| SelectorType.Parent
| SelectorType.Sibling
| SelectorType.ColumnCombinator;

185
node_modules/css-what/src/wpt.spec.ts generated vendored Normal file
View File

@@ -0,0 +1,185 @@
/**
* @fileoverview CSS Selector parsing tests from WPT
* @see https://github.com/web-platform-tests/wpt/tree/0bb883967c888261a8372923fd61eb5ad14305b2/css/selectors/parsing
* @license BSD-3-Clause (https://github.com/web-platform-tests/wpt/blob/master/LICENSE.md)
*/
import { describe, it, expect } from "vitest";
import { parse, stringify } from "./index.js";
function test_valid_selector(
selector: string,
serialized: string | string[] = selector,
) {
const result = stringify(parse(selector));
if (Array.isArray(serialized)) {
// Should be a part of the array
expect(serialized).toContain(result);
} else {
expect(result).toStrictEqual(serialized);
}
}
function test_invalid_selector(selector: string) {
expect(() => parse(selector)).toThrow(Error);
}
describe("Web Platform Tests", () => {
it("Attribute selectors", () => {
// Attribute presence and value selectors
test_valid_selector("[att]");
test_valid_selector("[att=val]", '[att="val"]');
test_valid_selector("[att~=val]", '[att~="val"]');
test_valid_selector("[att|=val]", '[att|="val"]');
test_valid_selector("h1[title]");
test_valid_selector("span[class='example']", 'span[class="example"]');
test_valid_selector("a[hreflang=fr]", 'a[hreflang="fr"]');
test_valid_selector("a[hreflang|='en']", 'a[hreflang|="en"]');
// Substring matching attribute selectors
test_valid_selector("[att^=val]", '[att^="val"]');
test_valid_selector("[att$=val]", '[att$="val"]');
test_valid_selector("[att*=val]", '[att*="val"]');
test_valid_selector('object[type^="image/"]');
test_valid_selector('a[href$=".html"]');
test_valid_selector('p[title*="hello"]');
// From Attribute selectors and namespaces examples in spec:
test_valid_selector("[*|att]");
test_valid_selector("[|att]", "[att]");
});
it("Child combinators", () => {
test_valid_selector("body > p");
test_valid_selector("div ol>li p", "div ol > li p");
});
it("Class selectors", () => {
test_valid_selector("*.pastoral", ["*.pastoral", ".pastoral"]);
test_valid_selector(".pastoral", ["*.pastoral", ".pastoral"]);
test_valid_selector("h1.pastoral");
test_valid_selector("p.pastoral.marine");
});
it("Descendant combinator", () => {
test_valid_selector("h1 em");
test_valid_selector("div * p");
test_valid_selector("div p *[href]", ["div p *[href]", "div p [href]"]);
});
it(":focus-visible pseudo-class", () => {
test_valid_selector(":focus-visible");
test_valid_selector("a:focus-visible");
test_valid_selector(":focus:not(:focus-visible)");
});
it("The relational pseudo-class", () => {
test_valid_selector(":has(a)");
test_valid_selector(":has(#a)");
test_valid_selector(":has(.a)");
test_valid_selector(":has([a])");
test_valid_selector(':has([a="b"])');
test_valid_selector(':has([a|="b"])');
test_valid_selector(":has(:hover)");
test_valid_selector("*:has(.a)", ["*:has(.a)", ":has(.a)"]);
test_valid_selector(".a:has(.b)");
test_valid_selector(".a:has(> .b)");
test_valid_selector(".a:has(~ .b)");
test_valid_selector(".a:has(+ .b)");
test_valid_selector(".a:has(.b) .c");
test_valid_selector(".a .b:has(.c)");
test_valid_selector(".a .b:has(.c .d)");
test_valid_selector(".a .b:has(.c .d) .e");
test_valid_selector(".a:has(.b:has(.c))");
test_valid_selector(".a:has(.b:is(.c .d))");
test_valid_selector(".a:has(.b:is(.c:has(.d) .e))");
test_valid_selector(".a:is(.b:has(.c) .d)");
test_valid_selector(".a:not(:has(.b))");
test_valid_selector(".a:has(:not(.b))");
test_valid_selector(".a:has(.b):has(.c)");
test_valid_selector("*|*:has(*)", ":has(*)");
test_valid_selector(":has(*|*)");
test_invalid_selector(".a:has()");
});
it("ID selectors", () => {
test_valid_selector("h1#chapter1");
test_valid_selector("#chapter1");
test_valid_selector("*#z98y", ["*#z98y", "#z98y"]);
});
it("The Matches-Any Pseudo-class: ':is()'", () => {
test_valid_selector(
":is(ul,ol,.list) > [hidden]",
":is(ul, ol, .list) > [hidden]",
);
test_valid_selector(":is(:hover,:focus)", ":is(:hover, :focus)");
test_valid_selector("a:is(:not(:hover))");
test_valid_selector(":is(#a)");
test_valid_selector(".a.b ~ :is(.c.d ~ .e.f)");
test_valid_selector(".a.b ~ .c.d:is(span.e + .f, .g.h > .i.j .k)");
});
it("The negation pseudo-class", () => {
test_valid_selector("button:not([disabled])");
test_valid_selector("*:not(foo)", ["*:not(foo)", ":not(foo)"]);
test_valid_selector(":not(:link):not(:visited)");
test_valid_selector("*|*:not(*)", ":not(*)");
test_valid_selector(":not(:hover)");
test_valid_selector(":not(*|*)");
test_valid_selector("foo:not(bar)");
test_valid_selector(":not(:not(foo))");
test_valid_selector(":not(.a .b)");
test_valid_selector(":not(.a + .b)");
test_valid_selector(":not(.a .b ~ c)");
test_valid_selector(":not(span.a, div.b)");
test_valid_selector(":not(.a .b ~ c, .d .e)");
test_valid_selector(":not(:host)");
test_valid_selector(":not(:host(.a))");
test_valid_selector(":host(:not(.a))");
test_valid_selector(":not(:host(:not(.a)))");
test_valid_selector(
":not([disabled][selected])",
":not([disabled][selected])",
);
test_valid_selector(
":not([disabled],[selected])",
":not([disabled], [selected])",
);
test_invalid_selector(":not()");
test_invalid_selector(":not(:not())");
});
it("Sibling combinators", () => {
test_valid_selector("math + p");
test_valid_selector("h1.opener + h2");
test_valid_selector("h1 ~ pre");
});
it("Universal selector", () => {
test_valid_selector("*");
test_valid_selector("div :first-child", [
"div *:first-child",
"div :first-child",
]);
test_valid_selector("div *:first-child", [
"div *:first-child",
"div :first-child",
]);
});
it("The Specificity-adjustment Pseudo-class: ':where()'", () => {
test_valid_selector(
":where(ul,ol,.list) > [hidden]",
":where(ul, ol, .list) > [hidden]",
);
test_valid_selector(":where(:hover,:focus)", ":where(:hover, :focus)");
test_valid_selector("a:where(:not(:hover))");
test_valid_selector(":where(#a)");
test_valid_selector(".a.b ~ :where(.c.d ~ .e.f)");
test_valid_selector(".a.b ~ .c.d:where(span.e + .f, .g.h > .i.j .k)");
});
});