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.
Files changed (30) hide show
  1. package/README.md +72 -0
  2. package/dist/commonjs/hooks/useMarkdownWithComponents.js +74 -0
  3. package/dist/commonjs/hooks/useMarkdownWithComponents.js.map +1 -0
  4. package/dist/commonjs/index.js +14 -0
  5. package/dist/commonjs/index.js.map +1 -1
  6. package/dist/commonjs/lib/ReactComponentRegistry.js +30 -0
  7. package/dist/commonjs/lib/ReactComponentRegistry.js.map +1 -0
  8. package/dist/commonjs/lib/ReactComponentTokenizer.js +106 -0
  9. package/dist/commonjs/lib/ReactComponentTokenizer.js.map +1 -0
  10. package/dist/module/hooks/useMarkdownWithComponents.js +66 -0
  11. package/dist/module/hooks/useMarkdownWithComponents.js.map +1 -0
  12. package/dist/module/index.js +3 -1
  13. package/dist/module/index.js.map +1 -1
  14. package/dist/module/lib/ReactComponentRegistry.js +23 -0
  15. package/dist/module/lib/ReactComponentRegistry.js.map +1 -0
  16. package/dist/module/lib/ReactComponentTokenizer.js +100 -0
  17. package/dist/module/lib/ReactComponentTokenizer.js.map +1 -0
  18. package/dist/typescript/hooks/useMarkdownWithComponents.d.ts +7 -0
  19. package/dist/typescript/hooks/useMarkdownWithComponents.d.ts.map +1 -0
  20. package/dist/typescript/index.d.ts +5 -2
  21. package/dist/typescript/index.d.ts.map +1 -1
  22. package/dist/typescript/lib/ReactComponentRegistry.d.ts +22 -0
  23. package/dist/typescript/lib/ReactComponentRegistry.d.ts.map +1 -0
  24. package/dist/typescript/lib/ReactComponentTokenizer.d.ts +15 -0
  25. package/dist/typescript/lib/ReactComponentTokenizer.d.ts.map +1 -0
  26. package/package.json +9 -9
  27. package/src/hooks/useMarkdownWithComponents.tsx +89 -0
  28. package/src/index.ts +13 -1
  29. package/src/lib/ReactComponentRegistry.tsx +52 -0
  30. 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":[]}
@@ -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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,IAAAA,OAAA,GAAAC,OAAA;AAKA,IAAAC,YAAA,GAAAC,sBAAA,CAAAF,OAAA;AACA,IAAAG,SAAA,GAAAD,sBAAA,CAAAF,OAAA;AACA,IAAAI,SAAA,GAAAF,sBAAA,CAAAF,OAAA;AAAsC,SAAAE,uBAAAG,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,GAclBM,iBAAQ","ignoreList":[]}
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":[]}
@@ -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
@@ -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;AACrC,OAAOC,QAAQ,MAAM,gBAAgB;AAQrC,MAAMC,WAAW,GAAGJ,MAAM,CAACK,KAAK;AAYhC,SAASJ,WAAW,EAAEG,WAAW,EAAED,QAAQ,EAAEJ,eAAe,EAAEF,WAAW;AAEzE,eAAeK,QAAQ","ignoreList":[]}
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,GACN,CAAC;AAEF,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,QAAQ,EAAE,eAAe,EAAE,WAAW,EAAE,CAAC;AAE5E,eAAe,QAAQ,CAAC"}
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": "7.1.1",
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.8",
57
+ "@biomejs/biome": "2.3.10",
58
58
  "@commitlint/config-conventional": "20.2.0",
59
- "@evilmartians/lefthook": "2.0.9",
59
+ "@evilmartians/lefthook": "2.0.13",
60
60
  "@react-native/babel-preset": "0.78.2",
61
- "@release-it/conventional-changelog": "10.0.2",
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.2",
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.1",
74
+ "react": "19.2.3",
75
75
  "react-native": "0.78.2",
76
- "react-native-builder-bob": "0.40.16",
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.0.6",
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.1"
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 { useMarkdown, MarkedLexer, Renderer, MarkedTokenizer, MarkedHooks };
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;