react-native-marked 1.1.2 → 1.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,4 @@
1
+ import type { ReactNode } from 'react';
1
2
  import type { marked } from 'marked';
2
3
  import Renderer from './Renderer';
3
4
  import type { MarkedStyles } from '../theme/types';
@@ -21,35 +22,17 @@ class Parser {
21
22
  }
22
23
 
23
24
  parse(tokens: marked.Token[]) {
24
- const elements: React.ReactNode[] = tokens.map((token) => {
25
+ const elements: ReactNode[] = tokens.map((token) => {
25
26
  switch (token.type) {
26
27
  case 'space': {
27
28
  return null;
28
29
  }
29
30
  case 'paragraph': {
30
- let tempTokens: marked.Token[] = [];
31
- const paragraphChildren: React.ReactNode[] = [];
32
- token.tokens.forEach((t) => {
33
- /* To avoid inlining of image */
34
- if (t.type === 'image') {
35
- paragraphChildren.push(
36
- this.renderer.getTextNode(
37
- this.parseInline(tempTokens),
38
- this.styles.text
39
- )
40
- );
41
- paragraphChildren.push(this.parseInline([t]));
42
- tempTokens = [];
43
- return;
44
- }
45
- tempTokens = [...tempTokens, t];
46
- });
47
-
48
- if (tempTokens.length > 0) {
49
- paragraphChildren.push(
50
- this.renderer.getTextNode(this.parseInline(tempTokens), {})
31
+ const paragraphChildren =
32
+ this.getNormalizedSiblingNodesForBlockAndInlineTokens(
33
+ token.tokens,
34
+ this.styles.text
51
35
  );
52
- }
53
36
 
54
37
  return this.renderer.getViewNode(
55
38
  paragraphChildren,
@@ -64,7 +47,10 @@ class Parser {
64
47
  }
65
48
  case 'heading': {
66
49
  const styles = this.headingStylesMap[token.depth] ?? this.styles.text;
67
- return this.renderer.getTextNode(token.text, styles);
50
+ return this.renderer.getTextNode(
51
+ this.parseInline(token.tokens, styles),
52
+ styles
53
+ );
68
54
  }
69
55
  case 'code': {
70
56
  return this.renderer.getCodeBlockNode(
@@ -78,19 +64,24 @@ class Parser {
78
64
  }
79
65
  case 'list': {
80
66
  const li = token.items.map((item) => {
81
- if (
82
- item.tokens.length > 0 &&
83
- item.tokens[0]?.type === 'text' &&
84
- // @ts-ignore
85
- Array.isArray(item.tokens[0]?.tokens)
86
- ) {
87
- return this.renderer.getTextNode(
88
- // @ts-ignore
89
- this.parseInline(item.tokens[0]?.tokens),
90
- this.styles.li
91
- );
92
- }
93
- return this.renderer.getTextNode(item.text, this.styles.li);
67
+ const children = item.tokens.map((cItem) => {
68
+ if (cItem.type === 'text') {
69
+ /* getViewNode since tokens could contain a block like elements (i.e. img) */
70
+ const listChildren =
71
+ this.getNormalizedSiblingNodesForBlockAndInlineTokens(
72
+ // Note: Upstream types aren't available for this specific use-case
73
+ // @types/marked (https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/marked/index.d.ts)
74
+ // @ts-ignore
75
+ cItem.tokens,
76
+ this.styles.li
77
+ );
78
+ return this.renderer.getViewNode(listChildren, this.styles.li);
79
+ }
80
+
81
+ /* Parse the nested token */
82
+ return this.parse([cItem]);
83
+ });
84
+ return this.renderer.getViewNode(children, this.styles.li);
94
85
  });
95
86
  return this.renderer.getListNode(
96
87
  token.ordered,
@@ -99,51 +90,63 @@ class Parser {
99
90
  this.styles.li
100
91
  );
101
92
  }
102
- case 'html': {
103
- return this.renderer.getTextNode(token.text.trim(), this.styles.text);
104
- }
105
93
  default:
106
- console.warn(`Token with '${token.type}' type was not found.`);
107
- return null;
94
+ return this.parseInline([token]);
108
95
  }
109
96
  });
110
- return elements;
97
+ return elements.filter((element) => element !== null);
111
98
  }
112
99
 
113
100
  parseInline(tokens: marked.Token[], styles?: CustomStyleProp) {
114
- const elements: React.ReactNode[] = tokens.map((token) => {
101
+ const elements: ReactNode[] = tokens.map((token) => {
115
102
  if (!token) return null;
116
103
 
117
104
  switch (token.type) {
118
105
  case 'escape': {
119
106
  return this.renderer.getTextNode(token.text, {
120
- ...styles,
121
107
  ...this.styles.text,
108
+ ...styles,
122
109
  });
123
110
  }
124
111
  case 'link': {
125
- return this.renderer.getLinkNode(token.text, token.href, {
112
+ const linkStyle = {
126
113
  ...styles,
127
- ...this.styles.link,
128
- });
114
+ ...this.styles.link, // To override color property
115
+ };
116
+ return this.renderer.getLinkNode(
117
+ this.parseInline(token.tokens, linkStyle),
118
+ token.href,
119
+ linkStyle
120
+ );
129
121
  }
130
122
  case 'image': {
131
123
  return this.renderer.getImageNode(token.href);
132
124
  }
133
125
  case 'strong': {
126
+ const boldStyle = {
127
+ ...this.styles.strong,
128
+ ...styles,
129
+ };
134
130
  return this.renderer.getTextNode(
135
- this.parseInline(token.tokens, this.styles.strong),
136
- { ...styles, ...this.styles.strong }
131
+ this.parseInline(token.tokens, boldStyle),
132
+ boldStyle
137
133
  );
138
134
  }
139
135
  case 'em': {
136
+ const italicStyle = {
137
+ ...this.styles.em,
138
+ ...styles,
139
+ };
140
140
  return this.renderer.getTextNode(
141
- this.parseInline(token.tokens, this.styles.em),
142
- { ...styles, ...this.styles.em }
141
+ this.parseInline(token.tokens, italicStyle),
142
+ italicStyle
143
143
  );
144
144
  }
145
145
  case 'codespan': {
146
- return this.renderer.getTextNode(token.text, this.styles.codespan);
146
+ return this.renderer.getTextNode(token.text, {
147
+ ...this.styles.codespan,
148
+ ...styles,
149
+ });
147
150
  }
148
151
  case 'br': {
149
152
  return this.renderer.getTextNode('\n', {});
@@ -151,10 +154,15 @@ class Parser {
151
154
  case 'del': {
152
155
  return null;
153
156
  }
154
- case 'text': {
157
+ case 'text':
158
+ case 'html': {
159
+ if (token.raw.trim().length < 1) {
160
+ return null;
161
+ }
162
+
155
163
  return this.renderer.getTextNode(token.raw, {
156
- ...styles,
157
164
  ...this.styles.text,
165
+ ...styles,
158
166
  });
159
167
  }
160
168
  default: {
@@ -163,8 +171,42 @@ class Parser {
163
171
  }
164
172
  }
165
173
  });
166
- return elements;
174
+ return elements.filter((element) => element !== null);
167
175
  }
176
+
177
+ private getNormalizedSiblingNodesForBlockAndInlineTokens = (
178
+ tokens: marked.Token[],
179
+ textStyle: TextStyleProp
180
+ ): ReactNode[] => {
181
+ let tempTokens: marked.Token[] = [];
182
+ const siblingNodes: ReactNode[] = [];
183
+ tokens.forEach((t) => {
184
+ /**
185
+ * To avoid inlining images
186
+ * Note: to be extend for other token types
187
+ */
188
+ if (t.type === 'image') {
189
+ const parsed = this.parseInline(tempTokens);
190
+ if (parsed.length > 0) {
191
+ siblingNodes.push(
192
+ this.renderer.getTextNode(this.parseInline(tempTokens), textStyle)
193
+ );
194
+ }
195
+ siblingNodes.push(this.parseInline([t]));
196
+ tempTokens = [];
197
+ return;
198
+ }
199
+ tempTokens = [...tempTokens, t];
200
+ });
201
+
202
+ /* Remaining temp tokens if any */
203
+ if (tempTokens.length > 0) {
204
+ siblingNodes.push(
205
+ this.renderer.getTextNode(this.parseInline(tempTokens), {})
206
+ );
207
+ }
208
+ return siblingNodes;
209
+ };
168
210
  }
169
211
 
170
212
  export default Parser;
@@ -1,19 +1,15 @@
1
- import * as React from 'react';
2
- import { Linking, ScrollView, View, Text } from 'react-native';
1
+ import React, { ReactNode } from 'react';
2
+ import { ScrollView, View, Text } from 'react-native';
3
3
  import MarkedList from '@jsamr/react-native-li';
4
4
  import Disc from '@jsamr/counter-style/presets/disc';
5
5
  import Decimal from '@jsamr/counter-style/presets/decimal';
6
6
  import MDImage from './../components/MDImage';
7
7
  import { generateRandomString } from '../utils/string';
8
8
  import type { TextStyleProp, ViewStyleProp } from './types';
9
+ import { onLinkPress } from '../utils/handlers';
9
10
 
10
11
  class Renderer {
11
- private onLinkPress = (href: string) => () => Linking.openURL(href);
12
-
13
- getTextNode = (
14
- children: string | React.ReactNode[],
15
- styles: TextStyleProp
16
- ) => {
12
+ getTextNode = (children: string | ReactNode[], styles: TextStyleProp) => {
17
13
  return (
18
14
  <Text key={generateRandomString()} style={styles}>
19
15
  {children}
@@ -21,19 +17,23 @@ class Renderer {
21
17
  );
22
18
  };
23
19
 
24
- getLinkNode = (text: string, href: string, styles: TextStyleProp) => {
20
+ getLinkNode = (
21
+ children: string | ReactNode[],
22
+ href: string,
23
+ styles: TextStyleProp
24
+ ) => {
25
25
  return (
26
26
  <Text
27
27
  key={generateRandomString()}
28
- onPress={this.onLinkPress(href)}
28
+ onPress={onLinkPress(href)}
29
29
  style={styles}
30
30
  >
31
- {text}
31
+ {children}
32
32
  </Text>
33
33
  );
34
34
  };
35
35
 
36
- getViewNode(children: React.ReactNode[] | null, styles: ViewStyleProp) {
36
+ getViewNode(children: ReactNode[] | null, styles: ViewStyleProp) {
37
37
  return (
38
38
  <View key={generateRandomString()} style={styles}>
39
39
  {children}
@@ -47,13 +47,17 @@ class Renderer {
47
47
  textStyle: TextStyleProp
48
48
  ) {
49
49
  return (
50
- <ScrollView contentContainerStyle={containerStyle} horizontal>
50
+ <ScrollView
51
+ key={generateRandomString()}
52
+ contentContainerStyle={containerStyle}
53
+ horizontal
54
+ >
51
55
  <Text style={textStyle}>{text}</Text>
52
56
  </ScrollView>
53
57
  );
54
58
  }
55
59
 
56
- getBlockquoteNode(children: React.ReactNode[], styles: ViewStyleProp) {
60
+ getBlockquoteNode(children: ReactNode[], styles: ViewStyleProp) {
57
61
  return (
58
62
  <View key={generateRandomString()} style={styles}>
59
63
  {children}
@@ -67,7 +71,7 @@ class Renderer {
67
71
 
68
72
  getListNode(
69
73
  ordered: boolean,
70
- li: React.ReactNode[],
74
+ li: ReactNode[],
71
75
  listStyle: ViewStyleProp,
72
76
  textStyle: TextStyleProp
73
77
  ) {
@@ -76,6 +80,7 @@ class Renderer {
76
80
  counterRenderer={ordered ? Decimal : Disc}
77
81
  markerTextStyle={textStyle}
78
82
  markerBoxStyle={listStyle}
83
+ key={generateRandomString()}
79
84
  >
80
85
  {li.map((node) => node)}
81
86
  </MarkedList>
@@ -34,7 +34,9 @@ const getStyles = (
34
34
  ...fontStyle.regular,
35
35
  fontWeight: 'bold',
36
36
  },
37
- text: { ...fontStyle.regular },
37
+ text: {
38
+ ...fontStyle.regular,
39
+ },
38
40
  paragraph: {
39
41
  ...fontStyle.regular,
40
42
  paddingVertical: 8,
@@ -0,0 +1,9 @@
1
+ import { Linking } from 'react-native';
2
+
3
+ export const onLinkPress = (url: string) => () => {
4
+ try {
5
+ Linking.openURL(url);
6
+ } catch (e) {
7
+ console.warn("URL can't be opened", e);
8
+ }
9
+ };