react-native-nitro-markdown 0.5.4 → 0.5.5
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.
- package/README.md +12 -0
- package/cpp/CMakeLists.txt +2 -1
- package/cpp/core/MD4CParser.cpp +49 -0
- package/cpp/core/MD4CParser.hpp +17 -0
- package/lib/commonjs/markdown.js +43 -35
- package/lib/commonjs/markdown.js.map +1 -1
- package/lib/module/markdown.js +43 -35
- package/lib/module/markdown.js.map +1 -1
- package/lib/typescript/commonjs/markdown.d.ts +6 -0
- package/lib/typescript/commonjs/markdown.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme.d.ts +2 -2
- package/lib/typescript/commonjs/theme.d.ts.map +1 -1
- package/lib/typescript/module/markdown.d.ts +6 -0
- package/lib/typescript/module/markdown.d.ts.map +1 -1
- package/lib/typescript/module/theme.d.ts +2 -2
- package/lib/typescript/module/theme.d.ts.map +1 -1
- package/package.json +2 -1
- package/src/markdown.tsx +74 -48
- package/src/theme.ts +2 -2
package/src/markdown.tsx
CHANGED
|
@@ -18,7 +18,6 @@ import {
|
|
|
18
18
|
type ListRenderItemInfo,
|
|
19
19
|
type FlatListProps,
|
|
20
20
|
type StyleProp,
|
|
21
|
-
type TextStyle,
|
|
22
21
|
type ViewStyle,
|
|
23
22
|
} from "react-native";
|
|
24
23
|
import {
|
|
@@ -101,7 +100,12 @@ function safeOnError<P extends string>(
|
|
|
101
100
|
}
|
|
102
101
|
|
|
103
102
|
const baseStylesCache = new WeakMap<MarkdownTheme, BaseStyles>();
|
|
104
|
-
|
|
103
|
+
type ParseAstCacheEntry = {
|
|
104
|
+
text: string;
|
|
105
|
+
ast: MarkdownNode;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const parseAstCache = new Map<string, ParseAstCacheEntry>();
|
|
105
109
|
const MAX_PARSE_CACHE_ENTRIES = 32;
|
|
106
110
|
const MAX_CACHEABLE_TEXT_LENGTH = 24_000;
|
|
107
111
|
const EMPTY_RENDERERS: CustomRenderers = {};
|
|
@@ -216,15 +220,18 @@ const getCachedParsedAst = (
|
|
|
216
220
|
}
|
|
217
221
|
|
|
218
222
|
const cacheKey = `${getParserOptionsKey(options)}|${text.length}|${hashString(text)}`;
|
|
219
|
-
const
|
|
220
|
-
if (
|
|
223
|
+
const cachedEntry = parseAstCache.get(cacheKey);
|
|
224
|
+
if (cachedEntry?.text === text) {
|
|
221
225
|
parseAstCache.delete(cacheKey);
|
|
222
|
-
parseAstCache.set(cacheKey,
|
|
223
|
-
return cloneMarkdownNode(
|
|
226
|
+
parseAstCache.set(cacheKey, cachedEntry);
|
|
227
|
+
return cloneMarkdownNode(cachedEntry.ast);
|
|
224
228
|
}
|
|
225
229
|
|
|
226
230
|
const parsedNode = parseWithNativeParser(text, options);
|
|
227
|
-
parseAstCache.set(cacheKey,
|
|
231
|
+
parseAstCache.set(cacheKey, {
|
|
232
|
+
text,
|
|
233
|
+
ast: parsedNode,
|
|
234
|
+
});
|
|
228
235
|
if (parseAstCache.size > MAX_PARSE_CACHE_ENTRIES) {
|
|
229
236
|
const oldestCacheKey = parseAstCache.keys().next().value;
|
|
230
237
|
if (typeof oldestCacheKey === "string") {
|
|
@@ -235,20 +242,27 @@ const getCachedParsedAst = (
|
|
|
235
242
|
return cloneMarkdownNode(parsedNode);
|
|
236
243
|
};
|
|
237
244
|
|
|
245
|
+
const sortPluginsByPriority = (
|
|
246
|
+
plugins?: MarkdownPlugin[],
|
|
247
|
+
): MarkdownPlugin[] | undefined => {
|
|
248
|
+
if (!plugins || plugins.length === 0) {
|
|
249
|
+
return undefined;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return [...plugins].sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
|
|
253
|
+
};
|
|
254
|
+
|
|
238
255
|
const applyBeforeParsePlugins = (
|
|
239
256
|
markdown: string,
|
|
240
|
-
|
|
257
|
+
sortedPlugins?: MarkdownPlugin[],
|
|
241
258
|
onError?: (error: Error, phase: "before-plugin", pluginName?: string) => void,
|
|
242
259
|
): string => {
|
|
243
|
-
if (!
|
|
260
|
+
if (!sortedPlugins || sortedPlugins.length === 0) {
|
|
244
261
|
return markdown;
|
|
245
262
|
}
|
|
246
263
|
|
|
247
|
-
const sorted = [...plugins].sort(
|
|
248
|
-
(a, b) => (b.priority ?? 0) - (a.priority ?? 0),
|
|
249
|
-
);
|
|
250
264
|
let nextMarkdown = markdown;
|
|
251
|
-
for (const plugin of
|
|
265
|
+
for (const plugin of sortedPlugins) {
|
|
252
266
|
if (!plugin.beforeParse) continue;
|
|
253
267
|
|
|
254
268
|
try {
|
|
@@ -271,18 +285,15 @@ const applyBeforeParsePlugins = (
|
|
|
271
285
|
|
|
272
286
|
const applyAfterParsePlugins = (
|
|
273
287
|
ast: MarkdownNode,
|
|
274
|
-
|
|
288
|
+
sortedPlugins?: MarkdownPlugin[],
|
|
275
289
|
onError?: (error: Error, phase: "after-plugin", pluginName?: string) => void,
|
|
276
290
|
): MarkdownNode => {
|
|
277
|
-
if (!
|
|
291
|
+
if (!sortedPlugins || sortedPlugins.length === 0) {
|
|
278
292
|
return ast;
|
|
279
293
|
}
|
|
280
294
|
|
|
281
|
-
const sorted = [...plugins].sort(
|
|
282
|
-
(a, b) => (b.priority ?? 0) - (a.priority ?? 0),
|
|
283
|
-
);
|
|
284
295
|
let nextAst = ast;
|
|
285
|
-
for (const plugin of
|
|
296
|
+
for (const plugin of sortedPlugins) {
|
|
286
297
|
if (!plugin.afterParse) continue;
|
|
287
298
|
|
|
288
299
|
try {
|
|
@@ -321,6 +332,12 @@ export type MarkdownProps = {
|
|
|
321
332
|
* When provided, native parse is skipped and this tree is rendered instead.
|
|
322
333
|
*/
|
|
323
334
|
sourceAst?: MarkdownNode;
|
|
335
|
+
/**
|
|
336
|
+
* Enables internal parse AST cache keyed by parser options and markdown.
|
|
337
|
+
* Disable to force native parse on each parse cycle.
|
|
338
|
+
* @default true
|
|
339
|
+
*/
|
|
340
|
+
parseCache?: boolean;
|
|
324
341
|
/**
|
|
325
342
|
* Optional transform applied after parsing and before rendering.
|
|
326
343
|
* The transformed AST is also returned in `onParseComplete`.
|
|
@@ -425,6 +442,7 @@ export const Markdown: FC<MarkdownProps> = ({
|
|
|
425
442
|
options,
|
|
426
443
|
plugins,
|
|
427
444
|
sourceAst,
|
|
445
|
+
parseCache = true,
|
|
428
446
|
astTransform,
|
|
429
447
|
renderers = EMPTY_RENDERERS,
|
|
430
448
|
theme: userTheme,
|
|
@@ -451,11 +469,10 @@ export const Markdown: FC<MarkdownProps> = ({
|
|
|
451
469
|
|
|
452
470
|
const parseResult = useMemo(() => {
|
|
453
471
|
try {
|
|
454
|
-
const
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
onErrorRef.current
|
|
458
|
-
);
|
|
472
|
+
const sortedPlugins = sortPluginsByPriority(plugins);
|
|
473
|
+
const markdownToParse = sourceAst
|
|
474
|
+
? children
|
|
475
|
+
: applyBeforeParsePlugins(children, sortedPlugins, onErrorRef.current);
|
|
459
476
|
const parserOptions = normalizeParserOptions({
|
|
460
477
|
gfm: parserOptionGfm,
|
|
461
478
|
math: parserOptionMath,
|
|
@@ -463,10 +480,12 @@ export const Markdown: FC<MarkdownProps> = ({
|
|
|
463
480
|
});
|
|
464
481
|
let parsedAst = sourceAst
|
|
465
482
|
? cloneMarkdownNode(sourceAst)
|
|
466
|
-
:
|
|
483
|
+
: parseCache
|
|
484
|
+
? getCachedParsedAst(markdownToParse, parserOptions)
|
|
485
|
+
: parseWithNativeParser(markdownToParse, parserOptions);
|
|
467
486
|
parsedAst = applyAfterParsePlugins(
|
|
468
487
|
parsedAst,
|
|
469
|
-
|
|
488
|
+
sortedPlugins,
|
|
470
489
|
onErrorRef.current,
|
|
471
490
|
);
|
|
472
491
|
|
|
@@ -501,6 +520,7 @@ export const Markdown: FC<MarkdownProps> = ({
|
|
|
501
520
|
parserOptionMath,
|
|
502
521
|
parserOptionHtml,
|
|
503
522
|
sourceAst,
|
|
523
|
+
parseCache,
|
|
504
524
|
astTransform,
|
|
505
525
|
plugins,
|
|
506
526
|
]);
|
|
@@ -767,28 +787,24 @@ const NodeRendererComponent: FC<NodeRendererProps> = ({
|
|
|
767
787
|
}
|
|
768
788
|
}
|
|
769
789
|
|
|
770
|
-
const nodeStyleOverride = nodeStyles?.[node.type] as
|
|
771
|
-
| (ViewStyle & TextStyle)
|
|
772
|
-
| undefined;
|
|
773
|
-
|
|
774
790
|
switch (node.type) {
|
|
775
791
|
case "document":
|
|
776
792
|
return (
|
|
777
|
-
<View style={[baseStyles.document,
|
|
793
|
+
<View style={[baseStyles.document, nodeStyles?.document]}>
|
|
778
794
|
{renderChildren(node.children, false, false)}
|
|
779
795
|
</View>
|
|
780
796
|
);
|
|
781
797
|
|
|
782
798
|
case "heading":
|
|
783
799
|
return (
|
|
784
|
-
<Heading level={node.level ?? 1} style={
|
|
800
|
+
<Heading level={node.level ?? 1} style={nodeStyles?.heading}>
|
|
785
801
|
{renderChildren(node.children, inListItem, true)}
|
|
786
802
|
</Heading>
|
|
787
803
|
);
|
|
788
804
|
|
|
789
805
|
case "paragraph":
|
|
790
806
|
return (
|
|
791
|
-
<Paragraph inListItem={inListItem} style={
|
|
807
|
+
<Paragraph inListItem={inListItem} style={nodeStyles?.paragraph}>
|
|
792
808
|
{renderChildren(node.children, inListItem, false)}
|
|
793
809
|
</Paragraph>
|
|
794
810
|
);
|
|
@@ -798,33 +814,33 @@ const NodeRendererComponent: FC<NodeRendererProps> = ({
|
|
|
798
814
|
return <Text>{node.content}</Text>;
|
|
799
815
|
}
|
|
800
816
|
return (
|
|
801
|
-
<Text style={[baseStyles.text,
|
|
817
|
+
<Text style={[baseStyles.text, nodeStyles?.text]}>{node.content}</Text>
|
|
802
818
|
);
|
|
803
819
|
|
|
804
820
|
case "bold":
|
|
805
821
|
return (
|
|
806
|
-
<Text style={[baseStyles.bold,
|
|
822
|
+
<Text style={[baseStyles.bold, nodeStyles?.bold]}>
|
|
807
823
|
{renderChildren(node.children, inListItem, true)}
|
|
808
824
|
</Text>
|
|
809
825
|
);
|
|
810
826
|
|
|
811
827
|
case "italic":
|
|
812
828
|
return (
|
|
813
|
-
<Text style={[baseStyles.italic,
|
|
829
|
+
<Text style={[baseStyles.italic, nodeStyles?.italic]}>
|
|
814
830
|
{renderChildren(node.children, inListItem, true)}
|
|
815
831
|
</Text>
|
|
816
832
|
);
|
|
817
833
|
|
|
818
834
|
case "strikethrough":
|
|
819
835
|
return (
|
|
820
|
-
<Text style={[baseStyles.strikethrough,
|
|
836
|
+
<Text style={[baseStyles.strikethrough, nodeStyles?.strikethrough]}>
|
|
821
837
|
{renderChildren(node.children, inListItem, true)}
|
|
822
838
|
</Text>
|
|
823
839
|
);
|
|
824
840
|
|
|
825
841
|
case "link":
|
|
826
842
|
return (
|
|
827
|
-
<Link href={node.href ?? ""} style={
|
|
843
|
+
<Link href={node.href ?? ""} style={nodeStyles?.link}>
|
|
828
844
|
{renderChildren(node.children, inListItem, true)}
|
|
829
845
|
</Link>
|
|
830
846
|
);
|
|
@@ -836,31 +852,33 @@ const NodeRendererComponent: FC<NodeRendererProps> = ({
|
|
|
836
852
|
title={node.title}
|
|
837
853
|
alt={node.alt}
|
|
838
854
|
Renderer={NodeRenderer}
|
|
839
|
-
style={
|
|
855
|
+
style={nodeStyles?.image}
|
|
840
856
|
/>
|
|
841
857
|
);
|
|
842
858
|
|
|
843
859
|
case "code_inline":
|
|
844
|
-
return
|
|
860
|
+
return (
|
|
861
|
+
<InlineCode style={nodeStyles?.code_inline}>{node.content}</InlineCode>
|
|
862
|
+
);
|
|
845
863
|
|
|
846
864
|
case "code_block":
|
|
847
865
|
return (
|
|
848
866
|
<CodeBlock
|
|
849
867
|
language={node.language}
|
|
850
868
|
content={getTextContent(node)}
|
|
851
|
-
style={
|
|
869
|
+
style={nodeStyles?.code_block}
|
|
852
870
|
/>
|
|
853
871
|
);
|
|
854
872
|
|
|
855
873
|
case "blockquote":
|
|
856
874
|
return (
|
|
857
|
-
<Blockquote style={
|
|
875
|
+
<Blockquote style={nodeStyles?.blockquote}>
|
|
858
876
|
{renderChildren(node.children, inListItem, false)}
|
|
859
877
|
</Blockquote>
|
|
860
878
|
);
|
|
861
879
|
|
|
862
880
|
case "horizontal_rule":
|
|
863
|
-
return <HorizontalRule style={
|
|
881
|
+
return <HorizontalRule style={nodeStyles?.horizontal_rule} />;
|
|
864
882
|
|
|
865
883
|
case "line_break":
|
|
866
884
|
return <Text>{"\n"}</Text>;
|
|
@@ -872,12 +890,17 @@ const NodeRendererComponent: FC<NodeRendererProps> = ({
|
|
|
872
890
|
let mathContent = getTextContent(node);
|
|
873
891
|
if (!mathContent) return null;
|
|
874
892
|
mathContent = mathContent.replace(/^\$+|\$+$/g, "").trim();
|
|
875
|
-
return
|
|
893
|
+
return (
|
|
894
|
+
<MathInline content={mathContent} style={nodeStyles?.math_inline} />
|
|
895
|
+
);
|
|
876
896
|
}
|
|
877
897
|
|
|
878
898
|
case "math_block":
|
|
879
899
|
return (
|
|
880
|
-
<MathBlock
|
|
900
|
+
<MathBlock
|
|
901
|
+
content={getTextContent(node)}
|
|
902
|
+
style={nodeStyles?.math_block}
|
|
903
|
+
/>
|
|
881
904
|
);
|
|
882
905
|
|
|
883
906
|
case "list":
|
|
@@ -886,7 +909,7 @@ const NodeRendererComponent: FC<NodeRendererProps> = ({
|
|
|
886
909
|
ordered={node.ordered ?? false}
|
|
887
910
|
start={node.start}
|
|
888
911
|
depth={depth}
|
|
889
|
-
style={
|
|
912
|
+
style={nodeStyles?.list}
|
|
890
913
|
>
|
|
891
914
|
{node.children?.map((child, index) => {
|
|
892
915
|
if (child.type === "task_list_item") {
|
|
@@ -924,7 +947,10 @@ const NodeRendererComponent: FC<NodeRendererProps> = ({
|
|
|
924
947
|
|
|
925
948
|
case "task_list_item":
|
|
926
949
|
return (
|
|
927
|
-
<TaskListItem
|
|
950
|
+
<TaskListItem
|
|
951
|
+
checked={node.checked ?? false}
|
|
952
|
+
style={nodeStyles?.task_list_item}
|
|
953
|
+
>
|
|
928
954
|
{renderChildren(node.children, true, false)}
|
|
929
955
|
</TaskListItem>
|
|
930
956
|
);
|
|
@@ -934,7 +960,7 @@ const NodeRendererComponent: FC<NodeRendererProps> = ({
|
|
|
934
960
|
<TableRenderer
|
|
935
961
|
node={node}
|
|
936
962
|
Renderer={NodeRenderer}
|
|
937
|
-
style={
|
|
963
|
+
style={nodeStyles?.table}
|
|
938
964
|
/>
|
|
939
965
|
);
|
|
940
966
|
|
package/src/theme.ts
CHANGED
|
@@ -155,8 +155,6 @@ type TextNodeType =
|
|
|
155
155
|
| "link"
|
|
156
156
|
| "code_inline"
|
|
157
157
|
| "heading"
|
|
158
|
-
| "paragraph"
|
|
159
|
-
| "math_inline"
|
|
160
158
|
| "html_inline";
|
|
161
159
|
type ViewNodeType =
|
|
162
160
|
| "document"
|
|
@@ -172,6 +170,8 @@ type ViewNodeType =
|
|
|
172
170
|
| "table_body"
|
|
173
171
|
| "table_row"
|
|
174
172
|
| "table_cell"
|
|
173
|
+
| "paragraph"
|
|
174
|
+
| "math_inline"
|
|
175
175
|
| "math_block"
|
|
176
176
|
| "html_block"
|
|
177
177
|
| "line_break"
|