@limetech/lime-elements
Version:
94 lines (93 loc) • 4.25 kB
JavaScript
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;
}