Index: frontend/node_modules/css-what/lib/commonjs/index.d.ts
===================================================================
--- frontend/node_modules/css-what/lib/commonjs/index.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/css-what/lib/commonjs/index.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,4 @@
+export * from "./types";
+export { isTraversal, parse } from "./parse";
+export { stringify } from "./stringify";
+//# sourceMappingURL=index.d.ts.map
Index: frontend/node_modules/css-what/lib/commonjs/index.d.ts.map
===================================================================
--- frontend/node_modules/css-what/lib/commonjs/index.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/css-what/lib/commonjs/index.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC"}
Index: frontend/node_modules/css-what/lib/commonjs/index.js
===================================================================
--- frontend/node_modules/css-what/lib/commonjs/index.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/css-what/lib/commonjs/index.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -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"), exports);
+var parse_1 = require("./parse");
+Object.defineProperty(exports, "isTraversal", { enumerable: true, get: function () { return parse_1.isTraversal; } });
+Object.defineProperty(exports, "parse", { enumerable: true, get: function () { return parse_1.parse; } });
+var stringify_1 = require("./stringify");
+Object.defineProperty(exports, "stringify", { enumerable: true, get: function () { return stringify_1.stringify; } });
Index: frontend/node_modules/css-what/lib/commonjs/parse.d.ts
===================================================================
--- frontend/node_modules/css-what/lib/commonjs/parse.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/css-what/lib/commonjs/parse.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,20 @@
+import { Selector, Traversal } from "./types";
+/**
+ * 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`, optionally with the passed `options`.
+ *
+ * @param selector Selector to parse.
+ * @param options Options for parsing.
+ * @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
Index: frontend/node_modules/css-what/lib/commonjs/parse.d.ts.map
===================================================================
--- frontend/node_modules/css-what/lib/commonjs/parse.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/css-what/lib/commonjs/parse.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/parse.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,QAAQ,EAGR,SAAS,EAIZ,MAAM,SAAS,CAAC;AA6DjB;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,QAAQ,GAAG,QAAQ,IAAI,SAAS,CAYrE;AAoCD;;;;;;;;GAQG;AACH,wBAAgB,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,EAAE,EAAE,CAUpD"}
Index: frontend/node_modules/css-what/lib/commonjs/parse.js
===================================================================
--- frontend/node_modules/css-what/lib/commonjs/parse.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/css-what/lib/commonjs/parse.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,425 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.parse = exports.isTraversal = void 0;
+var types_1 = require("./types");
+var reName = /^[^\\#]?(?:\\(?:[\da-f]{1,6}\s?|.)|[\w\-\u00b0-\uFFFF])+/;
+var reEscape = /\\([\da-f]{1,6}\s?|(\s)|.)/gi;
+var actionTypes = new Map([
+    [126 /* Tilde */, types_1.AttributeAction.Element],
+    [94 /* Circumflex */, types_1.AttributeAction.Start],
+    [36 /* Dollar */, types_1.AttributeAction.End],
+    [42 /* Asterisk */, types_1.AttributeAction.Any],
+    [33 /* ExclamationMark */, types_1.AttributeAction.Not],
+    [124 /* Pipe */, types_1.AttributeAction.Hyphen],
+]);
+// Pseudos, whose data property is parsed as well.
+var unpackPseudos = new Set([
+    "has",
+    "not",
+    "matches",
+    "is",
+    "where",
+    "host",
+    "host-context",
+]);
+/**
+ * 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_1.SelectorType.Adjacent:
+        case types_1.SelectorType.Child:
+        case types_1.SelectorType.Descendant:
+        case types_1.SelectorType.Parent:
+        case types_1.SelectorType.Sibling:
+        case types_1.SelectorType.ColumnCombinator:
+            return true;
+        default:
+            return false;
+    }
+}
+exports.isTraversal = isTraversal;
+var stripQuotesFromPseudos = new Set(["contains", "icontains"]);
+// Unescape function taken from https://github.com/jquery/sizzle/blob/master/src/sizzle.js#L152
+function funescape(_, escaped, escapedWhitespace) {
+    var high = parseInt(escaped, 16) - 0x10000;
+    // NaN means non-codepoint
+    return high !== high || escapedWhitespace
+        ? escaped
+        : high < 0
+            ? // BMP codepoint
+                String.fromCharCode(high + 0x10000)
+            : // Supplemental Plane codepoint (surrogate pair)
+                String.fromCharCode((high >> 10) | 0xd800, (high & 0x3ff) | 0xdc00);
+}
+function unescapeCSS(str) {
+    return str.replace(reEscape, funescape);
+}
+function isQuote(c) {
+    return c === 39 /* SingleQuote */ || c === 34 /* DoubleQuote */;
+}
+function isWhitespace(c) {
+    return (c === 32 /* Space */ ||
+        c === 9 /* Tab */ ||
+        c === 10 /* NewLine */ ||
+        c === 12 /* FormFeed */ ||
+        c === 13 /* CarriageReturn */);
+}
+/**
+ * Parses `selector`, optionally with the passed `options`.
+ *
+ * @param selector Selector to parse.
+ * @param options Options for parsing.
+ * @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) {
+    var subselects = [];
+    var endIndex = parseSelector(subselects, "".concat(selector), 0);
+    if (endIndex < selector.length) {
+        throw new Error("Unmatched selector: ".concat(selector.slice(endIndex)));
+    }
+    return subselects;
+}
+exports.parse = parse;
+function parseSelector(subselects, selector, selectorIndex) {
+    var tokens = [];
+    function getName(offset) {
+        var match = selector.slice(selectorIndex + offset).match(reName);
+        if (!match) {
+            throw new Error("Expected name, found ".concat(selector.slice(selectorIndex)));
+        }
+        var name = match[0];
+        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;
+        var start = selectorIndex;
+        var counter = 1;
+        for (; counter > 0 && selectorIndex < selector.length; selectorIndex++) {
+            if (selector.charCodeAt(selectorIndex) ===
+                40 /* LeftParenthesis */ &&
+                !isEscaped(selectorIndex)) {
+                counter++;
+            }
+            else if (selector.charCodeAt(selectorIndex) ===
+                41 /* RightParenthesis */ &&
+                !isEscaped(selectorIndex)) {
+                counter--;
+            }
+        }
+        if (counter) {
+            throw new Error("Parenthesis not matched");
+        }
+        return unescapeCSS(selector.slice(start, selectorIndex - 1));
+    }
+    function isEscaped(pos) {
+        var slashCount = 0;
+        while (selector.charCodeAt(--pos) === 92 /* BackSlash */)
+            slashCount++;
+        return (slashCount & 1) === 1;
+    }
+    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_1.SelectorType.Descendant) {
+            tokens[tokens.length - 1].type = type;
+            return;
+        }
+        ensureNotTraversal();
+        tokens.push({ type: type });
+    }
+    function addSpecialAttribute(name, action) {
+        tokens.push({
+            type: types_1.SelectorType.Attribute,
+            name: name,
+            action: 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 &&
+            tokens[tokens.length - 1].type === types_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) {
+        var firstChar = selector.charCodeAt(selectorIndex);
+        switch (firstChar) {
+            // Whitespace
+            case 32 /* Space */:
+            case 9 /* Tab */:
+            case 10 /* NewLine */:
+            case 12 /* FormFeed */:
+            case 13 /* CarriageReturn */: {
+                if (tokens.length === 0 ||
+                    tokens[0].type !== types_1.SelectorType.Descendant) {
+                    ensureNotTraversal();
+                    tokens.push({ type: types_1.SelectorType.Descendant });
+                }
+                stripWhitespace(1);
+                break;
+            }
+            // Traversals
+            case 62 /* GreaterThan */: {
+                addTraversal(types_1.SelectorType.Child);
+                stripWhitespace(1);
+                break;
+            }
+            case 60 /* LessThan */: {
+                addTraversal(types_1.SelectorType.Parent);
+                stripWhitespace(1);
+                break;
+            }
+            case 126 /* Tilde */: {
+                addTraversal(types_1.SelectorType.Sibling);
+                stripWhitespace(1);
+                break;
+            }
+            case 43 /* Plus */: {
+                addTraversal(types_1.SelectorType.Adjacent);
+                stripWhitespace(1);
+                break;
+            }
+            // Special attribute selectors: .class, #id
+            case 46 /* Period */: {
+                addSpecialAttribute("class", types_1.AttributeAction.Element);
+                break;
+            }
+            case 35 /* Hash */: {
+                addSpecialAttribute("id", types_1.AttributeAction.Equals);
+                break;
+            }
+            case 91 /* LeftSquareBracket */: {
+                stripWhitespace(1);
+                // Determine attribute name and namespace
+                var name_1 = void 0;
+                var namespace = null;
+                if (selector.charCodeAt(selectorIndex) === 124 /* Pipe */) {
+                    // Equivalent to no namespace
+                    name_1 = getName(1);
+                }
+                else if (selector.startsWith("*|", selectorIndex)) {
+                    namespace = "*";
+                    name_1 = getName(2);
+                }
+                else {
+                    name_1 = getName(0);
+                    if (selector.charCodeAt(selectorIndex) === 124 /* Pipe */ &&
+                        selector.charCodeAt(selectorIndex + 1) !==
+                            61 /* Equal */) {
+                        namespace = name_1;
+                        name_1 = getName(1);
+                    }
+                }
+                stripWhitespace(0);
+                // Determine comparison operation
+                var action = types_1.AttributeAction.Exists;
+                var possibleAction = actionTypes.get(selector.charCodeAt(selectorIndex));
+                if (possibleAction) {
+                    action = possibleAction;
+                    if (selector.charCodeAt(selectorIndex + 1) !==
+                        61 /* Equal */) {
+                        throw new Error("Expected `=`");
+                    }
+                    stripWhitespace(2);
+                }
+                else if (selector.charCodeAt(selectorIndex) === 61 /* Equal */) {
+                    action = types_1.AttributeAction.Equals;
+                    stripWhitespace(1);
+                }
+                // Determine value
+                var value = "";
+                var ignoreCase = null;
+                if (action !== "exists") {
+                    if (isQuote(selector.charCodeAt(selectorIndex))) {
+                        var quote = selector.charCodeAt(selectorIndex);
+                        var sectionEnd = selectorIndex + 1;
+                        while (sectionEnd < selector.length &&
+                            (selector.charCodeAt(sectionEnd) !== quote ||
+                                isEscaped(sectionEnd))) {
+                            sectionEnd += 1;
+                        }
+                        if (selector.charCodeAt(sectionEnd) !== quote) {
+                            throw new Error("Attribute value didn't end");
+                        }
+                        value = unescapeCSS(selector.slice(selectorIndex + 1, sectionEnd));
+                        selectorIndex = sectionEnd + 1;
+                    }
+                    else {
+                        var valueStart = selectorIndex;
+                        while (selectorIndex < selector.length &&
+                            ((!isWhitespace(selector.charCodeAt(selectorIndex)) &&
+                                selector.charCodeAt(selectorIndex) !==
+                                    93 /* RightSquareBracket */) ||
+                                isEscaped(selectorIndex))) {
+                            selectorIndex += 1;
+                        }
+                        value = unescapeCSS(selector.slice(valueStart, selectorIndex));
+                    }
+                    stripWhitespace(0);
+                    // See if we have a force ignore flag
+                    var forceIgnore = selector.charCodeAt(selectorIndex) | 0x20;
+                    // If the forceIgnore flag is set (either `i` or `s`), use that value
+                    if (forceIgnore === 115 /* LowerS */) {
+                        ignoreCase = false;
+                        stripWhitespace(1);
+                    }
+                    else if (forceIgnore === 105 /* LowerI */) {
+                        ignoreCase = true;
+                        stripWhitespace(1);
+                    }
+                }
+                if (selector.charCodeAt(selectorIndex) !==
+                    93 /* RightSquareBracket */) {
+                    throw new Error("Attribute selector didn't terminate");
+                }
+                selectorIndex += 1;
+                var attributeSelector = {
+                    type: types_1.SelectorType.Attribute,
+                    name: name_1,
+                    action: action,
+                    value: value,
+                    namespace: namespace,
+                    ignoreCase: ignoreCase,
+                };
+                tokens.push(attributeSelector);
+                break;
+            }
+            case 58 /* Colon */: {
+                if (selector.charCodeAt(selectorIndex + 1) === 58 /* Colon */) {
+                    tokens.push({
+                        type: types_1.SelectorType.PseudoElement,
+                        name: getName(2).toLowerCase(),
+                        data: selector.charCodeAt(selectorIndex) ===
+                            40 /* LeftParenthesis */
+                            ? readValueWithParenthesis()
+                            : null,
+                    });
+                    continue;
+                }
+                var name_2 = getName(1).toLowerCase();
+                var data = null;
+                if (selector.charCodeAt(selectorIndex) ===
+                    40 /* LeftParenthesis */) {
+                    if (unpackPseudos.has(name_2)) {
+                        if (isQuote(selector.charCodeAt(selectorIndex + 1))) {
+                            throw new Error("Pseudo-selector ".concat(name_2, " cannot be quoted"));
+                        }
+                        data = [];
+                        selectorIndex = parseSelector(data, selector, selectorIndex + 1);
+                        if (selector.charCodeAt(selectorIndex) !==
+                            41 /* RightParenthesis */) {
+                            throw new Error("Missing closing parenthesis in :".concat(name_2, " (").concat(selector, ")"));
+                        }
+                        selectorIndex += 1;
+                    }
+                    else {
+                        data = readValueWithParenthesis();
+                        if (stripQuotesFromPseudos.has(name_2)) {
+                            var 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_1.SelectorType.Pseudo, name: name_2, data: data });
+                break;
+            }
+            case 44 /* Comma */: {
+                finalizeSubselector();
+                tokens = [];
+                stripWhitespace(1);
+                break;
+            }
+            default: {
+                if (selector.startsWith("/*", selectorIndex)) {
+                    var 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;
+                }
+                var namespace = null;
+                var name_3 = void 0;
+                if (firstChar === 42 /* Asterisk */) {
+                    selectorIndex += 1;
+                    name_3 = "*";
+                }
+                else if (firstChar === 124 /* Pipe */) {
+                    name_3 = "";
+                    if (selector.charCodeAt(selectorIndex + 1) === 124 /* Pipe */) {
+                        addTraversal(types_1.SelectorType.ColumnCombinator);
+                        stripWhitespace(2);
+                        break;
+                    }
+                }
+                else if (reName.test(selector.slice(selectorIndex))) {
+                    name_3 = getName(0);
+                }
+                else {
+                    break loop;
+                }
+                if (selector.charCodeAt(selectorIndex) === 124 /* Pipe */ &&
+                    selector.charCodeAt(selectorIndex + 1) !== 124 /* Pipe */) {
+                    namespace = name_3;
+                    if (selector.charCodeAt(selectorIndex + 1) ===
+                        42 /* Asterisk */) {
+                        name_3 = "*";
+                        selectorIndex += 2;
+                    }
+                    else {
+                        name_3 = getName(1);
+                    }
+                }
+                tokens.push(name_3 === "*"
+                    ? { type: types_1.SelectorType.Universal, namespace: namespace }
+                    : { type: types_1.SelectorType.Tag, name: name_3, namespace: namespace });
+            }
+        }
+    }
+    finalizeSubselector();
+    return selectorIndex;
+}
Index: frontend/node_modules/css-what/lib/commonjs/stringify.d.ts
===================================================================
--- frontend/node_modules/css-what/lib/commonjs/stringify.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/css-what/lib/commonjs/stringify.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,8 @@
+import { Selector } from "./types";
+/**
+ * Turns `selector` back into a string.
+ *
+ * @param selector Selector to stringify.
+ */
+export declare function stringify(selector: Selector[][]): string;
+//# sourceMappingURL=stringify.d.ts.map
Index: frontend/node_modules/css-what/lib/commonjs/stringify.d.ts.map
===================================================================
--- frontend/node_modules/css-what/lib/commonjs/stringify.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/css-what/lib/commonjs/stringify.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"stringify.d.ts","sourceRoot":"","sources":["../../src/stringify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAiC,MAAM,SAAS,CAAC;AA6BlE;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE,GAAG,MAAM,CAIxD"}
Index: frontend/node_modules/css-what/lib/commonjs/stringify.js
===================================================================
--- frontend/node_modules/css-what/lib/commonjs/stringify.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/css-what/lib/commonjs/stringify.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,138 @@
+"use strict";
+var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
+    if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
+        if (ar || !(i in from)) {
+            if (!ar) ar = Array.prototype.slice.call(from, 0, i);
+            ar[i] = from[i];
+        }
+    }
+    return to.concat(ar || Array.prototype.slice.call(from));
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.stringify = void 0;
+var types_1 = require("./types");
+var attribValChars = ["\\", '"'];
+var pseudoValChars = __spreadArray(__spreadArray([], attribValChars, true), ["(", ")"], false);
+var charsToEscapeInAttributeValue = new Set(attribValChars.map(function (c) { return c.charCodeAt(0); }));
+var charsToEscapeInPseudoValue = new Set(pseudoValChars.map(function (c) { return c.charCodeAt(0); }));
+var charsToEscapeInName = new Set(__spreadArray(__spreadArray([], pseudoValChars, true), [
+    "~",
+    "^",
+    "$",
+    "*",
+    "+",
+    "!",
+    "|",
+    ":",
+    "[",
+    "]",
+    " ",
+    ".",
+], false).map(function (c) { return c.charCodeAt(0); }));
+/**
+ * Turns `selector` back into a string.
+ *
+ * @param selector Selector to stringify.
+ */
+function stringify(selector) {
+    return selector
+        .map(function (token) { return token.map(stringifyToken).join(""); })
+        .join(", ");
+}
+exports.stringify = stringify;
+function stringifyToken(token, index, arr) {
+    switch (token.type) {
+        // Simple types
+        case types_1.SelectorType.Child:
+            return index === 0 ? "> " : " > ";
+        case types_1.SelectorType.Parent:
+            return index === 0 ? "< " : " < ";
+        case types_1.SelectorType.Sibling:
+            return index === 0 ? "~ " : " ~ ";
+        case types_1.SelectorType.Adjacent:
+            return index === 0 ? "+ " : " + ";
+        case types_1.SelectorType.Descendant:
+            return " ";
+        case types_1.SelectorType.ColumnCombinator:
+            return index === 0 ? "|| " : " || ";
+        case types_1.SelectorType.Universal:
+            // Return an empty string if the selector isn't needed.
+            return token.namespace === "*" &&
+                index + 1 < arr.length &&
+                "name" in arr[index + 1]
+                ? ""
+                : "".concat(getNamespace(token.namespace), "*");
+        case types_1.SelectorType.Tag:
+            return getNamespacedName(token);
+        case types_1.SelectorType.PseudoElement:
+            return "::".concat(escapeName(token.name, charsToEscapeInName)).concat(token.data === null
+                ? ""
+                : "(".concat(escapeName(token.data, charsToEscapeInPseudoValue), ")"));
+        case types_1.SelectorType.Pseudo:
+            return ":".concat(escapeName(token.name, charsToEscapeInName)).concat(token.data === null
+                ? ""
+                : "(".concat(typeof token.data === "string"
+                    ? escapeName(token.data, charsToEscapeInPseudoValue)
+                    : stringify(token.data), ")"));
+        case types_1.SelectorType.Attribute: {
+            if (token.name === "id" &&
+                token.action === types_1.AttributeAction.Equals &&
+                token.ignoreCase === "quirks" &&
+                !token.namespace) {
+                return "#".concat(escapeName(token.value, charsToEscapeInName));
+            }
+            if (token.name === "class" &&
+                token.action === types_1.AttributeAction.Element &&
+                token.ignoreCase === "quirks" &&
+                !token.namespace) {
+                return ".".concat(escapeName(token.value, charsToEscapeInName));
+            }
+            var name_1 = getNamespacedName(token);
+            if (token.action === types_1.AttributeAction.Exists) {
+                return "[".concat(name_1, "]");
+            }
+            return "[".concat(name_1).concat(getActionValue(token.action), "=\"").concat(escapeName(token.value, charsToEscapeInAttributeValue), "\"").concat(token.ignoreCase === null ? "" : token.ignoreCase ? " i" : " s", "]");
+        }
+    }
+}
+function getActionValue(action) {
+    switch (action) {
+        case types_1.AttributeAction.Equals:
+            return "";
+        case types_1.AttributeAction.Element:
+            return "~";
+        case types_1.AttributeAction.Start:
+            return "^";
+        case types_1.AttributeAction.End:
+            return "$";
+        case types_1.AttributeAction.Any:
+            return "*";
+        case types_1.AttributeAction.Not:
+            return "!";
+        case types_1.AttributeAction.Hyphen:
+            return "|";
+        case types_1.AttributeAction.Exists:
+            throw new Error("Shouldn't be here");
+    }
+}
+function getNamespacedName(token) {
+    return "".concat(getNamespace(token.namespace)).concat(escapeName(token.name, charsToEscapeInName));
+}
+function getNamespace(namespace) {
+    return namespace !== null
+        ? "".concat(namespace === "*"
+            ? "*"
+            : escapeName(namespace, charsToEscapeInName), "|")
+        : "";
+}
+function escapeName(str, charsToEscape) {
+    var lastIdx = 0;
+    var ret = "";
+    for (var i = 0; i < str.length; i++) {
+        if (charsToEscape.has(str.charCodeAt(i))) {
+            ret += "".concat(str.slice(lastIdx, i), "\\").concat(str.charAt(i));
+            lastIdx = i + 1;
+        }
+    }
+    return ret.length > 0 ? ret + str.slice(lastIdx) : str;
+}
Index: frontend/node_modules/css-what/lib/commonjs/types.d.ts
===================================================================
--- frontend/node_modules/css-what/lib/commonjs/types.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/css-what/lib/commonjs/types.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,70 @@
+export declare 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 declare 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 declare type TraversalType = SelectorType.Adjacent | SelectorType.Child | SelectorType.Descendant | SelectorType.Parent | SelectorType.Sibling | SelectorType.ColumnCombinator;
+//# sourceMappingURL=types.d.ts.map
Index: frontend/node_modules/css-what/lib/commonjs/types.d.ts.map
===================================================================
--- frontend/node_modules/css-what/lib/commonjs/types.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/css-what/lib/commonjs/types.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,oBAAY,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,oBAAY,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,oBAAY,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"}
Index: frontend/node_modules/css-what/lib/commonjs/types.js
===================================================================
--- frontend/node_modules/css-what/lib/commonjs/types.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/css-what/lib/commonjs/types.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -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 || (exports.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 || (exports.AttributeAction = {}));
Index: frontend/node_modules/css-what/lib/es/index.d.ts
===================================================================
--- frontend/node_modules/css-what/lib/es/index.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/css-what/lib/es/index.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,4 @@
+export * from "./types";
+export { isTraversal, parse } from "./parse";
+export { stringify } from "./stringify";
+//# sourceMappingURL=index.d.ts.map
Index: frontend/node_modules/css-what/lib/es/index.d.ts.map
===================================================================
--- frontend/node_modules/css-what/lib/es/index.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/css-what/lib/es/index.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC"}
Index: frontend/node_modules/css-what/lib/es/index.js
===================================================================
--- frontend/node_modules/css-what/lib/es/index.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/css-what/lib/es/index.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+export * from "./types";
+export { isTraversal, parse } from "./parse";
+export { stringify } from "./stringify";
Index: frontend/node_modules/css-what/lib/es/parse.d.ts
===================================================================
--- frontend/node_modules/css-what/lib/es/parse.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/css-what/lib/es/parse.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,20 @@
+import { Selector, Traversal } from "./types";
+/**
+ * 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`, optionally with the passed `options`.
+ *
+ * @param selector Selector to parse.
+ * @param options Options for parsing.
+ * @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
Index: frontend/node_modules/css-what/lib/es/parse.d.ts.map
===================================================================
--- frontend/node_modules/css-what/lib/es/parse.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/css-what/lib/es/parse.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/parse.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,QAAQ,EAGR,SAAS,EAIZ,MAAM,SAAS,CAAC;AA6DjB;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,QAAQ,GAAG,QAAQ,IAAI,SAAS,CAYrE;AAoCD;;;;;;;;GAQG;AACH,wBAAgB,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,EAAE,EAAE,CAUpD"}
Index: frontend/node_modules/css-what/lib/es/parse.js
===================================================================
--- frontend/node_modules/css-what/lib/es/parse.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/css-what/lib/es/parse.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,420 @@
+import { SelectorType, AttributeAction, } from "./types";
+const reName = /^[^\\#]?(?:\\(?:[\da-f]{1,6}\s?|.)|[\w\-\u00b0-\uFFFF])+/;
+const reEscape = /\\([\da-f]{1,6}\s?|(\s)|.)/gi;
+const actionTypes = new Map([
+    [126 /* Tilde */, AttributeAction.Element],
+    [94 /* Circumflex */, AttributeAction.Start],
+    [36 /* Dollar */, AttributeAction.End],
+    [42 /* Asterisk */, AttributeAction.Any],
+    [33 /* ExclamationMark */, AttributeAction.Not],
+    [124 /* Pipe */, AttributeAction.Hyphen],
+]);
+// Pseudos, whose data property is parsed as well.
+const unpackPseudos = new Set([
+    "has",
+    "not",
+    "matches",
+    "is",
+    "where",
+    "host",
+    "host-context",
+]);
+/**
+ * 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 = parseInt(escaped, 16) - 0x10000;
+    // NaN means non-codepoint
+    return high !== high || escapedWhitespace
+        ? escaped
+        : high < 0
+            ? // BMP codepoint
+                String.fromCharCode(high + 0x10000)
+            : // Supplemental Plane codepoint (surrogate pair)
+                String.fromCharCode((high >> 10) | 0xd800, (high & 0x3ff) | 0xdc00);
+}
+function unescapeCSS(str) {
+    return str.replace(reEscape, funescape);
+}
+function isQuote(c) {
+    return c === 39 /* SingleQuote */ || c === 34 /* DoubleQuote */;
+}
+function isWhitespace(c) {
+    return (c === 32 /* Space */ ||
+        c === 9 /* Tab */ ||
+        c === 10 /* NewLine */ ||
+        c === 12 /* FormFeed */ ||
+        c === 13 /* CarriageReturn */);
+}
+/**
+ * Parses `selector`, optionally with the passed `options`.
+ *
+ * @param selector Selector to parse.
+ * @param options Options for parsing.
+ * @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;
+        let counter = 1;
+        for (; counter > 0 && selectorIndex < selector.length; selectorIndex++) {
+            if (selector.charCodeAt(selectorIndex) ===
+                40 /* LeftParenthesis */ &&
+                !isEscaped(selectorIndex)) {
+                counter++;
+            }
+            else if (selector.charCodeAt(selectorIndex) ===
+                41 /* RightParenthesis */ &&
+                !isEscaped(selectorIndex)) {
+                counter--;
+            }
+        }
+        if (counter) {
+            throw new Error("Parenthesis not matched");
+        }
+        return unescapeCSS(selector.slice(start, selectorIndex - 1));
+    }
+    function isEscaped(pos) {
+        let slashCount = 0;
+        while (selector.charCodeAt(--pos) === 92 /* BackSlash */)
+            slashCount++;
+        return (slashCount & 1) === 1;
+    }
+    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 &&
+            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 32 /* Space */:
+            case 9 /* Tab */:
+            case 10 /* NewLine */:
+            case 12 /* FormFeed */:
+            case 13 /* CarriageReturn */: {
+                if (tokens.length === 0 ||
+                    tokens[0].type !== SelectorType.Descendant) {
+                    ensureNotTraversal();
+                    tokens.push({ type: SelectorType.Descendant });
+                }
+                stripWhitespace(1);
+                break;
+            }
+            // Traversals
+            case 62 /* GreaterThan */: {
+                addTraversal(SelectorType.Child);
+                stripWhitespace(1);
+                break;
+            }
+            case 60 /* LessThan */: {
+                addTraversal(SelectorType.Parent);
+                stripWhitespace(1);
+                break;
+            }
+            case 126 /* Tilde */: {
+                addTraversal(SelectorType.Sibling);
+                stripWhitespace(1);
+                break;
+            }
+            case 43 /* Plus */: {
+                addTraversal(SelectorType.Adjacent);
+                stripWhitespace(1);
+                break;
+            }
+            // Special attribute selectors: .class, #id
+            case 46 /* Period */: {
+                addSpecialAttribute("class", AttributeAction.Element);
+                break;
+            }
+            case 35 /* Hash */: {
+                addSpecialAttribute("id", AttributeAction.Equals);
+                break;
+            }
+            case 91 /* LeftSquareBracket */: {
+                stripWhitespace(1);
+                // Determine attribute name and namespace
+                let name;
+                let namespace = null;
+                if (selector.charCodeAt(selectorIndex) === 124 /* 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) === 124 /* Pipe */ &&
+                        selector.charCodeAt(selectorIndex + 1) !==
+                            61 /* 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) !==
+                        61 /* Equal */) {
+                        throw new Error("Expected `=`");
+                    }
+                    stripWhitespace(2);
+                }
+                else if (selector.charCodeAt(selectorIndex) === 61 /* 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);
+                        let sectionEnd = selectorIndex + 1;
+                        while (sectionEnd < selector.length &&
+                            (selector.charCodeAt(sectionEnd) !== quote ||
+                                isEscaped(sectionEnd))) {
+                            sectionEnd += 1;
+                        }
+                        if (selector.charCodeAt(sectionEnd) !== quote) {
+                            throw new Error("Attribute value didn't end");
+                        }
+                        value = unescapeCSS(selector.slice(selectorIndex + 1, sectionEnd));
+                        selectorIndex = sectionEnd + 1;
+                    }
+                    else {
+                        const valueStart = selectorIndex;
+                        while (selectorIndex < selector.length &&
+                            ((!isWhitespace(selector.charCodeAt(selectorIndex)) &&
+                                selector.charCodeAt(selectorIndex) !==
+                                    93 /* RightSquareBracket */) ||
+                                isEscaped(selectorIndex))) {
+                            selectorIndex += 1;
+                        }
+                        value = unescapeCSS(selector.slice(valueStart, selectorIndex));
+                    }
+                    stripWhitespace(0);
+                    // See if we have a force ignore flag
+                    const forceIgnore = selector.charCodeAt(selectorIndex) | 0x20;
+                    // If the forceIgnore flag is set (either `i` or `s`), use that value
+                    if (forceIgnore === 115 /* LowerS */) {
+                        ignoreCase = false;
+                        stripWhitespace(1);
+                    }
+                    else if (forceIgnore === 105 /* LowerI */) {
+                        ignoreCase = true;
+                        stripWhitespace(1);
+                    }
+                }
+                if (selector.charCodeAt(selectorIndex) !==
+                    93 /* 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 58 /* Colon */: {
+                if (selector.charCodeAt(selectorIndex + 1) === 58 /* Colon */) {
+                    tokens.push({
+                        type: SelectorType.PseudoElement,
+                        name: getName(2).toLowerCase(),
+                        data: selector.charCodeAt(selectorIndex) ===
+                            40 /* LeftParenthesis */
+                            ? readValueWithParenthesis()
+                            : null,
+                    });
+                    continue;
+                }
+                const name = getName(1).toLowerCase();
+                let data = null;
+                if (selector.charCodeAt(selectorIndex) ===
+                    40 /* 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) !==
+                            41 /* 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 44 /* 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 === 42 /* Asterisk */) {
+                    selectorIndex += 1;
+                    name = "*";
+                }
+                else if (firstChar === 124 /* Pipe */) {
+                    name = "";
+                    if (selector.charCodeAt(selectorIndex + 1) === 124 /* Pipe */) {
+                        addTraversal(SelectorType.ColumnCombinator);
+                        stripWhitespace(2);
+                        break;
+                    }
+                }
+                else if (reName.test(selector.slice(selectorIndex))) {
+                    name = getName(0);
+                }
+                else {
+                    break loop;
+                }
+                if (selector.charCodeAt(selectorIndex) === 124 /* Pipe */ &&
+                    selector.charCodeAt(selectorIndex + 1) !== 124 /* Pipe */) {
+                    namespace = name;
+                    if (selector.charCodeAt(selectorIndex + 1) ===
+                        42 /* Asterisk */) {
+                        name = "*";
+                        selectorIndex += 2;
+                    }
+                    else {
+                        name = getName(1);
+                    }
+                }
+                tokens.push(name === "*"
+                    ? { type: SelectorType.Universal, namespace }
+                    : { type: SelectorType.Tag, name, namespace });
+            }
+        }
+    }
+    finalizeSubselector();
+    return selectorIndex;
+}
Index: frontend/node_modules/css-what/lib/es/stringify.d.ts
===================================================================
--- frontend/node_modules/css-what/lib/es/stringify.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/css-what/lib/es/stringify.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,8 @@
+import { Selector } from "./types";
+/**
+ * Turns `selector` back into a string.
+ *
+ * @param selector Selector to stringify.
+ */
+export declare function stringify(selector: Selector[][]): string;
+//# sourceMappingURL=stringify.d.ts.map
Index: frontend/node_modules/css-what/lib/es/stringify.d.ts.map
===================================================================
--- frontend/node_modules/css-what/lib/es/stringify.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/css-what/lib/es/stringify.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"stringify.d.ts","sourceRoot":"","sources":["../../src/stringify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAiC,MAAM,SAAS,CAAC;AA6BlE;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE,GAAG,MAAM,CAIxD"}
Index: frontend/node_modules/css-what/lib/es/stringify.js
===================================================================
--- frontend/node_modules/css-what/lib/es/stringify.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/css-what/lib/es/stringify.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,126 @@
+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;
+}
Index: frontend/node_modules/css-what/lib/es/types.d.ts
===================================================================
--- frontend/node_modules/css-what/lib/es/types.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/css-what/lib/es/types.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,70 @@
+export declare 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 declare 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 declare type TraversalType = SelectorType.Adjacent | SelectorType.Child | SelectorType.Descendant | SelectorType.Parent | SelectorType.Sibling | SelectorType.ColumnCombinator;
+//# sourceMappingURL=types.d.ts.map
Index: frontend/node_modules/css-what/lib/es/types.d.ts.map
===================================================================
--- frontend/node_modules/css-what/lib/es/types.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/css-what/lib/es/types.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,oBAAY,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,oBAAY,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,oBAAY,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"}
Index: frontend/node_modules/css-what/lib/es/types.js
===================================================================
--- frontend/node_modules/css-what/lib/es/types.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/css-what/lib/es/types.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -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 = {}));
