@willwang-io/react-djot 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,140 @@
1
+ # react-djot
2
+
3
+ `react-djot` renders [Djot](https://djot.net/) into React elements with a
4
+ `react-markdown`-style API.
5
+
6
+ The design goal is familiarity: if you know `react-markdown`, you should feel
7
+ at home with `react-djot`.
8
+
9
+ ## Features
10
+
11
+ - React 18+ support
12
+ - `components` override map for per-node rendering control
13
+ - No `dangerouslySetInnerHTML`
14
+ - React Server Component friendly main path (no hooks)
15
+ - TypeScript-first API
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install react-djot
21
+ ```
22
+
23
+ Peer dependencies:
24
+
25
+ - `react`
26
+ - `react-dom`
27
+ - `@djot/djot`
28
+
29
+ ## Quick Start
30
+
31
+ ```tsx
32
+ import { Djot } from "react-djot";
33
+
34
+ export function Example() {
35
+ return <Djot>{"# Hello\n\nThis is *Djot*."}</Djot>;
36
+ }
37
+ ```
38
+
39
+ ## API
40
+
41
+ ### `<Djot />`
42
+
43
+ ```tsx
44
+ import { Djot } from "react-djot";
45
+
46
+ <Djot
47
+ children={"# Title"}
48
+ components={
49
+ {
50
+ /* overrides */
51
+ }
52
+ }
53
+ />;
54
+ ```
55
+
56
+ Props:
57
+
58
+ - `children?: string | null | undefined`
59
+ - Djot source text to parse and render.
60
+ - `components?: DjotComponents`
61
+ - Optional map of node-tag keys to React components.
62
+
63
+ ### `components` overrides
64
+
65
+ The override pattern mirrors `react-markdown`: provide a component per node
66
+ type key.
67
+
68
+ ```tsx
69
+ import { Djot } from "react-djot";
70
+ import type { DjotComponents } from "react-djot";
71
+
72
+ const components: DjotComponents = {
73
+ para: ({ node, children, ...props }) => (
74
+ <p className="lead" {...props}>
75
+ {children}
76
+ </p>
77
+ ),
78
+ heading: ({ level, node, children, ...props }) => {
79
+ const Tag = `h${Math.min(Math.max(level, 1), 6)}` as const;
80
+ return (
81
+ <Tag {...props} data-level={level}>
82
+ {children}
83
+ </Tag>
84
+ );
85
+ },
86
+ link: ({ node, href, children, ...props }) => (
87
+ <a {...props} href={href} rel="noreferrer" target="_blank">
88
+ {children}
89
+ </a>
90
+ )
91
+ };
92
+
93
+ export function Example() {
94
+ return <Djot components={components}>{"## Custom\n\n[Go](https://example.com)"}</Djot>;
95
+ }
96
+ ```
97
+
98
+ ### Supported node keys (current core set)
99
+
100
+ - `doc`
101
+ - `para`
102
+ - `heading`
103
+ - `emph`
104
+ - `strong`
105
+ - `code`
106
+ - `code_block`
107
+ - `link`
108
+ - `image`
109
+ - `bullet_list`
110
+ - `ordered_list`
111
+ - `list_item`
112
+ - `blockquote`
113
+ - `thematic_break`
114
+ - `str`
115
+ - `soft_break` and `softbreak`
116
+ - `hard_break` and `hardbreak`
117
+
118
+ ## Rendering Guarantees
119
+
120
+ - Parses Djot via `@djot/djot` `parse()`
121
+ - Walks the AST recursively and creates React elements directly
122
+ - Does not use `dangerouslySetInnerHTML`
123
+
124
+ ## React Server Components
125
+
126
+ `<Djot />` has no hooks in the main render path, so it can be used in Server
127
+ Components.
128
+
129
+ ## Development
130
+
131
+ ```bash
132
+ npm run build
133
+ npm run typecheck
134
+ npm run lint
135
+ npm test
136
+ ```
137
+
138
+ ## License
139
+
140
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,371 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var djot = require('@djot/djot');
6
+ var react = require('react');
7
+ var jsxRuntime = require('react/jsx-runtime');
8
+
9
+ // src/Djot.tsx
10
+ function isParentNode(node) {
11
+ return Array.isArray(node.children);
12
+ }
13
+ function isSoftBreakNode(node) {
14
+ return node.tag === "soft_break" || node.tag === "softbreak";
15
+ }
16
+ function isHardBreakNode(node) {
17
+ return node.tag === "hard_break" || node.tag === "hardbreak";
18
+ }
19
+ function pickComponent(components, primary, alias) {
20
+ if (!components) {
21
+ return void 0;
22
+ }
23
+ return components[primary] ?? (alias ? components[alias] : void 0);
24
+ }
25
+ function renderChildren(children, components) {
26
+ return children.map(
27
+ (child, index) => renderNode(child, {
28
+ components,
29
+ key: index
30
+ })
31
+ );
32
+ }
33
+ function toAltText(nodes) {
34
+ let output = "";
35
+ for (const node of nodes) {
36
+ switch (node.tag) {
37
+ case "str":
38
+ case "code":
39
+ case "code_block":
40
+ output += node.text;
41
+ break;
42
+ case "soft_break":
43
+ case "softbreak":
44
+ output += " ";
45
+ break;
46
+ case "hard_break":
47
+ case "hardbreak":
48
+ output += "\n";
49
+ break;
50
+ default:
51
+ if (isParentNode(node)) {
52
+ output += toAltText(node.children);
53
+ }
54
+ break;
55
+ }
56
+ }
57
+ return output.trim();
58
+ }
59
+ function clampHeadingLevel(level) {
60
+ if (level <= 1) {
61
+ return 1;
62
+ }
63
+ if (level >= 6) {
64
+ return 6;
65
+ }
66
+ return level;
67
+ }
68
+ function withKey(props, key) {
69
+ if (key === void 0) {
70
+ return props;
71
+ }
72
+ return {
73
+ ...props,
74
+ key
75
+ };
76
+ }
77
+ function renderWithOverride(override, fallback, domProps, customProps, key, children) {
78
+ const Component = override ?? fallback;
79
+ const props = typeof Component === "string" ? withKey(domProps, key) : withKey(
80
+ {
81
+ ...domProps,
82
+ ...customProps
83
+ },
84
+ key
85
+ );
86
+ return react.createElement(Component, props, children);
87
+ }
88
+ function renderDoc(node, components, key) {
89
+ const children = renderChildren(node.children, components);
90
+ const Component = pickComponent(components, "doc");
91
+ if (Component) {
92
+ if (typeof Component === "string") {
93
+ return react.createElement(Component, withKey({}, key), children);
94
+ }
95
+ return react.createElement(Component, withKey({ node }, key), children);
96
+ }
97
+ return react.createElement(react.Fragment, withKey({}, key), children);
98
+ }
99
+ function renderHeading(node, components, key) {
100
+ const level = clampHeadingLevel(node.level);
101
+ const children = renderChildren(node.children, components);
102
+ return renderWithOverride(
103
+ pickComponent(components, "heading"),
104
+ `h${level}`,
105
+ {},
106
+ {
107
+ level,
108
+ node
109
+ },
110
+ key,
111
+ children
112
+ );
113
+ }
114
+ function renderCode(node, components, key) {
115
+ const value = node.text;
116
+ return renderWithOverride(
117
+ pickComponent(components, "code"),
118
+ "code",
119
+ {},
120
+ {
121
+ node,
122
+ value
123
+ },
124
+ key,
125
+ value
126
+ );
127
+ }
128
+ function renderCodeBlock(node, components, key) {
129
+ const value = node.text;
130
+ const language = node.lang;
131
+ const fallbackChildren = react.createElement(
132
+ "code",
133
+ {
134
+ className: language ? `language-${language}` : void 0
135
+ },
136
+ value
137
+ );
138
+ return renderWithOverride(
139
+ pickComponent(components, "code_block"),
140
+ "pre",
141
+ {},
142
+ {
143
+ language,
144
+ node,
145
+ value
146
+ },
147
+ key,
148
+ fallbackChildren
149
+ );
150
+ }
151
+ function renderLink(node, components, key) {
152
+ const children = renderChildren(node.children, components);
153
+ const href = node.destination;
154
+ return renderWithOverride(
155
+ pickComponent(components, "link"),
156
+ "a",
157
+ {
158
+ href
159
+ },
160
+ {
161
+ node
162
+ },
163
+ key,
164
+ children
165
+ );
166
+ }
167
+ function renderImage(node, components, key) {
168
+ const alt = toAltText(node.children) || void 0;
169
+ const src = node.destination;
170
+ return renderWithOverride(
171
+ pickComponent(components, "image"),
172
+ "img",
173
+ {
174
+ alt,
175
+ src
176
+ },
177
+ {
178
+ alt,
179
+ node
180
+ },
181
+ key
182
+ );
183
+ }
184
+ function renderOrderedList(node, components, key) {
185
+ const children = renderChildren(node.children, components);
186
+ return renderWithOverride(
187
+ pickComponent(components, "ordered_list"),
188
+ "ol",
189
+ {
190
+ start: node.start
191
+ },
192
+ {
193
+ node,
194
+ start: node.start
195
+ },
196
+ key,
197
+ children
198
+ );
199
+ }
200
+ function renderStr(node, components, key) {
201
+ const value = node.text;
202
+ const Component = pickComponent(components, "str");
203
+ if (!Component) {
204
+ return value;
205
+ }
206
+ if (typeof Component === "string") {
207
+ return react.createElement(Component, withKey({}, key), value);
208
+ }
209
+ return react.createElement(
210
+ Component,
211
+ withKey(
212
+ {
213
+ node,
214
+ value
215
+ },
216
+ key
217
+ ),
218
+ value
219
+ );
220
+ }
221
+ function renderSoftBreak(node, components, key) {
222
+ const Component = pickComponent(components, "soft_break", "softbreak");
223
+ if (!Component) {
224
+ return "\n";
225
+ }
226
+ if (typeof Component === "string") {
227
+ return react.createElement(Component, withKey({}, key), "\n");
228
+ }
229
+ return react.createElement(
230
+ Component,
231
+ withKey(
232
+ {
233
+ node
234
+ },
235
+ key
236
+ ),
237
+ "\n"
238
+ );
239
+ }
240
+ function renderHardBreak(node, components, key) {
241
+ return renderWithOverride(
242
+ pickComponent(components, "hard_break", "hardbreak"),
243
+ "br",
244
+ {},
245
+ {
246
+ node
247
+ },
248
+ key
249
+ );
250
+ }
251
+ function renderNode(node, options = {}) {
252
+ const { components, key } = options;
253
+ const children = isParentNode(node) ? renderChildren(node.children, components) : void 0;
254
+ switch (node.tag) {
255
+ case "doc":
256
+ return renderDoc(node, components, key);
257
+ case "para":
258
+ return renderWithOverride(
259
+ pickComponent(components, "para"),
260
+ "p",
261
+ {},
262
+ {
263
+ node
264
+ },
265
+ key,
266
+ children
267
+ );
268
+ case "heading":
269
+ return renderHeading(node, components, key);
270
+ case "emph":
271
+ return renderWithOverride(
272
+ pickComponent(components, "emph"),
273
+ "em",
274
+ {},
275
+ {
276
+ node
277
+ },
278
+ key,
279
+ children
280
+ );
281
+ case "strong":
282
+ return renderWithOverride(
283
+ pickComponent(components, "strong"),
284
+ "strong",
285
+ {},
286
+ {
287
+ node
288
+ },
289
+ key,
290
+ children
291
+ );
292
+ case "code":
293
+ return renderCode(node, components, key);
294
+ case "code_block":
295
+ return renderCodeBlock(node, components, key);
296
+ case "link":
297
+ return renderLink(node, components, key);
298
+ case "image":
299
+ return renderImage(node, components, key);
300
+ case "bullet_list":
301
+ return renderWithOverride(
302
+ pickComponent(components, "bullet_list"),
303
+ "ul",
304
+ {},
305
+ {
306
+ node
307
+ },
308
+ key,
309
+ children
310
+ );
311
+ case "ordered_list":
312
+ return renderOrderedList(node, components, key);
313
+ case "list_item":
314
+ return renderWithOverride(
315
+ pickComponent(components, "list_item"),
316
+ "li",
317
+ {},
318
+ {
319
+ node
320
+ },
321
+ key,
322
+ children
323
+ );
324
+ case "blockquote":
325
+ return renderWithOverride(
326
+ pickComponent(components, "blockquote"),
327
+ "blockquote",
328
+ {},
329
+ {
330
+ node
331
+ },
332
+ key,
333
+ children
334
+ );
335
+ case "thematic_break":
336
+ return renderWithOverride(
337
+ pickComponent(components, "thematic_break"),
338
+ "hr",
339
+ {},
340
+ {
341
+ node
342
+ },
343
+ key
344
+ );
345
+ case "str":
346
+ return renderStr(node, components, key);
347
+ default:
348
+ if (isSoftBreakNode(node)) {
349
+ return renderSoftBreak(node, components, key);
350
+ }
351
+ if (isHardBreakNode(node)) {
352
+ return renderHardBreak(node, components, key);
353
+ }
354
+ return null;
355
+ }
356
+ }
357
+ function Djot({ children, components }) {
358
+ const source = children ?? "";
359
+ if (source.length === 0) {
360
+ return null;
361
+ }
362
+ const ast = djot.parse(source);
363
+ return /* @__PURE__ */ jsxRuntime.jsx(react.Fragment, { children: renderNode(ast, { components }) });
364
+ }
365
+ var Djot_default = Djot;
366
+
367
+ exports.Djot = Djot;
368
+ exports.default = Djot_default;
369
+ exports.renderNode = renderNode;
370
+ //# sourceMappingURL=index.cjs.map
371
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/renderNode.tsx","../src/Djot.tsx"],"names":["createElement","Fragment","parse","jsx"],"mappings":"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","file":"index.cjs","sourcesContent":["import { createElement, Fragment } from \"react\";\nimport type React from \"react\";\nimport type {\n DjotBaseNode,\n DjotCodeBlockNode,\n DjotCodeNode,\n DjotComponentPropsMap,\n DjotComponents,\n DjotDocNode,\n DjotHardBreakNode,\n DjotHeadingNode,\n DjotImageNode,\n DjotLinkNode,\n DjotNode,\n DjotOrderedListNode,\n DjotParentNode,\n DjotSoftBreakNode,\n DjotStrNode\n} from \"./types\";\n\nexport interface RenderNodeOptions {\n components?: DjotComponents | undefined;\n key?: React.Key;\n}\n\ntype ComponentKey = keyof DjotComponentPropsMap;\n\nfunction isParentNode(node: DjotBaseNode): node is DjotParentNode {\n return Array.isArray((node as DjotParentNode).children);\n}\n\nfunction isSoftBreakNode(node: DjotNode): node is DjotSoftBreakNode {\n return node.tag === \"soft_break\" || node.tag === \"softbreak\";\n}\n\nfunction isHardBreakNode(node: DjotNode): node is DjotHardBreakNode {\n return node.tag === \"hard_break\" || node.tag === \"hardbreak\";\n}\n\nfunction pickComponent(\n components: DjotComponents | undefined,\n primary: ComponentKey,\n alias?: ComponentKey\n): React.ElementType | undefined {\n if (!components) {\n return undefined;\n }\n\n return (components[primary] ?? (alias ? components[alias] : undefined)) as\n | React.ElementType\n | undefined;\n}\n\nfunction renderChildren(children: DjotNode[], components?: DjotComponents): React.ReactNode[] {\n return children.map((child, index) =>\n renderNode(child, {\n components,\n key: index\n })\n );\n}\n\nfunction toAltText(nodes: DjotNode[]): string {\n let output = \"\";\n\n for (const node of nodes) {\n switch (node.tag) {\n case \"str\":\n case \"code\":\n case \"code_block\":\n output += node.text;\n break;\n case \"soft_break\":\n case \"softbreak\":\n output += \" \";\n break;\n case \"hard_break\":\n case \"hardbreak\":\n output += \"\\n\";\n break;\n default:\n if (isParentNode(node)) {\n output += toAltText(node.children);\n }\n break;\n }\n }\n\n return output.trim();\n}\n\nfunction clampHeadingLevel(level: number): 1 | 2 | 3 | 4 | 5 | 6 {\n if (level <= 1) {\n return 1;\n }\n\n if (level >= 6) {\n return 6;\n }\n\n return level as 1 | 2 | 3 | 4 | 5 | 6;\n}\n\nfunction withKey<T extends Record<string, unknown>>(props: T, key?: React.Key): T & { key?: React.Key } {\n if (key === undefined) {\n return props;\n }\n\n return {\n ...props,\n key\n };\n}\n\nfunction renderWithOverride(\n override: React.ElementType | undefined,\n fallback: React.ElementType,\n domProps: Record<string, unknown>,\n customProps: Record<string, unknown>,\n key?: React.Key,\n children?: React.ReactNode\n): React.ReactNode {\n const Component = override ?? fallback;\n const props =\n typeof Component === \"string\"\n ? withKey(domProps, key)\n : withKey(\n {\n ...domProps,\n ...customProps\n },\n key\n );\n\n return createElement(Component, props, children);\n}\n\nfunction renderDoc(\n node: DjotDocNode,\n components: DjotComponents | undefined,\n key?: React.Key\n): React.ReactNode {\n const children = renderChildren(node.children, components);\n const Component = pickComponent(components, \"doc\");\n\n if (Component) {\n if (typeof Component === \"string\") {\n return createElement(Component, withKey({}, key), children);\n }\n\n return createElement(Component, withKey({ node }, key), children);\n }\n\n return createElement(Fragment, withKey({}, key), children);\n}\n\nfunction renderHeading(\n node: DjotHeadingNode,\n components: DjotComponents | undefined,\n key?: React.Key\n): React.ReactNode {\n const level = clampHeadingLevel(node.level);\n const children = renderChildren(node.children, components);\n return renderWithOverride(\n pickComponent(components, \"heading\"),\n `h${level}`,\n {},\n {\n level,\n node\n },\n key,\n children\n );\n}\n\nfunction renderCode(\n node: DjotCodeNode,\n components: DjotComponents | undefined,\n key?: React.Key\n): React.ReactNode {\n const value = node.text;\n return renderWithOverride(\n pickComponent(components, \"code\"),\n \"code\",\n {},\n {\n node,\n value\n },\n key,\n value\n );\n}\n\nfunction renderCodeBlock(\n node: DjotCodeBlockNode,\n components: DjotComponents | undefined,\n key?: React.Key\n): React.ReactNode {\n const value = node.text;\n const language = node.lang;\n const fallbackChildren = createElement(\n \"code\",\n {\n className: language ? `language-${language}` : undefined\n },\n value\n );\n\n return renderWithOverride(\n pickComponent(components, \"code_block\"),\n \"pre\",\n {},\n {\n language,\n node,\n value\n },\n key,\n fallbackChildren\n );\n}\n\nfunction renderLink(\n node: DjotLinkNode,\n components: DjotComponents | undefined,\n key?: React.Key\n): React.ReactNode {\n const children = renderChildren(node.children, components);\n const href = node.destination;\n return renderWithOverride(\n pickComponent(components, \"link\"),\n \"a\",\n {\n href\n },\n {\n node\n },\n key,\n children\n );\n}\n\nfunction renderImage(\n node: DjotImageNode,\n components: DjotComponents | undefined,\n key?: React.Key\n): React.ReactNode {\n const alt = toAltText(node.children) || undefined;\n const src = node.destination;\n return renderWithOverride(\n pickComponent(components, \"image\"),\n \"img\",\n {\n alt,\n src\n },\n {\n alt,\n node\n },\n key\n );\n}\n\nfunction renderOrderedList(\n node: DjotOrderedListNode,\n components: DjotComponents | undefined,\n key?: React.Key\n): React.ReactNode {\n const children = renderChildren(node.children, components);\n return renderWithOverride(\n pickComponent(components, \"ordered_list\"),\n \"ol\",\n {\n start: node.start\n },\n {\n node,\n start: node.start\n },\n key,\n children\n );\n}\n\nfunction renderStr(\n node: DjotStrNode,\n components: DjotComponents | undefined,\n key?: React.Key\n): React.ReactNode {\n const value = node.text;\n const Component = pickComponent(components, \"str\");\n\n if (!Component) {\n return value;\n }\n\n if (typeof Component === \"string\") {\n return createElement(Component, withKey({}, key), value);\n }\n\n return createElement(\n Component,\n withKey(\n {\n node,\n value\n },\n key\n ),\n value\n );\n}\n\nfunction renderSoftBreak(\n node: DjotSoftBreakNode,\n components: DjotComponents | undefined,\n key?: React.Key\n): React.ReactNode {\n const Component = pickComponent(components, \"soft_break\", \"softbreak\");\n\n if (!Component) {\n return \"\\n\";\n }\n\n if (typeof Component === \"string\") {\n return createElement(Component, withKey({}, key), \"\\n\");\n }\n\n return createElement(\n Component,\n withKey(\n {\n node\n },\n key\n ),\n \"\\n\"\n );\n}\n\nfunction renderHardBreak(\n node: DjotHardBreakNode,\n components: DjotComponents | undefined,\n key?: React.Key\n): React.ReactNode {\n return renderWithOverride(\n pickComponent(components, \"hard_break\", \"hardbreak\"),\n \"br\",\n {},\n {\n node\n },\n key\n );\n}\n\nexport function renderNode(node: DjotNode, options: RenderNodeOptions = {}): React.ReactNode {\n const { components, key } = options;\n const children = isParentNode(node) ? renderChildren(node.children, components) : undefined;\n\n switch (node.tag) {\n case \"doc\":\n return renderDoc(node, components, key);\n case \"para\":\n return renderWithOverride(\n pickComponent(components, \"para\"),\n \"p\",\n {},\n {\n node\n },\n key,\n children\n );\n case \"heading\":\n return renderHeading(node, components, key);\n case \"emph\":\n return renderWithOverride(\n pickComponent(components, \"emph\"),\n \"em\",\n {},\n {\n node\n },\n key,\n children\n );\n case \"strong\":\n return renderWithOverride(\n pickComponent(components, \"strong\"),\n \"strong\",\n {},\n {\n node\n },\n key,\n children\n );\n case \"code\":\n return renderCode(node, components, key);\n case \"code_block\":\n return renderCodeBlock(node, components, key);\n case \"link\":\n return renderLink(node, components, key);\n case \"image\":\n return renderImage(node, components, key);\n case \"bullet_list\":\n return renderWithOverride(\n pickComponent(components, \"bullet_list\"),\n \"ul\",\n {},\n {\n node\n },\n key,\n children\n );\n case \"ordered_list\":\n return renderOrderedList(node, components, key);\n case \"list_item\":\n return renderWithOverride(\n pickComponent(components, \"list_item\"),\n \"li\",\n {},\n {\n node\n },\n key,\n children\n );\n case \"blockquote\":\n return renderWithOverride(\n pickComponent(components, \"blockquote\"),\n \"blockquote\",\n {},\n {\n node\n },\n key,\n children\n );\n case \"thematic_break\":\n return renderWithOverride(\n pickComponent(components, \"thematic_break\"),\n \"hr\",\n {},\n {\n node\n },\n key\n );\n case \"str\":\n return renderStr(node, components, key);\n default:\n if (isSoftBreakNode(node)) {\n return renderSoftBreak(node, components, key);\n }\n\n if (isHardBreakNode(node)) {\n return renderHardBreak(node, components, key);\n }\n\n return null;\n }\n}\n","import { parse } from \"@djot/djot\";\nimport { Fragment } from \"react\";\nimport type React from \"react\";\nimport { renderNode } from \"./renderNode\";\nimport type { DjotNode, DjotProps } from \"./types\";\n\nexport function Djot({ children, components }: DjotProps): React.ReactElement | null {\n const source = children ?? \"\";\n\n if (source.length === 0) {\n return null;\n }\n\n const ast = parse(source) as DjotNode;\n return <Fragment>{renderNode(ast, { components })}</Fragment>;\n}\n\nexport default Djot;\n"]}
@@ -0,0 +1,127 @@
1
+ import React from 'react';
2
+
3
+ type DjotAttributes = Record<string, string>;
4
+ interface DjotBaseNode {
5
+ attributes?: DjotAttributes;
6
+ tag: string;
7
+ }
8
+ interface DjotParentNode extends DjotBaseNode {
9
+ children: DjotNode[];
10
+ }
11
+ interface DjotDocNode extends DjotParentNode {
12
+ tag: "doc";
13
+ }
14
+ interface DjotParaNode extends DjotParentNode {
15
+ tag: "para";
16
+ }
17
+ interface DjotHeadingNode extends DjotParentNode {
18
+ level: number;
19
+ tag: "heading";
20
+ }
21
+ interface DjotEmphNode extends DjotParentNode {
22
+ tag: "emph";
23
+ }
24
+ interface DjotStrongNode extends DjotParentNode {
25
+ tag: "strong";
26
+ }
27
+ interface DjotCodeNode extends DjotBaseNode {
28
+ tag: "code";
29
+ text: string;
30
+ }
31
+ interface DjotCodeBlockNode extends DjotBaseNode {
32
+ lang?: string;
33
+ tag: "code_block";
34
+ text: string;
35
+ }
36
+ interface DjotLinkNode extends DjotParentNode {
37
+ destination: string;
38
+ tag: "link";
39
+ }
40
+ interface DjotImageNode extends DjotParentNode {
41
+ destination: string;
42
+ tag: "image";
43
+ }
44
+ interface DjotBulletListNode extends DjotParentNode {
45
+ tag: "bullet_list";
46
+ }
47
+ interface DjotOrderedListNode extends DjotParentNode {
48
+ start?: number;
49
+ tag: "ordered_list";
50
+ }
51
+ interface DjotListItemNode extends DjotParentNode {
52
+ tag: "list_item";
53
+ }
54
+ interface DjotBlockquoteNode extends DjotParentNode {
55
+ tag: "blockquote";
56
+ }
57
+ interface DjotThematicBreakNode extends DjotBaseNode {
58
+ tag: "thematic_break";
59
+ }
60
+ interface DjotStrNode extends DjotBaseNode {
61
+ tag: "str";
62
+ text: string;
63
+ }
64
+ interface DjotSoftBreakNode extends DjotBaseNode {
65
+ tag: "soft_break" | "softbreak";
66
+ }
67
+ interface DjotHardBreakNode extends DjotBaseNode {
68
+ tag: "hard_break" | "hardbreak";
69
+ }
70
+ type DjotNode = DjotDocNode | DjotParaNode | DjotHeadingNode | DjotEmphNode | DjotStrongNode | DjotCodeNode | DjotCodeBlockNode | DjotLinkNode | DjotImageNode | DjotBulletListNode | DjotOrderedListNode | DjotListItemNode | DjotBlockquoteNode | DjotThematicBreakNode | DjotStrNode | DjotSoftBreakNode | DjotHardBreakNode;
71
+ type DjotNodeTag = DjotNode["tag"];
72
+ type DjotNodeByTag<Tag extends DjotNodeTag> = Extract<DjotNode, {
73
+ tag: Tag;
74
+ }>;
75
+ interface DjotNodePropsBase<Tag extends DjotNodeTag> {
76
+ children?: React.ReactNode;
77
+ node: DjotNodeByTag<Tag>;
78
+ }
79
+ interface DjotComponentPropsMap {
80
+ doc: DjotNodePropsBase<"doc">;
81
+ para: React.HTMLAttributes<HTMLParagraphElement> & DjotNodePropsBase<"para">;
82
+ heading: React.HTMLAttributes<HTMLHeadingElement> & DjotNodePropsBase<"heading"> & {
83
+ level: number;
84
+ };
85
+ emph: React.HTMLAttributes<HTMLElement> & DjotNodePropsBase<"emph">;
86
+ strong: React.HTMLAttributes<HTMLElement> & DjotNodePropsBase<"strong">;
87
+ code: React.HTMLAttributes<HTMLElement> & DjotNodePropsBase<"code"> & {
88
+ value: string;
89
+ };
90
+ code_block: React.HTMLAttributes<HTMLPreElement> & DjotNodePropsBase<"code_block"> & {
91
+ language?: string;
92
+ value: string;
93
+ };
94
+ link: React.AnchorHTMLAttributes<HTMLAnchorElement> & DjotNodePropsBase<"link">;
95
+ image: React.ImgHTMLAttributes<HTMLImageElement> & DjotNodePropsBase<"image"> & {
96
+ alt?: string;
97
+ };
98
+ bullet_list: React.HTMLAttributes<HTMLUListElement> & DjotNodePropsBase<"bullet_list">;
99
+ ordered_list: React.OlHTMLAttributes<HTMLOListElement> & DjotNodePropsBase<"ordered_list">;
100
+ list_item: React.LiHTMLAttributes<HTMLLIElement> & DjotNodePropsBase<"list_item">;
101
+ blockquote: React.BlockquoteHTMLAttributes<HTMLQuoteElement> & DjotNodePropsBase<"blockquote">;
102
+ thematic_break: React.HTMLAttributes<HTMLHRElement> & DjotNodePropsBase<"thematic_break">;
103
+ str: DjotNodePropsBase<"str"> & {
104
+ value: string;
105
+ };
106
+ soft_break: DjotNodePropsBase<"soft_break">;
107
+ softbreak: DjotNodePropsBase<"softbreak">;
108
+ hard_break: DjotNodePropsBase<"hard_break">;
109
+ hardbreak: DjotNodePropsBase<"hardbreak">;
110
+ }
111
+ type DjotComponents = Partial<{
112
+ [K in keyof DjotComponentPropsMap]: React.ElementType<DjotComponentPropsMap[K]>;
113
+ }>;
114
+ interface DjotProps {
115
+ children?: string | null | undefined;
116
+ components?: DjotComponents | undefined;
117
+ }
118
+
119
+ declare function Djot({ children, components }: DjotProps): React.ReactElement | null;
120
+
121
+ interface RenderNodeOptions {
122
+ components?: DjotComponents | undefined;
123
+ key?: React.Key;
124
+ }
125
+ declare function renderNode(node: DjotNode, options?: RenderNodeOptions): React.ReactNode;
126
+
127
+ export { Djot, type DjotComponentPropsMap, type DjotComponents, type DjotNode, type DjotNodeByTag, type DjotNodeTag, type DjotProps, Djot as default, renderNode };
@@ -0,0 +1,127 @@
1
+ import React from 'react';
2
+
3
+ type DjotAttributes = Record<string, string>;
4
+ interface DjotBaseNode {
5
+ attributes?: DjotAttributes;
6
+ tag: string;
7
+ }
8
+ interface DjotParentNode extends DjotBaseNode {
9
+ children: DjotNode[];
10
+ }
11
+ interface DjotDocNode extends DjotParentNode {
12
+ tag: "doc";
13
+ }
14
+ interface DjotParaNode extends DjotParentNode {
15
+ tag: "para";
16
+ }
17
+ interface DjotHeadingNode extends DjotParentNode {
18
+ level: number;
19
+ tag: "heading";
20
+ }
21
+ interface DjotEmphNode extends DjotParentNode {
22
+ tag: "emph";
23
+ }
24
+ interface DjotStrongNode extends DjotParentNode {
25
+ tag: "strong";
26
+ }
27
+ interface DjotCodeNode extends DjotBaseNode {
28
+ tag: "code";
29
+ text: string;
30
+ }
31
+ interface DjotCodeBlockNode extends DjotBaseNode {
32
+ lang?: string;
33
+ tag: "code_block";
34
+ text: string;
35
+ }
36
+ interface DjotLinkNode extends DjotParentNode {
37
+ destination: string;
38
+ tag: "link";
39
+ }
40
+ interface DjotImageNode extends DjotParentNode {
41
+ destination: string;
42
+ tag: "image";
43
+ }
44
+ interface DjotBulletListNode extends DjotParentNode {
45
+ tag: "bullet_list";
46
+ }
47
+ interface DjotOrderedListNode extends DjotParentNode {
48
+ start?: number;
49
+ tag: "ordered_list";
50
+ }
51
+ interface DjotListItemNode extends DjotParentNode {
52
+ tag: "list_item";
53
+ }
54
+ interface DjotBlockquoteNode extends DjotParentNode {
55
+ tag: "blockquote";
56
+ }
57
+ interface DjotThematicBreakNode extends DjotBaseNode {
58
+ tag: "thematic_break";
59
+ }
60
+ interface DjotStrNode extends DjotBaseNode {
61
+ tag: "str";
62
+ text: string;
63
+ }
64
+ interface DjotSoftBreakNode extends DjotBaseNode {
65
+ tag: "soft_break" | "softbreak";
66
+ }
67
+ interface DjotHardBreakNode extends DjotBaseNode {
68
+ tag: "hard_break" | "hardbreak";
69
+ }
70
+ type DjotNode = DjotDocNode | DjotParaNode | DjotHeadingNode | DjotEmphNode | DjotStrongNode | DjotCodeNode | DjotCodeBlockNode | DjotLinkNode | DjotImageNode | DjotBulletListNode | DjotOrderedListNode | DjotListItemNode | DjotBlockquoteNode | DjotThematicBreakNode | DjotStrNode | DjotSoftBreakNode | DjotHardBreakNode;
71
+ type DjotNodeTag = DjotNode["tag"];
72
+ type DjotNodeByTag<Tag extends DjotNodeTag> = Extract<DjotNode, {
73
+ tag: Tag;
74
+ }>;
75
+ interface DjotNodePropsBase<Tag extends DjotNodeTag> {
76
+ children?: React.ReactNode;
77
+ node: DjotNodeByTag<Tag>;
78
+ }
79
+ interface DjotComponentPropsMap {
80
+ doc: DjotNodePropsBase<"doc">;
81
+ para: React.HTMLAttributes<HTMLParagraphElement> & DjotNodePropsBase<"para">;
82
+ heading: React.HTMLAttributes<HTMLHeadingElement> & DjotNodePropsBase<"heading"> & {
83
+ level: number;
84
+ };
85
+ emph: React.HTMLAttributes<HTMLElement> & DjotNodePropsBase<"emph">;
86
+ strong: React.HTMLAttributes<HTMLElement> & DjotNodePropsBase<"strong">;
87
+ code: React.HTMLAttributes<HTMLElement> & DjotNodePropsBase<"code"> & {
88
+ value: string;
89
+ };
90
+ code_block: React.HTMLAttributes<HTMLPreElement> & DjotNodePropsBase<"code_block"> & {
91
+ language?: string;
92
+ value: string;
93
+ };
94
+ link: React.AnchorHTMLAttributes<HTMLAnchorElement> & DjotNodePropsBase<"link">;
95
+ image: React.ImgHTMLAttributes<HTMLImageElement> & DjotNodePropsBase<"image"> & {
96
+ alt?: string;
97
+ };
98
+ bullet_list: React.HTMLAttributes<HTMLUListElement> & DjotNodePropsBase<"bullet_list">;
99
+ ordered_list: React.OlHTMLAttributes<HTMLOListElement> & DjotNodePropsBase<"ordered_list">;
100
+ list_item: React.LiHTMLAttributes<HTMLLIElement> & DjotNodePropsBase<"list_item">;
101
+ blockquote: React.BlockquoteHTMLAttributes<HTMLQuoteElement> & DjotNodePropsBase<"blockquote">;
102
+ thematic_break: React.HTMLAttributes<HTMLHRElement> & DjotNodePropsBase<"thematic_break">;
103
+ str: DjotNodePropsBase<"str"> & {
104
+ value: string;
105
+ };
106
+ soft_break: DjotNodePropsBase<"soft_break">;
107
+ softbreak: DjotNodePropsBase<"softbreak">;
108
+ hard_break: DjotNodePropsBase<"hard_break">;
109
+ hardbreak: DjotNodePropsBase<"hardbreak">;
110
+ }
111
+ type DjotComponents = Partial<{
112
+ [K in keyof DjotComponentPropsMap]: React.ElementType<DjotComponentPropsMap[K]>;
113
+ }>;
114
+ interface DjotProps {
115
+ children?: string | null | undefined;
116
+ components?: DjotComponents | undefined;
117
+ }
118
+
119
+ declare function Djot({ children, components }: DjotProps): React.ReactElement | null;
120
+
121
+ interface RenderNodeOptions {
122
+ components?: DjotComponents | undefined;
123
+ key?: React.Key;
124
+ }
125
+ declare function renderNode(node: DjotNode, options?: RenderNodeOptions): React.ReactNode;
126
+
127
+ export { Djot, type DjotComponentPropsMap, type DjotComponents, type DjotNode, type DjotNodeByTag, type DjotNodeTag, type DjotProps, Djot as default, renderNode };
package/dist/index.js ADDED
@@ -0,0 +1,365 @@
1
+ import { parse } from '@djot/djot';
2
+ import { Fragment, createElement } from 'react';
3
+ import { jsx } from 'react/jsx-runtime';
4
+
5
+ // src/Djot.tsx
6
+ function isParentNode(node) {
7
+ return Array.isArray(node.children);
8
+ }
9
+ function isSoftBreakNode(node) {
10
+ return node.tag === "soft_break" || node.tag === "softbreak";
11
+ }
12
+ function isHardBreakNode(node) {
13
+ return node.tag === "hard_break" || node.tag === "hardbreak";
14
+ }
15
+ function pickComponent(components, primary, alias) {
16
+ if (!components) {
17
+ return void 0;
18
+ }
19
+ return components[primary] ?? (alias ? components[alias] : void 0);
20
+ }
21
+ function renderChildren(children, components) {
22
+ return children.map(
23
+ (child, index) => renderNode(child, {
24
+ components,
25
+ key: index
26
+ })
27
+ );
28
+ }
29
+ function toAltText(nodes) {
30
+ let output = "";
31
+ for (const node of nodes) {
32
+ switch (node.tag) {
33
+ case "str":
34
+ case "code":
35
+ case "code_block":
36
+ output += node.text;
37
+ break;
38
+ case "soft_break":
39
+ case "softbreak":
40
+ output += " ";
41
+ break;
42
+ case "hard_break":
43
+ case "hardbreak":
44
+ output += "\n";
45
+ break;
46
+ default:
47
+ if (isParentNode(node)) {
48
+ output += toAltText(node.children);
49
+ }
50
+ break;
51
+ }
52
+ }
53
+ return output.trim();
54
+ }
55
+ function clampHeadingLevel(level) {
56
+ if (level <= 1) {
57
+ return 1;
58
+ }
59
+ if (level >= 6) {
60
+ return 6;
61
+ }
62
+ return level;
63
+ }
64
+ function withKey(props, key) {
65
+ if (key === void 0) {
66
+ return props;
67
+ }
68
+ return {
69
+ ...props,
70
+ key
71
+ };
72
+ }
73
+ function renderWithOverride(override, fallback, domProps, customProps, key, children) {
74
+ const Component = override ?? fallback;
75
+ const props = typeof Component === "string" ? withKey(domProps, key) : withKey(
76
+ {
77
+ ...domProps,
78
+ ...customProps
79
+ },
80
+ key
81
+ );
82
+ return createElement(Component, props, children);
83
+ }
84
+ function renderDoc(node, components, key) {
85
+ const children = renderChildren(node.children, components);
86
+ const Component = pickComponent(components, "doc");
87
+ if (Component) {
88
+ if (typeof Component === "string") {
89
+ return createElement(Component, withKey({}, key), children);
90
+ }
91
+ return createElement(Component, withKey({ node }, key), children);
92
+ }
93
+ return createElement(Fragment, withKey({}, key), children);
94
+ }
95
+ function renderHeading(node, components, key) {
96
+ const level = clampHeadingLevel(node.level);
97
+ const children = renderChildren(node.children, components);
98
+ return renderWithOverride(
99
+ pickComponent(components, "heading"),
100
+ `h${level}`,
101
+ {},
102
+ {
103
+ level,
104
+ node
105
+ },
106
+ key,
107
+ children
108
+ );
109
+ }
110
+ function renderCode(node, components, key) {
111
+ const value = node.text;
112
+ return renderWithOverride(
113
+ pickComponent(components, "code"),
114
+ "code",
115
+ {},
116
+ {
117
+ node,
118
+ value
119
+ },
120
+ key,
121
+ value
122
+ );
123
+ }
124
+ function renderCodeBlock(node, components, key) {
125
+ const value = node.text;
126
+ const language = node.lang;
127
+ const fallbackChildren = createElement(
128
+ "code",
129
+ {
130
+ className: language ? `language-${language}` : void 0
131
+ },
132
+ value
133
+ );
134
+ return renderWithOverride(
135
+ pickComponent(components, "code_block"),
136
+ "pre",
137
+ {},
138
+ {
139
+ language,
140
+ node,
141
+ value
142
+ },
143
+ key,
144
+ fallbackChildren
145
+ );
146
+ }
147
+ function renderLink(node, components, key) {
148
+ const children = renderChildren(node.children, components);
149
+ const href = node.destination;
150
+ return renderWithOverride(
151
+ pickComponent(components, "link"),
152
+ "a",
153
+ {
154
+ href
155
+ },
156
+ {
157
+ node
158
+ },
159
+ key,
160
+ children
161
+ );
162
+ }
163
+ function renderImage(node, components, key) {
164
+ const alt = toAltText(node.children) || void 0;
165
+ const src = node.destination;
166
+ return renderWithOverride(
167
+ pickComponent(components, "image"),
168
+ "img",
169
+ {
170
+ alt,
171
+ src
172
+ },
173
+ {
174
+ alt,
175
+ node
176
+ },
177
+ key
178
+ );
179
+ }
180
+ function renderOrderedList(node, components, key) {
181
+ const children = renderChildren(node.children, components);
182
+ return renderWithOverride(
183
+ pickComponent(components, "ordered_list"),
184
+ "ol",
185
+ {
186
+ start: node.start
187
+ },
188
+ {
189
+ node,
190
+ start: node.start
191
+ },
192
+ key,
193
+ children
194
+ );
195
+ }
196
+ function renderStr(node, components, key) {
197
+ const value = node.text;
198
+ const Component = pickComponent(components, "str");
199
+ if (!Component) {
200
+ return value;
201
+ }
202
+ if (typeof Component === "string") {
203
+ return createElement(Component, withKey({}, key), value);
204
+ }
205
+ return createElement(
206
+ Component,
207
+ withKey(
208
+ {
209
+ node,
210
+ value
211
+ },
212
+ key
213
+ ),
214
+ value
215
+ );
216
+ }
217
+ function renderSoftBreak(node, components, key) {
218
+ const Component = pickComponent(components, "soft_break", "softbreak");
219
+ if (!Component) {
220
+ return "\n";
221
+ }
222
+ if (typeof Component === "string") {
223
+ return createElement(Component, withKey({}, key), "\n");
224
+ }
225
+ return createElement(
226
+ Component,
227
+ withKey(
228
+ {
229
+ node
230
+ },
231
+ key
232
+ ),
233
+ "\n"
234
+ );
235
+ }
236
+ function renderHardBreak(node, components, key) {
237
+ return renderWithOverride(
238
+ pickComponent(components, "hard_break", "hardbreak"),
239
+ "br",
240
+ {},
241
+ {
242
+ node
243
+ },
244
+ key
245
+ );
246
+ }
247
+ function renderNode(node, options = {}) {
248
+ const { components, key } = options;
249
+ const children = isParentNode(node) ? renderChildren(node.children, components) : void 0;
250
+ switch (node.tag) {
251
+ case "doc":
252
+ return renderDoc(node, components, key);
253
+ case "para":
254
+ return renderWithOverride(
255
+ pickComponent(components, "para"),
256
+ "p",
257
+ {},
258
+ {
259
+ node
260
+ },
261
+ key,
262
+ children
263
+ );
264
+ case "heading":
265
+ return renderHeading(node, components, key);
266
+ case "emph":
267
+ return renderWithOverride(
268
+ pickComponent(components, "emph"),
269
+ "em",
270
+ {},
271
+ {
272
+ node
273
+ },
274
+ key,
275
+ children
276
+ );
277
+ case "strong":
278
+ return renderWithOverride(
279
+ pickComponent(components, "strong"),
280
+ "strong",
281
+ {},
282
+ {
283
+ node
284
+ },
285
+ key,
286
+ children
287
+ );
288
+ case "code":
289
+ return renderCode(node, components, key);
290
+ case "code_block":
291
+ return renderCodeBlock(node, components, key);
292
+ case "link":
293
+ return renderLink(node, components, key);
294
+ case "image":
295
+ return renderImage(node, components, key);
296
+ case "bullet_list":
297
+ return renderWithOverride(
298
+ pickComponent(components, "bullet_list"),
299
+ "ul",
300
+ {},
301
+ {
302
+ node
303
+ },
304
+ key,
305
+ children
306
+ );
307
+ case "ordered_list":
308
+ return renderOrderedList(node, components, key);
309
+ case "list_item":
310
+ return renderWithOverride(
311
+ pickComponent(components, "list_item"),
312
+ "li",
313
+ {},
314
+ {
315
+ node
316
+ },
317
+ key,
318
+ children
319
+ );
320
+ case "blockquote":
321
+ return renderWithOverride(
322
+ pickComponent(components, "blockquote"),
323
+ "blockquote",
324
+ {},
325
+ {
326
+ node
327
+ },
328
+ key,
329
+ children
330
+ );
331
+ case "thematic_break":
332
+ return renderWithOverride(
333
+ pickComponent(components, "thematic_break"),
334
+ "hr",
335
+ {},
336
+ {
337
+ node
338
+ },
339
+ key
340
+ );
341
+ case "str":
342
+ return renderStr(node, components, key);
343
+ default:
344
+ if (isSoftBreakNode(node)) {
345
+ return renderSoftBreak(node, components, key);
346
+ }
347
+ if (isHardBreakNode(node)) {
348
+ return renderHardBreak(node, components, key);
349
+ }
350
+ return null;
351
+ }
352
+ }
353
+ function Djot({ children, components }) {
354
+ const source = children ?? "";
355
+ if (source.length === 0) {
356
+ return null;
357
+ }
358
+ const ast = parse(source);
359
+ return /* @__PURE__ */ jsx(Fragment, { children: renderNode(ast, { components }) });
360
+ }
361
+ var Djot_default = Djot;
362
+
363
+ export { Djot, Djot_default as default, renderNode };
364
+ //# sourceMappingURL=index.js.map
365
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/renderNode.tsx","../src/Djot.tsx"],"names":["Fragment"],"mappings":"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","file":"index.js","sourcesContent":["import { createElement, Fragment } from \"react\";\nimport type React from \"react\";\nimport type {\n DjotBaseNode,\n DjotCodeBlockNode,\n DjotCodeNode,\n DjotComponentPropsMap,\n DjotComponents,\n DjotDocNode,\n DjotHardBreakNode,\n DjotHeadingNode,\n DjotImageNode,\n DjotLinkNode,\n DjotNode,\n DjotOrderedListNode,\n DjotParentNode,\n DjotSoftBreakNode,\n DjotStrNode\n} from \"./types\";\n\nexport interface RenderNodeOptions {\n components?: DjotComponents | undefined;\n key?: React.Key;\n}\n\ntype ComponentKey = keyof DjotComponentPropsMap;\n\nfunction isParentNode(node: DjotBaseNode): node is DjotParentNode {\n return Array.isArray((node as DjotParentNode).children);\n}\n\nfunction isSoftBreakNode(node: DjotNode): node is DjotSoftBreakNode {\n return node.tag === \"soft_break\" || node.tag === \"softbreak\";\n}\n\nfunction isHardBreakNode(node: DjotNode): node is DjotHardBreakNode {\n return node.tag === \"hard_break\" || node.tag === \"hardbreak\";\n}\n\nfunction pickComponent(\n components: DjotComponents | undefined,\n primary: ComponentKey,\n alias?: ComponentKey\n): React.ElementType | undefined {\n if (!components) {\n return undefined;\n }\n\n return (components[primary] ?? (alias ? components[alias] : undefined)) as\n | React.ElementType\n | undefined;\n}\n\nfunction renderChildren(children: DjotNode[], components?: DjotComponents): React.ReactNode[] {\n return children.map((child, index) =>\n renderNode(child, {\n components,\n key: index\n })\n );\n}\n\nfunction toAltText(nodes: DjotNode[]): string {\n let output = \"\";\n\n for (const node of nodes) {\n switch (node.tag) {\n case \"str\":\n case \"code\":\n case \"code_block\":\n output += node.text;\n break;\n case \"soft_break\":\n case \"softbreak\":\n output += \" \";\n break;\n case \"hard_break\":\n case \"hardbreak\":\n output += \"\\n\";\n break;\n default:\n if (isParentNode(node)) {\n output += toAltText(node.children);\n }\n break;\n }\n }\n\n return output.trim();\n}\n\nfunction clampHeadingLevel(level: number): 1 | 2 | 3 | 4 | 5 | 6 {\n if (level <= 1) {\n return 1;\n }\n\n if (level >= 6) {\n return 6;\n }\n\n return level as 1 | 2 | 3 | 4 | 5 | 6;\n}\n\nfunction withKey<T extends Record<string, unknown>>(props: T, key?: React.Key): T & { key?: React.Key } {\n if (key === undefined) {\n return props;\n }\n\n return {\n ...props,\n key\n };\n}\n\nfunction renderWithOverride(\n override: React.ElementType | undefined,\n fallback: React.ElementType,\n domProps: Record<string, unknown>,\n customProps: Record<string, unknown>,\n key?: React.Key,\n children?: React.ReactNode\n): React.ReactNode {\n const Component = override ?? fallback;\n const props =\n typeof Component === \"string\"\n ? withKey(domProps, key)\n : withKey(\n {\n ...domProps,\n ...customProps\n },\n key\n );\n\n return createElement(Component, props, children);\n}\n\nfunction renderDoc(\n node: DjotDocNode,\n components: DjotComponents | undefined,\n key?: React.Key\n): React.ReactNode {\n const children = renderChildren(node.children, components);\n const Component = pickComponent(components, \"doc\");\n\n if (Component) {\n if (typeof Component === \"string\") {\n return createElement(Component, withKey({}, key), children);\n }\n\n return createElement(Component, withKey({ node }, key), children);\n }\n\n return createElement(Fragment, withKey({}, key), children);\n}\n\nfunction renderHeading(\n node: DjotHeadingNode,\n components: DjotComponents | undefined,\n key?: React.Key\n): React.ReactNode {\n const level = clampHeadingLevel(node.level);\n const children = renderChildren(node.children, components);\n return renderWithOverride(\n pickComponent(components, \"heading\"),\n `h${level}`,\n {},\n {\n level,\n node\n },\n key,\n children\n );\n}\n\nfunction renderCode(\n node: DjotCodeNode,\n components: DjotComponents | undefined,\n key?: React.Key\n): React.ReactNode {\n const value = node.text;\n return renderWithOverride(\n pickComponent(components, \"code\"),\n \"code\",\n {},\n {\n node,\n value\n },\n key,\n value\n );\n}\n\nfunction renderCodeBlock(\n node: DjotCodeBlockNode,\n components: DjotComponents | undefined,\n key?: React.Key\n): React.ReactNode {\n const value = node.text;\n const language = node.lang;\n const fallbackChildren = createElement(\n \"code\",\n {\n className: language ? `language-${language}` : undefined\n },\n value\n );\n\n return renderWithOverride(\n pickComponent(components, \"code_block\"),\n \"pre\",\n {},\n {\n language,\n node,\n value\n },\n key,\n fallbackChildren\n );\n}\n\nfunction renderLink(\n node: DjotLinkNode,\n components: DjotComponents | undefined,\n key?: React.Key\n): React.ReactNode {\n const children = renderChildren(node.children, components);\n const href = node.destination;\n return renderWithOverride(\n pickComponent(components, \"link\"),\n \"a\",\n {\n href\n },\n {\n node\n },\n key,\n children\n );\n}\n\nfunction renderImage(\n node: DjotImageNode,\n components: DjotComponents | undefined,\n key?: React.Key\n): React.ReactNode {\n const alt = toAltText(node.children) || undefined;\n const src = node.destination;\n return renderWithOverride(\n pickComponent(components, \"image\"),\n \"img\",\n {\n alt,\n src\n },\n {\n alt,\n node\n },\n key\n );\n}\n\nfunction renderOrderedList(\n node: DjotOrderedListNode,\n components: DjotComponents | undefined,\n key?: React.Key\n): React.ReactNode {\n const children = renderChildren(node.children, components);\n return renderWithOverride(\n pickComponent(components, \"ordered_list\"),\n \"ol\",\n {\n start: node.start\n },\n {\n node,\n start: node.start\n },\n key,\n children\n );\n}\n\nfunction renderStr(\n node: DjotStrNode,\n components: DjotComponents | undefined,\n key?: React.Key\n): React.ReactNode {\n const value = node.text;\n const Component = pickComponent(components, \"str\");\n\n if (!Component) {\n return value;\n }\n\n if (typeof Component === \"string\") {\n return createElement(Component, withKey({}, key), value);\n }\n\n return createElement(\n Component,\n withKey(\n {\n node,\n value\n },\n key\n ),\n value\n );\n}\n\nfunction renderSoftBreak(\n node: DjotSoftBreakNode,\n components: DjotComponents | undefined,\n key?: React.Key\n): React.ReactNode {\n const Component = pickComponent(components, \"soft_break\", \"softbreak\");\n\n if (!Component) {\n return \"\\n\";\n }\n\n if (typeof Component === \"string\") {\n return createElement(Component, withKey({}, key), \"\\n\");\n }\n\n return createElement(\n Component,\n withKey(\n {\n node\n },\n key\n ),\n \"\\n\"\n );\n}\n\nfunction renderHardBreak(\n node: DjotHardBreakNode,\n components: DjotComponents | undefined,\n key?: React.Key\n): React.ReactNode {\n return renderWithOverride(\n pickComponent(components, \"hard_break\", \"hardbreak\"),\n \"br\",\n {},\n {\n node\n },\n key\n );\n}\n\nexport function renderNode(node: DjotNode, options: RenderNodeOptions = {}): React.ReactNode {\n const { components, key } = options;\n const children = isParentNode(node) ? renderChildren(node.children, components) : undefined;\n\n switch (node.tag) {\n case \"doc\":\n return renderDoc(node, components, key);\n case \"para\":\n return renderWithOverride(\n pickComponent(components, \"para\"),\n \"p\",\n {},\n {\n node\n },\n key,\n children\n );\n case \"heading\":\n return renderHeading(node, components, key);\n case \"emph\":\n return renderWithOverride(\n pickComponent(components, \"emph\"),\n \"em\",\n {},\n {\n node\n },\n key,\n children\n );\n case \"strong\":\n return renderWithOverride(\n pickComponent(components, \"strong\"),\n \"strong\",\n {},\n {\n node\n },\n key,\n children\n );\n case \"code\":\n return renderCode(node, components, key);\n case \"code_block\":\n return renderCodeBlock(node, components, key);\n case \"link\":\n return renderLink(node, components, key);\n case \"image\":\n return renderImage(node, components, key);\n case \"bullet_list\":\n return renderWithOverride(\n pickComponent(components, \"bullet_list\"),\n \"ul\",\n {},\n {\n node\n },\n key,\n children\n );\n case \"ordered_list\":\n return renderOrderedList(node, components, key);\n case \"list_item\":\n return renderWithOverride(\n pickComponent(components, \"list_item\"),\n \"li\",\n {},\n {\n node\n },\n key,\n children\n );\n case \"blockquote\":\n return renderWithOverride(\n pickComponent(components, \"blockquote\"),\n \"blockquote\",\n {},\n {\n node\n },\n key,\n children\n );\n case \"thematic_break\":\n return renderWithOverride(\n pickComponent(components, \"thematic_break\"),\n \"hr\",\n {},\n {\n node\n },\n key\n );\n case \"str\":\n return renderStr(node, components, key);\n default:\n if (isSoftBreakNode(node)) {\n return renderSoftBreak(node, components, key);\n }\n\n if (isHardBreakNode(node)) {\n return renderHardBreak(node, components, key);\n }\n\n return null;\n }\n}\n","import { parse } from \"@djot/djot\";\nimport { Fragment } from \"react\";\nimport type React from \"react\";\nimport { renderNode } from \"./renderNode\";\nimport type { DjotNode, DjotProps } from \"./types\";\n\nexport function Djot({ children, components }: DjotProps): React.ReactElement | null {\n const source = children ?? \"\";\n\n if (source.length === 0) {\n return null;\n }\n\n const ast = parse(source) as DjotNode;\n return <Fragment>{renderNode(ast, { components })}</Fragment>;\n}\n\nexport default Djot;\n"]}
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@willwang-io/react-djot",
3
+ "version": "0.1.1",
4
+ "description": "Render Djot to React with a react-markdown style component override API.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.cjs",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "require": "./dist/index.cjs"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "sideEffects": false,
21
+ "engines": {
22
+ "node": ">=18"
23
+ },
24
+ "scripts": {
25
+ "build": "tsup",
26
+ "dev": "tsup --watch",
27
+ "typecheck": "tsc --noEmit",
28
+ "test": "vitest run",
29
+ "test:watch": "vitest",
30
+ "lint": "eslint . --ext .ts,.tsx",
31
+ "format": "prettier . --write",
32
+ "format:check": "prettier . --check"
33
+ },
34
+ "peerDependencies": {
35
+ "@djot/djot": "^0.3.0",
36
+ "react": ">=18",
37
+ "react-dom": ">=18"
38
+ },
39
+ "devDependencies": {
40
+ "@djot/djot": "^0.3.0",
41
+ "@types/node": "^24.0.0",
42
+ "@types/react": "^18.3.0",
43
+ "@types/react-dom": "^18.3.0",
44
+ "@typescript-eslint/eslint-plugin": "^8.0.0",
45
+ "@typescript-eslint/parser": "^8.0.0",
46
+ "eslint": "^8.57.0",
47
+ "eslint-config-prettier": "^9.1.0",
48
+ "prettier": "^3.3.0",
49
+ "react": "^18.3.1",
50
+ "react-dom": "^18.3.1",
51
+ "tsup": "^8.2.0",
52
+ "typescript": "^5.6.0",
53
+ "vitest": "^2.1.0"
54
+ },
55
+ "publishConfig": {
56
+ "access": "public"
57
+ }
58
+ }