UNPKG

@limetech/lime-elements

Version:
94 lines (93 loc) 4.25 kB
import { unified } from "unified"; import remarkParse from "remark-parse"; import remarkRehype from "remark-rehype"; import remarkGfm from "remark-gfm"; import rehypeParse from "rehype-parse"; import rehypeSanitize, { defaultSchema } from "rehype-sanitize"; import rehypeStringify from "rehype-stringify"; import rehypeRaw from "rehype-raw"; import { visit } from "unist-util-visit"; import { sanitizeStyle } from "./sanitize-style"; import { createLazyLoadImagesPlugin } from "./image-markdown-plugin"; import { createLinksPlugin } from "./link-markdown-plugin"; import { createRemoveEmptyParagraphsPlugin } from "./remove-empty-paragraphs-plugin"; /** * Takes a string as input and returns a new string * where the text has been converted to HTML. * * If the text is formatted with .md markdown, it will * be transformed to HTML. * * If the text already is in HTML it will be sanitized and * "dangerous" tags such as <script> will be removed. * * @param text - The string to convert. * @param options - Options for the conversions. * @returns The resulting HTML. */ export async function markdownToHTML(text, options) { var _a; if (options === null || options === void 0 ? void 0 : options.forceHardLineBreaks) { text = text.replaceAll(/(?<!\\)([\n\r])/g, ' $1'); } const file = await unified() .use(remarkParse) .use(remarkGfm) .use(remarkRehype, { allowDangerousHtml: true }) .use(rehypeRaw) .use(createLinksPlugin()) .use(rehypeSanitize, Object.assign({}, getWhiteList((_a = options === null || options === void 0 ? void 0 : options.whitelist) !== null && _a !== void 0 ? _a : []))) .use(() => { return (tree) => { // Run the sanitizeStyle function on all elements, to sanitize // the value of the `style` attribute, if there is one. visit(tree, 'element', sanitizeStyle); }; }) .use(createRemoveEmptyParagraphsPlugin(options === null || options === void 0 ? void 0 : options.removeEmptyParagraphs)) .use(createLazyLoadImagesPlugin(options === null || options === void 0 ? void 0 : options.lazyLoadImages)) .use(rehypeStringify) .process(text); return file.toString(); } /** * Sanitizes a given HTML string by removing dangerous tags and attributes. * * @param html - The string containing HTML to sanitize. * @param whitelist - Optional whitelist of custom components. * @returns The sanitized HTML string. */ export async function sanitizeHTML(html, whitelist) { const file = await unified() .use(rehypeParse) .use(rehypeSanitize, Object.assign({}, getWhiteList(whitelist !== null && whitelist !== void 0 ? whitelist : []))) .use(() => { return (tree) => { // Run the sanitizeStyle function on all elements, to sanitize // the value of the `style` attribute, if there is one. visit(tree, 'element', sanitizeStyle); }; }) .use(rehypeStringify) .process(html); return file.toString(); } function getWhiteList(allowedComponents) { var _a, _b, _c, _d; const defaultSchemaClone = [...((_a = defaultSchema.attributes['*']) !== null && _a !== void 0 ? _a : [])]; const asteriskAttributeWhitelist = defaultSchemaClone.filter((attr) => { return attr !== 'height'; }); asteriskAttributeWhitelist.push('style'); const whitelist = Object.assign(Object.assign({}, defaultSchema), { strip: [...((_b = defaultSchema.strip) !== null && _b !== void 0 ? _b : []), 'style'], tagNames: [ ...(defaultSchema.tagNames || []), ...allowedComponents.map((component) => component.tagName), ], attributes: Object.assign(Object.assign({}, defaultSchema.attributes), { p: [ ...((_c = defaultSchema.attributes.p) !== null && _c !== void 0 ? _c : []), ['className', 'MsoNormal'], ], a: [...((_d = defaultSchema.attributes.a) !== null && _d !== void 0 ? _d : []), 'referrerpolicy'], '*': asteriskAttributeWhitelist }) }); for (const component of allowedComponents) { whitelist.attributes[component.tagName] = component.attributes; } return whitelist; }