mirror of
https://github.com/stackblitz/bolt.new
synced 2024-11-27 14:32:46 +00:00
117 lines
2.1 KiB
TypeScript
117 lines
2.1 KiB
TypeScript
import rehypeRaw from 'rehype-raw';
|
|
import remarkGfm from 'remark-gfm';
|
|
import type { PluggableList, Plugin } from 'unified';
|
|
import rehypeSanitize, { defaultSchema, type Options as RehypeSanitizeOptions } from 'rehype-sanitize';
|
|
import { SKIP, visit } from 'unist-util-visit';
|
|
import type { UnistNode, UnistParent } from 'node_modules/unist-util-visit/lib';
|
|
|
|
export const allowedHTMLElements = [
|
|
'a',
|
|
'b',
|
|
'blockquote',
|
|
'br',
|
|
'code',
|
|
'dd',
|
|
'del',
|
|
'details',
|
|
'div',
|
|
'dl',
|
|
'dt',
|
|
'em',
|
|
'h1',
|
|
'h2',
|
|
'h3',
|
|
'h4',
|
|
'h5',
|
|
'h6',
|
|
'hr',
|
|
'i',
|
|
'ins',
|
|
'kbd',
|
|
'li',
|
|
'ol',
|
|
'p',
|
|
'pre',
|
|
'q',
|
|
'rp',
|
|
'rt',
|
|
'ruby',
|
|
's',
|
|
'samp',
|
|
'source',
|
|
'span',
|
|
'strike',
|
|
'strong',
|
|
'sub',
|
|
'summary',
|
|
'sup',
|
|
'table',
|
|
'tbody',
|
|
'td',
|
|
'tfoot',
|
|
'th',
|
|
'thead',
|
|
'tr',
|
|
'ul',
|
|
'var',
|
|
];
|
|
|
|
const rehypeSanitizeOptions: RehypeSanitizeOptions = {
|
|
...defaultSchema,
|
|
tagNames: allowedHTMLElements,
|
|
attributes: {
|
|
...defaultSchema.attributes,
|
|
div: [...(defaultSchema.attributes?.div ?? []), 'data*', ['className', '__boltArtifact__']],
|
|
},
|
|
strip: [],
|
|
};
|
|
|
|
export function remarkPlugins(limitedMarkdown: boolean) {
|
|
const plugins: PluggableList = [remarkGfm];
|
|
|
|
if (limitedMarkdown) {
|
|
plugins.unshift(limitedMarkdownPlugin);
|
|
}
|
|
|
|
return plugins;
|
|
}
|
|
|
|
export function rehypePlugins(html: boolean) {
|
|
const plugins: PluggableList = [];
|
|
|
|
if (html) {
|
|
plugins.push(rehypeRaw, [rehypeSanitize, rehypeSanitizeOptions]);
|
|
}
|
|
|
|
return plugins;
|
|
}
|
|
|
|
const limitedMarkdownPlugin: Plugin = () => {
|
|
return (tree, file) => {
|
|
const contents = file.toString();
|
|
|
|
visit(tree, (node: UnistNode, index, parent: UnistParent) => {
|
|
if (
|
|
index == null ||
|
|
['paragraph', 'text', 'inlineCode', 'code', 'strong', 'emphasis'].includes(node.type) ||
|
|
!node.position
|
|
) {
|
|
return true;
|
|
}
|
|
|
|
let value = contents.slice(node.position.start.offset, node.position.end.offset);
|
|
|
|
if (node.type === 'heading') {
|
|
value = `\n${value}`;
|
|
}
|
|
|
|
parent.children[index] = {
|
|
type: 'text',
|
|
value,
|
|
} as any;
|
|
|
|
return [SKIP, index] as const;
|
|
});
|
|
};
|
|
};
|