UNPKG

ultrahtml

Version:

A 1.75kB library for enhancing `html`. `ultrahtml` has zero dependencies and is compatible with any JavaScript runtime.

105 lines (104 loc) 3.23 kB
// src/transformers/sanitize.ts import { ELEMENT_NODE, walkSync } from "../index.js"; function resolveSantizeOptions(sanitize2) { if (sanitize2 === void 0) { return { allowElements: [], dropElements: ["script"], allowComponents: false, allowCustomElements: false, allowComments: false }; } else { const dropElements = /* @__PURE__ */ new Set([]); if (!sanitize2.allowElements?.includes("script")) { dropElements.add("script"); } for (const dropElement of sanitize2.dropElements ?? []) { dropElements.add(dropElement); } return { allowComponents: false, allowCustomElements: false, allowComments: false, ...sanitize2, dropElements: Array.from(dropElements) }; } } function getNodeKind(node) { if (node.name.includes("-")) return "custom-element"; if (/[\_\$A-Z]/.test(node.name[0]) || node.name.includes(".")) return "component"; return "element"; } function getAction(name, kind, sanitize2) { if (sanitize2.allowElements?.length > 0) { if (sanitize2.allowElements.includes(name)) return "allow"; } if (sanitize2.blockElements?.length > 0) { if (sanitize2.blockElements.includes(name)) return "block"; } if (sanitize2.dropElements?.length > 0) { if (sanitize2.dropElements.find((n) => n === name)) return "drop"; } if (kind === "component" && !sanitize2.allowComponents) return "drop"; if (kind === "custom-element" && !sanitize2.allowCustomElements) return "drop"; if (sanitize2.unblockElements) { return sanitize2.unblockElements.some((n) => n === name) ? "allow" : "block"; } return sanitize2.allowElements?.length > 0 ? "drop" : "allow"; } function sanitizeAttributes(node, sanitize2) { const attrs = node.attributes; for (const key of Object.keys(node.attributes)) { if (sanitize2.allowAttributes?.[key] && sanitize2.allowAttributes?.[key].includes(node.name) || sanitize2.allowAttributes?.[key]?.includes("*")) { continue; } if (sanitize2.dropAttributes?.[key] && sanitize2.dropAttributes?.[key].includes(node.name) || sanitize2.dropAttributes?.[key]?.includes("*")) { delete attrs[key]; } } return attrs; } function sanitizeElement(opts, node, parent) { const kind = getNodeKind(node); const { name } = node; const action = getAction(name, kind, opts); if (action === "drop") return () => { parent.children = parent.children.filter( (child) => child !== node ); }; if (action === "block") return () => { parent.children = parent.children.map((child) => child === node ? child.children : child).flat(1); }; return () => { node.attributes = sanitizeAttributes(node, opts); }; } function sanitize(opts) { const sanitize2 = resolveSantizeOptions(opts); return (doc) => { let actions = []; walkSync(doc, (node, parent) => { switch (node.type) { case ELEMENT_NODE: { actions.push(sanitizeElement(sanitize2, node, parent)); return; } default: return; } }); for (let i = actions.length - 1; i >= 0; i--) { actions[i](); } return doc; }; } export { sanitize as default };