react-native-nitro-markdown 0.1.0 → 0.1.1
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 +7 -7
- package/cpp/core/MD4CParser.cpp +15 -51
- package/lib/commonjs/{MarkdownJS.nitro.js → MarkdownJS.reference.js} +10 -10
- package/lib/commonjs/MarkdownJS.reference.js.map +1 -0
- package/lib/module/{MarkdownJS.nitro.js → MarkdownJS.reference.js} +10 -10
- package/lib/module/MarkdownJS.reference.js.map +1 -0
- package/lib/typescript/{MarkdownJS.nitro.d.ts → MarkdownJS.reference.d.ts} +1 -1
- package/lib/typescript/MarkdownJS.reference.d.ts.map +1 -0
- package/package.json +4 -1
- package/src/{MarkdownJS.nitro.ts → MarkdownJS.reference.ts} +26 -17
- package/lib/commonjs/MarkdownJS.nitro.js.map +0 -1
- package/lib/module/MarkdownJS.nitro.js.map +0 -1
- package/lib/typescript/MarkdownJS.nitro.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="./
|
|
2
|
+
<img src="./readme/demo.gif" alt="react-native-nitro-markdown demo" width="300" />
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
# react-native-nitro-markdown 🚀
|
|
@@ -241,7 +241,7 @@ export type NodeType =
|
|
|
241
241
|
|
|
242
242
|
We parse math delimiters (`$` and `$$`) natively using the `MD_FLAG_LATEXMATHSPANS` flag in `md4c`.
|
|
243
243
|
|
|
244
|
-
To render the math, you should use a library like `react-native-math-view` inside your renderer:
|
|
244
|
+
To render the math, you should use a library like `react-native-math-view`, `react-native-mathjax-svg`, or `react-native-katex` inside your renderer:
|
|
245
245
|
|
|
246
246
|
```tsx
|
|
247
247
|
// Inside your switch(node.type)
|
|
@@ -253,11 +253,11 @@ case 'math_block':
|
|
|
253
253
|
|
|
254
254
|
## 📊 Package Size
|
|
255
255
|
|
|
256
|
-
| Metric
|
|
257
|
-
|
|
|
258
|
-
| **Packed (tarball)** | ~75 kB
|
|
259
|
-
| **Unpacked**
|
|
260
|
-
| **Total files**
|
|
256
|
+
| Metric | Size |
|
|
257
|
+
| :------------------- | :------ |
|
|
258
|
+
| **Packed (tarball)** | ~75 kB |
|
|
259
|
+
| **Unpacked** | ~325 kB |
|
|
260
|
+
| **Total files** | 55 |
|
|
261
261
|
|
|
262
262
|
> The package includes the [md4c](https://github.com/mity/md4c) C source code (~244 kB) which is compiled natively on iOS and Android. This is a one-time cost that enables the high-performance parsing.
|
|
263
263
|
|
package/cpp/core/MD4CParser.cpp
CHANGED
|
@@ -18,13 +18,14 @@ public:
|
|
|
18
18
|
while (!nodeStack.empty()) nodeStack.pop();
|
|
19
19
|
nodeStack.push(root);
|
|
20
20
|
currentText.clear();
|
|
21
|
+
currentText.reserve(256);
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
void flushText() {
|
|
24
25
|
if (!currentText.empty() && !nodeStack.empty()) {
|
|
25
26
|
auto textNode = std::make_shared<MarkdownNode>(NodeType::Text);
|
|
26
|
-
textNode->content = currentText;
|
|
27
|
-
nodeStack.top()->addChild(textNode);
|
|
27
|
+
textNode->content = std::move(currentText);
|
|
28
|
+
nodeStack.top()->addChild(std::move(textNode));
|
|
28
29
|
currentText.clear();
|
|
29
30
|
}
|
|
30
31
|
}
|
|
@@ -33,9 +34,7 @@ public:
|
|
|
33
34
|
flushText();
|
|
34
35
|
if (node && !nodeStack.empty()) {
|
|
35
36
|
nodeStack.top()->addChild(node);
|
|
36
|
-
|
|
37
|
-
if (node) {
|
|
38
|
-
nodeStack.push(node);
|
|
37
|
+
nodeStack.push(std::move(node));
|
|
39
38
|
}
|
|
40
39
|
}
|
|
41
40
|
|
|
@@ -49,14 +48,10 @@ public:
|
|
|
49
48
|
std::string getAttributeText(const MD_ATTRIBUTE* attr) {
|
|
50
49
|
if (!attr || attr->size == 0) return "";
|
|
51
50
|
|
|
52
|
-
// Safety check for attribute size
|
|
53
|
-
MD_SIZE safeSize = attr->size;
|
|
54
|
-
if (safeSize > 100000) { // 100KB limit for attributes
|
|
55
|
-
safeSize = 100000;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
51
|
std::string result;
|
|
59
|
-
|
|
52
|
+
result.reserve(attr->size);
|
|
53
|
+
|
|
54
|
+
for (unsigned i = 0; i < attr->size; i++) {
|
|
60
55
|
if (attr->substr_types[i] == MD_TEXT_NORMAL ||
|
|
61
56
|
attr->substr_types[i] == MD_TEXT_ENTITY ||
|
|
62
57
|
attr->substr_types[i] == MD_TEXT_NULLCHAR) {
|
|
@@ -67,7 +62,7 @@ public:
|
|
|
67
62
|
}
|
|
68
63
|
|
|
69
64
|
if (result.empty() && attr->text && attr->size > 0) {
|
|
70
|
-
result
|
|
65
|
+
result.assign(attr->text, attr->size);
|
|
71
66
|
}
|
|
72
67
|
|
|
73
68
|
return result;
|
|
@@ -295,25 +290,21 @@ public:
|
|
|
295
290
|
(void)detail;
|
|
296
291
|
auto* impl = static_cast<Impl*>(userdata);
|
|
297
292
|
|
|
298
|
-
// For spans that capture content directly (not as child nodes)
|
|
299
293
|
if (!impl->nodeStack.empty()) {
|
|
300
294
|
auto currentNode = impl->nodeStack.top();
|
|
301
295
|
|
|
302
296
|
switch (type) {
|
|
303
297
|
case MD_SPAN_CODE:
|
|
304
|
-
// For inline code, capture the accumulated text as content
|
|
305
298
|
currentNode->content = impl->currentText;
|
|
306
299
|
impl->currentText.clear();
|
|
307
300
|
break;
|
|
308
301
|
|
|
309
302
|
case MD_SPAN_IMG:
|
|
310
|
-
// For images, capture the accumulated text as alt text
|
|
311
303
|
currentNode->alt = impl->currentText;
|
|
312
304
|
impl->currentText.clear();
|
|
313
305
|
break;
|
|
314
306
|
|
|
315
307
|
default:
|
|
316
|
-
// For other spans, use normal text flushing
|
|
317
308
|
break;
|
|
318
309
|
}
|
|
319
310
|
}
|
|
@@ -325,15 +316,7 @@ public:
|
|
|
325
316
|
static int text(MD_TEXTTYPE type, const MD_CHAR* text, MD_SIZE size, void* userdata) {
|
|
326
317
|
auto* impl = static_cast<Impl*>(userdata);
|
|
327
318
|
|
|
328
|
-
|
|
329
|
-
if (!text || size == 0) {
|
|
330
|
-
return 0;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
// Prevent excessive memory usage from very long text segments
|
|
334
|
-
if (size > 1000000) { // 1MB limit per text segment
|
|
335
|
-
size = 1000000;
|
|
336
|
-
}
|
|
319
|
+
if (!text || size == 0) return 0;
|
|
337
320
|
|
|
338
321
|
switch (type) {
|
|
339
322
|
case MD_TEXT_NULLCHAR:
|
|
@@ -382,13 +365,6 @@ MD4CParser::MD4CParser() : impl_(std::make_unique<Impl>()) {}
|
|
|
382
365
|
MD4CParser::~MD4CParser() = default;
|
|
383
366
|
|
|
384
367
|
std::shared_ptr<MarkdownNode> MD4CParser::parse(const std::string& markdown, const ParserOptions& options) {
|
|
385
|
-
// Input validation and safety checks
|
|
386
|
-
if (markdown.size() > 10000000) { // 10MB limit to prevent excessive memory usage
|
|
387
|
-
// Return empty document for extremely large inputs
|
|
388
|
-
auto root = std::make_shared<MarkdownNode>(NodeType::Document);
|
|
389
|
-
return root;
|
|
390
|
-
}
|
|
391
|
-
|
|
392
368
|
impl_->reset();
|
|
393
369
|
impl_->inputText = markdown.c_str();
|
|
394
370
|
|
|
@@ -406,35 +382,23 @@ std::shared_ptr<MarkdownNode> MD4CParser::parse(const std::string& markdown, con
|
|
|
406
382
|
}
|
|
407
383
|
|
|
408
384
|
MD_PARSER parser = {
|
|
409
|
-
0,
|
|
385
|
+
0,
|
|
410
386
|
flags,
|
|
411
387
|
&Impl::enterBlock,
|
|
412
388
|
&Impl::leaveBlock,
|
|
413
389
|
&Impl::enterSpan,
|
|
414
390
|
&Impl::leaveSpan,
|
|
415
391
|
&Impl::text,
|
|
416
|
-
nullptr,
|
|
392
|
+
nullptr,
|
|
417
393
|
nullptr
|
|
418
394
|
};
|
|
419
395
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
396
|
+
md_parse(markdown.c_str(),
|
|
397
|
+
static_cast<MD_SIZE>(markdown.size()),
|
|
398
|
+
&parser,
|
|
399
|
+
impl_.get());
|
|
424
400
|
|
|
425
401
|
impl_->flushText();
|
|
426
|
-
|
|
427
|
-
if (result != 0) {
|
|
428
|
-
// Parse error - return empty document
|
|
429
|
-
auto errorDoc = std::make_shared<MarkdownNode>(NodeType::Document);
|
|
430
|
-
auto errorPara = std::make_shared<MarkdownNode>(NodeType::Paragraph);
|
|
431
|
-
auto errorText = std::make_shared<MarkdownNode>(NodeType::Text);
|
|
432
|
-
errorText->content = "[Parse Error]";
|
|
433
|
-
errorPara->addChild(errorText);
|
|
434
|
-
errorDoc->addChild(errorPara);
|
|
435
|
-
return errorDoc;
|
|
436
|
-
}
|
|
437
|
-
|
|
438
402
|
return impl_->root;
|
|
439
403
|
}
|
|
440
404
|
|
|
@@ -10,10 +10,10 @@ class JSMarkdownParser {
|
|
|
10
10
|
// Simple regex-based parser for comparison
|
|
11
11
|
// This is much slower than the C++ version but demonstrates JSI usage
|
|
12
12
|
const root = {
|
|
13
|
-
type:
|
|
13
|
+
type: "document",
|
|
14
14
|
children: []
|
|
15
15
|
};
|
|
16
|
-
const lines = text.split(
|
|
16
|
+
const lines = text.split("\n");
|
|
17
17
|
let i = 0;
|
|
18
18
|
while (i < lines.length) {
|
|
19
19
|
const line = lines[i].trim();
|
|
@@ -26,10 +26,10 @@ class JSMarkdownParser {
|
|
|
26
26
|
const headerMatch = line.match(/^(#{1,6})\s+(.+)$/);
|
|
27
27
|
if (headerMatch) {
|
|
28
28
|
root.children.push({
|
|
29
|
-
type:
|
|
29
|
+
type: "heading",
|
|
30
30
|
level: headerMatch[1].length,
|
|
31
31
|
children: [{
|
|
32
|
-
type:
|
|
32
|
+
type: "text",
|
|
33
33
|
content: headerMatch[2]
|
|
34
34
|
}]
|
|
35
35
|
});
|
|
@@ -41,11 +41,11 @@ class JSMarkdownParser {
|
|
|
41
41
|
const boldMatch = line.match(/\*\*(.+?)\*\*/);
|
|
42
42
|
if (boldMatch) {
|
|
43
43
|
root.children.push({
|
|
44
|
-
type:
|
|
44
|
+
type: "paragraph",
|
|
45
45
|
children: [{
|
|
46
|
-
type:
|
|
46
|
+
type: "bold",
|
|
47
47
|
children: [{
|
|
48
|
-
type:
|
|
48
|
+
type: "text",
|
|
49
49
|
content: boldMatch[1]
|
|
50
50
|
}]
|
|
51
51
|
}]
|
|
@@ -56,9 +56,9 @@ class JSMarkdownParser {
|
|
|
56
56
|
|
|
57
57
|
// Default paragraph
|
|
58
58
|
root.children.push({
|
|
59
|
-
type:
|
|
59
|
+
type: "paragraph",
|
|
60
60
|
children: [{
|
|
61
|
-
type:
|
|
61
|
+
type: "text",
|
|
62
62
|
content: line
|
|
63
63
|
}]
|
|
64
64
|
});
|
|
@@ -111,4 +111,4 @@ class HybridMarkdownParser {
|
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
113
|
exports.HybridMarkdownParser = HybridMarkdownParser;
|
|
114
|
-
//# sourceMappingURL=MarkdownJS.
|
|
114
|
+
//# sourceMappingURL=MarkdownJS.reference.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["JSMarkdownParser","parseImpl","text","options","root","type","children","lines","split","i","length","line","trim","headerMatch","match","push","level","content","boldMatch","parse","gfm","math","exports","JSINitroMarkdownParser","parser","HybridMarkdownParser","constructor","cppParser","jsParser"],"sourceRoot":"../../src","sources":["MarkdownJS.reference.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -6,10 +6,10 @@ export class JSMarkdownParser {
|
|
|
6
6
|
// Simple regex-based parser for comparison
|
|
7
7
|
// This is much slower than the C++ version but demonstrates JSI usage
|
|
8
8
|
const root = {
|
|
9
|
-
type:
|
|
9
|
+
type: "document",
|
|
10
10
|
children: []
|
|
11
11
|
};
|
|
12
|
-
const lines = text.split(
|
|
12
|
+
const lines = text.split("\n");
|
|
13
13
|
let i = 0;
|
|
14
14
|
while (i < lines.length) {
|
|
15
15
|
const line = lines[i].trim();
|
|
@@ -22,10 +22,10 @@ export class JSMarkdownParser {
|
|
|
22
22
|
const headerMatch = line.match(/^(#{1,6})\s+(.+)$/);
|
|
23
23
|
if (headerMatch) {
|
|
24
24
|
root.children.push({
|
|
25
|
-
type:
|
|
25
|
+
type: "heading",
|
|
26
26
|
level: headerMatch[1].length,
|
|
27
27
|
children: [{
|
|
28
|
-
type:
|
|
28
|
+
type: "text",
|
|
29
29
|
content: headerMatch[2]
|
|
30
30
|
}]
|
|
31
31
|
});
|
|
@@ -37,11 +37,11 @@ export class JSMarkdownParser {
|
|
|
37
37
|
const boldMatch = line.match(/\*\*(.+?)\*\*/);
|
|
38
38
|
if (boldMatch) {
|
|
39
39
|
root.children.push({
|
|
40
|
-
type:
|
|
40
|
+
type: "paragraph",
|
|
41
41
|
children: [{
|
|
42
|
-
type:
|
|
42
|
+
type: "bold",
|
|
43
43
|
children: [{
|
|
44
|
-
type:
|
|
44
|
+
type: "text",
|
|
45
45
|
content: boldMatch[1]
|
|
46
46
|
}]
|
|
47
47
|
}]
|
|
@@ -52,9 +52,9 @@ export class JSMarkdownParser {
|
|
|
52
52
|
|
|
53
53
|
// Default paragraph
|
|
54
54
|
root.children.push({
|
|
55
|
-
type:
|
|
55
|
+
type: "paragraph",
|
|
56
56
|
children: [{
|
|
57
|
-
type:
|
|
57
|
+
type: "text",
|
|
58
58
|
content: line
|
|
59
59
|
}]
|
|
60
60
|
});
|
|
@@ -104,4 +104,4 @@ export class HybridMarkdownParser {
|
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
|
-
//# sourceMappingURL=MarkdownJS.
|
|
107
|
+
//# sourceMappingURL=MarkdownJS.reference.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["JSMarkdownParser","parseImpl","text","options","root","type","children","lines","split","i","length","line","trim","headerMatch","match","push","level","content","boldMatch","parse","gfm","math","JSINitroMarkdownParser","parser","HybridMarkdownParser","constructor","cppParser","jsParser"],"sourceRoot":"../../src","sources":["MarkdownJS.reference.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MarkdownJS.reference.d.ts","sourceRoot":"","sources":["../../src/MarkdownJS.reference.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,YAAY,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAGD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,SAAS;IAqDjB,KAAK,CACH,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,aAAyC,GACjD,YAAY;CAGhB;AAGD,qBAAa,sBAAsB;IACjC,KAAK,CACH,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,aAAyC,GACjD,YAAY;CAMhB;AAGD,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,SAAS,CAAM;IACvB,OAAO,CAAC,QAAQ,CAAmB;;IAQnC,KAAK,CACH,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,aAAyC,GACjD,YAAY;CAUhB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-nitro-markdown",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "High-performance Markdown parser for React Native using Nitro Modules and md4c",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|
|
@@ -21,9 +21,12 @@
|
|
|
21
21
|
"!**/__mocks__",
|
|
22
22
|
"!cpp/core/*Test.cpp",
|
|
23
23
|
"!cpp/build",
|
|
24
|
+
"!android/build",
|
|
25
|
+
"!android/.cxx",
|
|
24
26
|
"!scripts"
|
|
25
27
|
],
|
|
26
28
|
"scripts": {
|
|
29
|
+
"prebuild": "npm run codegen",
|
|
27
30
|
"build": "bob build",
|
|
28
31
|
"clean": "rimraf lib nitrogen/generated",
|
|
29
32
|
"codegen": "nitrogen --logLevel=\"debug\"",
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { NitroModules } from 'react-native-nitro-modules';
|
|
2
|
-
|
|
3
1
|
export interface MarkdownNode {
|
|
4
2
|
type: string;
|
|
5
3
|
content?: string;
|
|
@@ -26,8 +24,8 @@ export class JSMarkdownParser {
|
|
|
26
24
|
private parseImpl(text: string, options: ParserOptions): MarkdownNode {
|
|
27
25
|
// Simple regex-based parser for comparison
|
|
28
26
|
// This is much slower than the C++ version but demonstrates JSI usage
|
|
29
|
-
const root: MarkdownNode = { type:
|
|
30
|
-
const lines = text.split(
|
|
27
|
+
const root: MarkdownNode = { type: "document", children: [] };
|
|
28
|
+
const lines = text.split("\n");
|
|
31
29
|
let i = 0;
|
|
32
30
|
|
|
33
31
|
while (i < lines.length) {
|
|
@@ -41,9 +39,9 @@ export class JSMarkdownParser {
|
|
|
41
39
|
const headerMatch = line.match(/^(#{1,6})\s+(.+)$/);
|
|
42
40
|
if (headerMatch) {
|
|
43
41
|
root.children!.push({
|
|
44
|
-
type:
|
|
42
|
+
type: "heading",
|
|
45
43
|
level: headerMatch[1].length,
|
|
46
|
-
children: [{ type:
|
|
44
|
+
children: [{ type: "text", content: headerMatch[2] }],
|
|
47
45
|
});
|
|
48
46
|
i++;
|
|
49
47
|
continue;
|
|
@@ -53,11 +51,13 @@ export class JSMarkdownParser {
|
|
|
53
51
|
const boldMatch = line.match(/\*\*(.+?)\*\*/);
|
|
54
52
|
if (boldMatch) {
|
|
55
53
|
root.children!.push({
|
|
56
|
-
type:
|
|
57
|
-
children: [
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
54
|
+
type: "paragraph",
|
|
55
|
+
children: [
|
|
56
|
+
{
|
|
57
|
+
type: "bold",
|
|
58
|
+
children: [{ type: "text", content: boldMatch[1] }],
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
61
|
});
|
|
62
62
|
i++;
|
|
63
63
|
continue;
|
|
@@ -65,8 +65,8 @@ export class JSMarkdownParser {
|
|
|
65
65
|
|
|
66
66
|
// Default paragraph
|
|
67
67
|
root.children!.push({
|
|
68
|
-
type:
|
|
69
|
-
children: [{ type:
|
|
68
|
+
type: "paragraph",
|
|
69
|
+
children: [{ type: "text", content: line }],
|
|
70
70
|
});
|
|
71
71
|
i++;
|
|
72
72
|
}
|
|
@@ -74,14 +74,20 @@ export class JSMarkdownParser {
|
|
|
74
74
|
return root;
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
parse(
|
|
77
|
+
parse(
|
|
78
|
+
text: string,
|
|
79
|
+
options: ParserOptions = { gfm: true, math: true }
|
|
80
|
+
): MarkdownNode {
|
|
78
81
|
return this.parseImpl(text, options);
|
|
79
82
|
}
|
|
80
83
|
}
|
|
81
84
|
|
|
82
85
|
// JSI-enabled version using Nitro but with JS implementation
|
|
83
86
|
export class JSINitroMarkdownParser {
|
|
84
|
-
parse(
|
|
87
|
+
parse(
|
|
88
|
+
text: string,
|
|
89
|
+
options: ParserOptions = { gfm: true, math: true }
|
|
90
|
+
): MarkdownNode {
|
|
85
91
|
// This would use JSI to call into JavaScriptCore
|
|
86
92
|
// For now, we'll simulate it
|
|
87
93
|
const parser = new JSMarkdownParser();
|
|
@@ -100,7 +106,10 @@ export class HybridMarkdownParser {
|
|
|
100
106
|
this.jsParser = new JSMarkdownParser();
|
|
101
107
|
}
|
|
102
108
|
|
|
103
|
-
parse(
|
|
109
|
+
parse(
|
|
110
|
+
text: string,
|
|
111
|
+
options: ParserOptions = { gfm: true, math: true }
|
|
112
|
+
): MarkdownNode {
|
|
104
113
|
// Use C++ parser for complex cases, JS for simple
|
|
105
114
|
if (text.length > 1000 || options.gfm || options.math) {
|
|
106
115
|
// Would call C++ parser via Nitro
|
|
@@ -110,4 +119,4 @@ export class HybridMarkdownParser {
|
|
|
110
119
|
return this.jsParser.parse(text, options);
|
|
111
120
|
}
|
|
112
121
|
}
|
|
113
|
-
}
|
|
122
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"names":["JSMarkdownParser","parseImpl","text","options","root","type","children","lines","split","i","length","line","trim","headerMatch","match","push","level","content","boldMatch","parse","gfm","math","exports","JSINitroMarkdownParser","parser","HybridMarkdownParser","constructor","cppParser","jsParser"],"sourceRoot":"../../src","sources":["MarkdownJS.nitro.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"names":["JSMarkdownParser","parseImpl","text","options","root","type","children","lines","split","i","length","line","trim","headerMatch","match","push","level","content","boldMatch","parse","gfm","math","JSINitroMarkdownParser","parser","HybridMarkdownParser","constructor","cppParser","jsParser"],"sourceRoot":"../../src","sources":["MarkdownJS.nitro.ts"],"mappings":";;AAuBA;AACA,OAAO,MAAMA,gBAAgB,CAAC;EACpBC,SAASA,CAACC,IAAY,EAAEC,OAAsB,EAAgB;IACpE;IACA;IACA,MAAMC,IAAkB,GAAG;MAAEC,IAAI,EAAE,UAAU;MAAEC,QAAQ,EAAE;IAAG,CAAC;IAC7D,MAAMC,KAAK,GAAGL,IAAI,CAACM,KAAK,CAAC,IAAI,CAAC;IAC9B,IAAIC,CAAC,GAAG,CAAC;IAET,OAAOA,CAAC,GAAGF,KAAK,CAACG,MAAM,EAAE;MACvB,MAAMC,IAAI,GAAGJ,KAAK,CAACE,CAAC,CAAC,CAACG,IAAI,CAAC,CAAC;MAC5B,IAAI,CAACD,IAAI,EAAE;QACTF,CAAC,EAAE;QACH;MACF;;MAEA;MACA,MAAMI,WAAW,GAAGF,IAAI,CAACG,KAAK,CAAC,mBAAmB,CAAC;MACnD,IAAID,WAAW,EAAE;QACfT,IAAI,CAACE,QAAQ,CAAES,IAAI,CAAC;UAClBV,IAAI,EAAE,SAAS;UACfW,KAAK,EAAEH,WAAW,CAAC,CAAC,CAAC,CAACH,MAAM;UAC5BJ,QAAQ,EAAE,CAAC;YAAED,IAAI,EAAE,MAAM;YAAEY,OAAO,EAAEJ,WAAW,CAAC,CAAC;UAAE,CAAC;QACtD,CAAC,CAAC;QACFJ,CAAC,EAAE;QACH;MACF;;MAEA;MACA,MAAMS,SAAS,GAAGP,IAAI,CAACG,KAAK,CAAC,eAAe,CAAC;MAC7C,IAAII,SAAS,EAAE;QACbd,IAAI,CAACE,QAAQ,CAAES,IAAI,CAAC;UAClBV,IAAI,EAAE,WAAW;UACjBC,QAAQ,EAAE,CAAC;YACTD,IAAI,EAAE,MAAM;YACZC,QAAQ,EAAE,CAAC;cAAED,IAAI,EAAE,MAAM;cAAEY,OAAO,EAAEC,SAAS,CAAC,CAAC;YAAE,CAAC;UACpD,CAAC;QACH,CAAC,CAAC;QACFT,CAAC,EAAE;QACH;MACF;;MAEA;MACAL,IAAI,CAACE,QAAQ,CAAES,IAAI,CAAC;QAClBV,IAAI,EAAE,WAAW;QACjBC,QAAQ,EAAE,CAAC;UAAED,IAAI,EAAE,MAAM;UAAEY,OAAO,EAAEN;QAAK,CAAC;MAC5C,CAAC,CAAC;MACFF,CAAC,EAAE;IACL;IAEA,OAAOL,IAAI;EACb;EAEAe,KAAKA,CAACjB,IAAY,EAAEC,OAAsB,GAAG;IAAEiB,GAAG,EAAE,IAAI;IAAEC,IAAI,EAAE;EAAK,CAAC,EAAgB;IACpF,OAAO,IAAI,CAACpB,SAAS,CAACC,IAAI,EAAEC,OAAO,CAAC;EACtC;AACF;;AAEA;AACA,OAAO,MAAMmB,sBAAsB,CAAC;EAClCH,KAAKA,CAACjB,IAAY,EAAEC,OAAsB,GAAG;IAAEiB,GAAG,EAAE,IAAI;IAAEC,IAAI,EAAE;EAAK,CAAC,EAAgB;IACpF;IACA;IACA,MAAME,MAAM,GAAG,IAAIvB,gBAAgB,CAAC,CAAC;IACrC,OAAOuB,MAAM,CAACJ,KAAK,CAACjB,IAAI,EAAEC,OAAO,CAAC;EACpC;AACF;;AAEA;AACA,OAAO,MAAMqB,oBAAoB,CAAC;EAIhCC,WAAWA,CAAA,EAAG;IACZ;IACA,IAAI,CAACC,SAAS,GAAG,IAAI;IACrB,IAAI,CAACC,QAAQ,GAAG,IAAI3B,gBAAgB,CAAC,CAAC;EACxC;EAEAmB,KAAKA,CAACjB,IAAY,EAAEC,OAAsB,GAAG;IAAEiB,GAAG,EAAE,IAAI;IAAEC,IAAI,EAAE;EAAK,CAAC,EAAgB;IACpF;IACA,IAAInB,IAAI,CAACQ,MAAM,GAAG,IAAI,IAAIP,OAAO,CAACiB,GAAG,IAAIjB,OAAO,CAACkB,IAAI,EAAE;MACrD;MACA,OAAO,IAAI,CAACM,QAAQ,CAACR,KAAK,CAACjB,IAAI,EAAEC,OAAO,CAAC;IAC3C,CAAC,MAAM;MACL;MACA,OAAO,IAAI,CAACwB,QAAQ,CAACR,KAAK,CAACjB,IAAI,EAAEC,OAAO,CAAC;IAC3C;EACF;AACF","ignoreList":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"MarkdownJS.nitro.d.ts","sourceRoot":"","sources":["../../src/MarkdownJS.nitro.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,YAAY,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAGD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,SAAS;IAmDjB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,aAAyC,GAAG,YAAY;CAGtF;AAGD,qBAAa,sBAAsB;IACjC,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,aAAyC,GAAG,YAAY;CAMtF;AAGD,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,SAAS,CAAM;IACvB,OAAO,CAAC,QAAQ,CAAmB;;IAQnC,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,aAAyC,GAAG,YAAY;CAUtF"}
|