react-native-nitro-markdown 0.5.4 → 0.5.6
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 +21 -4
- package/cpp/CMakeLists.txt +2 -1
- package/cpp/core/MD4CParser.cpp +49 -0
- package/cpp/core/MD4CParser.hpp +17 -0
- package/lib/commonjs/headless.js +38 -15
- package/lib/commonjs/headless.js.map +1 -1
- package/lib/commonjs/markdown.js +43 -35
- package/lib/commonjs/markdown.js.map +1 -1
- package/lib/module/headless.js +38 -15
- package/lib/module/headless.js.map +1 -1
- package/lib/module/markdown.js +43 -35
- package/lib/module/markdown.js.map +1 -1
- package/lib/typescript/commonjs/headless.d.ts.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/headless.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/headless.ts +44 -9
- package/src/markdown.tsx +74 -48
- package/src/theme.ts +2 -2
package/README.md
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
|
+
# react-native-nitro-markdown
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/react-native-nitro-markdown)
|
|
4
|
+
[](./LICENSE)
|
|
5
|
+
[](https://reactnative.dev/)
|
|
6
|
+
[](https://github.com/mrousavy/nitro)
|
|
7
|
+
|
|
1
8
|
<p align="center">
|
|
2
|
-
<img src="
|
|
9
|
+
<img src="https://raw.githubusercontent.com/JoaoPauloCMarra/react-native-nitro-markdown/main/readme/demo.gif" alt="react-native-nitro-markdown demo" width="400" />
|
|
3
10
|
</p>
|
|
4
11
|
|
|
5
|
-
# react-native-nitro-markdown
|
|
6
|
-
|
|
7
12
|
Native Markdown parsing and rendering for React Native, powered by [md4c](https://github.com/mity/md4c) (C++) through [Nitro Modules](https://github.com/mrousavy/nitro) (JSI).
|
|
8
13
|
|
|
9
14
|
## Features
|
|
@@ -24,7 +29,7 @@ Native Markdown parsing and rendering for React Native, powered by [md4c](https:
|
|
|
24
29
|
| Dependency | Version |
|
|
25
30
|
|---|---|
|
|
26
31
|
| React Native | `>=0.75.0` |
|
|
27
|
-
| react-native-nitro-modules | `>=0.35.
|
|
32
|
+
| react-native-nitro-modules | `>=0.35.5` |
|
|
28
33
|
| react-native-mathjax-svg *(optional)* | `>=0.9.0` |
|
|
29
34
|
| react-native-svg *(optional, for math)* | `>=13.0.0` |
|
|
30
35
|
|
|
@@ -103,6 +108,7 @@ import { Markdown } from "react-native-nitro-markdown";
|
|
|
103
108
|
| `options` | `ParserOptions` | -- | Parser flags (`gfm`, `math`, `html`) |
|
|
104
109
|
| `plugins` | `MarkdownPlugin[]` | -- | Plugin hooks (`beforeParse`, `afterParse`) |
|
|
105
110
|
| `sourceAst` | `MarkdownNode` | -- | Pre-parsed AST; skips native parse when provided |
|
|
111
|
+
| `parseCache` | `boolean` | `true` | Enable internal parse result caching for repeated inputs |
|
|
106
112
|
| `astTransform` | `AstTransform` | -- | Post-parse AST rewrite before render |
|
|
107
113
|
| `renderers` | `CustomRenderers` | `{}` | Per-node custom renderer overrides |
|
|
108
114
|
| `theme` | `PartialMarkdownTheme` | `defaultMarkdownTheme` | Theme token overrides |
|
|
@@ -121,6 +127,10 @@ import { Markdown } from "react-native-nitro-markdown";
|
|
|
121
127
|
|
|
122
128
|
**Pipeline order:** `beforeParse` plugins (by priority desc) -> parse/sourceAst -> `afterParse` plugins (by priority desc) -> `astTransform` -> render.
|
|
123
129
|
|
|
130
|
+
When `sourceAst` is provided, `beforeParse` plugins are skipped because no source string is parsed. `afterParse` plugins still run on the provided AST.
|
|
131
|
+
|
|
132
|
+
`parseCache` defaults to `true` and caches parse results for repeated markdown inputs. Set `parseCache={false}` to force a fresh native parse on each input change. This flag has no effect when `sourceAst` is provided.
|
|
133
|
+
|
|
124
134
|
### `<MarkdownStream>`
|
|
125
135
|
|
|
126
136
|
Renders markdown from a streaming session. Extends `MarkdownProps` (minus `children`).
|
|
@@ -406,6 +416,8 @@ const ast = parseMarkdownWithOptions(content, {
|
|
|
406
416
|
<Markdown sourceAst={ast}>{"ignored when sourceAst is provided"}</Markdown>;
|
|
407
417
|
```
|
|
408
418
|
|
|
419
|
+
With `sourceAst`, `beforeParse` plugins are skipped, while `afterParse` and `astTransform` still apply.
|
|
420
|
+
|
|
409
421
|
### Virtualization (large documents)
|
|
410
422
|
|
|
411
423
|
```tsx
|
|
@@ -502,6 +514,11 @@ The `apps/example` directory contains a full demo app with these screens:
|
|
|
502
514
|
| Custom | `app/render-custom.tsx` | HTML AST rendering, custom renderers, AST transform |
|
|
503
515
|
| Stream | `app/render-stream.tsx` | Live streaming with token append |
|
|
504
516
|
|
|
517
|
+
## Release Checks
|
|
518
|
+
|
|
519
|
+
- `bun run harness` runs lint, typecheck, JS coverage, benchmark checks, and C++ coverage.
|
|
520
|
+
- `bun run publish-package -- --dry-run --yes` validates release docs, runs publish verification, builds the package, and performs an npm dry run.
|
|
521
|
+
|
|
505
522
|
## Contributing
|
|
506
523
|
|
|
507
524
|
See [CONTRIBUTING.md](./CONTRIBUTING.md).
|
package/cpp/CMakeLists.txt
CHANGED
|
@@ -30,6 +30,7 @@ target_include_directories(MD4CCore PUBLIC
|
|
|
30
30
|
# Preprocessor definitions
|
|
31
31
|
target_compile_definitions(MD4CCore PRIVATE
|
|
32
32
|
MD4C_USE_UTF8=1
|
|
33
|
+
NITRO_MARKDOWN_TESTING=1
|
|
33
34
|
)
|
|
34
35
|
|
|
35
36
|
# Create the test executable
|
|
@@ -43,4 +44,4 @@ target_link_libraries(MD4CParserTest PRIVATE MD4CCore)
|
|
|
43
44
|
# Include directories for the test
|
|
44
45
|
target_include_directories(MD4CParserTest PRIVATE
|
|
45
46
|
"${CPP_ROOT}/core"
|
|
46
|
-
)
|
|
47
|
+
)
|
package/cpp/core/MD4CParser.cpp
CHANGED
|
@@ -489,6 +489,14 @@ MD4CParser::MD4CParser() : impl_(std::make_unique<Impl>()) {}
|
|
|
489
489
|
MD4CParser::~MD4CParser() = default;
|
|
490
490
|
|
|
491
491
|
std::shared_ptr<MarkdownNode> MD4CParser::parse(const std::string& markdown, const ParserOptions& options) {
|
|
492
|
+
return parseWithFlags(markdown, options, 0);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
std::shared_ptr<MarkdownNode> MD4CParser::parseWithFlags(
|
|
496
|
+
const std::string& markdown,
|
|
497
|
+
const ParserOptions& options,
|
|
498
|
+
unsigned int extraFlags
|
|
499
|
+
) {
|
|
492
500
|
impl_->reset();
|
|
493
501
|
impl_->inputText = markdown.c_str();
|
|
494
502
|
size_t inputSize = clampInputSize(markdown.size());
|
|
@@ -506,6 +514,7 @@ std::shared_ptr<MarkdownNode> MD4CParser::parse(const std::string& markdown, con
|
|
|
506
514
|
if (options.math) {
|
|
507
515
|
flags |= MD_FLAG_LATEXMATHSPANS;
|
|
508
516
|
}
|
|
517
|
+
flags |= extraFlags;
|
|
509
518
|
|
|
510
519
|
MD_PARSER parser = {
|
|
511
520
|
0,
|
|
@@ -533,4 +542,44 @@ std::shared_ptr<MarkdownNode> MD4CParser::parse(const std::string& markdown, con
|
|
|
533
542
|
return impl_->root;
|
|
534
543
|
}
|
|
535
544
|
|
|
545
|
+
#ifdef NITRO_MARKDOWN_TESTING
|
|
546
|
+
std::shared_ptr<MarkdownNode> MD4CParser::parseWithExtraFlagsForTest(
|
|
547
|
+
const std::string& markdown,
|
|
548
|
+
const ParserOptions& options,
|
|
549
|
+
unsigned int extraFlags
|
|
550
|
+
) {
|
|
551
|
+
return parseWithFlags(markdown, options, extraFlags);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
int MD4CParser::enterBlockNullUserdataForTest() {
|
|
555
|
+
return Impl::enterBlock(MD_BLOCK_DOC, nullptr, 0, nullptr);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
int MD4CParser::leaveBlockNullUserdataForTest() {
|
|
559
|
+
return Impl::leaveBlock(MD_BLOCK_DOC, nullptr, 0, nullptr);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
int MD4CParser::enterSpanNullUserdataForTest() {
|
|
563
|
+
return Impl::enterSpan(MD_SPAN_EM, nullptr, 0, nullptr);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
int MD4CParser::leaveSpanNullUserdataForTest() {
|
|
567
|
+
return Impl::leaveSpan(MD_SPAN_EM, nullptr, 0, nullptr);
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
int MD4CParser::textNullUserdataForTest() {
|
|
571
|
+
return Impl::text(MD_TEXT_NORMAL, "x", 1, nullptr);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
int MD4CParser::offsetBeforeBaseForTest() {
|
|
575
|
+
char buffer[2] = {'a', 'b'};
|
|
576
|
+
return safeOffset(buffer, buffer + 1, 1);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
int MD4CParser::offsetPastBaseForTest() {
|
|
580
|
+
char buffer[2] = {'a', 'b'};
|
|
581
|
+
return safeOffset(buffer + 1, buffer, 0);
|
|
582
|
+
}
|
|
583
|
+
#endif
|
|
584
|
+
|
|
536
585
|
} // namespace NitroMarkdown
|
package/cpp/core/MD4CParser.hpp
CHANGED
|
@@ -19,6 +19,18 @@ public:
|
|
|
19
19
|
std::shared_ptr<MarkdownNode> parse(const std::string& markdown, const ParserOptions& options);
|
|
20
20
|
|
|
21
21
|
#ifdef NITRO_MARKDOWN_TESTING
|
|
22
|
+
std::shared_ptr<MarkdownNode> parseWithExtraFlagsForTest(
|
|
23
|
+
const std::string& markdown,
|
|
24
|
+
const ParserOptions& options,
|
|
25
|
+
unsigned int extraFlags
|
|
26
|
+
);
|
|
27
|
+
static int enterBlockNullUserdataForTest();
|
|
28
|
+
static int leaveBlockNullUserdataForTest();
|
|
29
|
+
static int enterSpanNullUserdataForTest();
|
|
30
|
+
static int leaveSpanNullUserdataForTest();
|
|
31
|
+
static int textNullUserdataForTest();
|
|
32
|
+
static int offsetBeforeBaseForTest();
|
|
33
|
+
static int offsetPastBaseForTest();
|
|
22
34
|
static size_t clampInputSizeForTest(size_t inputSize) {
|
|
23
35
|
size_t maxSize = static_cast<size_t>(std::numeric_limits<MD_SIZE>::max());
|
|
24
36
|
return inputSize > maxSize ? maxSize : inputSize;
|
|
@@ -27,6 +39,11 @@ public:
|
|
|
27
39
|
|
|
28
40
|
private:
|
|
29
41
|
class Impl;
|
|
42
|
+
std::shared_ptr<MarkdownNode> parseWithFlags(
|
|
43
|
+
const std::string& markdown,
|
|
44
|
+
const ParserOptions& options,
|
|
45
|
+
unsigned int extraFlags
|
|
46
|
+
);
|
|
30
47
|
std::unique_ptr<Impl> impl_;
|
|
31
48
|
};
|
|
32
49
|
|
package/lib/commonjs/headless.js
CHANGED
|
@@ -29,6 +29,16 @@ var _reactNativeNitroModules = require("react-native-nitro-modules");
|
|
|
29
29
|
* Each node has a type and optional properties depending on the node type.
|
|
30
30
|
*/
|
|
31
31
|
|
|
32
|
+
const createEmptyDocument = () => ({
|
|
33
|
+
type: "document",
|
|
34
|
+
children: []
|
|
35
|
+
});
|
|
36
|
+
function reportNativeParserFailure(methodName, error) {
|
|
37
|
+
if (__DEV__) {
|
|
38
|
+
// eslint-disable-next-line no-console
|
|
39
|
+
console.error(`[NitroMarkdown] ${methodName}: native parser failed.`, error);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
32
42
|
let MarkdownParserModule = exports.MarkdownParserModule = null;
|
|
33
43
|
try {
|
|
34
44
|
exports.MarkdownParserModule = MarkdownParserModule = _reactNativeNitroModules.NitroModules.createHybridObject("MarkdownParser");
|
|
@@ -56,17 +66,19 @@ function parseMarkdown(text, options) {
|
|
|
56
66
|
return parseMarkdownWithOptions(text, options);
|
|
57
67
|
}
|
|
58
68
|
if (MarkdownParserModule != null && typeof MarkdownParserModule.parse === "function") {
|
|
59
|
-
|
|
60
|
-
|
|
69
|
+
try {
|
|
70
|
+
const jsonStr = MarkdownParserModule.parse(text);
|
|
71
|
+
return JSON.parse(jsonStr);
|
|
72
|
+
} catch (error) {
|
|
73
|
+
reportNativeParserFailure("parseMarkdown", error);
|
|
74
|
+
return createEmptyDocument();
|
|
75
|
+
}
|
|
61
76
|
}
|
|
62
77
|
if (__DEV__) {
|
|
63
78
|
// eslint-disable-next-line no-console
|
|
64
79
|
console.error("[NitroMarkdown] parseMarkdown: native parser unavailable — check installation.");
|
|
65
80
|
}
|
|
66
|
-
return
|
|
67
|
-
type: "document",
|
|
68
|
-
children: []
|
|
69
|
-
};
|
|
81
|
+
return createEmptyDocument();
|
|
70
82
|
}
|
|
71
83
|
|
|
72
84
|
/**
|
|
@@ -77,17 +89,19 @@ function parseMarkdown(text, options) {
|
|
|
77
89
|
*/
|
|
78
90
|
function parseMarkdownWithOptions(text, options) {
|
|
79
91
|
if (MarkdownParserModule != null && typeof MarkdownParserModule.parseWithOptions === "function") {
|
|
80
|
-
|
|
81
|
-
|
|
92
|
+
try {
|
|
93
|
+
const jsonStr = MarkdownParserModule.parseWithOptions(text, options);
|
|
94
|
+
return JSON.parse(jsonStr);
|
|
95
|
+
} catch (error) {
|
|
96
|
+
reportNativeParserFailure("parseMarkdownWithOptions", error);
|
|
97
|
+
return createEmptyDocument();
|
|
98
|
+
}
|
|
82
99
|
}
|
|
83
100
|
if (__DEV__) {
|
|
84
101
|
// eslint-disable-next-line no-console
|
|
85
102
|
console.error("[NitroMarkdown] parseMarkdownWithOptions: native parser unavailable — check installation.");
|
|
86
103
|
}
|
|
87
|
-
return
|
|
88
|
-
type: "document",
|
|
89
|
-
children: []
|
|
90
|
-
};
|
|
104
|
+
return createEmptyDocument();
|
|
91
105
|
}
|
|
92
106
|
|
|
93
107
|
/**
|
|
@@ -96,7 +110,11 @@ function parseMarkdownWithOptions(text, options) {
|
|
|
96
110
|
*/
|
|
97
111
|
function extractPlainText(text) {
|
|
98
112
|
if (MarkdownParserModule != null && typeof MarkdownParserModule.extractPlainText === "function") {
|
|
99
|
-
|
|
113
|
+
try {
|
|
114
|
+
return MarkdownParserModule.extractPlainText(text);
|
|
115
|
+
} catch (error) {
|
|
116
|
+
reportNativeParserFailure("extractPlainText", error);
|
|
117
|
+
}
|
|
100
118
|
}
|
|
101
119
|
return getFlattenedText(parseMarkdown(text));
|
|
102
120
|
}
|
|
@@ -106,7 +124,11 @@ function extractPlainText(text) {
|
|
|
106
124
|
*/
|
|
107
125
|
function extractPlainTextWithOptions(text, options) {
|
|
108
126
|
if (MarkdownParserModule != null && typeof MarkdownParserModule.extractPlainTextWithOptions === "function") {
|
|
109
|
-
|
|
127
|
+
try {
|
|
128
|
+
return MarkdownParserModule.extractPlainTextWithOptions(text, options);
|
|
129
|
+
} catch (error) {
|
|
130
|
+
reportNativeParserFailure("extractPlainTextWithOptions", error);
|
|
131
|
+
}
|
|
110
132
|
}
|
|
111
133
|
return getFlattenedText(parseMarkdownWithOptions(text, options));
|
|
112
134
|
}
|
|
@@ -130,7 +152,8 @@ const getFlattenedText = node => {
|
|
|
130
152
|
return node.content ?? "";
|
|
131
153
|
}
|
|
132
154
|
if (node.type === "code_block" || node.type === "math_block" || node.type === "html_block") {
|
|
133
|
-
|
|
155
|
+
const blockContent = node.content ?? node.children?.map(getFlattenedText).join("") ?? "";
|
|
156
|
+
return blockContent.trim() + "\n\n";
|
|
134
157
|
}
|
|
135
158
|
if (node.type === "line_break") return "\n";
|
|
136
159
|
if (node.type === "soft_break") return " ";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_reactNativeNitroModules","require","
|
|
1
|
+
{"version":3,"names":["_reactNativeNitroModules","require","createEmptyDocument","type","children","reportNativeParserFailure","methodName","error","__DEV__","console","MarkdownParserModule","exports","NitroModules","createHybridObject","e","parseMarkdown","text","options","parseMarkdownWithOptions","parse","jsonStr","JSON","parseWithOptions","extractPlainText","getFlattenedText","extractPlainTextWithOptions","getTextContent","node","content","map","join","blockContent","trim","alt","title","childrenText","stripSourceOffsets","beg","_beg","end","_end","rest"],"sourceRoot":"../../src","sources":["headless.ts"],"mappings":"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","ignoreList":[]}
|
package/lib/commonjs/markdown.js
CHANGED
|
@@ -103,14 +103,17 @@ const getCachedParsedAst = (text, options) => {
|
|
|
103
103
|
return parseWithNativeParser(text, options);
|
|
104
104
|
}
|
|
105
105
|
const cacheKey = `${getParserOptionsKey(options)}|${text.length}|${hashString(text)}`;
|
|
106
|
-
const
|
|
107
|
-
if (
|
|
106
|
+
const cachedEntry = parseAstCache.get(cacheKey);
|
|
107
|
+
if (cachedEntry?.text === text) {
|
|
108
108
|
parseAstCache.delete(cacheKey);
|
|
109
|
-
parseAstCache.set(cacheKey,
|
|
110
|
-
return cloneMarkdownNode(
|
|
109
|
+
parseAstCache.set(cacheKey, cachedEntry);
|
|
110
|
+
return cloneMarkdownNode(cachedEntry.ast);
|
|
111
111
|
}
|
|
112
112
|
const parsedNode = parseWithNativeParser(text, options);
|
|
113
|
-
parseAstCache.set(cacheKey,
|
|
113
|
+
parseAstCache.set(cacheKey, {
|
|
114
|
+
text,
|
|
115
|
+
ast: parsedNode
|
|
116
|
+
});
|
|
114
117
|
if (parseAstCache.size > MAX_PARSE_CACHE_ENTRIES) {
|
|
115
118
|
const oldestCacheKey = parseAstCache.keys().next().value;
|
|
116
119
|
if (typeof oldestCacheKey === "string") {
|
|
@@ -119,13 +122,18 @@ const getCachedParsedAst = (text, options) => {
|
|
|
119
122
|
}
|
|
120
123
|
return cloneMarkdownNode(parsedNode);
|
|
121
124
|
};
|
|
122
|
-
const
|
|
125
|
+
const sortPluginsByPriority = plugins => {
|
|
123
126
|
if (!plugins || plugins.length === 0) {
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
return [...plugins].sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
|
|
130
|
+
};
|
|
131
|
+
const applyBeforeParsePlugins = (markdown, sortedPlugins, onError) => {
|
|
132
|
+
if (!sortedPlugins || sortedPlugins.length === 0) {
|
|
124
133
|
return markdown;
|
|
125
134
|
}
|
|
126
|
-
const sorted = [...plugins].sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
|
|
127
135
|
let nextMarkdown = markdown;
|
|
128
|
-
for (const plugin of
|
|
136
|
+
for (const plugin of sortedPlugins) {
|
|
129
137
|
if (!plugin.beforeParse) continue;
|
|
130
138
|
try {
|
|
131
139
|
const transformed = plugin.beforeParse(nextMarkdown);
|
|
@@ -140,13 +148,12 @@ const applyBeforeParsePlugins = (markdown, plugins, onError) => {
|
|
|
140
148
|
}
|
|
141
149
|
return nextMarkdown;
|
|
142
150
|
};
|
|
143
|
-
const applyAfterParsePlugins = (ast,
|
|
144
|
-
if (!
|
|
151
|
+
const applyAfterParsePlugins = (ast, sortedPlugins, onError) => {
|
|
152
|
+
if (!sortedPlugins || sortedPlugins.length === 0) {
|
|
145
153
|
return ast;
|
|
146
154
|
}
|
|
147
|
-
const sorted = [...plugins].sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
|
|
148
155
|
let nextAst = ast;
|
|
149
|
-
for (const plugin of
|
|
156
|
+
for (const plugin of sortedPlugins) {
|
|
150
157
|
if (!plugin.afterParse) continue;
|
|
151
158
|
try {
|
|
152
159
|
const transformed = plugin.afterParse(nextAst);
|
|
@@ -166,6 +173,7 @@ const Markdown = ({
|
|
|
166
173
|
options,
|
|
167
174
|
plugins,
|
|
168
175
|
sourceAst,
|
|
176
|
+
parseCache = true,
|
|
169
177
|
astTransform,
|
|
170
178
|
renderers = EMPTY_RENDERERS,
|
|
171
179
|
theme: userTheme,
|
|
@@ -191,14 +199,15 @@ const Markdown = ({
|
|
|
191
199
|
onErrorRef.current = onError;
|
|
192
200
|
const parseResult = (0, _react.useMemo)(() => {
|
|
193
201
|
try {
|
|
194
|
-
const
|
|
202
|
+
const sortedPlugins = sortPluginsByPriority(plugins);
|
|
203
|
+
const markdownToParse = sourceAst ? children : applyBeforeParsePlugins(children, sortedPlugins, onErrorRef.current);
|
|
195
204
|
const parserOptions = normalizeParserOptions({
|
|
196
205
|
gfm: parserOptionGfm,
|
|
197
206
|
math: parserOptionMath,
|
|
198
207
|
html: parserOptionHtml
|
|
199
208
|
});
|
|
200
|
-
let parsedAst = sourceAst ? cloneMarkdownNode(sourceAst) : getCachedParsedAst(markdownToParse, parserOptions);
|
|
201
|
-
parsedAst = applyAfterParsePlugins(parsedAst,
|
|
209
|
+
let parsedAst = sourceAst ? cloneMarkdownNode(sourceAst) : parseCache ? getCachedParsedAst(markdownToParse, parserOptions) : parseWithNativeParser(markdownToParse, parserOptions);
|
|
210
|
+
parsedAst = applyAfterParsePlugins(parsedAst, sortedPlugins, onErrorRef.current);
|
|
202
211
|
let ast = parsedAst;
|
|
203
212
|
if (astTransform) {
|
|
204
213
|
try {
|
|
@@ -220,7 +229,7 @@ const Markdown = ({
|
|
|
220
229
|
ast: null
|
|
221
230
|
};
|
|
222
231
|
}
|
|
223
|
-
}, [children, parserOptionGfm, parserOptionMath, parserOptionHtml, sourceAst, astTransform, plugins]);
|
|
232
|
+
}, [children, parserOptionGfm, parserOptionMath, parserOptionHtml, sourceAst, parseCache, astTransform, plugins]);
|
|
224
233
|
/* eslint-enable react-hooks/refs */
|
|
225
234
|
|
|
226
235
|
(0, _react.useEffect)(() => {
|
|
@@ -409,23 +418,22 @@ const NodeRendererComponent = ({
|
|
|
409
418
|
return result;
|
|
410
419
|
}
|
|
411
420
|
}
|
|
412
|
-
const nodeStyleOverride = nodeStyles?.[node.type];
|
|
413
421
|
switch (node.type) {
|
|
414
422
|
case "document":
|
|
415
423
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
416
|
-
style: [baseStyles.document,
|
|
424
|
+
style: [baseStyles.document, nodeStyles?.document],
|
|
417
425
|
children: renderChildren(node.children, false, false)
|
|
418
426
|
});
|
|
419
427
|
case "heading":
|
|
420
428
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_heading.Heading, {
|
|
421
429
|
level: node.level ?? 1,
|
|
422
|
-
style:
|
|
430
|
+
style: nodeStyles?.heading,
|
|
423
431
|
children: renderChildren(node.children, inListItem, true)
|
|
424
432
|
});
|
|
425
433
|
case "paragraph":
|
|
426
434
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_paragraph.Paragraph, {
|
|
427
435
|
inListItem: inListItem,
|
|
428
|
-
style:
|
|
436
|
+
style: nodeStyles?.paragraph,
|
|
429
437
|
children: renderChildren(node.children, inListItem, false)
|
|
430
438
|
});
|
|
431
439
|
case "text":
|
|
@@ -435,28 +443,28 @@ const NodeRendererComponent = ({
|
|
|
435
443
|
});
|
|
436
444
|
}
|
|
437
445
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
438
|
-
style: [baseStyles.text,
|
|
446
|
+
style: [baseStyles.text, nodeStyles?.text],
|
|
439
447
|
children: node.content
|
|
440
448
|
});
|
|
441
449
|
case "bold":
|
|
442
450
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
443
|
-
style: [baseStyles.bold,
|
|
451
|
+
style: [baseStyles.bold, nodeStyles?.bold],
|
|
444
452
|
children: renderChildren(node.children, inListItem, true)
|
|
445
453
|
});
|
|
446
454
|
case "italic":
|
|
447
455
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
448
|
-
style: [baseStyles.italic,
|
|
456
|
+
style: [baseStyles.italic, nodeStyles?.italic],
|
|
449
457
|
children: renderChildren(node.children, inListItem, true)
|
|
450
458
|
});
|
|
451
459
|
case "strikethrough":
|
|
452
460
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
453
|
-
style: [baseStyles.strikethrough,
|
|
461
|
+
style: [baseStyles.strikethrough, nodeStyles?.strikethrough],
|
|
454
462
|
children: renderChildren(node.children, inListItem, true)
|
|
455
463
|
});
|
|
456
464
|
case "link":
|
|
457
465
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_link.Link, {
|
|
458
466
|
href: node.href ?? "",
|
|
459
|
-
style:
|
|
467
|
+
style: nodeStyles?.link,
|
|
460
468
|
children: renderChildren(node.children, inListItem, true)
|
|
461
469
|
});
|
|
462
470
|
case "image":
|
|
@@ -465,27 +473,27 @@ const NodeRendererComponent = ({
|
|
|
465
473
|
title: node.title,
|
|
466
474
|
alt: node.alt,
|
|
467
475
|
Renderer: NodeRenderer,
|
|
468
|
-
style:
|
|
476
|
+
style: nodeStyles?.image
|
|
469
477
|
});
|
|
470
478
|
case "code_inline":
|
|
471
479
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_code.InlineCode, {
|
|
472
|
-
style:
|
|
480
|
+
style: nodeStyles?.code_inline,
|
|
473
481
|
children: node.content
|
|
474
482
|
});
|
|
475
483
|
case "code_block":
|
|
476
484
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_code.CodeBlock, {
|
|
477
485
|
language: node.language,
|
|
478
486
|
content: (0, _headless.getTextContent)(node),
|
|
479
|
-
style:
|
|
487
|
+
style: nodeStyles?.code_block
|
|
480
488
|
});
|
|
481
489
|
case "blockquote":
|
|
482
490
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_blockquote.Blockquote, {
|
|
483
|
-
style:
|
|
491
|
+
style: nodeStyles?.blockquote,
|
|
484
492
|
children: renderChildren(node.children, inListItem, false)
|
|
485
493
|
});
|
|
486
494
|
case "horizontal_rule":
|
|
487
495
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_horizontalRule.HorizontalRule, {
|
|
488
|
-
style:
|
|
496
|
+
style: nodeStyles?.horizontal_rule
|
|
489
497
|
});
|
|
490
498
|
case "line_break":
|
|
491
499
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
@@ -502,20 +510,20 @@ const NodeRendererComponent = ({
|
|
|
502
510
|
mathContent = mathContent.replace(/^\$+|\$+$/g, "").trim();
|
|
503
511
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_math.MathInline, {
|
|
504
512
|
content: mathContent,
|
|
505
|
-
style:
|
|
513
|
+
style: nodeStyles?.math_inline
|
|
506
514
|
});
|
|
507
515
|
}
|
|
508
516
|
case "math_block":
|
|
509
517
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_math.MathBlock, {
|
|
510
518
|
content: (0, _headless.getTextContent)(node),
|
|
511
|
-
style:
|
|
519
|
+
style: nodeStyles?.math_block
|
|
512
520
|
});
|
|
513
521
|
case "list":
|
|
514
522
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_list.List, {
|
|
515
523
|
ordered: node.ordered ?? false,
|
|
516
524
|
start: node.start,
|
|
517
525
|
depth: depth,
|
|
518
|
-
style:
|
|
526
|
+
style: nodeStyles?.list,
|
|
519
527
|
children: node.children?.map((child, index) => {
|
|
520
528
|
if (child.type === "task_list_item") {
|
|
521
529
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(NodeRenderer, {
|
|
@@ -545,14 +553,14 @@ const NodeRendererComponent = ({
|
|
|
545
553
|
case "task_list_item":
|
|
546
554
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_list.TaskListItem, {
|
|
547
555
|
checked: node.checked ?? false,
|
|
548
|
-
style:
|
|
556
|
+
style: nodeStyles?.task_list_item,
|
|
549
557
|
children: renderChildren(node.children, true, false)
|
|
550
558
|
});
|
|
551
559
|
case "table":
|
|
552
560
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_table.TableRenderer, {
|
|
553
561
|
node: node,
|
|
554
562
|
Renderer: NodeRenderer,
|
|
555
|
-
style:
|
|
563
|
+
style: nodeStyles?.table
|
|
556
564
|
});
|
|
557
565
|
case "table_head":
|
|
558
566
|
case "table_body":
|