/
react-markdown.js
119 lines (98 loc) · 3.7 KB
/
react-markdown.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
'use strict'
const xtend = require('xtend')
const unified = require('unified')
const parse = require('remark-parse')
const PropTypes = require('prop-types')
const addListMetadata = require('mdast-add-list-metadata')
const naiveHtml = require('./plugins/naive-html')
const disallowNode = require('./plugins/disallow-node')
const astToReact = require('./ast-to-react')
const wrapTableRows = require('./wrap-table-rows')
const getDefinitions = require('./get-definitions')
const uriTransformer = require('./uri-transformer')
const defaultRenderers = require('./renderers')
const symbols = require('./symbols')
const allTypes = Object.keys(defaultRenderers)
const ReactMarkdown = function ReactMarkdown(props) {
const src = props.source || props.children || ''
const parserOptions = props.parserOptions
if (props.allowedTypes && props.disallowedTypes) {
throw new Error('Only one of `allowedTypes` and `disallowedTypes` should be defined')
}
const renderers = xtend(defaultRenderers, props.renderers)
const plugins = [[parse, parserOptions]].concat(props.plugins || [])
const parser = plugins.reduce(applyParserPlugin, unified())
const rawAst = parser.parse(src)
const renderProps = xtend(props, {
renderers: renderers,
definitions: getDefinitions(rawAst)
})
const astPlugins = determineAstPlugins(props)
// eslint-disable-next-line no-sync
const transformedAst = parser.runSync(rawAst)
const ast = astPlugins.reduce((node, plugin) => plugin(node, renderProps), transformedAst)
return astToReact(ast, renderProps)
}
function applyParserPlugin(parser, plugin) {
return Array.isArray(plugin) ? parser.use(...plugin) : parser.use(plugin)
}
function determineAstPlugins(props) {
const plugins = [wrapTableRows, addListMetadata()]
let disallowedTypes = props.disallowedTypes
if (props.allowedTypes) {
disallowedTypes = allTypes.filter(
type => type !== 'root' && props.allowedTypes.indexOf(type) === -1
)
}
const removalMethod = props.unwrapDisallowed ? 'unwrap' : 'remove'
if (disallowedTypes && disallowedTypes.length > 0) {
plugins.push(disallowNode.ofType(disallowedTypes, removalMethod))
}
if (props.allowNode) {
plugins.push(disallowNode.ifNotMatch(props.allowNode, removalMethod))
}
const renderHtml = !props.escapeHtml && !props.skipHtml
const hasHtmlParser = (props.astPlugins || []).some(item => {
const plugin = Array.isArray(item) ? item[0] : item
return plugin.identity === symbols.HtmlParser
})
if (renderHtml && !hasHtmlParser) {
plugins.push(naiveHtml)
}
return props.astPlugins ? plugins.concat(props.astPlugins) : plugins
}
ReactMarkdown.defaultProps = {
renderers: {},
escapeHtml: true,
skipHtml: false,
sourcePos: false,
rawSourcePos: false,
transformLinkUri: uriTransformer,
astPlugins: [],
plugins: [],
parserOptions: {}
}
ReactMarkdown.propTypes = {
className: PropTypes.string,
source: PropTypes.string,
children: PropTypes.string,
sourcePos: PropTypes.bool,
rawSourcePos: PropTypes.bool,
escapeHtml: PropTypes.bool,
skipHtml: PropTypes.bool,
allowNode: PropTypes.func,
allowedTypes: PropTypes.arrayOf(PropTypes.oneOf(allTypes)),
disallowedTypes: PropTypes.arrayOf(PropTypes.oneOf(allTypes)),
transformLinkUri: PropTypes.oneOfType([PropTypes.func, PropTypes.bool]),
linkTarget: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
transformImageUri: PropTypes.func,
astPlugins: PropTypes.arrayOf(PropTypes.func),
unwrapDisallowed: PropTypes.bool,
renderers: PropTypes.object,
plugins: PropTypes.array,
parserOptions: PropTypes.object
}
ReactMarkdown.types = allTypes
ReactMarkdown.renderers = defaultRenderers
ReactMarkdown.uriTransformer = uriTransformer
module.exports = ReactMarkdown