react-native-marked 7.1.1 → 8.0.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 +72 -0
- package/dist/commonjs/hooks/useMarkdownWithComponents.js +74 -0
- package/dist/commonjs/hooks/useMarkdownWithComponents.js.map +1 -0
- package/dist/commonjs/index.js +14 -0
- package/dist/commonjs/index.js.map +1 -1
- package/dist/commonjs/lib/ReactComponentRegistry.js +30 -0
- package/dist/commonjs/lib/ReactComponentRegistry.js.map +1 -0
- package/dist/commonjs/lib/ReactComponentTokenizer.js +106 -0
- package/dist/commonjs/lib/ReactComponentTokenizer.js.map +1 -0
- package/dist/module/hooks/useMarkdownWithComponents.js +66 -0
- package/dist/module/hooks/useMarkdownWithComponents.js.map +1 -0
- package/dist/module/index.js +3 -1
- package/dist/module/index.js.map +1 -1
- package/dist/module/lib/ReactComponentRegistry.js +23 -0
- package/dist/module/lib/ReactComponentRegistry.js.map +1 -0
- package/dist/module/lib/ReactComponentTokenizer.js +100 -0
- package/dist/module/lib/ReactComponentTokenizer.js.map +1 -0
- package/dist/typescript/hooks/useMarkdownWithComponents.d.ts +7 -0
- package/dist/typescript/hooks/useMarkdownWithComponents.d.ts.map +1 -0
- package/dist/typescript/index.d.ts +5 -2
- package/dist/typescript/index.d.ts.map +1 -1
- package/dist/typescript/lib/ReactComponentRegistry.d.ts +22 -0
- package/dist/typescript/lib/ReactComponentRegistry.d.ts.map +1 -0
- package/dist/typescript/lib/ReactComponentTokenizer.d.ts +15 -0
- package/dist/typescript/lib/ReactComponentTokenizer.d.ts.map +1 -0
- package/package.json +9 -9
- package/src/hooks/useMarkdownWithComponents.tsx +89 -0
- package/src/index.ts +13 -1
- package/src/lib/ReactComponentRegistry.tsx +52 -0
- package/src/lib/ReactComponentTokenizer.ts +149 -0
package/README.md
CHANGED
|
@@ -112,6 +112,7 @@ const CustomComponent = () => {
|
|
|
112
112
|
- [x] List (ordered, unordered)
|
|
113
113
|
- [x] Horizontal Rule
|
|
114
114
|
- [x] Table
|
|
115
|
+
- [x] React Components (via `useMarkdownWithComponents`)
|
|
115
116
|
- [ ] HTML
|
|
116
117
|
|
|
117
118
|
Ref: [CommonMark](https://commonmark.org/help/)
|
|
@@ -119,6 +120,77 @@ Ref: [CommonMark](https://commonmark.org/help/)
|
|
|
119
120
|
> HTML will be treated as plain text. Please refer [issue#290](https://github.com/gmsgowtham/react-native-marked/issues/290) for a potential solution
|
|
120
121
|
|
|
121
122
|
## Advanced
|
|
123
|
+
|
|
124
|
+
### Embedding React Components in Markdown
|
|
125
|
+
|
|
126
|
+
You can embed React components directly in your markdown using JSX-style syntax. This is useful for adding interactive elements like buttons, custom info boxes, or any other React component.
|
|
127
|
+
|
|
128
|
+
```tsx
|
|
129
|
+
import React, { Fragment } from "react";
|
|
130
|
+
import { Pressable, ScrollView, Text, View } from "react-native";
|
|
131
|
+
import {
|
|
132
|
+
ReactComponentRegistryProvider,
|
|
133
|
+
useMarkdownWithComponents,
|
|
134
|
+
type ReactComponentRegistry,
|
|
135
|
+
} from "react-native-marked";
|
|
136
|
+
|
|
137
|
+
// Define your components
|
|
138
|
+
const components: ReactComponentRegistry = {
|
|
139
|
+
Button: ({ props }) => (
|
|
140
|
+
<Pressable onPress={() => console.log("Pressed!")}>
|
|
141
|
+
<Text>{String(props.label ?? "Click me")}</Text>
|
|
142
|
+
</Pressable>
|
|
143
|
+
),
|
|
144
|
+
InfoBox: ({ props, children }) => (
|
|
145
|
+
<View style={{ backgroundColor: "#E3F2FD", padding: 16 }}>
|
|
146
|
+
{props.title && <Text style={{ fontWeight: "bold" }}>{String(props.title)}</Text>}
|
|
147
|
+
<Text>{children}</Text>
|
|
148
|
+
</View>
|
|
149
|
+
),
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const markdown = `
|
|
153
|
+
# Hello World
|
|
154
|
+
|
|
155
|
+
Click the button below:
|
|
156
|
+
|
|
157
|
+
<Button label="Get Started" />
|
|
158
|
+
|
|
159
|
+
<InfoBox title="Note">
|
|
160
|
+
This is an info box with **markdown** content.
|
|
161
|
+
</InfoBox>
|
|
162
|
+
`;
|
|
163
|
+
|
|
164
|
+
function MarkdownContent() {
|
|
165
|
+
const elements = useMarkdownWithComponents(markdown);
|
|
166
|
+
return (
|
|
167
|
+
<ScrollView>
|
|
168
|
+
{elements.map((element, index) => (
|
|
169
|
+
<Fragment key={index}>{element}</Fragment>
|
|
170
|
+
))}
|
|
171
|
+
</ScrollView>
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export default function App() {
|
|
176
|
+
return (
|
|
177
|
+
<ReactComponentRegistryProvider components={components}>
|
|
178
|
+
<MarkdownContent />
|
|
179
|
+
</ReactComponentRegistryProvider>
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
#### Component Syntax
|
|
185
|
+
|
|
186
|
+
- **Self-closing:** `<ComponentName prop="value" />`
|
|
187
|
+
- **With children:** `<ComponentName>content</ComponentName>`
|
|
188
|
+
- **Props:** Supports string (`"value"`), number (`{42}`), and boolean (`{true}`) props
|
|
189
|
+
|
|
190
|
+
#### Component Registry
|
|
191
|
+
|
|
192
|
+
Components must be registered via `ReactComponentRegistryProvider`. Unregistered components are automatically removed from the output.
|
|
193
|
+
|
|
122
194
|
### Using custom components
|
|
123
195
|
|
|
124
196
|
> Custom components can be used to override elements, i.e. Code Highlighting, Fast Image integration
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
exports.useMarkdownWithComponents = useMarkdownWithComponents;
|
|
8
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
9
|
+
var _ReactComponentRegistry = require("../lib/ReactComponentRegistry");
|
|
10
|
+
var _ReactComponentTokenizer = require("../lib/ReactComponentTokenizer");
|
|
11
|
+
var _Renderer = _interopRequireDefault(require("../lib/Renderer"));
|
|
12
|
+
var _useMarkdown = _interopRequireDefault(require("./useMarkdown"));
|
|
13
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
14
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
15
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
16
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
17
|
+
function useMarkdownWithComponents(value, options) {
|
|
18
|
+
const registry = (0, _ReactComponentRegistry.useReactComponentRegistry)();
|
|
19
|
+
const {
|
|
20
|
+
tokenizer,
|
|
21
|
+
componentMap
|
|
22
|
+
} = (0, _react.useMemo)(() => {
|
|
23
|
+
const map = new Map();
|
|
24
|
+
const customTokenizer = new _ReactComponentTokenizer.ReactComponentTokenizer();
|
|
25
|
+
const originalHtml = customTokenizer.html.bind(customTokenizer);
|
|
26
|
+
let componentCounter = 0;
|
|
27
|
+
customTokenizer.html = src => {
|
|
28
|
+
const token = originalHtml(src);
|
|
29
|
+
if (token && (0, _ReactComponentTokenizer.isReactComponentToken)(token)) {
|
|
30
|
+
const id = `${token.componentName}-${componentCounter++}`;
|
|
31
|
+
map.set(token.raw, {
|
|
32
|
+
token,
|
|
33
|
+
id
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return token;
|
|
37
|
+
};
|
|
38
|
+
return {
|
|
39
|
+
tokenizer: customTokenizer,
|
|
40
|
+
componentMap: map
|
|
41
|
+
};
|
|
42
|
+
}, []);
|
|
43
|
+
const baseRenderer = (0, _react.useMemo)(() => options?.renderer ?? new _Renderer.default(), [options?.renderer]);
|
|
44
|
+
const renderer = (0, _react.useMemo)(() => {
|
|
45
|
+
const wrappedRenderer = Object.create(baseRenderer);
|
|
46
|
+
wrappedRenderer.html = (text, styles) => {
|
|
47
|
+
if (typeof text === "string") {
|
|
48
|
+
const data = componentMap.get(text);
|
|
49
|
+
if (data) {
|
|
50
|
+
if (registry?.hasComponent(data.token.componentName)) {
|
|
51
|
+
const Component = registry.getComponent(data.token.componentName);
|
|
52
|
+
if (Component) {
|
|
53
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(Component, {
|
|
54
|
+
props: data.token.componentProps,
|
|
55
|
+
children: data.token.componentChildren || undefined
|
|
56
|
+
}, data.id);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return baseRenderer.html(text, styles);
|
|
63
|
+
};
|
|
64
|
+
return wrappedRenderer;
|
|
65
|
+
}, [baseRenderer, componentMap, registry]);
|
|
66
|
+
const elements = (0, _useMarkdown.default)(value, {
|
|
67
|
+
...options,
|
|
68
|
+
tokenizer,
|
|
69
|
+
renderer
|
|
70
|
+
});
|
|
71
|
+
return elements;
|
|
72
|
+
}
|
|
73
|
+
var _default = exports.default = useMarkdownWithComponents;
|
|
74
|
+
//# sourceMappingURL=useMarkdownWithComponents.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_react","_interopRequireWildcard","require","_ReactComponentRegistry","_ReactComponentTokenizer","_Renderer","_interopRequireDefault","_useMarkdown","_jsxRuntime","e","__esModule","default","_getRequireWildcardCache","WeakMap","r","t","has","get","n","__proto__","a","Object","defineProperty","getOwnPropertyDescriptor","u","hasOwnProperty","call","i","set","useMarkdownWithComponents","value","options","registry","useReactComponentRegistry","tokenizer","componentMap","useMemo","map","Map","customTokenizer","ReactComponentTokenizer","originalHtml","html","bind","componentCounter","src","token","isReactComponentToken","id","componentName","raw","baseRenderer","renderer","Renderer","wrappedRenderer","create","text","styles","data","hasComponent","Component","getComponent","jsx","props","componentProps","children","componentChildren","undefined","elements","useMarkdown","_default","exports"],"sourceRoot":"../../../src","sources":["hooks/useMarkdownWithComponents.tsx"],"mappings":"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","ignoreList":[]}
|
package/dist/commonjs/index.js
CHANGED
|
@@ -16,6 +16,12 @@ Object.defineProperty(exports, "MarkedTokenizer", {
|
|
|
16
16
|
return _marked.Tokenizer;
|
|
17
17
|
}
|
|
18
18
|
});
|
|
19
|
+
Object.defineProperty(exports, "ReactComponentRegistryProvider", {
|
|
20
|
+
enumerable: true,
|
|
21
|
+
get: function () {
|
|
22
|
+
return _ReactComponentRegistry.ReactComponentRegistryProvider;
|
|
23
|
+
}
|
|
24
|
+
});
|
|
19
25
|
Object.defineProperty(exports, "Renderer", {
|
|
20
26
|
enumerable: true,
|
|
21
27
|
get: function () {
|
|
@@ -29,9 +35,17 @@ Object.defineProperty(exports, "useMarkdown", {
|
|
|
29
35
|
return _useMarkdown.default;
|
|
30
36
|
}
|
|
31
37
|
});
|
|
38
|
+
Object.defineProperty(exports, "useMarkdownWithComponents", {
|
|
39
|
+
enumerable: true,
|
|
40
|
+
get: function () {
|
|
41
|
+
return _useMarkdownWithComponents.default;
|
|
42
|
+
}
|
|
43
|
+
});
|
|
32
44
|
var _marked = require("marked");
|
|
33
45
|
var _useMarkdown = _interopRequireDefault(require("./hooks/useMarkdown"));
|
|
46
|
+
var _useMarkdownWithComponents = _interopRequireDefault(require("./hooks/useMarkdownWithComponents"));
|
|
34
47
|
var _Markdown = _interopRequireDefault(require("./lib/Markdown"));
|
|
48
|
+
var _ReactComponentRegistry = require("./lib/ReactComponentRegistry");
|
|
35
49
|
var _Renderer = _interopRequireDefault(require("./lib/Renderer"));
|
|
36
50
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
37
51
|
const MarkedLexer = exports.MarkedLexer = _marked.marked.lexer;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_marked","require","_useMarkdown","_interopRequireDefault","_Markdown","_Renderer","e","__esModule","default","MarkedLexer","exports","marked","lexer","_default","Markdown"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":"
|
|
1
|
+
{"version":3,"names":["_marked","require","_useMarkdown","_interopRequireDefault","_useMarkdownWithComponents","_Markdown","_ReactComponentRegistry","_Renderer","e","__esModule","default","MarkedLexer","exports","marked","lexer","_default","Markdown"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,IAAAA,OAAA,GAAAC,OAAA;AAKA,IAAAC,YAAA,GAAAC,sBAAA,CAAAF,OAAA;AACA,IAAAG,0BAAA,GAAAD,sBAAA,CAAAF,OAAA;AACA,IAAAI,SAAA,GAAAF,sBAAA,CAAAF,OAAA;AAEA,IAAAK,uBAAA,GAAAL,OAAA;AACA,IAAAM,SAAA,GAAAJ,sBAAA,CAAAF,OAAA;AAAsC,SAAAE,uBAAAK,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAQtC,MAAMG,WAAW,GAAAC,OAAA,CAAAD,WAAA,GAAGE,cAAM,CAACC,KAAK;AAAC,IAAAC,QAAA,GAAAH,OAAA,CAAAF,OAAA,GAuBlBM,iBAAQ","ignoreList":[]}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.ReactComponentRegistryProvider = ReactComponentRegistryProvider;
|
|
7
|
+
exports.useReactComponentRegistry = useReactComponentRegistry;
|
|
8
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
9
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
10
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
11
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
12
|
+
const ReactComponentRegistryContext = /*#__PURE__*/(0, _react.createContext)(null);
|
|
13
|
+
function ReactComponentRegistryProvider({
|
|
14
|
+
components,
|
|
15
|
+
children
|
|
16
|
+
}) {
|
|
17
|
+
const contextValue = (0, _react.useMemo)(() => ({
|
|
18
|
+
components,
|
|
19
|
+
getComponent: name => components[name],
|
|
20
|
+
hasComponent: name => name in components
|
|
21
|
+
}), [components]);
|
|
22
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(ReactComponentRegistryContext.Provider, {
|
|
23
|
+
value: contextValue,
|
|
24
|
+
children: children
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
function useReactComponentRegistry() {
|
|
28
|
+
return (0, _react.useContext)(ReactComponentRegistryContext);
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=ReactComponentRegistry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_react","_interopRequireWildcard","require","_jsxRuntime","_getRequireWildcardCache","e","WeakMap","r","t","__esModule","default","has","get","n","__proto__","a","Object","defineProperty","getOwnPropertyDescriptor","u","hasOwnProperty","call","i","set","ReactComponentRegistryContext","createContext","ReactComponentRegistryProvider","components","children","contextValue","useMemo","getComponent","name","hasComponent","jsx","Provider","value","useReactComponentRegistry","useContext"],"sourceRoot":"../../../src","sources":["lib/ReactComponentRegistry.tsx"],"mappings":";;;;;;;AACA,IAAAA,MAAA,GAAAC,uBAAA,CAAAC,OAAA;AAAkE,IAAAC,WAAA,GAAAD,OAAA;AAAA,SAAAE,yBAAAC,CAAA,6BAAAC,OAAA,mBAAAC,CAAA,OAAAD,OAAA,IAAAE,CAAA,OAAAF,OAAA,YAAAF,wBAAA,YAAAA,CAAAC,CAAA,WAAAA,CAAA,GAAAG,CAAA,GAAAD,CAAA,KAAAF,CAAA;AAAA,SAAAJ,wBAAAI,CAAA,EAAAE,CAAA,SAAAA,CAAA,IAAAF,CAAA,IAAAA,CAAA,CAAAI,UAAA,SAAAJ,CAAA,eAAAA,CAAA,uBAAAA,CAAA,yBAAAA,CAAA,WAAAK,OAAA,EAAAL,CAAA,QAAAG,CAAA,GAAAJ,wBAAA,CAAAG,CAAA,OAAAC,CAAA,IAAAA,CAAA,CAAAG,GAAA,CAAAN,CAAA,UAAAG,CAAA,CAAAI,GAAA,CAAAP,CAAA,OAAAQ,CAAA,KAAAC,SAAA,UAAAC,CAAA,GAAAC,MAAA,CAAAC,cAAA,IAAAD,MAAA,CAAAE,wBAAA,WAAAC,CAAA,IAAAd,CAAA,oBAAAc,CAAA,OAAAC,cAAA,CAAAC,IAAA,CAAAhB,CAAA,EAAAc,CAAA,SAAAG,CAAA,GAAAP,CAAA,GAAAC,MAAA,CAAAE,wBAAA,CAAAb,CAAA,EAAAc,CAAA,UAAAG,CAAA,KAAAA,CAAA,CAAAV,GAAA,IAAAU,CAAA,CAAAC,GAAA,IAAAP,MAAA,CAAAC,cAAA,CAAAJ,CAAA,EAAAM,CAAA,EAAAG,CAAA,IAAAT,CAAA,CAAAM,CAAA,IAAAd,CAAA,CAAAc,CAAA,YAAAN,CAAA,CAAAH,OAAA,GAAAL,CAAA,EAAAG,CAAA,IAAAA,CAAA,CAAAe,GAAA,CAAAlB,CAAA,EAAAQ,CAAA,GAAAA,CAAA;AAoBlE,MAAMW,6BAA6B,gBAClC,IAAAC,oBAAa,EAA4C,IAAI,CAAC;AAOxD,SAASC,8BAA8BA,CAAC;EAC9CC,UAAU;EACVC;AACoC,CAAC,EAAE;EACvC,MAAMC,YAAY,GAAG,IAAAC,cAAO,EAC3B,OAAO;IACNH,UAAU;IACVI,YAAY,EAAGC,IAAY,IAAKL,UAAU,CAACK,IAAI,CAAC;IAChDC,YAAY,EAAGD,IAAY,IAAKA,IAAI,IAAIL;EACzC,CAAC,CAAC,EACF,CAACA,UAAU,CACZ,CAAC;EAED,oBACC,IAAAxB,WAAA,CAAA+B,GAAA,EAACV,6BAA6B,CAACW,QAAQ;IAACC,KAAK,EAAEP,YAAa;IAAAD,QAAA,EAC1DA;EAAQ,CAC8B,CAAC;AAE3C;AAEO,SAASS,yBAAyBA,CAAA,EAA8C;EACtF,OAAO,IAAAC,iBAAU,EAACd,6BAA6B,CAAC;AACjD","ignoreList":[]}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = exports.ReactComponentTokenizer = void 0;
|
|
7
|
+
exports.isReactComponentToken = isReactComponentToken;
|
|
8
|
+
var _marked = require("marked");
|
|
9
|
+
const SELF_CLOSING_REGEX = /^<([A-Z][a-zA-Z0-9]*)([^>]*?)\s*\/>/;
|
|
10
|
+
const OPENING_TAG_REGEX = /^<([A-Z][a-zA-Z0-9]*)([^>]*)>/;
|
|
11
|
+
const PROP_REGEX = /(\w+)(?:=(?:"([^"]*)"|'([^']*)'|\{([^}]*)\}))?/g;
|
|
12
|
+
function parseProps(propsString) {
|
|
13
|
+
const props = {};
|
|
14
|
+
if (!propsString) return props;
|
|
15
|
+
let match;
|
|
16
|
+
match = PROP_REGEX.exec(propsString);
|
|
17
|
+
while (match !== null) {
|
|
18
|
+
const [, propName, doubleQuoted, singleQuoted, braced] = match;
|
|
19
|
+
if (propName) {
|
|
20
|
+
if (doubleQuoted !== undefined) {
|
|
21
|
+
props[propName] = doubleQuoted;
|
|
22
|
+
} else if (singleQuoted !== undefined) {
|
|
23
|
+
props[propName] = singleQuoted;
|
|
24
|
+
} else if (braced !== undefined) {
|
|
25
|
+
const trimmed = braced.trim();
|
|
26
|
+
if (trimmed === "true") {
|
|
27
|
+
props[propName] = true;
|
|
28
|
+
} else if (trimmed === "false") {
|
|
29
|
+
props[propName] = false;
|
|
30
|
+
} else if (!Number.isNaN(Number(trimmed))) {
|
|
31
|
+
props[propName] = Number(trimmed);
|
|
32
|
+
} else {
|
|
33
|
+
props[propName] = trimmed;
|
|
34
|
+
}
|
|
35
|
+
} else {
|
|
36
|
+
props[propName] = true;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
match = PROP_REGEX.exec(propsString);
|
|
40
|
+
}
|
|
41
|
+
PROP_REGEX.lastIndex = 0;
|
|
42
|
+
return props;
|
|
43
|
+
}
|
|
44
|
+
function findClosingTag(src, componentName, startIndex) {
|
|
45
|
+
const openingTag = new RegExp(`<${componentName}(?:\\s[^>]*)?>`, "g");
|
|
46
|
+
const closingTag = `</${componentName}>`;
|
|
47
|
+
let depth = 1;
|
|
48
|
+
let currentIndex = startIndex;
|
|
49
|
+
while (depth > 0 && currentIndex < src.length) {
|
|
50
|
+
const closingIndex = src.indexOf(closingTag, currentIndex);
|
|
51
|
+
if (closingIndex === -1) return null;
|
|
52
|
+
openingTag.lastIndex = currentIndex;
|
|
53
|
+
let nestedMatch;
|
|
54
|
+
nestedMatch = openingTag.exec(src);
|
|
55
|
+
while (nestedMatch !== null && nestedMatch.index < closingIndex) {
|
|
56
|
+
depth++;
|
|
57
|
+
nestedMatch = openingTag.exec(src);
|
|
58
|
+
}
|
|
59
|
+
depth--;
|
|
60
|
+
if (depth === 0) {
|
|
61
|
+
return {
|
|
62
|
+
endIndex: closingIndex + closingTag.length,
|
|
63
|
+
children: src.slice(startIndex, closingIndex)
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
currentIndex = closingIndex + closingTag.length;
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
class ReactComponentTokenizer extends _marked.Tokenizer {
|
|
71
|
+
html(src) {
|
|
72
|
+
const selfClosingMatch = SELF_CLOSING_REGEX.exec(src);
|
|
73
|
+
if (selfClosingMatch) {
|
|
74
|
+
const [raw, componentName, propsString] = selfClosingMatch;
|
|
75
|
+
return this.createReactComponentToken(raw, componentName ?? "", propsString ?? "", "");
|
|
76
|
+
}
|
|
77
|
+
const openingMatch = OPENING_TAG_REGEX.exec(src);
|
|
78
|
+
if (openingMatch) {
|
|
79
|
+
const [openingTag, componentName, propsString] = openingMatch;
|
|
80
|
+
const result = findClosingTag(src, componentName ?? "", openingTag?.length ?? 0);
|
|
81
|
+
if (result) {
|
|
82
|
+
const raw = src.slice(0, result.endIndex);
|
|
83
|
+
return this.createReactComponentToken(raw, componentName ?? "", propsString ?? "", result.children);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return super.html(src);
|
|
87
|
+
}
|
|
88
|
+
createReactComponentToken(raw, componentName, propsString, children) {
|
|
89
|
+
return {
|
|
90
|
+
type: "html",
|
|
91
|
+
raw,
|
|
92
|
+
text: raw,
|
|
93
|
+
block: true,
|
|
94
|
+
pre: false,
|
|
95
|
+
componentName,
|
|
96
|
+
componentProps: parseProps(propsString),
|
|
97
|
+
componentChildren: children.trim()
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
exports.ReactComponentTokenizer = ReactComponentTokenizer;
|
|
102
|
+
function isReactComponentToken(token) {
|
|
103
|
+
return "componentName" in token && typeof token.componentName === "string";
|
|
104
|
+
}
|
|
105
|
+
var _default = exports.default = ReactComponentTokenizer;
|
|
106
|
+
//# sourceMappingURL=ReactComponentTokenizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_marked","require","SELF_CLOSING_REGEX","OPENING_TAG_REGEX","PROP_REGEX","parseProps","propsString","props","match","exec","propName","doubleQuoted","singleQuoted","braced","undefined","trimmed","trim","Number","isNaN","lastIndex","findClosingTag","src","componentName","startIndex","openingTag","RegExp","closingTag","depth","currentIndex","length","closingIndex","indexOf","nestedMatch","index","endIndex","children","slice","ReactComponentTokenizer","Tokenizer","html","selfClosingMatch","raw","createReactComponentToken","openingMatch","result","type","text","block","pre","componentProps","componentChildren","exports","isReactComponentToken","token","_default","default"],"sourceRoot":"../../../src","sources":["lib/ReactComponentTokenizer.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import React, { useMemo } from "react";
|
|
4
|
+
import { useReactComponentRegistry } from "../lib/ReactComponentRegistry";
|
|
5
|
+
import { isReactComponentToken, ReactComponentTokenizer } from "../lib/ReactComponentTokenizer";
|
|
6
|
+
import Renderer from "../lib/Renderer";
|
|
7
|
+
import useMarkdown from "./useMarkdown";
|
|
8
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
9
|
+
export function useMarkdownWithComponents(value, options) {
|
|
10
|
+
const registry = useReactComponentRegistry();
|
|
11
|
+
const {
|
|
12
|
+
tokenizer,
|
|
13
|
+
componentMap
|
|
14
|
+
} = useMemo(() => {
|
|
15
|
+
const map = new Map();
|
|
16
|
+
const customTokenizer = new ReactComponentTokenizer();
|
|
17
|
+
const originalHtml = customTokenizer.html.bind(customTokenizer);
|
|
18
|
+
let componentCounter = 0;
|
|
19
|
+
customTokenizer.html = src => {
|
|
20
|
+
const token = originalHtml(src);
|
|
21
|
+
if (token && isReactComponentToken(token)) {
|
|
22
|
+
const id = `${token.componentName}-${componentCounter++}`;
|
|
23
|
+
map.set(token.raw, {
|
|
24
|
+
token,
|
|
25
|
+
id
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
return token;
|
|
29
|
+
};
|
|
30
|
+
return {
|
|
31
|
+
tokenizer: customTokenizer,
|
|
32
|
+
componentMap: map
|
|
33
|
+
};
|
|
34
|
+
}, []);
|
|
35
|
+
const baseRenderer = useMemo(() => options?.renderer ?? new Renderer(), [options?.renderer]);
|
|
36
|
+
const renderer = useMemo(() => {
|
|
37
|
+
const wrappedRenderer = Object.create(baseRenderer);
|
|
38
|
+
wrappedRenderer.html = (text, styles) => {
|
|
39
|
+
if (typeof text === "string") {
|
|
40
|
+
const data = componentMap.get(text);
|
|
41
|
+
if (data) {
|
|
42
|
+
if (registry?.hasComponent(data.token.componentName)) {
|
|
43
|
+
const Component = registry.getComponent(data.token.componentName);
|
|
44
|
+
if (Component) {
|
|
45
|
+
return /*#__PURE__*/_jsx(Component, {
|
|
46
|
+
props: data.token.componentProps,
|
|
47
|
+
children: data.token.componentChildren || undefined
|
|
48
|
+
}, data.id);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return baseRenderer.html(text, styles);
|
|
55
|
+
};
|
|
56
|
+
return wrappedRenderer;
|
|
57
|
+
}, [baseRenderer, componentMap, registry]);
|
|
58
|
+
const elements = useMarkdown(value, {
|
|
59
|
+
...options,
|
|
60
|
+
tokenizer,
|
|
61
|
+
renderer
|
|
62
|
+
});
|
|
63
|
+
return elements;
|
|
64
|
+
}
|
|
65
|
+
export default useMarkdownWithComponents;
|
|
66
|
+
//# sourceMappingURL=useMarkdownWithComponents.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["React","useMemo","useReactComponentRegistry","isReactComponentToken","ReactComponentTokenizer","Renderer","useMarkdown","jsx","_jsx","useMarkdownWithComponents","value","options","registry","tokenizer","componentMap","map","Map","customTokenizer","originalHtml","html","bind","componentCounter","src","token","id","componentName","set","raw","baseRenderer","renderer","wrappedRenderer","Object","create","text","styles","data","get","hasComponent","Component","getComponent","props","componentProps","children","componentChildren","undefined","elements"],"sourceRoot":"../../../src","sources":["hooks/useMarkdownWithComponents.tsx"],"mappings":";;AACA,OAAOA,KAAK,IAAIC,OAAO,QAAQ,OAAO;AACtC,SAASC,yBAAyB,QAAQ,+BAA+B;AAEzE,SACCC,qBAAqB,EACrBC,uBAAuB,QACjB,gCAAgC;AACvC,OAAOC,QAAQ,MAAM,iBAAiB;AAGtC,OAAOC,WAAW,MAAM,eAAe;AAAC,SAAAC,GAAA,IAAAC,IAAA;AAUxC,OAAO,SAASC,yBAAyBA,CACxCC,KAAa,EACbC,OAA0C,EAC5B;EACd,MAAMC,QAAQ,GAAGV,yBAAyB,CAAC,CAAC;EAE5C,MAAM;IAAEW,SAAS;IAAEC;EAAa,CAAC,GAAGb,OAAO,CAAC,MAAM;IACjD,MAAMc,GAAG,GAAG,IAAIC,GAAG,CAAwB,CAAC;IAE5C,MAAMC,eAAe,GAAG,IAAIb,uBAAuB,CAAC,CAAC;IACrD,MAAMc,YAAY,GAAGD,eAAe,CAACE,IAAI,CAACC,IAAI,CAACH,eAAe,CAAC;IAE/D,IAAII,gBAAgB,GAAG,CAAC;IAExBJ,eAAe,CAACE,IAAI,GAAIG,GAAW,IAAK;MACvC,MAAMC,KAAK,GAAGL,YAAY,CAACI,GAAG,CAAC;MAC/B,IAAIC,KAAK,IAAIpB,qBAAqB,CAACoB,KAAK,CAAC,EAAE;QAC1C,MAAMC,EAAE,GAAG,GAAGD,KAAK,CAACE,aAAa,IAAIJ,gBAAgB,EAAE,EAAE;QACzDN,GAAG,CAACW,GAAG,CAACH,KAAK,CAACI,GAAG,EAAE;UAAEJ,KAAK;UAAEC;QAAG,CAAC,CAAC;MAClC;MACA,OAAOD,KAAK;IACb,CAAC;IAED,OAAO;MAAEV,SAAS,EAAEI,eAAe;MAAEH,YAAY,EAAEC;IAAI,CAAC;EACzD,CAAC,EAAE,EAAE,CAAC;EAEN,MAAMa,YAAY,GAAG3B,OAAO,CAC3B,MAAMU,OAAO,EAAEkB,QAAQ,IAAI,IAAIxB,QAAQ,CAAC,CAAC,EACzC,CAACM,OAAO,EAAEkB,QAAQ,CACnB,CAAC;EAED,MAAMA,QAAQ,GAAG5B,OAAO,CAAoB,MAAM;IACjD,MAAM6B,eAAe,GAAGC,MAAM,CAACC,MAAM,CAACJ,YAAY,CAAwB;IAE1EE,eAAe,CAACX,IAAI,GAAG,CAACc,IAA0B,EAAEC,MAAM,KAAgB;MACzE,IAAI,OAAOD,IAAI,KAAK,QAAQ,EAAE;QAC7B,MAAME,IAAI,GAAGrB,YAAY,CAACsB,GAAG,CAACH,IAAI,CAAC;QACnC,IAAIE,IAAI,EAAE;UACT,IAAIvB,QAAQ,EAAEyB,YAAY,CAACF,IAAI,CAACZ,KAAK,CAACE,aAAa,CAAC,EAAE;YACrD,MAAMa,SAAS,GAAG1B,QAAQ,CAAC2B,YAAY,CAACJ,IAAI,CAACZ,KAAK,CAACE,aAAa,CAAC;YACjE,IAAIa,SAAS,EAAE;cACd,oBACC9B,IAAA,CAAC8B,SAAS;gBAAeE,KAAK,EAAEL,IAAI,CAACZ,KAAK,CAACkB,cAAe;gBAAAC,QAAA,EACxDP,IAAI,CAACZ,KAAK,CAACoB,iBAAiB,IAAIC;cAAS,GAD3BT,IAAI,CAACX,EAEV,CAAC;YAEd;UACD;UACA,OAAO,IAAI;QACZ;MACD;MAEA,OAAOI,YAAY,CAACT,IAAI,CAACc,IAAI,EAAEC,MAAM,CAAC;IACvC,CAAC;IAED,OAAOJ,eAAe;EACvB,CAAC,EAAE,CAACF,YAAY,EAAEd,YAAY,EAAEF,QAAQ,CAAC,CAAC;EAE1C,MAAMiC,QAAQ,GAAGvC,WAAW,CAACI,KAAK,EAAE;IACnC,GAAGC,OAAO;IACVE,SAAS;IACTgB;EACD,CAAC,CAAC;EAEF,OAAOgB,QAAQ;AAChB;AAEA,eAAepC,yBAAyB","ignoreList":[]}
|
package/dist/module/index.js
CHANGED
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
import { Hooks as MarkedHooks, Tokenizer as MarkedTokenizer, marked } from "marked";
|
|
4
4
|
import useMarkdown from "./hooks/useMarkdown";
|
|
5
|
+
import useMarkdownWithComponents from "./hooks/useMarkdownWithComponents";
|
|
5
6
|
import Markdown from "./lib/Markdown";
|
|
7
|
+
import { ReactComponentRegistryProvider } from "./lib/ReactComponentRegistry";
|
|
6
8
|
import Renderer from "./lib/Renderer";
|
|
7
9
|
const MarkedLexer = marked.lexer;
|
|
8
|
-
export { useMarkdown, MarkedLexer, Renderer, MarkedTokenizer, MarkedHooks };
|
|
10
|
+
export { useMarkdown, useMarkdownWithComponents, MarkedLexer, Renderer, MarkedTokenizer, MarkedHooks, ReactComponentRegistryProvider };
|
|
9
11
|
export default Markdown;
|
|
10
12
|
//# sourceMappingURL=index.js.map
|
package/dist/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["Hooks","MarkedHooks","Tokenizer","MarkedTokenizer","marked","useMarkdown","Markdown","Renderer","MarkedLexer","lexer"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;AACA,SACCA,KAAK,IAAIC,WAAW,EACpBC,SAAS,IAAIC,eAAe,EAC5BC,MAAM,QACA,QAAQ;AACf,OAAOC,WAAW,MAAuC,qBAAqB;AAC9E,OAAOC,QAAQ,MAAM,gBAAgB;
|
|
1
|
+
{"version":3,"names":["Hooks","MarkedHooks","Tokenizer","MarkedTokenizer","marked","useMarkdown","useMarkdownWithComponents","Markdown","ReactComponentRegistryProvider","Renderer","MarkedLexer","lexer"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;AACA,SACCA,KAAK,IAAIC,WAAW,EACpBC,SAAS,IAAIC,eAAe,EAC5BC,MAAM,QACA,QAAQ;AACf,OAAOC,WAAW,MAAuC,qBAAqB;AAC9E,OAAOC,yBAAyB,MAAM,mCAAmC;AACzE,OAAOC,QAAQ,MAAM,gBAAgB;AAErC,SAASC,8BAA8B,QAAQ,8BAA8B;AAC7E,OAAOC,QAAQ,MAAM,gBAAgB;AAQrC,MAAMC,WAAW,GAAGN,MAAM,CAACO,KAAK;AAahC,SACCN,WAAW,EACXC,yBAAyB,EACzBI,WAAW,EACXD,QAAQ,EACRN,eAAe,EACfF,WAAW,EACXO,8BAA8B;AAG/B,eAAeD,QAAQ","ignoreList":[]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import React, { createContext, useContext, useMemo } from "react";
|
|
4
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
5
|
+
const ReactComponentRegistryContext = /*#__PURE__*/createContext(null);
|
|
6
|
+
export function ReactComponentRegistryProvider({
|
|
7
|
+
components,
|
|
8
|
+
children
|
|
9
|
+
}) {
|
|
10
|
+
const contextValue = useMemo(() => ({
|
|
11
|
+
components,
|
|
12
|
+
getComponent: name => components[name],
|
|
13
|
+
hasComponent: name => name in components
|
|
14
|
+
}), [components]);
|
|
15
|
+
return /*#__PURE__*/_jsx(ReactComponentRegistryContext.Provider, {
|
|
16
|
+
value: contextValue,
|
|
17
|
+
children: children
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
export function useReactComponentRegistry() {
|
|
21
|
+
return useContext(ReactComponentRegistryContext);
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=ReactComponentRegistry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["React","createContext","useContext","useMemo","jsx","_jsx","ReactComponentRegistryContext","ReactComponentRegistryProvider","components","children","contextValue","getComponent","name","hasComponent","Provider","value","useReactComponentRegistry"],"sourceRoot":"../../../src","sources":["lib/ReactComponentRegistry.tsx"],"mappings":";;AACA,OAAOA,KAAK,IAAIC,aAAa,EAAEC,UAAU,EAAEC,OAAO,QAAQ,OAAO;AAAC,SAAAC,GAAA,IAAAC,IAAA;AAoBlE,MAAMC,6BAA6B,gBAClCL,aAAa,CAA4C,IAAI,CAAC;AAO/D,OAAO,SAASM,8BAA8BA,CAAC;EAC9CC,UAAU;EACVC;AACoC,CAAC,EAAE;EACvC,MAAMC,YAAY,GAAGP,OAAO,CAC3B,OAAO;IACNK,UAAU;IACVG,YAAY,EAAGC,IAAY,IAAKJ,UAAU,CAACI,IAAI,CAAC;IAChDC,YAAY,EAAGD,IAAY,IAAKA,IAAI,IAAIJ;EACzC,CAAC,CAAC,EACF,CAACA,UAAU,CACZ,CAAC;EAED,oBACCH,IAAA,CAACC,6BAA6B,CAACQ,QAAQ;IAACC,KAAK,EAAEL,YAAa;IAAAD,QAAA,EAC1DA;EAAQ,CAC8B,CAAC;AAE3C;AAEA,OAAO,SAASO,yBAAyBA,CAAA,EAA8C;EACtF,OAAOd,UAAU,CAACI,6BAA6B,CAAC;AACjD","ignoreList":[]}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { Tokenizer } from "marked";
|
|
4
|
+
const SELF_CLOSING_REGEX = /^<([A-Z][a-zA-Z0-9]*)([^>]*?)\s*\/>/;
|
|
5
|
+
const OPENING_TAG_REGEX = /^<([A-Z][a-zA-Z0-9]*)([^>]*)>/;
|
|
6
|
+
const PROP_REGEX = /(\w+)(?:=(?:"([^"]*)"|'([^']*)'|\{([^}]*)\}))?/g;
|
|
7
|
+
function parseProps(propsString) {
|
|
8
|
+
const props = {};
|
|
9
|
+
if (!propsString) return props;
|
|
10
|
+
let match;
|
|
11
|
+
match = PROP_REGEX.exec(propsString);
|
|
12
|
+
while (match !== null) {
|
|
13
|
+
const [, propName, doubleQuoted, singleQuoted, braced] = match;
|
|
14
|
+
if (propName) {
|
|
15
|
+
if (doubleQuoted !== undefined) {
|
|
16
|
+
props[propName] = doubleQuoted;
|
|
17
|
+
} else if (singleQuoted !== undefined) {
|
|
18
|
+
props[propName] = singleQuoted;
|
|
19
|
+
} else if (braced !== undefined) {
|
|
20
|
+
const trimmed = braced.trim();
|
|
21
|
+
if (trimmed === "true") {
|
|
22
|
+
props[propName] = true;
|
|
23
|
+
} else if (trimmed === "false") {
|
|
24
|
+
props[propName] = false;
|
|
25
|
+
} else if (!Number.isNaN(Number(trimmed))) {
|
|
26
|
+
props[propName] = Number(trimmed);
|
|
27
|
+
} else {
|
|
28
|
+
props[propName] = trimmed;
|
|
29
|
+
}
|
|
30
|
+
} else {
|
|
31
|
+
props[propName] = true;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
match = PROP_REGEX.exec(propsString);
|
|
35
|
+
}
|
|
36
|
+
PROP_REGEX.lastIndex = 0;
|
|
37
|
+
return props;
|
|
38
|
+
}
|
|
39
|
+
function findClosingTag(src, componentName, startIndex) {
|
|
40
|
+
const openingTag = new RegExp(`<${componentName}(?:\\s[^>]*)?>`, "g");
|
|
41
|
+
const closingTag = `</${componentName}>`;
|
|
42
|
+
let depth = 1;
|
|
43
|
+
let currentIndex = startIndex;
|
|
44
|
+
while (depth > 0 && currentIndex < src.length) {
|
|
45
|
+
const closingIndex = src.indexOf(closingTag, currentIndex);
|
|
46
|
+
if (closingIndex === -1) return null;
|
|
47
|
+
openingTag.lastIndex = currentIndex;
|
|
48
|
+
let nestedMatch;
|
|
49
|
+
nestedMatch = openingTag.exec(src);
|
|
50
|
+
while (nestedMatch !== null && nestedMatch.index < closingIndex) {
|
|
51
|
+
depth++;
|
|
52
|
+
nestedMatch = openingTag.exec(src);
|
|
53
|
+
}
|
|
54
|
+
depth--;
|
|
55
|
+
if (depth === 0) {
|
|
56
|
+
return {
|
|
57
|
+
endIndex: closingIndex + closingTag.length,
|
|
58
|
+
children: src.slice(startIndex, closingIndex)
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
currentIndex = closingIndex + closingTag.length;
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
export class ReactComponentTokenizer extends Tokenizer {
|
|
66
|
+
html(src) {
|
|
67
|
+
const selfClosingMatch = SELF_CLOSING_REGEX.exec(src);
|
|
68
|
+
if (selfClosingMatch) {
|
|
69
|
+
const [raw, componentName, propsString] = selfClosingMatch;
|
|
70
|
+
return this.createReactComponentToken(raw, componentName ?? "", propsString ?? "", "");
|
|
71
|
+
}
|
|
72
|
+
const openingMatch = OPENING_TAG_REGEX.exec(src);
|
|
73
|
+
if (openingMatch) {
|
|
74
|
+
const [openingTag, componentName, propsString] = openingMatch;
|
|
75
|
+
const result = findClosingTag(src, componentName ?? "", openingTag?.length ?? 0);
|
|
76
|
+
if (result) {
|
|
77
|
+
const raw = src.slice(0, result.endIndex);
|
|
78
|
+
return this.createReactComponentToken(raw, componentName ?? "", propsString ?? "", result.children);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return super.html(src);
|
|
82
|
+
}
|
|
83
|
+
createReactComponentToken(raw, componentName, propsString, children) {
|
|
84
|
+
return {
|
|
85
|
+
type: "html",
|
|
86
|
+
raw,
|
|
87
|
+
text: raw,
|
|
88
|
+
block: true,
|
|
89
|
+
pre: false,
|
|
90
|
+
componentName,
|
|
91
|
+
componentProps: parseProps(propsString),
|
|
92
|
+
componentChildren: children.trim()
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
export function isReactComponentToken(token) {
|
|
97
|
+
return "componentName" in token && typeof token.componentName === "string";
|
|
98
|
+
}
|
|
99
|
+
export default ReactComponentTokenizer;
|
|
100
|
+
//# sourceMappingURL=ReactComponentTokenizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["Tokenizer","SELF_CLOSING_REGEX","OPENING_TAG_REGEX","PROP_REGEX","parseProps","propsString","props","match","exec","propName","doubleQuoted","singleQuoted","braced","undefined","trimmed","trim","Number","isNaN","lastIndex","findClosingTag","src","componentName","startIndex","openingTag","RegExp","closingTag","depth","currentIndex","length","closingIndex","indexOf","nestedMatch","index","endIndex","children","slice","ReactComponentTokenizer","html","selfClosingMatch","raw","createReactComponentToken","openingMatch","result","type","text","block","pre","componentProps","componentChildren","isReactComponentToken","token"],"sourceRoot":"../../../src","sources":["lib/ReactComponentTokenizer.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { useMarkdownHookOptions } from "./useMarkdown";
|
|
3
|
+
export interface useMarkdownWithComponentsOptions extends Omit<useMarkdownHookOptions, "tokenizer"> {
|
|
4
|
+
}
|
|
5
|
+
export declare function useMarkdownWithComponents(value: string, options?: useMarkdownWithComponentsOptions): ReactNode[];
|
|
6
|
+
export default useMarkdownWithComponents;
|
|
7
|
+
//# sourceMappingURL=useMarkdownWithComponents.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useMarkdownWithComponents.d.ts","sourceRoot":"","sources":["../../../src/hooks/useMarkdownWithComponents.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAUvC,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAG5D,MAAM,WAAW,gCAChB,SAAQ,IAAI,CAAC,sBAAsB,EAAE,WAAW,CAAC;CAAG;AAOrD,wBAAgB,yBAAyB,CACxC,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,gCAAgC,GACxC,SAAS,EAAE,CA8Db;AAED,eAAe,yBAAyB,CAAC"}
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import type { Token, Tokens } from "marked";
|
|
2
2
|
import { Hooks as MarkedHooks, Tokenizer as MarkedTokenizer } from "marked";
|
|
3
3
|
import useMarkdown, { type useMarkdownHookOptions } from "./hooks/useMarkdown";
|
|
4
|
+
import useMarkdownWithComponents from "./hooks/useMarkdownWithComponents";
|
|
4
5
|
import Markdown from "./lib/Markdown";
|
|
6
|
+
import type { ReactComponentRegistry } from "./lib/ReactComponentRegistry";
|
|
7
|
+
import { ReactComponentRegistryProvider } from "./lib/ReactComponentRegistry";
|
|
5
8
|
import Renderer from "./lib/Renderer";
|
|
6
9
|
import type { MarkdownProps, ParserOptions, RendererInterface } from "./lib/types";
|
|
7
10
|
import type { MarkedStyles } from "./theme/types";
|
|
8
11
|
declare const MarkedLexer: typeof import("marked").Lexer.lex;
|
|
9
|
-
export type { MarkdownProps, MarkedStyles, ParserOptions, RendererInterface, useMarkdownHookOptions, Token, Tokens, };
|
|
10
|
-
export { useMarkdown, MarkedLexer, Renderer, MarkedTokenizer, MarkedHooks };
|
|
12
|
+
export type { MarkdownProps, MarkedStyles, ParserOptions, RendererInterface, useMarkdownHookOptions, Token, Tokens, ReactComponentRegistry, };
|
|
13
|
+
export { useMarkdown, useMarkdownWithComponents, MarkedLexer, Renderer, MarkedTokenizer, MarkedHooks, ReactComponentRegistryProvider, };
|
|
11
14
|
export default Markdown;
|
|
12
15
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC5C,OAAO,EACN,KAAK,IAAI,WAAW,EACpB,SAAS,IAAI,eAAe,EAE5B,MAAM,QAAQ,CAAC;AAChB,OAAO,WAAW,EAAE,EAAE,KAAK,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC/E,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,KAAK,EACX,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,QAAA,MAAM,WAAW,mCAAe,CAAC;AAEjC,YAAY,EACX,aAAa,EACb,YAAY,EACZ,aAAa,EACb,iBAAiB,EACjB,sBAAsB,EACtB,KAAK,EACL,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC5C,OAAO,EACN,KAAK,IAAI,WAAW,EACpB,SAAS,IAAI,eAAe,EAE5B,MAAM,QAAQ,CAAC;AAChB,OAAO,WAAW,EAAE,EAAE,KAAK,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC/E,OAAO,yBAAyB,MAAM,mCAAmC,CAAC;AAC1E,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAC3E,OAAO,EAAE,8BAA8B,EAAE,MAAM,8BAA8B,CAAC;AAC9E,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,KAAK,EACX,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,QAAA,MAAM,WAAW,mCAAe,CAAC;AAEjC,YAAY,EACX,aAAa,EACb,YAAY,EACZ,aAAa,EACb,iBAAiB,EACjB,sBAAsB,EACtB,KAAK,EACL,MAAM,EACN,sBAAsB,GACtB,CAAC;AAEF,OAAO,EACN,WAAW,EACX,yBAAyB,EACzB,WAAW,EACX,QAAQ,EACR,eAAe,EACf,WAAW,EACX,8BAA8B,GAC9B,CAAC;AAEF,eAAe,QAAQ,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import type { ReactComponentProps } from "./ReactComponentTokenizer";
|
|
4
|
+
export interface ReactComponentRendererProps {
|
|
5
|
+
props: ReactComponentProps;
|
|
6
|
+
children?: ReactNode;
|
|
7
|
+
}
|
|
8
|
+
export type ReactComponentRenderer = (props: ReactComponentRendererProps) => ReactNode;
|
|
9
|
+
export type ReactComponentRegistry = Record<string, ReactComponentRenderer>;
|
|
10
|
+
interface ReactComponentRegistryContextValue {
|
|
11
|
+
components: ReactComponentRegistry;
|
|
12
|
+
getComponent: (name: string) => ReactComponentRenderer | undefined;
|
|
13
|
+
hasComponent: (name: string) => boolean;
|
|
14
|
+
}
|
|
15
|
+
interface ReactComponentRegistryProviderProps {
|
|
16
|
+
components: ReactComponentRegistry;
|
|
17
|
+
children: ReactNode;
|
|
18
|
+
}
|
|
19
|
+
export declare function ReactComponentRegistryProvider({ components, children, }: ReactComponentRegistryProviderProps): React.JSX.Element;
|
|
20
|
+
export declare function useReactComponentRegistry(): ReactComponentRegistryContextValue | null;
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=ReactComponentRegistry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReactComponentRegistry.d.ts","sourceRoot":"","sources":["../../../src/lib/ReactComponentRegistry.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAA6C,MAAM,OAAO,CAAC;AAClE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAErE,MAAM,WAAW,2BAA2B;IAC3C,KAAK,EAAE,mBAAmB,CAAC;IAC3B,QAAQ,CAAC,EAAE,SAAS,CAAC;CACrB;AAED,MAAM,MAAM,sBAAsB,GAAG,CACpC,KAAK,EAAE,2BAA2B,KAC9B,SAAS,CAAC;AAEf,MAAM,MAAM,sBAAsB,GAAG,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;AAE5E,UAAU,kCAAkC;IAC3C,UAAU,EAAE,sBAAsB,CAAC;IACnC,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,sBAAsB,GAAG,SAAS,CAAC;IACnE,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CACxC;AAKD,UAAU,mCAAmC;IAC5C,UAAU,EAAE,sBAAsB,CAAC;IACnC,QAAQ,EAAE,SAAS,CAAC;CACpB;AAED,wBAAgB,8BAA8B,CAAC,EAC9C,UAAU,EACV,QAAQ,GACR,EAAE,mCAAmC,qBAerC;AAED,wBAAgB,yBAAyB,IAAI,kCAAkC,GAAG,IAAI,CAErF"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Tokens } from "marked";
|
|
2
|
+
import { Tokenizer } from "marked";
|
|
3
|
+
export type ReactComponentProps = Record<string, string | boolean | number>;
|
|
4
|
+
export interface ReactComponentToken extends Tokens.HTML {
|
|
5
|
+
componentName: string;
|
|
6
|
+
componentProps: ReactComponentProps;
|
|
7
|
+
componentChildren: string;
|
|
8
|
+
}
|
|
9
|
+
export declare class ReactComponentTokenizer extends Tokenizer {
|
|
10
|
+
html(src: string): Tokens.HTML | undefined;
|
|
11
|
+
private createReactComponentToken;
|
|
12
|
+
}
|
|
13
|
+
export declare function isReactComponentToken(token: Tokens.HTML): token is ReactComponentToken;
|
|
14
|
+
export default ReactComponentTokenizer;
|
|
15
|
+
//# sourceMappingURL=ReactComponentTokenizer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReactComponentTokenizer.d.ts","sourceRoot":"","sources":["../../../src/lib/ReactComponentTokenizer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAMnC,MAAM,MAAM,mBAAmB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC,CAAC;AAE5E,MAAM,WAAW,mBAAoB,SAAQ,MAAM,CAAC,IAAI;IACvD,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,mBAAmB,CAAC;IACpC,iBAAiB,EAAE,MAAM,CAAC;CAC1B;AA0ED,qBAAa,uBAAwB,SAAQ,SAAS;IAC5C,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,IAAI,GAAG,SAAS;IAmCnD,OAAO,CAAC,yBAAyB;CAiBjC;AAED,wBAAgB,qBAAqB,CACpC,KAAK,EAAE,MAAM,CAAC,IAAI,GAChB,KAAK,IAAI,mBAAmB,CAE9B;AAED,eAAe,uBAAuB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-marked",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "8.0.0",
|
|
4
4
|
"description": "Markdown renderer for React Native powered by marked.js",
|
|
5
5
|
"main": "dist/commonjs/index",
|
|
6
6
|
"module": "dist/module/index",
|
|
@@ -54,14 +54,14 @@
|
|
|
54
54
|
"@babel/core": "7.28.5",
|
|
55
55
|
"@babel/helper-explode-assignable-expression": "7.18.6",
|
|
56
56
|
"@babel/preset-env": "7.28.5",
|
|
57
|
-
"@biomejs/biome": "2.3.
|
|
57
|
+
"@biomejs/biome": "2.3.10",
|
|
58
58
|
"@commitlint/config-conventional": "20.2.0",
|
|
59
|
-
"@evilmartians/lefthook": "2.0.
|
|
59
|
+
"@evilmartians/lefthook": "2.0.13",
|
|
60
60
|
"@react-native/babel-preset": "0.78.2",
|
|
61
|
-
"@release-it/conventional-changelog": "10.0.
|
|
61
|
+
"@release-it/conventional-changelog": "10.0.4",
|
|
62
62
|
"@testing-library/react-native": "v14.0.0-alpha.1",
|
|
63
63
|
"@types/jest": "30.0.0",
|
|
64
|
-
"@types/node": "24.10.
|
|
64
|
+
"@types/node": "24.10.4",
|
|
65
65
|
"@types/react": "19.2.7",
|
|
66
66
|
"@types/svg-parser": "^2.0.3",
|
|
67
67
|
"commitlint": "20.2.0",
|
|
@@ -71,12 +71,12 @@
|
|
|
71
71
|
"jest-environment-jsdom": "29.7.0",
|
|
72
72
|
"pod-install": "1.0.9",
|
|
73
73
|
"postinstall-postinstall": "2.1.0",
|
|
74
|
-
"react": "19.2.
|
|
74
|
+
"react": "19.2.3",
|
|
75
75
|
"react-native": "0.78.2",
|
|
76
|
-
"react-native-builder-bob": "0.40.
|
|
76
|
+
"react-native-builder-bob": "0.40.17",
|
|
77
77
|
"react-native-svg": "15.15.1",
|
|
78
78
|
"reassure": "1.4.0",
|
|
79
|
-
"release-it": "19.
|
|
79
|
+
"release-it": "19.2.2",
|
|
80
80
|
"typescript": "5.9.3",
|
|
81
81
|
"universal-test-renderer": "0.6.0"
|
|
82
82
|
},
|
|
@@ -170,6 +170,6 @@
|
|
|
170
170
|
},
|
|
171
171
|
"resolutions": {
|
|
172
172
|
"@types/react": "19.2.7",
|
|
173
|
-
"**/pretty-format/react-is": "19.2.
|
|
173
|
+
"**/pretty-format/react-is": "19.2.3"
|
|
174
174
|
}
|
|
175
175
|
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import React, { useMemo } from "react";
|
|
3
|
+
import { useReactComponentRegistry } from "../lib/ReactComponentRegistry";
|
|
4
|
+
import type { ReactComponentToken } from "../lib/ReactComponentTokenizer";
|
|
5
|
+
import {
|
|
6
|
+
isReactComponentToken,
|
|
7
|
+
ReactComponentTokenizer,
|
|
8
|
+
} from "../lib/ReactComponentTokenizer";
|
|
9
|
+
import Renderer from "../lib/Renderer";
|
|
10
|
+
import type { RendererInterface } from "../lib/types";
|
|
11
|
+
import type { useMarkdownHookOptions } from "./useMarkdown";
|
|
12
|
+
import useMarkdown from "./useMarkdown";
|
|
13
|
+
|
|
14
|
+
export interface useMarkdownWithComponentsOptions
|
|
15
|
+
extends Omit<useMarkdownHookOptions, "tokenizer"> {}
|
|
16
|
+
|
|
17
|
+
interface ComponentData {
|
|
18
|
+
token: ReactComponentToken;
|
|
19
|
+
id: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function useMarkdownWithComponents(
|
|
23
|
+
value: string,
|
|
24
|
+
options?: useMarkdownWithComponentsOptions,
|
|
25
|
+
): ReactNode[] {
|
|
26
|
+
const registry = useReactComponentRegistry();
|
|
27
|
+
|
|
28
|
+
const { tokenizer, componentMap } = useMemo(() => {
|
|
29
|
+
const map = new Map<string, ComponentData>();
|
|
30
|
+
|
|
31
|
+
const customTokenizer = new ReactComponentTokenizer();
|
|
32
|
+
const originalHtml = customTokenizer.html.bind(customTokenizer);
|
|
33
|
+
|
|
34
|
+
let componentCounter = 0;
|
|
35
|
+
|
|
36
|
+
customTokenizer.html = (src: string) => {
|
|
37
|
+
const token = originalHtml(src);
|
|
38
|
+
if (token && isReactComponentToken(token)) {
|
|
39
|
+
const id = `${token.componentName}-${componentCounter++}`;
|
|
40
|
+
map.set(token.raw, { token, id });
|
|
41
|
+
}
|
|
42
|
+
return token;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
return { tokenizer: customTokenizer, componentMap: map };
|
|
46
|
+
}, []);
|
|
47
|
+
|
|
48
|
+
const baseRenderer = useMemo(
|
|
49
|
+
() => options?.renderer ?? new Renderer(),
|
|
50
|
+
[options?.renderer],
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const renderer = useMemo<RendererInterface>(() => {
|
|
54
|
+
const wrappedRenderer = Object.create(baseRenderer) as typeof baseRenderer;
|
|
55
|
+
|
|
56
|
+
wrappedRenderer.html = (text: string | ReactNode[], styles): ReactNode => {
|
|
57
|
+
if (typeof text === "string") {
|
|
58
|
+
const data = componentMap.get(text);
|
|
59
|
+
if (data) {
|
|
60
|
+
if (registry?.hasComponent(data.token.componentName)) {
|
|
61
|
+
const Component = registry.getComponent(data.token.componentName);
|
|
62
|
+
if (Component) {
|
|
63
|
+
return (
|
|
64
|
+
<Component key={data.id} props={data.token.componentProps}>
|
|
65
|
+
{data.token.componentChildren || undefined}
|
|
66
|
+
</Component>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return baseRenderer.html(text, styles);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
return wrappedRenderer;
|
|
78
|
+
}, [baseRenderer, componentMap, registry]);
|
|
79
|
+
|
|
80
|
+
const elements = useMarkdown(value, {
|
|
81
|
+
...options,
|
|
82
|
+
tokenizer,
|
|
83
|
+
renderer,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
return elements;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export default useMarkdownWithComponents;
|
package/src/index.ts
CHANGED
|
@@ -5,7 +5,10 @@ import {
|
|
|
5
5
|
marked,
|
|
6
6
|
} from "marked";
|
|
7
7
|
import useMarkdown, { type useMarkdownHookOptions } from "./hooks/useMarkdown";
|
|
8
|
+
import useMarkdownWithComponents from "./hooks/useMarkdownWithComponents";
|
|
8
9
|
import Markdown from "./lib/Markdown";
|
|
10
|
+
import type { ReactComponentRegistry } from "./lib/ReactComponentRegistry";
|
|
11
|
+
import { ReactComponentRegistryProvider } from "./lib/ReactComponentRegistry";
|
|
9
12
|
import Renderer from "./lib/Renderer";
|
|
10
13
|
import type {
|
|
11
14
|
MarkdownProps,
|
|
@@ -24,8 +27,17 @@ export type {
|
|
|
24
27
|
useMarkdownHookOptions,
|
|
25
28
|
Token,
|
|
26
29
|
Tokens,
|
|
30
|
+
ReactComponentRegistry,
|
|
27
31
|
};
|
|
28
32
|
|
|
29
|
-
export {
|
|
33
|
+
export {
|
|
34
|
+
useMarkdown,
|
|
35
|
+
useMarkdownWithComponents,
|
|
36
|
+
MarkedLexer,
|
|
37
|
+
Renderer,
|
|
38
|
+
MarkedTokenizer,
|
|
39
|
+
MarkedHooks,
|
|
40
|
+
ReactComponentRegistryProvider,
|
|
41
|
+
};
|
|
30
42
|
|
|
31
43
|
export default Markdown;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import React, { createContext, useContext, useMemo } from "react";
|
|
3
|
+
import type { ReactComponentProps } from "./ReactComponentTokenizer";
|
|
4
|
+
|
|
5
|
+
export interface ReactComponentRendererProps {
|
|
6
|
+
props: ReactComponentProps;
|
|
7
|
+
children?: ReactNode;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type ReactComponentRenderer = (
|
|
11
|
+
props: ReactComponentRendererProps,
|
|
12
|
+
) => ReactNode;
|
|
13
|
+
|
|
14
|
+
export type ReactComponentRegistry = Record<string, ReactComponentRenderer>;
|
|
15
|
+
|
|
16
|
+
interface ReactComponentRegistryContextValue {
|
|
17
|
+
components: ReactComponentRegistry;
|
|
18
|
+
getComponent: (name: string) => ReactComponentRenderer | undefined;
|
|
19
|
+
hasComponent: (name: string) => boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const ReactComponentRegistryContext =
|
|
23
|
+
createContext<ReactComponentRegistryContextValue | null>(null);
|
|
24
|
+
|
|
25
|
+
interface ReactComponentRegistryProviderProps {
|
|
26
|
+
components: ReactComponentRegistry;
|
|
27
|
+
children: ReactNode;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function ReactComponentRegistryProvider({
|
|
31
|
+
components,
|
|
32
|
+
children,
|
|
33
|
+
}: ReactComponentRegistryProviderProps) {
|
|
34
|
+
const contextValue = useMemo<ReactComponentRegistryContextValue>(
|
|
35
|
+
() => ({
|
|
36
|
+
components,
|
|
37
|
+
getComponent: (name: string) => components[name],
|
|
38
|
+
hasComponent: (name: string) => name in components,
|
|
39
|
+
}),
|
|
40
|
+
[components],
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<ReactComponentRegistryContext.Provider value={contextValue}>
|
|
45
|
+
{children}
|
|
46
|
+
</ReactComponentRegistryContext.Provider>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function useReactComponentRegistry(): ReactComponentRegistryContextValue | null {
|
|
51
|
+
return useContext(ReactComponentRegistryContext);
|
|
52
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import type { Tokens } from "marked";
|
|
2
|
+
import { Tokenizer } from "marked";
|
|
3
|
+
|
|
4
|
+
const SELF_CLOSING_REGEX = /^<([A-Z][a-zA-Z0-9]*)([^>]*?)\s*\/>/;
|
|
5
|
+
const OPENING_TAG_REGEX = /^<([A-Z][a-zA-Z0-9]*)([^>]*)>/;
|
|
6
|
+
const PROP_REGEX = /(\w+)(?:=(?:"([^"]*)"|'([^']*)'|\{([^}]*)\}))?/g;
|
|
7
|
+
|
|
8
|
+
export type ReactComponentProps = Record<string, string | boolean | number>;
|
|
9
|
+
|
|
10
|
+
export interface ReactComponentToken extends Tokens.HTML {
|
|
11
|
+
componentName: string;
|
|
12
|
+
componentProps: ReactComponentProps;
|
|
13
|
+
componentChildren: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function parseProps(propsString: string): ReactComponentProps {
|
|
17
|
+
const props: ReactComponentProps = {};
|
|
18
|
+
if (!propsString) return props;
|
|
19
|
+
|
|
20
|
+
let match: RegExpExecArray | null;
|
|
21
|
+
|
|
22
|
+
match = PROP_REGEX.exec(propsString);
|
|
23
|
+
while (match !== null) {
|
|
24
|
+
const [, propName, doubleQuoted, singleQuoted, braced] = match;
|
|
25
|
+
if (propName) {
|
|
26
|
+
if (doubleQuoted !== undefined) {
|
|
27
|
+
props[propName] = doubleQuoted;
|
|
28
|
+
} else if (singleQuoted !== undefined) {
|
|
29
|
+
props[propName] = singleQuoted;
|
|
30
|
+
} else if (braced !== undefined) {
|
|
31
|
+
const trimmed = braced.trim();
|
|
32
|
+
if (trimmed === "true") {
|
|
33
|
+
props[propName] = true;
|
|
34
|
+
} else if (trimmed === "false") {
|
|
35
|
+
props[propName] = false;
|
|
36
|
+
} else if (!Number.isNaN(Number(trimmed))) {
|
|
37
|
+
props[propName] = Number(trimmed);
|
|
38
|
+
} else {
|
|
39
|
+
props[propName] = trimmed;
|
|
40
|
+
}
|
|
41
|
+
} else {
|
|
42
|
+
props[propName] = true;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
match = PROP_REGEX.exec(propsString);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
PROP_REGEX.lastIndex = 0;
|
|
49
|
+
return props;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function findClosingTag(
|
|
53
|
+
src: string,
|
|
54
|
+
componentName: string,
|
|
55
|
+
startIndex: number,
|
|
56
|
+
): { endIndex: number; children: string } | null {
|
|
57
|
+
const openingTag = new RegExp(`<${componentName}(?:\\s[^>]*)?>`, "g");
|
|
58
|
+
const closingTag = `</${componentName}>`;
|
|
59
|
+
|
|
60
|
+
let depth = 1;
|
|
61
|
+
let currentIndex = startIndex;
|
|
62
|
+
|
|
63
|
+
while (depth > 0 && currentIndex < src.length) {
|
|
64
|
+
const closingIndex = src.indexOf(closingTag, currentIndex);
|
|
65
|
+
if (closingIndex === -1) return null;
|
|
66
|
+
|
|
67
|
+
openingTag.lastIndex = currentIndex;
|
|
68
|
+
let nestedMatch: RegExpExecArray | null;
|
|
69
|
+
nestedMatch = openingTag.exec(src);
|
|
70
|
+
while (nestedMatch !== null && nestedMatch.index < closingIndex) {
|
|
71
|
+
depth++;
|
|
72
|
+
nestedMatch = openingTag.exec(src);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
depth--;
|
|
76
|
+
if (depth === 0) {
|
|
77
|
+
return {
|
|
78
|
+
endIndex: closingIndex + closingTag.length,
|
|
79
|
+
children: src.slice(startIndex, closingIndex),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
currentIndex = closingIndex + closingTag.length;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export class ReactComponentTokenizer extends Tokenizer {
|
|
89
|
+
override html(src: string): Tokens.HTML | undefined {
|
|
90
|
+
const selfClosingMatch = SELF_CLOSING_REGEX.exec(src);
|
|
91
|
+
if (selfClosingMatch) {
|
|
92
|
+
const [raw, componentName, propsString] = selfClosingMatch;
|
|
93
|
+
return this.createReactComponentToken(
|
|
94
|
+
raw,
|
|
95
|
+
componentName ?? "",
|
|
96
|
+
propsString ?? "",
|
|
97
|
+
"",
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const openingMatch = OPENING_TAG_REGEX.exec(src);
|
|
102
|
+
if (openingMatch) {
|
|
103
|
+
const [openingTag, componentName, propsString] = openingMatch;
|
|
104
|
+
const result = findClosingTag(
|
|
105
|
+
src,
|
|
106
|
+
componentName ?? "",
|
|
107
|
+
openingTag?.length ?? 0,
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
if (result) {
|
|
111
|
+
const raw = src.slice(0, result.endIndex);
|
|
112
|
+
return this.createReactComponentToken(
|
|
113
|
+
raw,
|
|
114
|
+
componentName ?? "",
|
|
115
|
+
propsString ?? "",
|
|
116
|
+
result.children,
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return super.html(src);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private createReactComponentToken(
|
|
125
|
+
raw: string,
|
|
126
|
+
componentName: string,
|
|
127
|
+
propsString: string,
|
|
128
|
+
children: string,
|
|
129
|
+
): ReactComponentToken {
|
|
130
|
+
return {
|
|
131
|
+
type: "html",
|
|
132
|
+
raw,
|
|
133
|
+
text: raw,
|
|
134
|
+
block: true,
|
|
135
|
+
pre: false,
|
|
136
|
+
componentName,
|
|
137
|
+
componentProps: parseProps(propsString),
|
|
138
|
+
componentChildren: children.trim(),
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function isReactComponentToken(
|
|
144
|
+
token: Tokens.HTML,
|
|
145
|
+
): token is ReactComponentToken {
|
|
146
|
+
return "componentName" in token && typeof token.componentName === "string";
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export default ReactComponentTokenizer;
|