react-native-nitro-markdown 0.1.0
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 +276 -0
- package/android/CMakeLists.txt +40 -0
- package/android/build.gradle +92 -0
- package/android/gradle.properties +5 -0
- package/android/src/main/AndroidManifest.xml +4 -0
- package/android/src/main/cpp/cpp-adapter.cpp +7 -0
- package/android/src/main/java/com/nitromarkdown/NitroMarkdownPackage.kt +23 -0
- package/cpp/CMakeLists.txt +46 -0
- package/cpp/bindings/HybridMarkdownParser.cpp +112 -0
- package/cpp/bindings/HybridMarkdownParser.hpp +28 -0
- package/cpp/core/MD4CParser.cpp +442 -0
- package/cpp/core/MD4CParser.hpp +21 -0
- package/cpp/core/MarkdownTypes.hpp +119 -0
- package/cpp/md4c/md4c.c +6492 -0
- package/cpp/md4c/md4c.h +407 -0
- package/ios/NitroMarkdown-Bridging-Header.h +14 -0
- package/lib/commonjs/Markdown.nitro.js +6 -0
- package/lib/commonjs/Markdown.nitro.js.map +1 -0
- package/lib/commonjs/MarkdownJS.nitro.js +114 -0
- package/lib/commonjs/MarkdownJS.nitro.js.map +1 -0
- package/lib/commonjs/index.js +19 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/module/Markdown.nitro.js +4 -0
- package/lib/module/Markdown.nitro.js.map +1 -0
- package/lib/module/MarkdownJS.nitro.js +107 -0
- package/lib/module/MarkdownJS.nitro.js.map +1 -0
- package/lib/module/index.js +13 -0
- package/lib/module/index.js.map +1 -0
- package/lib/typescript/Markdown.nitro.d.ts +13 -0
- package/lib/typescript/Markdown.nitro.d.ts.map +1 -0
- package/lib/typescript/MarkdownJS.nitro.d.ts +33 -0
- package/lib/typescript/MarkdownJS.nitro.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +22 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/nitro.json +16 -0
- package/nitrogen/generated/.gitattributes +1 -0
- package/nitrogen/generated/android/NitroMarkdown+autolinking.cmake +81 -0
- package/nitrogen/generated/android/NitroMarkdown+autolinking.gradle +27 -0
- package/nitrogen/generated/android/NitroMarkdownOnLoad.cpp +44 -0
- package/nitrogen/generated/android/NitroMarkdownOnLoad.hpp +25 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/com/nitromarkdown/NitroMarkdownOnLoad.kt +35 -0
- package/nitrogen/generated/ios/NitroMarkdown+autolinking.rb +60 -0
- package/nitrogen/generated/ios/NitroMarkdown-Swift-Cxx-Bridge.cpp +17 -0
- package/nitrogen/generated/ios/NitroMarkdown-Swift-Cxx-Bridge.hpp +27 -0
- package/nitrogen/generated/ios/NitroMarkdown-Swift-Cxx-Umbrella.hpp +38 -0
- package/nitrogen/generated/ios/NitroMarkdownAutolinking.mm +35 -0
- package/nitrogen/generated/ios/NitroMarkdownAutolinking.swift +12 -0
- package/nitrogen/generated/shared/c++/HybridMarkdownParserSpec.cpp +22 -0
- package/nitrogen/generated/shared/c++/HybridMarkdownParserSpec.hpp +65 -0
- package/nitrogen/generated/shared/c++/ParserOptions.hpp +79 -0
- package/package.json +101 -0
- package/react-native-nitro-markdown.podspec +42 -0
- package/src/Markdown.nitro.ts +12 -0
- package/src/MarkdownJS.nitro.ts +113 -0
- package/src/index.ts +65 -0
package/README.md
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="./apps/example/assets/icon.png" alt="react-native-nitro-markdown logo" width="150" height="150" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
# react-native-nitro-markdown 🚀
|
|
6
|
+
|
|
7
|
+
> The fastest Markdown parser for React Native. Period.
|
|
8
|
+
|
|
9
|
+
[](https://www.npmjs.com/package/react-native-nitro-markdown)
|
|
10
|
+
[](https://opensource.org/licenses/MIT)
|
|
11
|
+
[](https://nitro.margelo.com)
|
|
12
|
+
|
|
13
|
+
**react-native-nitro-markdown** is a high-performance Markdown parser built on **[md4c](https://github.com/mity/md4c)** (C++) and **[Nitro Modules](https://nitro.margelo.com)**. It parses complex Markdown, GFM, and LaTeX Math into a structured AST **synchronously** via JSI, bypassing the React Native Bridge entirely.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## ⚡ Why Nitro? (Benchmarks)
|
|
18
|
+
|
|
19
|
+
We benchmarked this library against the most popular JavaScript parsers on a real mobile device (iPhone 15 Pro, Release Mode) using a heavy **237KB** Markdown document.
|
|
20
|
+
|
|
21
|
+
| Parser | Time (ms) | Speedup | Frame Drops (60fps) |
|
|
22
|
+
| :-------------------------- | :--------- | :---------------- | :-------------------- |
|
|
23
|
+
| **🚀 Nitro Markdown (C++)** | **~29 ms** | **1x (Baseline)** | **~1 frame** (Smooth) |
|
|
24
|
+
| 📋 CommonMark (JS) | ~82 ms | 2.8x slower | ~5 frames (Jank) |
|
|
25
|
+
| 🏗️ Markdown-It (JS) | ~118 ms | 4.0x slower | ~7 frames (Jank) |
|
|
26
|
+
| 💨 Marked (JS) | ~400 ms | 13.5x slower | ~24 frames (Freeze) |
|
|
27
|
+
|
|
28
|
+
> **Takeaway:** JavaScript parsers trigger Garbage Collection pauses. Nitro uses C++ to parse efficiently with zero-copy overhead, keeping your UI thread responsive.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 📦 Installation
|
|
33
|
+
|
|
34
|
+
Choose your preferred package manager to install the package and its core dependency (`react-native-nitro-modules`).
|
|
35
|
+
|
|
36
|
+
### **1. Install Dependencies**
|
|
37
|
+
|
|
38
|
+
**npm**
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm install react-native-nitro-markdown react-native-nitro-modules
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Yarn**
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
yarn add react-native-nitro-markdown react-native-nitro-modules
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Bun**
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
bun add react-native-nitro-markdown react-native-nitro-modules
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**pnpm**
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pnpm add react-native-nitro-markdown react-native-nitro-modules
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### **2. Install Native Pods (iOS)**
|
|
63
|
+
|
|
64
|
+
**Standard**
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
cd ios && pod install
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### **3. Expo Users**
|
|
71
|
+
|
|
72
|
+
If you are using Expo, you must run a **Prebuild** (Development Build) because this package contains native C++ code.
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
npx expo install react-native-nitro-markdown react-native-nitro-modules
|
|
76
|
+
npx expo prebuild
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## 💻 Usage
|
|
82
|
+
|
|
83
|
+
### Basic Parsing
|
|
84
|
+
|
|
85
|
+
The parsing is synchronous and instant. It returns a fully typed JSON AST.
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import { parseMarkdown } from "react-native-nitro-markdown";
|
|
89
|
+
|
|
90
|
+
const markdown = `
|
|
91
|
+
# Hello World
|
|
92
|
+
This is **bold** text and a [link](https://github.com).
|
|
93
|
+
`;
|
|
94
|
+
|
|
95
|
+
const ast = parseMarkdown(markdown);
|
|
96
|
+
console.log(ast);
|
|
97
|
+
// Output: { type: "document", children: [...] }
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Advanced Options (GFM & Math)
|
|
101
|
+
|
|
102
|
+
Enable GitHub Flavored Markdown (Tables, TaskLists) or LaTeX Math support.
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
import { parseMarkdown } from "react-native-nitro-markdown";
|
|
106
|
+
|
|
107
|
+
const ast = parseMarkdown(markdown, {
|
|
108
|
+
gfm: true, // Tables, Strikethrough, Autolinks, TaskLists
|
|
109
|
+
math: true, // $E=mc^2$ and $$block$$
|
|
110
|
+
wiki: true, // [[WikiLinks]]
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## 🎨 Rendering
|
|
117
|
+
|
|
118
|
+
This library is a **Parser Only**. It gives you the raw data (AST) so you can render it with native components (`<Text>`, `<View>`) for maximum performance.
|
|
119
|
+
|
|
120
|
+
Here is a simple recursive renderer example:
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
import React from "react";
|
|
124
|
+
import { Text, View, StyleSheet } from "react-native";
|
|
125
|
+
import { parseMarkdown, type MarkdownNode } from "react-native-nitro-markdown";
|
|
126
|
+
|
|
127
|
+
export function MarkdownView({ content }: { content: string }) {
|
|
128
|
+
const ast = parseMarkdown(content, { gfm: true });
|
|
129
|
+
|
|
130
|
+
return (
|
|
131
|
+
<View style={styles.container}>
|
|
132
|
+
<Renderer node={ast} />
|
|
133
|
+
</View>
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function Renderer({ node }: { node: MarkdownNode }) {
|
|
138
|
+
switch (node.type) {
|
|
139
|
+
case "document":
|
|
140
|
+
return (
|
|
141
|
+
<View>
|
|
142
|
+
{node.children?.map((child, i) => (
|
|
143
|
+
<Renderer key={i} node={child} />
|
|
144
|
+
))}
|
|
145
|
+
</View>
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
case "heading":
|
|
149
|
+
return (
|
|
150
|
+
<Text style={styles.h1}>
|
|
151
|
+
{node.children?.map((c, i) => (
|
|
152
|
+
<Renderer key={i} node={c} />
|
|
153
|
+
))}
|
|
154
|
+
</Text>
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
case "paragraph":
|
|
158
|
+
return (
|
|
159
|
+
<Text style={styles.p}>
|
|
160
|
+
{node.children?.map((child, i) => (
|
|
161
|
+
<Renderer key={i} node={child} />
|
|
162
|
+
))}
|
|
163
|
+
</Text>
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
case "text":
|
|
167
|
+
return <Text>{node.content}</Text>;
|
|
168
|
+
|
|
169
|
+
case "bold":
|
|
170
|
+
return (
|
|
171
|
+
<Text style={styles.bold}>
|
|
172
|
+
{node.children?.map((c, i) => (
|
|
173
|
+
<Renderer key={i} node={c} />
|
|
174
|
+
))}
|
|
175
|
+
</Text>
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
// Handle 'table', 'code_block', 'math_inline' etc...
|
|
179
|
+
default:
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const styles = StyleSheet.create({
|
|
185
|
+
container: { padding: 16 },
|
|
186
|
+
h1: { fontSize: 24, fontWeight: "bold", marginVertical: 8 },
|
|
187
|
+
p: { fontSize: 16, lineHeight: 24, marginBottom: 8 },
|
|
188
|
+
bold: { fontWeight: "700" },
|
|
189
|
+
});
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## 📐 AST Structure
|
|
195
|
+
|
|
196
|
+
The parser returns a `MarkdownNode` tree. The Types are fully exported for TypeScript support.
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
export interface MarkdownNode {
|
|
200
|
+
type: NodeType;
|
|
201
|
+
// Content for Text/Code/Math
|
|
202
|
+
content?: string;
|
|
203
|
+
// Hierarchy
|
|
204
|
+
children?: MarkdownNode[];
|
|
205
|
+
// Metadata
|
|
206
|
+
level?: number; // Headings (1-6)
|
|
207
|
+
href?: string; // Links
|
|
208
|
+
checked?: boolean; // Task Lists
|
|
209
|
+
language?: string; // Code Blocks
|
|
210
|
+
// Table Props
|
|
211
|
+
align?: "left" | "center" | "right";
|
|
212
|
+
isHeader?: boolean;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export type NodeType =
|
|
216
|
+
| "document"
|
|
217
|
+
| "paragraph"
|
|
218
|
+
| "text"
|
|
219
|
+
| "heading"
|
|
220
|
+
| "bold"
|
|
221
|
+
| "italic"
|
|
222
|
+
| "strikethrough"
|
|
223
|
+
| "link"
|
|
224
|
+
| "image"
|
|
225
|
+
| "code_inline"
|
|
226
|
+
| "code_block"
|
|
227
|
+
| "blockquote"
|
|
228
|
+
| "list"
|
|
229
|
+
| "list_item"
|
|
230
|
+
| "task_list_item"
|
|
231
|
+
| "table"
|
|
232
|
+
| "table_row"
|
|
233
|
+
| "table_cell"
|
|
234
|
+
| "math_inline"
|
|
235
|
+
| "math_block";
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## 🧮 LaTeX Math Support
|
|
241
|
+
|
|
242
|
+
We parse math delimiters (`$` and `$$`) natively using the `MD_FLAG_LATEXMATHSPANS` flag in `md4c`.
|
|
243
|
+
|
|
244
|
+
To render the math, you should use a library like `react-native-math-view` inside your renderer:
|
|
245
|
+
|
|
246
|
+
```tsx
|
|
247
|
+
// Inside your switch(node.type)
|
|
248
|
+
case 'math_inline':
|
|
249
|
+
return <MathView math={node.content} style={styles.math} />;
|
|
250
|
+
case 'math_block':
|
|
251
|
+
return <MathView math={node.content} style={styles.mathBlock} />;
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## 📊 Package Size
|
|
255
|
+
|
|
256
|
+
| Metric | Size |
|
|
257
|
+
| :----- | :--- |
|
|
258
|
+
| **Packed (tarball)** | ~75 kB |
|
|
259
|
+
| **Unpacked** | ~325 kB |
|
|
260
|
+
| **Total files** | 55 |
|
|
261
|
+
|
|
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
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## 🤝 Contributing
|
|
267
|
+
|
|
268
|
+
See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.
|
|
269
|
+
|
|
270
|
+
## 📄 License
|
|
271
|
+
|
|
272
|
+
MIT
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
Built with ❤️ using [Nitro Modules](https://nitro.margelo.com) and [md4c](https://github.com/mity/md4c).
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.22.1)
|
|
2
|
+
project(NitroMarkdown)
|
|
3
|
+
|
|
4
|
+
set(CMAKE_CXX_STANDARD 20)
|
|
5
|
+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
6
|
+
set(CMAKE_C_STANDARD 11)
|
|
7
|
+
set(CMAKE_C_STANDARD_REQUIRED ON)
|
|
8
|
+
|
|
9
|
+
# Define the path to our C++ sources
|
|
10
|
+
set(CPP_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../cpp")
|
|
11
|
+
|
|
12
|
+
# Collect source files
|
|
13
|
+
file(GLOB MD4C_SOURCES "${CPP_ROOT}/md4c/*.c")
|
|
14
|
+
file(GLOB CORE_SOURCES "${CPP_ROOT}/core/*.cpp")
|
|
15
|
+
file(GLOB BINDING_SOURCES "${CPP_ROOT}/bindings/*.cpp")
|
|
16
|
+
|
|
17
|
+
# Create the shared library with our sources
|
|
18
|
+
add_library(${PROJECT_NAME} SHARED
|
|
19
|
+
${MD4C_SOURCES}
|
|
20
|
+
${CORE_SOURCES}
|
|
21
|
+
${BINDING_SOURCES}
|
|
22
|
+
# JNI adapter
|
|
23
|
+
src/main/cpp/cpp-adapter.cpp
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
# Include directories
|
|
27
|
+
target_include_directories(${PROJECT_NAME} PRIVATE
|
|
28
|
+
"${CPP_ROOT}/md4c"
|
|
29
|
+
"${CPP_ROOT}/core"
|
|
30
|
+
"${CPP_ROOT}/bindings"
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# Preprocessor definitions
|
|
34
|
+
target_compile_definitions(${PROJECT_NAME} PRIVATE
|
|
35
|
+
MD4C_USE_UTF8=1
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
# Include Nitro autolinking (adds nitrogen sources, definitions, and links)
|
|
39
|
+
include(${CMAKE_CURRENT_SOURCE_DIR}/../nitrogen/generated/android/NitroMarkdown+autolinking.cmake)
|
|
40
|
+
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
buildscript {
|
|
2
|
+
repositories {
|
|
3
|
+
google()
|
|
4
|
+
mavenCentral()
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
dependencies {
|
|
8
|
+
classpath "com.android.tools.build:gradle:8.10.1"
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
def reactNativeArchitectures() {
|
|
13
|
+
def value = rootProject.getProperties().get("reactNativeArchitectures")
|
|
14
|
+
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
def isNewArchitectureEnabled() {
|
|
18
|
+
return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
apply plugin: "com.android.library"
|
|
22
|
+
apply plugin: 'org.jetbrains.kotlin.android'
|
|
23
|
+
apply from: '../nitrogen/generated/android/NitroMarkdown+autolinking.gradle'
|
|
24
|
+
|
|
25
|
+
if (isNewArchitectureEnabled()) {
|
|
26
|
+
apply plugin: "com.facebook.react"
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
def getExtOrDefault(name) {
|
|
30
|
+
return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["NitroMarkdown_" + name]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
def getExtOrIntegerDefault(name) {
|
|
34
|
+
return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["NitroMarkdown_" + name]).toInteger()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
android {
|
|
38
|
+
namespace "com.nitromarkdown"
|
|
39
|
+
|
|
40
|
+
ndkVersion getExtOrDefault("ndkVersion")
|
|
41
|
+
compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
|
|
42
|
+
|
|
43
|
+
defaultConfig {
|
|
44
|
+
minSdkVersion getExtOrIntegerDefault("minSdkVersion")
|
|
45
|
+
targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
|
|
46
|
+
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
|
|
47
|
+
|
|
48
|
+
externalNativeBuild {
|
|
49
|
+
cmake {
|
|
50
|
+
cppFlags "-frtti -fexceptions -Wall -Wextra -fstack-protector-all"
|
|
51
|
+
arguments "-DANDROID_STL=c++_shared", "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
|
|
52
|
+
abiFilters (*reactNativeArchitectures())
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
externalNativeBuild {
|
|
58
|
+
cmake {
|
|
59
|
+
path "CMakeLists.txt"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
buildFeatures {
|
|
64
|
+
buildConfig true
|
|
65
|
+
prefab true
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
compileOptions {
|
|
69
|
+
sourceCompatibility JavaVersion.VERSION_1_8
|
|
70
|
+
targetCompatibility JavaVersion.VERSION_1_8
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
sourceSets {
|
|
74
|
+
main {
|
|
75
|
+
java.srcDirs += ["src/main/java"]
|
|
76
|
+
if (isNewArchitectureEnabled()) {
|
|
77
|
+
java.srcDirs += ["${project.buildDir}/generated/source/codegen/java"]
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
repositories {
|
|
84
|
+
mavenCentral()
|
|
85
|
+
google()
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
dependencies {
|
|
89
|
+
//noinspection GradleDynamicVersion
|
|
90
|
+
implementation "com.facebook.react:react-native:+"
|
|
91
|
+
implementation project(":react-native-nitro-modules")
|
|
92
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
package com.nitromarkdown
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.TurboReactPackage
|
|
4
|
+
import com.facebook.react.bridge.NativeModule
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
+
import com.facebook.react.module.model.ReactModuleInfoProvider
|
|
7
|
+
|
|
8
|
+
class NitroMarkdownPackage : TurboReactPackage() {
|
|
9
|
+
companion object {
|
|
10
|
+
init {
|
|
11
|
+
System.loadLibrary("NitroMarkdown")
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
|
|
16
|
+
return null
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
override fun getReactModuleInfoProvider(): ReactModuleInfoProvider {
|
|
20
|
+
return ReactModuleInfoProvider { emptyMap() }
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.22.1)
|
|
2
|
+
project(MD4CParserTest)
|
|
3
|
+
|
|
4
|
+
set(CMAKE_CXX_STANDARD 20)
|
|
5
|
+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
6
|
+
set(CMAKE_C_STANDARD 11)
|
|
7
|
+
set(CMAKE_C_STANDARD_REQUIRED ON)
|
|
8
|
+
|
|
9
|
+
# Define the path to our C++ sources
|
|
10
|
+
set(CPP_ROOT "${CMAKE_CURRENT_SOURCE_DIR}")
|
|
11
|
+
|
|
12
|
+
# Collect source files
|
|
13
|
+
file(GLOB MD4C_SOURCES "${CPP_ROOT}/md4c/*.c")
|
|
14
|
+
file(GLOB CORE_SOURCES "${CPP_ROOT}/core/*.cpp")
|
|
15
|
+
# Exclude the test file from the library sources
|
|
16
|
+
list(FILTER CORE_SOURCES EXCLUDE REGEX ".*Test\.cpp$")
|
|
17
|
+
|
|
18
|
+
# Create the core library
|
|
19
|
+
add_library(MD4CCore STATIC
|
|
20
|
+
${MD4C_SOURCES}
|
|
21
|
+
${CORE_SOURCES}
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# Include directories for the library
|
|
25
|
+
target_include_directories(MD4CCore PUBLIC
|
|
26
|
+
"${CPP_ROOT}/md4c"
|
|
27
|
+
"${CPP_ROOT}/core"
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
# Preprocessor definitions
|
|
31
|
+
target_compile_definitions(MD4CCore PRIVATE
|
|
32
|
+
MD4C_USE_UTF8=1
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
# Create the test executable
|
|
36
|
+
add_executable(MD4CParserTest
|
|
37
|
+
core/MD4CParserTest.cpp
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
# Link the test executable to the core library
|
|
41
|
+
target_link_libraries(MD4CParserTest PRIVATE MD4CCore)
|
|
42
|
+
|
|
43
|
+
# Include directories for the test
|
|
44
|
+
target_include_directories(MD4CParserTest PRIVATE
|
|
45
|
+
"${CPP_ROOT}/core"
|
|
46
|
+
)
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#include "HybridMarkdownParser.hpp"
|
|
2
|
+
#include <sstream>
|
|
3
|
+
#include <iomanip>
|
|
4
|
+
|
|
5
|
+
namespace margelo::nitro::NitroMarkdown {
|
|
6
|
+
|
|
7
|
+
std::string HybridMarkdownParser::parse(const std::string& text) {
|
|
8
|
+
InternalParserOptions opts;
|
|
9
|
+
opts.gfm = true;
|
|
10
|
+
opts.math = true;
|
|
11
|
+
|
|
12
|
+
auto ast = parser_->parse(text, opts);
|
|
13
|
+
return nodeToJson(ast);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
std::string HybridMarkdownParser::parseWithOptions(const std::string& text, const ParserOptions& options) {
|
|
17
|
+
InternalParserOptions internalOpts;
|
|
18
|
+
internalOpts.gfm = options.gfm.value_or(true);
|
|
19
|
+
internalOpts.math = options.math.value_or(true);
|
|
20
|
+
|
|
21
|
+
auto ast = parser_->parse(text, internalOpts);
|
|
22
|
+
return nodeToJson(ast);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static std::string escapeJson(const std::string& s) {
|
|
26
|
+
std::ostringstream o;
|
|
27
|
+
for (char c : s) {
|
|
28
|
+
switch (c) {
|
|
29
|
+
case '"': o << "\\\""; break;
|
|
30
|
+
case '\\': o << "\\\\"; break;
|
|
31
|
+
case '\b': o << "\\b"; break;
|
|
32
|
+
case '\f': o << "\\f"; break;
|
|
33
|
+
case '\n': o << "\\n"; break;
|
|
34
|
+
case '\r': o << "\\r"; break;
|
|
35
|
+
case '\t': o << "\\t"; break;
|
|
36
|
+
default:
|
|
37
|
+
if ('\x00' <= c && c <= '\x1f') {
|
|
38
|
+
o << "\\u" << std::hex << std::setw(4) << std::setfill('0') << (int)(unsigned char)c;
|
|
39
|
+
} else {
|
|
40
|
+
o << c;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return o.str();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
std::string HybridMarkdownParser::nodeToJson(const std::shared_ptr<InternalMarkdownNode>& node) {
|
|
48
|
+
std::ostringstream json;
|
|
49
|
+
json << "{";
|
|
50
|
+
json << "\"type\":\"" << ::NitroMarkdown::nodeTypeToString(node->type) << "\"";
|
|
51
|
+
|
|
52
|
+
if (node->content.has_value()) {
|
|
53
|
+
json << ",\"content\":\"" << escapeJson(node->content.value()) << "\"";
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (node->level.has_value()) {
|
|
57
|
+
json << ",\"level\":" << node->level.value();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (node->href.has_value()) {
|
|
61
|
+
json << ",\"href\":\"" << escapeJson(node->href.value()) << "\"";
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (node->title.has_value()) {
|
|
65
|
+
json << ",\"title\":\"" << escapeJson(node->title.value()) << "\"";
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (node->alt.has_value()) {
|
|
69
|
+
json << ",\"alt\":\"" << escapeJson(node->alt.value()) << "\"";
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (node->language.has_value()) {
|
|
73
|
+
json << ",\"language\":\"" << escapeJson(node->language.value()) << "\"";
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (node->ordered.has_value()) {
|
|
77
|
+
json << ",\"ordered\":" << (node->ordered.value() ? "true" : "false");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (node->start.has_value()) {
|
|
81
|
+
json << ",\"start\":" << node->start.value();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (node->checked.has_value()) {
|
|
85
|
+
json << ",\"checked\":" << (node->checked.value() ? "true" : "false");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (node->isHeader.has_value()) {
|
|
89
|
+
json << ",\"isHeader\":" << (node->isHeader.value() ? "true" : "false");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (node->align.has_value()) {
|
|
93
|
+
std::string alignStr = ::NitroMarkdown::textAlignToString(node->align.value());
|
|
94
|
+
if (!alignStr.empty()) {
|
|
95
|
+
json << ",\"align\":\"" << alignStr << "\"";
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (!node->children.empty()) {
|
|
100
|
+
json << ",\"children\":[";
|
|
101
|
+
for (size_t i = 0; i < node->children.size(); ++i) {
|
|
102
|
+
if (i > 0) json << ",";
|
|
103
|
+
json << nodeToJson(node->children[i]);
|
|
104
|
+
}
|
|
105
|
+
json << "]";
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
json << "}";
|
|
109
|
+
return json.str();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
} // namespace margelo::nitro::NitroMarkdown
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "HybridMarkdownParserSpec.hpp"
|
|
4
|
+
#include "../core/MD4CParser.hpp"
|
|
5
|
+
#include <memory>
|
|
6
|
+
|
|
7
|
+
namespace margelo::nitro::NitroMarkdown {
|
|
8
|
+
|
|
9
|
+
using InternalMarkdownNode = ::NitroMarkdown::MarkdownNode;
|
|
10
|
+
using InternalParserOptions = ::NitroMarkdown::ParserOptions;
|
|
11
|
+
|
|
12
|
+
class HybridMarkdownParser : public HybridMarkdownParserSpec {
|
|
13
|
+
public:
|
|
14
|
+
HybridMarkdownParser() : HybridObject(TAG), HybridMarkdownParserSpec() {
|
|
15
|
+
parser_ = std::make_unique<::NitroMarkdown::MD4CParser>();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
~HybridMarkdownParser() override = default;
|
|
19
|
+
|
|
20
|
+
std::string parse(const std::string& text) override;
|
|
21
|
+
std::string parseWithOptions(const std::string& text, const ParserOptions& options) override;
|
|
22
|
+
|
|
23
|
+
private:
|
|
24
|
+
std::unique_ptr<::NitroMarkdown::MD4CParser> parser_;
|
|
25
|
+
std::string nodeToJson(const std::shared_ptr<InternalMarkdownNode>& node);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
} // namespace margelo::nitro::NitroMarkdown
|