import { validateSelector } from "./validate-selector"; import { filterStyleKeys } from "./filter-style-keys"; import { filterStyleValues } from "./filter-style-values"; import { Style } from "./types"; function isSelectorMediaQuery(selector: string): boolean { return /^@media\s+/i.test(selector); } function buildRule( selector: string, styles: Style = {}, curriedKeysFilter: (styleRule: Style) => Style, ): string { let result; let constructedRule = selector + "{"; if (isSelectorMediaQuery(selector)) { Object.keys(styles).forEach((innerSelector) => { if (!validateSelector(innerSelector)) { return; } constructedRule += buildRule( innerSelector, styles[innerSelector] as Style, curriedKeysFilter, ); }); } else { result = curriedKeysFilter(styles); const sanitized = filterStyleValues(result); Object.keys(sanitized).forEach((rule) => { constructedRule += rule + ":" + sanitized[rule] + ";"; }); } constructedRule += "}"; return constructedRule; } export function injectStylesheet( styles: Style = {}, propertyList: string[] = [], isAllowlist?: boolean, ): HTMLStyleElement { let position = 0; const styleElement = document.createElement("style"); (document.querySelector("head") as HTMLHeadElement).appendChild(styleElement); const stylesheet = styleElement.sheet as CSSStyleSheet; function curriedKeysFilter(styleObject: Style): Style { return filterStyleKeys(styleObject, propertyList, isAllowlist); } Object.keys(styles).forEach((selector) => { if (!validateSelector(selector)) { return; } const constructedRule = buildRule( selector, styles[selector] as Style, curriedKeysFilter, ); try { if (stylesheet.insertRule) { stylesheet.insertRule(constructedRule, position); } else { stylesheet.addRule( selector, constructedRule.replace(/^[^{]+/, "").replace(/[{}]/g, ""), position, ); } position++; } catch (err) { if (!(err instanceof SyntaxError || err instanceof DOMException)) { throw err; } } }); return styleElement; }