| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126 | import { SelectorType, AttributeAction } from "./types";const attribValChars = ["\\", '"'];const pseudoValChars = [...attribValChars, "(", ")"];const charsToEscapeInAttributeValue = new Set(attribValChars.map((c) => c.charCodeAt(0)));const charsToEscapeInPseudoValue = new Set(pseudoValChars.map((c) => c.charCodeAt(0)));const charsToEscapeInName = new Set([    ...pseudoValChars,    "~",    "^",    "$",    "*",    "+",    "!",    "|",    ":",    "[",    "]",    " ",    ".",].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(stringifyToken).join(""))        .join(", ");}function stringifyToken(token, index, arr) {    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 < arr.length &&                "name" in arr[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 "|";        case AttributeAction.Exists:            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(str, charsToEscape) {    let lastIdx = 0;    let ret = "";    for (let i = 0; i < str.length; i++) {        if (charsToEscape.has(str.charCodeAt(i))) {            ret += `${str.slice(lastIdx, i)}\\${str.charAt(i)}`;            lastIdx = i + 1;        }    }    return ret.length > 0 ? ret + str.slice(lastIdx) : str;}
 |