Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

react-markdown

Package Overview
Dependencies
Maintainers
1
Versions
92
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-markdown - npm Package Compare versions

Comparing version 3.6.0 to 4.0.0

.prettierignore

18

CHANGELOG.md

@@ -5,2 +5,20 @@ # Change Log

## 4.0.0 - 2018-10-03
### BREAKING
* `text` is now a first-class node + renderer - if you are using `allowedNodes`, it needs to be included in this list. Since it is now a React component, it will be passed an object of props instead of the old approach where a string was passed. `children` will contain the actual text string.
* On React >= 16.2, if no `className` prop is provided, a fragment will be used instead of a div. To always render a div, pass `'div'` as the `root` renderer.
* On React >= 16.2, escaped HTML will no longer be rendered with div/span containers
* The UMD bundle now exports the component as `window.ReactMarkdown` instead of `window.reactMarkdown`
### Added
* HTML parser plugin for full HTML compatibility (Espen Hovlandsdal)
### Fixes
* URI transformer allows uppercase http/https URLs (Liam Kennedy)
* [TypeScript] Strongly type the keys of `renderers` (Linus Unnebäck)
## 3.6.0 - 2018-09-05

@@ -7,0 +25,0 @@

2

index.d.ts

@@ -82,3 +82,3 @@ // Type definitions for react-markdown > v3.3.0

readonly unwrapDisallowed?: boolean
readonly renderers?: {[nodeType: string]: ReactType}
readonly renderers?: {[nodeType: NodeType]: ReactType}
readonly astPlugins?: MdastPlugin[]

@@ -85,0 +85,0 @@ readonly plugins?: any[] | (() => void)

'use strict';
var React = require('react');
var xtend = require('xtend');

@@ -9,18 +10,11 @@

var index = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
var renderer = options.renderers[node.type];
var pos = node.position.start;
var key = [node.type, pos.line, pos.column].join('-');
if (node.type === 'text') {
return renderer ? renderer(node.value, key) : node.value;
}
if (typeof renderer !== 'function' && typeof renderer !== 'string' && !isReactFragment(renderer)) {
throw new Error('Renderer for type `' + node.type + '` not defined or is not renderable');
throw new Error("Renderer for type `".concat(node.type, "` not defined or is not renderable"));
}
var nodeProps = getNodeProps(node, key, options, renderer, parent, index);
return React.createElement(renderer, nodeProps, nodeProps.children || resolveChildren() || undefined);

@@ -30,3 +24,6 @@

return node.children && node.children.map(function (childNode, i) {
return astToReact(childNode, options, { node: node, props: nodeProps }, i);
return astToReact(childNode, options, {
node: node,
props: nodeProps
}, i);
});

@@ -38,11 +35,11 @@ }

