From 2a402c472db36d6f8dedc28a30b612743852d464 Mon Sep 17 00:00:00 2001 From: jaywcjlove <398188662@qq.com> Date: Tue, 8 Dec 2020 09:54:29 +0800 Subject: [PATCH] fix(deps): update dependency react-markdown to v5 --- README.md | 100 +++++++++++++++++++++----------------- package.json | 25 +++++----- src/allowNode.tsx | 12 ----- src/index.tsx | 119 +++++++++++++++++++--------------------------- 4 files changed, 119 insertions(+), 137 deletions(-) delete mode 100644 src/allowNode.tsx diff --git a/README.md b/README.md index 3ce91e04..ca7c0174 100644 --- a/README.md +++ b/README.md @@ -39,56 +39,68 @@ function Demo() { ```typescript import { ReactMarkdownProps } from 'react-markdown'; -interface IMarkdownPreviewProps extends Omit { - prefixCls?: string; +type MarkdownPreviewProps = { className?: string; + source?: string; style?: React.CSSProperties; onScroll?: (e: React.UIEvent) => void; onMouseOver?: (e: React.MouseEvent) => void; -} +} & ReactMarkdownProps; ``` -This [`ReactMarkdownProps`](https://github.com/rexxars/react-markdown/blob/2d991aa1097e95064f0209fc6d3a15b6300c07c7/index.d.ts#L76-L95) details. - -- `source` or `children` - _string_ The Markdown source to parse (**required**) -- `className` - _string_ Class name of the container element. If none is passed, a container will not be rendered. -- `escapeHtml` - _boolean_ Setting to `false` will cause HTML to be rendered (see notes below about proper HTML support). Be aware that setting this to `false` might cause security issues if the - input is user-generated. Use at your own risk. (default: `true`). -- `skipHtml` - _boolean_ Setting to `true` will skip inlined and blocks of HTML (default: `false`). -- `sourcePos` - _boolean_ Setting to `true` will add `data-sourcepos` attributes to all elements, - indicating where in the markdown source they were rendered from (default: `false`). -- `rawSourcePos` - _boolean_ Setting to `true` will pass a `sourcePosition` property to all renderers with structured source position information (default: `false`). -- `includeNodeIndex` - _boolean_ Setting to `true` will pass `index` and `parentChildCount` props to all renderers (default: `false`). -- `allowedTypes` - _array_ Defines which types of nodes should be allowed (rendered). (default: all - types). -- `disallowedTypes` - _array_ Defines which types of nodes should be disallowed (not rendered). - (default: none). -- `unwrapDisallowed` - _boolean_ Setting to `true` will try to extract/unwrap the children of - disallowed nodes. For instance, if disallowing `Strong`, the default behaviour is to simply skip - the text within the strong altogether, while the behaviour some might want is to simply have the - text returned without the strong wrapping it. (default: `false`) -- `allowNode` - _function_ Function execute if in order to determine if the node should be allowed. - Ran prior to checking `allowedTypes`/`disallowedTypes`. Returning a truthy value will allow the - node to be included. Note that if this function returns `true` and the type is not in - `allowedTypes` (or specified as a `disallowedType`), it won't be included. The function will - receive three arguments argument (`node`, `index`, `parent`), where `node` contains different - properties depending on the node type. -- `linkTarget` - _function|string_ Sets the default target attribute for links. If a function is - provided, it will be called with `url`, `text`, and `title` and should return a string - (e.g. `_blank` for a new tab). Default is `undefined` (no target attribute). -- `transformLinkUri` - _function|null_ Function that gets called for each encountered link with a - single argument - `uri`. The returned value is used in place of the original. The default link URI - transformer acts as an XSS-filter, neutralizing things like `javascript:`, `vbscript:` and `file:` - protocols. If you specify a custom function, this default filter won't be called, but you can - access it as `require('react-markdown').uriTransformer`. If you want to disable the default - transformer, pass `null` to this option. -- `transformImageUri` - _function|null_ Function that gets called for each encountered image with a - single argument - `uri`. The returned value is used in place of the original. -- `renderers` - _object_ An object where the keys represent the node type and the value is a React - component. The object is merged with the default renderers. The props passed to the component - varies based on the type of node. - -See [Options Props](https://github.com/rexxars/react-markdown/tree/2d991aa1097e95064f0209fc6d3a15b6300c07c7#options) for more details. +This [`ReactMarkdownProps`](https://github.com/remarkjs/react-markdown/blob/22bb78747d768181cb9ea8711b5e13c3768921d8/index.d.ts#L32-L84) details. + +- `source` (`string`, default: `''`)\ + Markdown to parse +- `className` (`string?`)\ + Wrap the markdown in a `div` with this class name +- `allowDangerousHtml` (`boolean`, default: `false`)\ + This project is safe by default and escapes HTML. + Use `allowDangerousHtml: true` to allow dangerous html instead. + See [security](https://github.com/remarkjs/react-markdown/tree/22bb78747d768181cb9ea8711b5e13c3768921d8#security) +- `skipHtml` (`boolean`, default: `false`)\ + Ignore HTML in Markdown +- `sourcePos` (`boolean`, default: `false`)\ + Pass a prop to all renderers with a serialized position + (`data-sourcepos="3:1-3:13"`) +- `rawSourcePos` (`boolean`, default: `false`)\ + Pass a prop to all renderers with their [position](https://github.com/syntax-tree/unist#position) + (`sourcePosition: {start: {line: 3, column: 1}, end:…}`) +- `includeNodeIndex` (`boolean`, default: `false`)\ + Pass [`index`](https://github.com/syntax-tree/unist#index) and `parentChildCount` in props to all renderers +- `allowedTypes` (`Array.`, default: list of all types)\ + Node types to allow (can’t combine w/ `disallowedTypes`). + All types are available at `ReactMarkdown.types` +- `disallowedTypes` (`Array.`, default: `[]`)\ + Node types to disallow (can’t combine w/ `allowedTypes`) +- `allowNode` (`(node, index, parent) => boolean?`, optional)\ + Function called to check if a node is allowed (when truthy) or not. + `allowedTypes` / `disallowedTypes` is used first! +- `unwrapDisallowed` (`boolean`, default: `false`)\ + Extract (unwrap) the children of not allowed nodes. + By default, when `strong` is not allowed, it and it’s content is dropped, + but with `unwrapDisallowed` the node itself is dropped but the content used +- `linkTarget` (`string` or `(url, text, title) => string`, optional)\ + Target to use on links (such as `_blank` for ` string`, default: + [`./uri-transformer.js`][uri], optional)\ + URL to use for links. + The default allows only `http`, `https`, `mailto`, and `tel`, and is + available at `ReactMarkdown.uriTransformer`. + Pass `null` to allow all URLs. + See [security](https://github.com/remarkjs/react-markdown/tree/22bb78747d768181cb9ea8711b5e13c3768921d8#security) +- `transformImageUri` (`(uri) => string`, default: + [`./uri-transformer.js`][uri], optional)\ + Same as `transformLinkUri` but for images +- `renderers` (`Object.`, default: `{}`)\ + Object mapping node types to React components. + Merged with the default renderers (available at `ReactMarkdown.renderers`). + Which props are passed varies based on the node +- `plugins` (`Array.`, default: `[]`)\ + List of [remark plugins](https://github.com/remarkjs/remark/blob/main/doc/plugins.md#list-of-plugins) to use. + See the next section for examples on how to pass options + +See [Options Props](https://github.com/remarkjs/react-markdown/tree/22bb78747d768181cb9ea8711b5e13c3768921d8#props) for more details. ## Development diff --git a/package.json b/package.json index 8751d118..6728bd69 100644 --- a/package.json +++ b/package.json @@ -43,24 +43,25 @@ "react-dom": ">=16.8.0" }, "dependencies": { - "@babel/runtime": "7.11.2", - "@types/prismjs": "1.16.1", - "prismjs": "1.21.0", - "react-markdown": "4.3.1" + "@babel/runtime": "7.12.5", + "@types/prismjs": "1.16.2", + "prismjs": "1.22.0", + "remark-gfm": "1.0.0", + "react-markdown": "5.0.2" }, "devDependencies": { - "@kkt/loader-less": "5.9.0", - "@kkt/loader-raw": "5.9.0", - "@types/react": "16.9.46", - "@types/react-dom": "16.9.8", + "@kkt/loader-less": "5.10.3", + "@kkt/loader-raw": "5.10.3", + "@types/react": "16.14.2", + "@types/react-dom": "16.9.10", "@uiw/react-github-corners": "1.2.0", "@uiw/react-shields": "1.1.0", "@uiw/reset.css": "1.0.4", "compile-less-cli": "1.5.1", - "kkt": "5.9.0", - "react": "16.13.1", - "react-dom": "16.13.1", - "tsbb": "1.7.7" + "kkt": "5.10.3", + "react": "16.14.0", + "react-dom": "16.14.0", + "tsbb": "1.7.8" }, "eslintConfig": { "extends": "react-app" diff --git a/src/allowNode.tsx b/src/allowNode.tsx deleted file mode 100644 index 5e41677d..00000000 --- a/src/allowNode.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { NodeType, MarkdownAbstractSyntaxTree } from 'react-markdown'; - -export default function allowNode(node: any, index: number, parent: NodeType) { - const nodeany: MarkdownAbstractSyntaxTree = node; - if (nodeany.type === 'html') { - // filter style - node.value = node.value.replace(/<((style|script|link|input|form)|\/(style|script|link|input|form))(\s?[^>]*>)/gi, (a: string) => { - return a.replace(/[<>]/g, (e: string) => (({ '<': '<', '>': '>' } as { [key: string]: string })[e])) - }); - } - return true; -} diff --git a/src/index.tsx b/src/index.tsx index f5fa19aa..a98e1fa4 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,88 +1,69 @@ -import React, { Component } from 'react'; +import React, { useEffect } from 'react'; +import ReactMarkdown, { ReactMarkdownProps } from 'react-markdown'; +import gfm from 'remark-gfm'; import Prism from 'prismjs'; import 'prismjs/components/prism-markup'; -import ReactMarkdown, { ReactMarkdownProps } from 'react-markdown'; -import allowNode from './allowNode'; import { loadLang } from './langs'; import './styles/markdown.less'; import './styles/markdowncolor.less'; -export type { - ReactMarkdownProps, - MarkdownAbstractSyntaxTree, - NodeType, - RemarkParseOptions, - Position, - Point, - AlignType, - ReferenceType, - LinkTargetResolver, - Renderers, -} from 'react-markdown'; - -export interface IMarkdownPreviewProps extends Omit { - prefixCls?: string; +export type MarkdownPreviewProps = { className?: string; + source?: string; style?: React.CSSProperties; onScroll?: (e: React.UIEvent) => void; onMouseOver?: (e: React.MouseEvent) => void; -} +} & ReactMarkdownProps; -export interface IMarkdownPreviewState { - value?: string; -} +const MarkdownPreview: React.FC = (props = {} as ReactMarkdownProps) => { + const { className, source, style, onScroll, onMouseOver, ...other } = props; + const mdp = React.createRef(); + const loadedLang = React.useRef(['markup']); + useEffect(() => { + highlight(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [source]); -export default class MarkdownPreview extends Component { - public mdp = React.createRef(); - public loadedLang: string[] = ['markup']; - public static defaultProps: IMarkdownPreviewProps = { - renderers: {}, - } - public constructor(props: IMarkdownPreviewProps) { - super(props); - this.state = { - value: '' || props.source, - }; - } - componentDidMount() { - this.highlight(); - } - componentDidUpdate(prevProps: IMarkdownPreviewProps) { - if (this.props.source !== prevProps.source) { - this.setState({ value: this.props.source }, () => { - this.highlight(); - }); - } - } - public renderHTML(mdStr?: string) { - this.setState({ value: mdStr }, () => { - this.highlight(); - }); - } - public async highlight() { - if (!this.mdp.current) return; - const codes = this.mdp.current.getElementsByTagName('code') as unknown as HTMLElement[]; - for (const value of codes) { - const tag = value.parentNode as HTMLElement; - if (tag && tag.tagName === 'PRE' && /^language-/.test(value.className.trim())) { - const lang = value.className.trim().replace(/^language-/, ''); + async function highlight() { + if (!mdp.current) return; + const codes = mdp.current.getElementsByTagName('code') as unknown as HTMLElement[]; + for (const val of codes) { + const tag = val.parentNode as HTMLElement; + if (tag && tag.tagName === 'PRE' && /^language-/.test(val.className.trim())) { + const lang = val.className.trim().replace(/^language-/, ''); try { - if (!this.loadedLang.includes(lang as never)) { - this.loadedLang.push(lang); + if (!loadedLang.current.includes(lang as never)) { + loadedLang.current.push(lang); await loadLang(lang); } - await Prism.highlightElement(value); + await Prism.highlightElement(val); } catch (error) { } } } } - render() { - const { className, style, onScroll, onMouseOver, ...other } = this.props; - const cls = `wmde-markdown wmde-markdown-color ${className || ''}`; - return ( -
- -
- ); - } -} \ No newline at end of file + + const cls = `wmde-markdown wmde-markdown-color ${className || ''}`; + const reactMarkdownProps = { + allowDangerousHtml: true, + ...other, + plugins: [gfm, ...(other.plugins || [])], + allowNode: (node, index, parent) => { + const nodeany = node; + if (nodeany.type === 'html' && reactMarkdownProps.allowDangerousHtml) { + // filter style + node.value = (node.value as string).replace(/<((style|script|link|input|form)|\/(style|script|link|input|form))(\s?[^>]*>)/gi, (a: string) => { + return a.replace(/[<>]/g, (e: string) => (({ '<': '<', '>': '>' } as { [key: string]: string })[e])) + }); + } + return true; + }, + source: source || '', + } as ReactMarkdownProps; + return ( +
+ +
+ ); +} + +export default MarkdownPreview;