Skip to content

Commit 3f7fa2c

Browse files
committed
fixes and cleanup
1 parent fa4245a commit 3f7fa2c

File tree

13 files changed

+261
-231
lines changed

13 files changed

+261
-231
lines changed

README.md

Lines changed: 217 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ And some additional, less used options:
6262
| --- | --- | ---
6363
| `renderer` | `false` | Used to specify a custom renderer, you can not use the rules or styles props with a custom renderer.
6464
| `markdownit` | `false` | A custom markdownit instance with your configuration, default is `MarkdownIt({typographer: true})`
65-
| `plugins` | `false` | An array of plugins to be applied to the markdownit instance
6665
| `maxTopLevelChildren` | `false` | Defaults to null, if defined as a number will only render out first `n` many top level children, then will try to render out `topLevelMaxExceededItem`
6766
| `topLevelMaxExceededItem` | `false` | Defaults to `<Text>...</Text>` - will render when `maxTopLevelChildren` is hit. Make sure to give it a key!
6867
| `allowedImageHandlers` | `false` | Defaults to `['data:image/png;base64', 'data:image/gif;base64', 'data:image/jpeg;base64', 'https://', 'http://']` - Any image that does not start with one of these will have the `defaultImageHandler` value prepended to it (unless `defaultImageHandler` is null in which case it won't try to render anything)
@@ -322,7 +321,223 @@ And some additional, less used options:
322321
<details><summary>Plugins and Extensions</summary>
323322
<p>
324323

325-
Plugins for **extra** syntax support - [see plugins](https://www.npmjs.com/browse/keyword/markdown-it-plugin) for the markdown-it library that this library is built on.
324+
Plugins for **extra** syntax support can be added using any markdown-it compatible plugins - [see plugins](https://www.npmjs.com/browse/keyword/markdown-it-plugin) for documentation from markdown-it. An example for integration follows:
325+
326+
#### Step 1
327+
328+
Inspect what the plugin will output - this can be achieved with the following code, using `markdown-it-block-embed` as an example for adding video support:
329+
330+
```jsx
331+
import Markdown, { MarkdownIt } from 'react-native-markdown-display';
332+
import blockEmbedPlugin from 'markdown-it-block-embed';
333+
334+
const markdownItInstance =
335+
MarkdownIt({typographer: true})
336+
.use(blockEmbedPlugin, {
337+
containerClassName: "video-embed"
338+
});
339+
340+
const copy = `
341+
# Some header
342+
343+
@[youtube](lJIrF4YjHfQ)
344+
`;
345+
346+
// this shows you the tree that is used by the react-native-markdown-display
347+
const astTree = markdownItInstance.parse(copy, {});
348+
console.log(astTree);
349+
350+
//this contains the html that would be generated - not used by react-native-markdown-display but useful for reference
351+
const html = markdownItInstance.render(copy);
352+
console.log(html);
353+
354+
```
355+
356+
The above code will output something like this:
357+
358+
```
359+
astTree:
360+
361+
(4) [Token, Token, Token, Token]
362+
363+
0: Token {type: "heading_open", tag: "h1", attrs: null, map: Array(2), nesting: 1, …}
364+
1: Token {type: "inline", tag: "", attrs: null, map: Array(2), nesting: 0, …}
365+
2: Token {type: "heading_close", tag: "h1", attrs: null, map: null, nesting: -1, …}
366+
3: Token {type: "video", tag: "div", attrs: null, map: Array(2), nesting: 0, …}
367+
368+
length: 4
369+
```
370+
371+
```
372+
html:
373+
374+
375+
<h1>Some header</h1>
376+
<div class="video-embed block-embed-service-youtube"><iframe type="text/html" src="//www.youtube.com/embed/lJIrF4YjHfQ" frameborder="0" width="640" height="390" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe></div>
377+
```
378+
379+
#### Step 2
380+
381+
Identify the new components and integrate the plugin with a rendered compoonent.
382+
383+
In the example above, the heading_open, inline and heading_close tags are all handled by the existing code base, but we need to handle the **'video'** type.
384+
385+
Note, in the example below we use the `debugPrintTree` property to see what rules we are rendering:
386+
387+
```jsx
388+
import React from 'react';
389+
import { SafeAreaView, ScrollView, View, StatusBar } from 'react-native';
390+
391+
import Markdown, { MarkdownIt } from 'react-native-markdown-display';
392+
import blockEmbedPlugin from 'markdown-it-block-embed';
393+
394+
const markdownItInstance =
395+
MarkdownIt({typographer: true})
396+
.use(blockEmbedPlugin, {
397+
containerClassName: "video-embed"
398+
});
399+
400+
const copy = `
401+
# Some header
402+
403+
@[youtube](lJIrF4YjHfQ)
404+
`;
405+
406+
const App: () => React$Node = () => {
407+
return (
408+
<>
409+
<StatusBar/>
410+
<SafeAreaView>
411+
<ScrollView
412+
style={{height: '100%'}}
413+
>
414+
<View>
415+
<Markdown
416+
debugPrintTree
417+
markdownit={markdownItInstance}
418+
>
419+
{copy}
420+
</Markdown>
421+
</View>
422+
</ScrollView>
423+
</SafeAreaView>
424+
</>
425+
);
426+
};
427+
428+
export default App;
429+
430+
```
431+
432+
In the console, we will see the following rendered tree:
433+
434+
```
435+
body
436+
-heading1
437+
--textgroup
438+
---text
439+
-video
440+
```
441+
442+
with the following error message
443+
444+
```
445+
Warning, unknown render rule encountered: video. 'unknown' render rule used (by default, returns null - nothing rendered)
446+
```
447+
448+
449+
#### Step 3
450+
451+
We need to create the render rules and styles to handle this new **'video'** component
452+
453+
454+
```jsx
455+
import React from 'react';
456+
import { SafeAreaView, ScrollView, View, StatusBar, Text } from 'react-native';
457+
458+
import Markdown, { MarkdownIt, getUniqueID } from 'react-native-markdown-display';
459+
import blockEmbedPlugin from 'markdown-it-block-embed';
460+
461+
const markdownItInstance =
462+
MarkdownIt({typographer: true})
463+
.use(blockEmbedPlugin, {
464+
containerClassName: "video-embed"
465+
});
466+
467+
const copy = `
468+
# Some header
469+
470+
@[youtube](lJIrF4YjHfQ)
471+
`;
472+
473+
const App: () => React$Node = () => {
474+
return (
475+
<>
476+
<StatusBar/>
477+
<SafeAreaView>
478+
<ScrollView
479+
style={{height: '100%'}}
480+
>
481+
<View>
482+
<Markdown
483+
debugPrintTree
484+
markdownit={markdownItInstance}
485+
style={{
486+
video: {
487+
color: 'red',
488+
}
489+
}}
490+
rules={{
491+
video: (node, children, parent, styles) =>{
492+
// examine the node properties to see what video we need to render
493+
console.log(node); // expected output of this is in readme.md below this code snip
494+
495+
return (<Text key={getUniqueID()} style={styles.video}>
496+
Return a video component instead of this text component!
497+
</Text>);
498+
}
499+
500+
}}
501+
>
502+
{copy}
503+
</Markdown>
504+
</View>
505+
</ScrollView>
506+
</SafeAreaView>
507+
</>
508+
);
509+
};
510+
511+
export default App;
512+
```
513+
514+
And all of the video properties needed to render something meaningful are on the node, like this:
515+
516+
```
517+
{type: "video", sourceType: "video", sourceInfo: {…}, sourceMeta: null, block: true, …}
518+
attributes: {}
519+
block: true
520+
children: []
521+
content: ""
522+
index: 1
523+
key: "rnmr_1720a98f540_video"
524+
markup: "@[youtube](lJIrF4YjHfQ)"
525+
sourceInfo:
526+
service: YouTubeService
527+
env: PluginEnvironment {md: MarkdownIt, options: {…}, services: {…}}
528+
name: "youtube"
529+
options:
530+
height: 390
531+
width: 640
532+
serviceName: "youtube"
533+
videoID: "lJIrF4YjHfQ"
534+
videoReference: "lJIrF4YjHfQ"
535+
sourceMeta: null
536+
sourceType: "video"
537+
tokenIndex: 5
538+
type: "video"
539+
```
540+
326541

327542
</p>
328543
</details>

package.json

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-native-markdown-display",
3-
"version": "6.0.1",
3+
"version": "6.1.0",
44
"description": "Markdown renderer for react-native, with CommonMark spec support + adds syntax extensions & sugar (URL autolinking, typographer), originally created by Mient-jan Stelling as react-native-markdown-renderer",
55
"main": "src/index.js",
66
"types": "src/index.d.ts",
@@ -35,16 +35,19 @@
3535
"react-native-fit-image": "^1.5.5"
3636
},
3737
"peerDependencies": {
38-
"@types/markdown-it": "^0.0.9",
38+
"@types/markdown-it": "^10.0.1",
3939
"@types/react-native": ">=0.50.0",
4040
"react": "^16.2.0",
4141
"react-native": ">=0.50.4"
4242
},
4343
"devDependencies": {
44-
"@babel/core": "^7.8.4",
45-
"@babel/runtime": "^7.8.4",
46-
"@react-native-community/eslint-config": "^0.0.7",
47-
"eslint": "^6.8.0",
48-
"pre-commit": "1.2.2"
44+
"@babel/core": "^7.9.6",
45+
"@babel/runtime": "^7.9.6",
46+
"@react-native-community/eslint-config": "^1.1.0",
47+
"@typescript-eslint/parser": "^2.32.0",
48+
"eslint": "^7.0.0",
49+
"json-schema": "^0.2.5",
50+
"pre-commit": "1.2.2",
51+
"typescript": "^3.8.3"
4952
}
5053
}

src/MarkdownIt/index.js

Lines changed: 0 additions & 1 deletion
This file was deleted.

src/index.d.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// tslint:disable:max-classes-per-file
2-
import { MarkdownIt, Token } from 'markdown-it';
3-
import { ComponentType, ReactNode } from 'react';
4-
import { StyleSheet, View } from 'react-native';
2+
import {MarkdownIt, Token} from 'markdown-it';
3+
import {ComponentType, ReactNode} from 'react';
4+
import {StyleSheet, View} from 'react-native';
55

66
export function getUniqueID(): string;
77
export function openUrl(url: string): void;

src/index.js

Lines changed: 16 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ import tokensToAST from './lib/util/tokensToAST';
1414
import renderRules from './lib/renderRules';
1515
import AstRenderer from './lib/AstRenderer';
1616
import MarkdownIt from 'markdown-it';
17-
import PluginContainer from './lib/plugin/PluginContainer';
18-
import blockPlugin from './lib/plugin/blockPlugin';
1917
import removeTextStyleProps from './lib/util/removeTextStyleProps';
2018
import {styles} from './lib/styles';
2119
import {stringToTokens} from './lib/util/stringToTokens';
@@ -30,8 +28,6 @@ export {
3028
stringToTokens,
3129
tokensToAST,
3230
MarkdownIt,
33-
PluginContainer,
34-
blockPlugin,
3531
styles,
3632
removeTextStyleProps,
3733
};
@@ -41,11 +37,19 @@ export {
4137
const getStyle = (mergeStyle, style) => {
4238
let useStyles = {};
4339

44-
if (mergeStyle === true && style) {
45-
Object.keys(styles).forEach(value => {
40+
if (mergeStyle === true && style !== null) {
41+
// make sure we get anything user defuned
42+
Object.keys(style).forEach((value) => {
43+
useStyles[value] = {
44+
...StyleSheet.flatten(style[value]),
45+
};
46+
});
47+
48+
// combine any existing styles
49+
Object.keys(styles).forEach((value) => {
4650
useStyles[value] = {
4751
...styles[value],
48-
...(style !== null ? StyleSheet.flatten(style[value]) : {}),
52+
...StyleSheet.flatten(style[value]),
4953
};
5054
});
5155
} else {
@@ -54,15 +58,15 @@ const getStyle = (mergeStyle, style) => {
5458
};
5559

5660
if (style !== null) {
57-
Object.keys(style).forEach(value => {
61+
Object.keys(style).forEach((value) => {
5862
useStyles[value] = {
5963
...StyleSheet.flatten(style[value]),
6064
};
6165
});
6266
}
6367
}
6468

65-
Object.keys(useStyles).forEach(value => {
69+
Object.keys(useStyles).forEach((value) => {
6670
useStyles['_VIEW_SAFE_' + value] = removeTextStyleProps(useStyles[value]);
6771
});
6872

@@ -121,23 +125,11 @@ const getRenderer = (
121125
}
122126
};
123127

124-
const getMarkdownParser = (markdownit, plugins) => {
125-
let md = markdownit;
126-
if (plugins && plugins.length > 0) {
127-
plugins.forEach(plugin => {
128-
md = md.use.apply(md, plugin.toArray());
129-
});
130-
}
131-
132-
return md;
133-
};
134-
135128
const Markdown = React.memo(
136129
({
137130
children,
138131
renderer = null,
139132
rules = null,
140-
plugins = [],
141133
style = null,
142134
mergeStyle = true,
143135
markdownit = MarkdownIt({
@@ -184,12 +176,9 @@ const Markdown = React.memo(
184176
],
185177
);
186178

187-
const markdownParser = useMemo(
188-
() => getMarkdownParser(markdownit, plugins),
189-
[markdownit, plugins],
190-
);
179+
const momoizedParser = useMemo(() => markdownit, [markdownit]);
191180

192-
return parser(children, momoizedRenderer.render, markdownParser);
181+
return parser(children, momoizedRenderer.render, momoizedParser);
193182
},
194183
);
195184

@@ -212,7 +201,7 @@ Markdown.propTypes = {
212201

213202
if (typeof prop === 'object') {
214203
invalidProps = Object.keys(prop).filter(
215-
key => typeof prop[key] !== 'function',
204+
(key) => typeof prop[key] !== 'function',
216205
);
217206
}
218207

@@ -228,7 +217,6 @@ Markdown.propTypes = {
228217
}
229218
},
230219
markdownit: PropTypes.instanceOf(MarkdownIt),
231-
plugins: PropTypes.arrayOf(PropTypes.instanceOf(PluginContainer)),
232220
style: PropTypes.any,
233221
mergeStyle: PropTypes.bool,
234222
allowedImageHandlers: PropTypes.arrayOf(PropTypes.string),

0 commit comments

Comments
 (0)