Skip to content

Commit

Permalink
[pre 4.0] Drop second argument in function signature
Browse files Browse the repository at this point in the history
Direct configuration of the remark parsing instance is no longer
possible (it wasn't very useful to begin with for 99% of users.)

Usage of the module is now a bit more straightforward:

compiler(markdown: string, options: object?)

With options currently allowing for an overrides property that
functions exactly like the third argument in 3.x and earlier.

compiler('# Hello world!', {
  overrides: {
    h1: {
      component: MyCustomHeader,
    },
  },
});
  • Loading branch information
Evan Jacobs committed Sep 16, 2016
1 parent f50219b commit f269a87
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 66 deletions.
72 changes: 42 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,53 +1,69 @@
# markdown to jsx converter
# markdown to jsx compiler

![build status](https://api.travis-ci.org/yaycmyk/markdown-to-jsx.svg) [![codecov](https://codecov.io/gh/yaycmyk/markdown-to-jsx/branch/master/graph/badge.svg)](https://codecov.io/gh/yaycmyk/markdown-to-jsx)


Enables the safe parsing of markdown into proper React JSX objects, so you don't need to use a pattern like `dangerouslySetInnerHTML` and potentially open your application up to security issues.

The only exception is arbitrary HTML in the markdown (kind of an antipattern), which will still use the unsafe method.
The only exception is arbitrary block-level HTML in the markdown (considered a markdown antipattern), which will still use the unsafe method.

Uses [remark-parse](https://github.com/wooorm/remark-parse) under the hood to parse markdown into a consistent AST format. The following [remark](https://github.com/wooorm/remark) settings are set by `markdown-to-jsx`:

Uses [remark](https://github.com/wooorm/remark) under the hood to parse markdown into a consistent AST format.
- footnotes: true
- gfm: true
- position: false

Requires React >= 0.14.

## Usage

```js
import converter from 'markdown-to-jsx';
import React from 'react';
import {render} from 'react-dom';
The default export function signature:

render(converter('# Hello world!'), document.body);
```js
compiler(markdown: string, options: object?)
```
[remark options](https://github.com/wooorm/remark#remarkprocessvalue-options-done) can be passed as the second argument:
ES6-style usage:
```js
converter('* abc\n* def\n* ghi', {bullet: '*'});
import compiler from 'markdown-to-jsx';
import React from 'react';
import {render} from 'react-dom';

render(compiler('# Hello world!'), document.body);
```
_Footnotes are enabled by default as of `markdown-to-jsx@2.0.0`._
Override a particular HTML tag's output:
## Overriding tags and adding props
```jsx
import compiler from 'markdown-to-jsx';
import React from 'react';
import {render} from 'react-dom';

As of `markdown-to-jsx@2.0.0`, it's now possible to selectively override a given HTML tag's JSX representation. This is done through a new third argument to the converter: an object made of keys, each being the lowercase html tag name (p, figure, a, etc.) to be overridden.
// surprise, it's a div instead!
const MyParagraph = ({children, ...props}) => (<div {...props}>{children}</div>);

render(
compiler('# Hello world!', {
overrides: {
h1: {
component: MyParagraph,
props: {
className: 'foo',
},
},
},
}), document.body
);

Each override can be given a `component` that will be substituted for the tag name and/or `props` that will be applied as you would expect.
/*
renders:
```js
converter('Hello there!', {}, {
p: {
component: MyParagraph,
props: {
className: 'foo'
},
}
});
<div class="foo">
Hello World
</div>
*/
```
The code above will replace all emitted `<p>` tags with the given component `MyParagraph`, and add the `className` specified in `props`.

Depending on the type of element, there are some props that must be preserved to ensure the markdown is converted as intended. They are:
- `a`: `title`, `href`
Expand All @@ -58,8 +74,4 @@ Depending on the type of element, there are some props that must be preserved to
Any conflicts between passed `props` and the specific properties above will be resolved in favor of `markdown-to-jsx`'s code.
## Known Issues

- remark's handling of arbitrary HTML causes nodes to be split, which causes garbage and malformed HTML - [Bug Ticket](https://github.com/wooorm/remark/issues/124)

MIT
18 changes: 7 additions & 11 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ function coalesceInlineHTML(ast) {
return ast.children.forEach(coalescer);
}

export default function markdownToJSX(markdown, options = {}, overrides = {}) {
export default function markdownToJSX(markdown, {overrides = {}} = {}) {
let definitions;
let footnotes;

Expand Down Expand Up @@ -550,14 +550,8 @@ export default function markdownToJSX(markdown, options = {}, overrides = {}) {
a string`);
}

if (getType.call(options) !== '[object Object]') {
throw new Error(`markdown-to-jsx: the second argument must be
undefined or an object literal ({}) containing
valid remark options`);
}

if (getType.call(overrides) !== '[object Object]') {
throw new Error(`markdown-to-jsx: the third argument must be
throw new Error(`markdown-to-jsx: options.overrides (second argument property) must be
undefined or an object literal with shape:
{
htmltagname: {
Expand All @@ -567,10 +561,12 @@ export default function markdownToJSX(markdown, options = {}, overrides = {}) {
}`);
}

options.position = options.position || false;
options.footnotes = options.footnotes || true;
const remarkAST = unified().use(parser).parse(markdown, {
footnotes: true,
gfm: true,
position: false,
});

const remarkAST = unified().use(parser).parse(markdown, options);
const extracted = extractDefinitionsFromASTTree(remarkAST, astToJSX);

definitions = extracted.definitions;
Expand Down
28 changes: 3 additions & 25 deletions index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,35 +15,13 @@ describe('markdown-to-jsx', () => {

expect(() => converter()).toThrow();
expect(() => converter(1)).toThrow();
expect(() => converter(function(){})).toThrow();
expect(() => converter(() => {})).toThrow();
expect(() => converter({})).toThrow();
expect(() => converter([])).toThrow();
expect(() => converter(null)).toThrow();
expect(() => converter(true)).toThrow();
});

it('should throw if not passed an object or undefined (second arg)', () => {
expect(() => converter('')).not.toThrow();
expect(() => converter('', {})).not.toThrow();

expect(() => converter('', 1)).toThrow();
expect(() => converter('', function(){})).toThrow();
expect(() => converter('', [])).toThrow();
expect(() => converter('', null)).toThrow();
expect(() => converter('', true)).toThrow();
});

it('should throw if not passed an object or undefined (third arg)', () => {
expect(() => converter('', {})).not.toThrow();
expect(() => converter('', {}, {})).not.toThrow();

expect(() => converter('', {}, 1)).toThrow();
expect(() => converter('', {}, function(){})).toThrow();
expect(() => converter('', {}, [])).toThrow();
expect(() => converter('', {}, null)).toThrow();
expect(() => converter('', {}, true)).toThrow();
});

it('should discard the root <div> wrapper if there is only one root child', () => {
const element = render(converter('Hello.'));
const $element = dom(element);
Expand Down Expand Up @@ -675,7 +653,7 @@ describe('markdown-to-jsx', () => {
}

const element = render(
converter('Hello.', {}, {p: {component: FakeParagraph}})
converter('Hello.', {overrides: {p: {component: FakeParagraph}}})
);

const $element = dom(element);
Expand All @@ -686,7 +664,7 @@ describe('markdown-to-jsx', () => {

it('should add props to the appropriate JSX tag if supplied', () => {
const element = render(
converter('Hello.', {}, {p: {props: {className: 'abc'}}})
converter('Hello.', {overrides: {p: {props: {className: 'abc'}}}})
);

const $element = dom(element);
Expand Down

0 comments on commit f269a87

Please sign in to comment.