|
1 | 1 | import React from 'react'; |
2 | | -import MarkdownIt from 'markdown-it'; |
| 2 | +import { unified } from 'unified'; |
| 3 | +import remarkParse from 'remark-parse'; |
| 4 | +import remarkRehype from 'remark-rehype'; |
| 5 | +import rehypeRaw from 'rehype-raw'; |
| 6 | +import rehypeAttr from 'rehype-attr'; |
| 7 | +import rehypeSanitize, { defaultSchema } from 'rehype-sanitize'; |
| 8 | +import rehypeStringify from 'rehype-stringify'; |
| 9 | + |
3 | 10 | import MdEditor from 'react-markdown-editor-lite'; |
4 | 11 | // import 'react-markdown-editor-lite/esm/index.less'; |
5 | 12 |
|
6 | 13 | import * as styles from './index.less'; |
7 | 14 |
|
8 | | -const mdParser = new MarkdownIt('commonmark', { |
9 | | - html: false, |
10 | | -}); |
11 | | - |
12 | | -const getAttributes = (content: string = 'image') => { |
13 | | - const attrs = content.split(' '); |
14 | | - const alt = attrs.shift(); |
15 | | - |
16 | | - const attributes = attrs.reduce((prev: string[], curr) => { |
17 | | - const [key, value] = curr.split('='); |
18 | | - |
19 | | - if (!key) { |
20 | | - return prev; |
21 | | - } |
22 | | - |
23 | | - if (!value) { |
24 | | - return prev.concat(`${key}`); |
25 | | - } |
26 | | - |
27 | | - return prev.concat(`${key}="${value}"`); |
28 | | - }, []); |
29 | | - |
30 | | - return { |
31 | | - alt, |
32 | | - attributes, |
33 | | - }; |
34 | | -}; |
35 | | - |
36 | | -mdParser.renderer.rules.image = function (tokens, index) { |
37 | | - const token = tokens[index]; |
38 | | - const srcIndex = token.attrIndex('src'); |
39 | | - |
40 | | - if (!token.attrs) { |
41 | | - return ''; |
42 | | - } |
43 | | - |
44 | | - const src = token.attrs[srcIndex][1]; |
45 | | - const content = mdParser.utils.escapeHtml(token.content); |
46 | | - const { alt, attributes } = getAttributes(content); |
47 | | - |
48 | | - return `<img src="${src}" alt="${alt}" ${attributes.join(' ')}/>`; |
49 | | -}; |
| 15 | +const processor = unified() |
| 16 | + .use(remarkParse) |
| 17 | + .use(remarkRehype, { allowDangerousHtml: true }) |
| 18 | + .use(rehypeRaw) |
| 19 | + .use(rehypeAttr, { properties: 'attr' }) |
| 20 | + .use(rehypeSanitize, { |
| 21 | + ...defaultSchema, |
| 22 | + attributes: { |
| 23 | + ...defaultSchema.attributes, |
| 24 | + img: [...(defaultSchema?.attributes?.img || []), ['style']], |
| 25 | + }, |
| 26 | + }) |
| 27 | + .use(rehypeStringify); |
50 | 28 |
|
51 | 29 | const Markdown: React.FC<Props> = (props) => { |
52 | 30 | const { value = '', type, onChange, customClassName = '' } = props; |
@@ -84,7 +62,10 @@ const Markdown: React.FC<Props> = (props) => { |
84 | 62 | readOnly={type === 'render'} |
85 | 63 | view={view} |
86 | 64 | value={value} |
87 | | - renderHTML={(text) => mdParser.render(text)} |
| 65 | + renderHTML={async (text) => { |
| 66 | + const content: any = await processor.process(text); |
| 67 | + return content.value; |
| 68 | + }} |
88 | 69 | onChange={(data) => { |
89 | 70 | onChange && onChange(data.text); |
90 | 71 | }} |
|
0 commit comments