UNPKG

@dfinity/gix-components

Version:
75 lines (74 loc) 2.75 kB
import { isNullish } from "@dfinity/utils"; import DOMPurify from "dompurify"; let domPurify = undefined; /** * A workaround to preserve target="_blank" attribute from sanitizer. * Utilizes data-target flag (set by flagTargetAttributeHook). * * Inspired by https://github.com/cure53/DOMPurify/issues/317#issuecomment-912474068 */ const restoreTargetAttributeHook = (node) => { if (node.getAttribute("data-target") === "blank") { // Use provided "rel" value if it contains "noopener" or "noreferrer" (https://web.dev/external-anchors-use-rel-noopener/) // otherwise add "noopener". // split() to avoid invalid values (e.g. "noopenernoreferrer") const originRel = node.getAttribute("rel") || ""; const parts = originRel.split(/\s+/); const rel = parts.includes("noopener") || parts.includes("noreferrer") ? originRel : "noopener"; node.setAttribute("target", "_blank"); node.setAttribute("rel", rel); // remove the flag node.removeAttribute("data-target"); } }; /** * Add the data attribute because "target" will be removed by sanitizer */ const flagTargetAttributeHook = (node) => { if (node.tagName === "A" && node.getAttribute("target") === "_blank") { node.setAttribute("data-target", "blank"); } return node; }; /** * Sanitize a text with DOMPurify. * * Note: this library needs a workaround to work in the NodeJS context - i.e. for our vitest test suite. * See the vitest.setup.ts for details. */ export const sanitize = (text) => { try { // DOMPurify initialization if (isNullish(domPurify)) { if (typeof DOMPurify.sanitize === "function") { domPurify = DOMPurify; } else { // eslint-disable-next-line @typescript-eslint/no-explicit-any if (typeof global.DOMPurify.sanitize === "function") { // eslint-disable-next-line @typescript-eslint/no-explicit-any domPurify = global.DOMPurify; } } // Preserve target="blank" workaround domPurify?.addHook("beforeSanitizeAttributes", flagTargetAttributeHook); domPurify?.addHook("afterSanitizeAttributes", restoreTargetAttributeHook); } return domPurify?.sanitize(text) ?? ""; } catch (err) { console.error(err); } console.error("no DOMPurify support"); return ""; }; let elementsCounters = {}; export const nextElementId = (prefix) => { elementsCounters = { ...elementsCounters, [prefix]: (elementsCounters[prefix] ?? 0) + 1, }; return `${prefix}${elementsCounters[prefix]}`; };