return React.Fragment && React.Fragment === renderer;
}
} // eslint-disable-next-line max-params, complexity
// eslint-disable-next-line max-params, complexity
function getNodeProps(node, key, opts, renderer, parent, index) {
var props = { key: key };
var props = {
key: key
};
var isTagRenderer = typeof renderer === 'string'; // `sourcePos` is true if the user wants source information (line/column info from markdown source)
var isTagRenderer = typeof renderer === 'string';
// `sourcePos` is true if the user wants source information (line/column info from markdown source)
if (opts.sourcePos && node.position) {

@@ -54,5 +51,5 @@ props['data-sourcepos'] = flattenPosition(node.position);

props.sourcePosition = node.position;
}
} // If `includeNodeIndex` is true, pass node index info to all non-tag renderers
// If `includeNodeIndex` is true, pass node index info to all non-tag renderers
if (opts.includeNodeIndex && parent.node && parent.node.children && !isTagRenderer) {

@@ -67,7 +64,16 @@ props.index = parent.node.children.indexOf(node);

case 'root':
assignDefined(props, { className: opts.className });
assignDefined(props, {
className: opts.className
});
break;
case 'text':
props.nodeKey = key;
props.children = node.value;
break;
case 'heading':
props.level = node.depth;
break;
case 'list':

@@ -79,2 +85,3 @@ props.start = node.start;

break;
case 'listItem':

@@ -86,11 +93,23 @@ props.checked = node.checked;

props.children = (props.tight ? unwrapParagraphs(node) : node.children).map(function (childNode, i) {
return astToReact(childNode, opts, { node: node, props: props }, i);
return astToReact(childNode, opts, {
node: node,
props: props
}, i);
});
break;
case 'definition':
assignDefined(props, { identifier: node.identifier, title: node.title, url: node.url });
assignDefined(props, {
identifier: node.identifier,
title: node.title,
url: node.url
});
break;
case 'code':
assignDefined(props, { language: node.lang && node.lang.split(/\s/, 1)[0] });
assignDefined(props, {
language: node.lang && node.lang.split(/\s/, 1)[0]
});
break;
case 'inlineCode':

@@ -100,2 +119,3 @@ props.children = node.value;

break;
case 'link':

@@ -108,2 +128,3 @@ assignDefined(props, {

break;
case 'image':

@@ -116,2 +137,3 @@ assignDefined(props, {

break;
case 'linkReference':

@@ -122,2 +144,3 @@ assignDefined(props, xtend(ref, {

break;
case 'imageReference':

@@ -130,2 +153,3 @@ assignDefined(props, {

break;
case 'table':

@@ -136,2 +160,3 @@ case 'tableHead':

break;
case 'tableRow':

@@ -141,2 +166,3 @@ props.isHeader = parent.node.type === 'tableHead';

break;
case 'tableCell':

@@ -148,5 +174,7 @@ assignDefined(props, {

break;
case 'virtualHtml':
props.tag = node.tag;
break;
case 'html':

@@ -158,2 +186,14 @@ // @todo find a better way than this

break;
case 'parsedHtml':
props.escapeHtml = opts.escapeHtml;
props.skipHtml = opts.skipHtml;
props.element = mergeNodeChildren(node, (node.children || []).map(function (child, i) {
return astToReact(child, opts, {
node: node,
props: props
}, i);
}));
break;
default:

@@ -182,2 +222,14 @@ assignDefined(props, xtend(node, {

function mergeNodeChildren(node, parsedChildren) {
var el = node.element;
if (Array.isArray(el)) {
var Fragment = React.Fragment || 'div';
return React.createElement(Fragment, null, el);
}
var children = (el.props.children || []).concat(parsedChildren);
return React.cloneElement(el, null, children);
}
function flattenPosition(pos) {

@@ -184,0 +236,0 @@ return [pos.start.line, ':', pos.start.column, '-', pos.end.line, ':', pos.end.column].map(String).join('');

@@ -5,3 +5,2 @@ 'use strict';

var defs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
return (node.children || []).reduce(function (definitions, child) {

@@ -8,0 +7,0 @@ if (child.type === 'definition') {

@@ -1,2 +0,2 @@

'use strict';
"use strict";

@@ -3,0 +3,0 @@ var visit = require('unist-util-visit');

@@ -1,2 +0,2 @@

'use strict';
"use strict";

@@ -15,4 +15,4 @@ /**

module.exports = function (tree) {
var open = void 0;
var currentParent = void 0;
var open;
var currentParent;
visit(tree, 'html', function (node, index, parent) {

@@ -25,2 +25,3 @@ if (currentParent !== parent) {

var selfClosing = getSelfClosing(node);
if (selfClosing) {

@@ -36,2 +37,3 @@ parent.children.splice(index, 1, {

var current = getSimpleTag(node, parent);
if (!current) {

@@ -52,3 +54,2 @@ return true;

);
return tree;

@@ -59,2 +60,3 @@ };

var i = open.length;
while (i--) {

@@ -71,3 +73,7 @@ if (open[i].tag === matchingTag) {

var match = node.value.match(simpleTagRe);
return match ? { tag: match[2], opening: !match[1], node: node } : false;
return match ? {
tag: match[2],
opening: !match[1],
node: node
} : false;
}

@@ -83,3 +89,2 @@

var toIndex = parent.children.indexOf(toNode.node);
var extracted = parent.children.splice(fromIndex, toIndex - fromIndex + 1);

@@ -86,0 +91,0 @@ var children = extracted.slice(1, -1);

'use strict';
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); }
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance"); }
function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); }
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } }
var xtend = require('xtend');
var unified = require('unified');
var parse = require('remark-parse');
var PropTypes = require('prop-types');
var addListMetadata = require('mdast-add-list-metadata');
var naiveHtml = require('./plugins/naive-html');
var disallowNode = require('./plugins/disallow-node');
var astToReact = require('./ast-to-react');
var wrapTableRows = require('./wrap-table-rows');
var getDefinitions = require('./get-definitions');
var uriTransformer = require('./uriTransformer');
var uriTransformer = require('./uri-transformer');
var defaultRenderers = require('./renderers');
var symbols = require('./symbols');
var allTypes = Object.keys(defaultRenderers);

@@ -28,6 +47,4 @@

var renderers = xtend(defaultRenderers, props.renderers);
var plugins = [parse].concat(props.plugins || []);
var parser = plugins.reduce(applyParserPlugin, unified());
var rawAst = parser.parse(src);

@@ -38,3 +55,2 @@ var renderProps = xtend(props, {

});
var astPlugins = determineAstPlugins(props);

@@ -44,3 +60,2 @@ var ast = astPlugins.reduce(function (node, plugin) {

}, rawAst);
return astToReact(ast, renderProps);

@@ -55,4 +70,4 @@ };

var plugins = [wrapTableRows, addListMetadata()];
var disallowedTypes = props.disallowedTypes;
var disallowedTypes = props.disallowedTypes;
if (props.allowedTypes) {

@@ -65,2 +80,3 @@ disallowedTypes = allTypes.filter(function (type) {

var removalMethod = props.unwrapDisallowed ? 'unwrap' : 'remove';
if (disallowedTypes && disallowedTypes.length > 0) {

@@ -75,3 +91,8 @@ plugins.push(disallowNode.ofType(disallowedTypes, removalMethod));

var renderHtml = !props.escapeHtml && !props.skipHtml;
if (renderHtml) {
var hasHtmlParser = (props.astPlugins || []).some(function (item) {
var plugin = Array.isArray(item) ? item[0] : item;
return plugin.identity === symbols.HtmlParser;
});
if (renderHtml && !hasHtmlParser) {
plugins.push(naiveHtml);

@@ -93,3 +114,2 @@ }

};
ReactMarkdown.propTypes = {

@@ -114,7 +134,5 @@ className: PropTypes.string,

};
ReactMarkdown.types = allTypes;
ReactMarkdown.renderers = defaultRenderers;
ReactMarkdown.uriTransformer = uriTransformer;
module.exports = ReactMarkdown;

@@ -5,7 +5,7 @@ /* eslint-disable react/prop-types, react/no-multi-comp */

var xtend = require('xtend');
var React = require('react');
var createElement = React.createElement;
module.exports = {
root: 'div',
break: 'br',

@@ -27,3 +27,4 @@ paragraph: 'p',

tableCell: TableCell,
root: Root,
text: TextRenderer,
list: List,

@@ -36,5 +37,16 @@ listItem: ListItem,

html: Html,
virtualHtml: VirtualHtml
virtualHtml: VirtualHtml,
parsedHtml: ParsedHtml
};
function TextRenderer(props) {
return props.children;
}
function Root(props) {
var useFragment = !props.className;
var root = useFragment ? React.Fragment || 'div' : 'div';
return createElement(root, useFragment ? null : props, props.children);
}
function SimpleRenderer(tag, props) {

@@ -45,9 +57,13 @@ return createElement(tag, getCoreProps(props), props.children);

function TableCell(props) {
var style = props.align ? { textAlign: props.align } : undefined;
var style = props.align ? {
textAlign: props.align
} : undefined;
var coreProps = getCoreProps(props);
return createElement(props.isHeader ? 'th' : 'td', style ? xtend({ style: style }, coreProps) : coreProps, props.children);
return createElement(props.isHeader ? 'th' : 'td', style ? xtend({
style: style
}, coreProps) : coreProps, props.children);
}
function Heading(props) {
return createElement('h' + props.level, getCoreProps(props), props.children);
return createElement("h".concat(props.level), getCoreProps(props), props.children);
}

@@ -57,2 +73,3 @@

var attrs = getCoreProps(props);
if (props.start !== null && props.start !== 1) {

@@ -67,5 +84,10 @@ attrs.start = props.start.toString();

var checkbox = null;
if (props.checked !== null) {
var checked = props.checked;
checkbox = createElement('input', { type: 'checkbox', checked: checked, readOnly: true });
checkbox = createElement('input', {
type: 'checkbox',
checked: checked,
readOnly: true
});
}

@@ -77,4 +99,6 @@

function CodeBlock(props) {
var className = props.language && 'language-' + props.language;
var code = createElement('code', className ? { className: className } : null, props.value);
var className = props.language && "language-".concat(props.language);
var code = createElement('code', className ? {
className: className
} : null, props.value);
return createElement('pre', getCoreProps(props), code);

@@ -93,11 +117,22 @@ }

var tag = props.isBlock ? 'div' : 'span';
if (props.escapeHtml) {
// @todo when fiber lands, we can simply render props.value
return createElement(tag, null, props.value);
var comp = React.Fragment || tag;
return createElement(comp, null, props.value);
}
var nodeProps = { dangerouslySetInnerHTML: { __html: props.value } };
var nodeProps = {
dangerouslySetInnerHTML: {
__html: props.value
}
};
return createElement(tag, nodeProps);
}
function ParsedHtml(props) {
return props['data-sourcepos'] ? React.cloneElement(props.element, {
'data-sourcepos': props['data-sourcepos']
}) : props.element;
}
function VirtualHtml(props) {

@@ -112,3 +147,5 @@ return createElement(props.tag, getCoreProps(props), props.children);

function getCoreProps(props) {
return props['data-sourcepos'] ? { 'data-sourcepos': props['data-sourcepos'] } : {};
return props['data-sourcepos'] ? {
'data-sourcepos': props['data-sourcepos']
} : {};
}

@@ -18,2 +18,3 @@ 'use strict';

}];
if (children.length > 1) {

@@ -20,0 +21,0 @@ table.children.push({

{
"name": "react-markdown",
"description": "Renders Markdown as React components",
"version": "3.6.0",
"version": "4.0.0",
"keywords": [

@@ -13,2 +13,3 @@ "markdown",

"scripts": {
"analyze": "npm run clean && npm run compile && NODE_ENV=production ANALYZE_BUNDLE=1 webpack -p",
"build": "npm run clean && npm run compile && NODE_ENV=production webpack -p && npm run build:demo",

@@ -24,3 +25,4 @@ "build:demo": "NODE_ENV=production webpack -p --config webpack.config.demo.js",

"test": "jest --coverage",
"watch": "webpack --watch"
"watch": "webpack --watch",
"watch:demo": "webpack --watch --config webpack.config.demo.js"
},

@@ -41,2 +43,3 @@ "repository": {

"dependencies": {
"html-to-react": "^1.3.3",
"mdast-add-list-metadata": "1.0.1",

@@ -50,24 +53,28 @@ "prop-types": "^15.6.1",

"devDependencies": {
"babel-cli": "^6.26.0",
"babel-loader": "^7.1.3",
"babel-plugin-transform-react-remove-prop-types": "^0.4.13",
"babel-preset-env": "^1.6.1",
"babel-preset-react": "^6.24.1",
"eslint": "^4.18.2",
"eslint-config-prettier": "^2.9.0",
"@babel/cli": "^7.1.0",
"@babel/core": "^7.1.0",
"@babel/preset-env": "^7.1.0",
"@babel/preset-react": "^7.0.0",
"babel-core": "^7.0.0-bridge.0",
"babel-jest": "^23.6.0",
"babel-loader": "^8.0.2",
"babel-plugin-transform-react-remove-prop-types": "^0.4.18",
"eslint": "^5.6.0",
"eslint-config-prettier": "^3.1.0",
"eslint-config-sanity": "^4.0.2",
"eslint-plugin-react": "^7.7.0",
"gh-pages-deploy": "^0.4.2",
"jest": "^22.4.2",
"prettier": "^1.11.1",
"react": "^16.2.0",
"gh-pages-deploy": "^0.5.0",
"jest": "^23.6.0",
"prettier": "^1.14.3",
"react": "^16.5.2",
"react-addons-test-utils": "^15.6.2",
"react-dom": "^16.2.0",
"react-test-renderer": "^16.2.0",
"react-dom": "^16.5.2",
"react-test-renderer": "^16.5.2",
"remark-breaks": "^1.0.0",
"remark-shortcodes": "^0.1.5",
"remark-shortcodes": "^0.2.1",
"rimraf": "^2.6.2",
"uglify-js": "^3.4.5",
"webpack": "^4.1.0",
"webpack-cli": "^2.0.10"
"webpack": "^4.20.2",
"webpack-bundle-analyzer": "^3.0.2",
"webpack-cli": "^3.1.1"
},

@@ -74,0 +81,0 @@ "peerDependencies": {

@@ -41,2 +41,10 @@ # react-markdown

## Upgrading to 4.0
Should be straightforward. You might need to alter you code slightly if you:
- Are using `allowedTypes` (add `text` to the list)
- Rely on there being a container `<div>` without a class name around your rendered markdown
See [CHANGELOG](CHANGELOG.md) for more details.
## Notes

@@ -47,13 +55,7 @@

## Inline HTML is broken
Inline HTML is currently broken for any tags that include attributes. A vague idea of how to fix
this has been planned, but if you're feeling up to the task, create an issue and let us know!
## Options
* `source` or `children` - _string_ The Markdown source to parse (**required**)
* `className` - _string_ Class name of the container element (default: `''`).
* `escapeHtml` - _boolean_ Setting to `false` will cause HTML to be rendered (see note above about
broken HTML, though). Be aware that setting this to `false` might cause security issues if the
* `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`).

@@ -93,6 +95,41 @@ * `skipHtml` - _boolean_ Setting to `true` will skip inlined and blocks of HTML (default: `false`).

varies based on the type of node.
* With one exception: if the key is `text`, the value should be a function that takes the literal text and returns a new string or React element.
* `plugins` - _array_ An array of unified/remark parser plugins. If you need to pass options to the plugin, pass an array with two elements, the first being the plugin and the second being the options - for instance: `{plugins: [[require('remark-shortcodes'), {your: 'options'}]]`. (default: `[]`)
## Parsing HTML
If you are in a trusted environment and want to parse and render HTML, you will want to use the `html-parser` plugin. For a default configuration, import `react-markdown/with-html` instead of the default:
```js
const ReactMarkdown = require('react-markdown/with-html')
const markdown = `
This block of Markdown contains <a href="https://en.wikipedia.org/wiki/HTML">HTML</a>, and will require the <code>html-parser</code> AST plugin to be loaded, in addition to setting the <code class="prop">escapeHtml</code> property to false.
`
<ReactMarkdown
source={markdown}
escapeHtml={false}
>
```
If you want to specify options for the HTML parsing step, you can instead import the HTML parser plugin directly:
```js
const ReactMarkdown = require('react-markdown')
const htmlParser = require('react-markdown/plugins/html-parser')
// See https://github.com/aknuds1/html-to-react#with-custom-processing-instructions
// for more info on the processing instructions
const parseHtml = htmlParser({
isValidNode: node => node.type !== 'script',
processingInstructions: [/* ... */]
})
<ReactMarkdown
source={markdown}
escapeHtml={false}
astPlugins={[parseHtml]}
>
```
## Node types

@@ -128,2 +165,4 @@

* `html` - HTML node (Best-effort rendering)
* `virtualHtml` - When not using the HTML parser plugin, a cheap and dirty approach to supporting simple HTML elements without a complete parser.
* `parsedHtml` - When using the HTML parser plugin, HTML parsed to a React element.

@@ -130,0 +169,0 @@ Note: Disallowing a node will also prevent the rendering of any children of that node, unless the

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc