@uniweb/content-reader 1.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.
@@ -0,0 +1,260 @@
1
+ import { markdownToProseMirror } from "../src/index.js";
2
+
3
+ describe("List Parsing", () => {
4
+ test("parses unordered list", () => {
5
+ const markdown = `
6
+ - First item
7
+ - Second item
8
+ - Third item`;
9
+
10
+ const result = markdownToProseMirror(markdown);
11
+
12
+ expect(result).toEqual({
13
+ type: "doc",
14
+ content: [
15
+ {
16
+ type: "bulletList",
17
+ content: [
18
+ {
19
+ type: "listItem",
20
+ content: [
21
+ {
22
+ type: "paragraph",
23
+ content: [{ type: "text", text: "First item" }],
24
+ },
25
+ ],
26
+ },
27
+ {
28
+ type: "listItem",
29
+ content: [
30
+ {
31
+ type: "paragraph",
32
+ content: [{ type: "text", text: "Second item" }],
33
+ },
34
+ ],
35
+ },
36
+ {
37
+ type: "listItem",
38
+ content: [
39
+ {
40
+ type: "paragraph",
41
+ content: [{ type: "text", text: "Third item" }],
42
+ },
43
+ ],
44
+ },
45
+ ],
46
+ },
47
+ ],
48
+ });
49
+ });
50
+
51
+ test("parses ordered list", () => {
52
+ const markdown = `
53
+ 1. First item
54
+ 2. Second item
55
+ 3. Third item`;
56
+
57
+ const result = markdownToProseMirror(markdown);
58
+
59
+ expect(result).toEqual({
60
+ type: "doc",
61
+ content: [
62
+ {
63
+ type: "orderedList",
64
+ attrs: { start: 1 },
65
+ content: [
66
+ {
67
+ type: "listItem",
68
+ content: [
69
+ {
70
+ type: "paragraph",
71
+ content: [{ type: "text", text: "First item" }],
72
+ },
73
+ ],
74
+ },
75
+ {
76
+ type: "listItem",
77
+ content: [
78
+ {
79
+ type: "paragraph",
80
+ content: [{ type: "text", text: "Second item" }],
81
+ },
82
+ ],
83
+ },
84
+ {
85
+ type: "listItem",
86
+ content: [
87
+ {
88
+ type: "paragraph",
89
+ content: [{ type: "text", text: "Third item" }],
90
+ },
91
+ ],
92
+ },
93
+ ],
94
+ },
95
+ ],
96
+ });
97
+ });
98
+
99
+ test("parses nested lists", () => {
100
+ const markdown = `
101
+ - First item
102
+ - Nested item 1
103
+ - Nested item 2
104
+ - Second item
105
+ 1. Nested ordered 1
106
+ 2. Nested ordered 2`;
107
+
108
+ const result = markdownToProseMirror(markdown);
109
+
110
+ expect(result).toEqual({
111
+ type: "doc",
112
+ content: [
113
+ {
114
+ type: "bulletList",
115
+ content: [
116
+ {
117
+ type: "listItem",
118
+ content: [
119
+ {
120
+ type: "paragraph",
121
+ content: [{ type: "text", text: "First item" }],
122
+ },
123
+ {
124
+ type: "bulletList",
125
+ content: [
126
+ {
127
+ type: "listItem",
128
+ content: [
129
+ {
130
+ type: "paragraph",
131
+ content: [{ type: "text", text: "Nested item 1" }],
132
+ },
133
+ ],
134
+ },
135
+ {
136
+ type: "listItem",
137
+ content: [
138
+ {
139
+ type: "paragraph",
140
+ content: [{ type: "text", text: "Nested item 2" }],
141
+ },
142
+ ],
143
+ },
144
+ ],
145
+ },
146
+ ],
147
+ },
148
+ {
149
+ type: "listItem",
150
+ content: [
151
+ {
152
+ type: "paragraph",
153
+ content: [{ type: "text", text: "Second item" }],
154
+ },
155
+ {
156
+ type: "orderedList",
157
+ attrs: { start: 1 },
158
+ content: [
159
+ {
160
+ type: "listItem",
161
+ content: [
162
+ {
163
+ type: "paragraph",
164
+ content: [{ type: "text", text: "Nested ordered 1" }],
165
+ },
166
+ ],
167
+ },
168
+ {
169
+ type: "listItem",
170
+ content: [
171
+ {
172
+ type: "paragraph",
173
+ content: [{ type: "text", text: "Nested ordered 2" }],
174
+ },
175
+ ],
176
+ },
177
+ ],
178
+ },
179
+ ],
180
+ },
181
+ ],
182
+ },
183
+ ],
184
+ });
185
+ });
186
+
187
+ test("parses list items with formatted text", () => {
188
+ const markdown = `
189
+ - Item with **bold** text
190
+ - Item with *italic* text
191
+ - Item with [link](https://example.com)`;
192
+
193
+ const result = markdownToProseMirror(markdown);
194
+
195
+ expect(result).toEqual({
196
+ type: "doc",
197
+ content: [
198
+ {
199
+ type: "bulletList",
200
+ content: [
201
+ {
202
+ type: "listItem",
203
+ content: [
204
+ {
205
+ type: "paragraph",
206
+ content: [
207
+ { type: "text", text: "Item with " },
208
+ { type: "text", text: "bold", marks: [{ type: "bold" }] },
209
+ { type: "text", text: " text" },
210
+ ],
211
+ },
212
+ ],
213
+ },
214
+ {
215
+ type: "listItem",
216
+ content: [
217
+ {
218
+ type: "paragraph",
219
+ content: [
220
+ { type: "text", text: "Item with " },
221
+ {
222
+ type: "text",
223
+ text: "italic",
224
+ marks: [{ type: "italic" }],
225
+ },
226
+ { type: "text", text: " text" },
227
+ ],
228
+ },
229
+ ],
230
+ },
231
+ {
232
+ type: "listItem",
233
+ content: [
234
+ {
235
+ type: "paragraph",
236
+ content: [
237
+ { type: "text", text: "Item with " },
238
+ {
239
+ type: "text",
240
+ text: "link",
241
+ marks: [
242
+ {
243
+ type: "link",
244
+ attrs: {
245
+ href: "https://example.com",
246
+ title: null,
247
+ },
248
+ },
249
+ ],
250
+ },
251
+ ],
252
+ },
253
+ ],
254
+ },
255
+ ],
256
+ },
257
+ ],
258
+ });
259
+ });
260
+ });
@@ -0,0 +1,343 @@
1
+ import { markdownToProseMirror } from "../src/index.js";
2
+
3
+ describe("Basic Markdown Parsing", () => {
4
+ test("handles plain text with special characters", () => {
5
+ const markdown = `text with 'single', "double", & specials – — © ® ™`;
6
+ const result = markdownToProseMirror(markdown);
7
+
8
+ expect(result).toEqual({
9
+ type: "doc",
10
+ content: [
11
+ {
12
+ type: "paragraph",
13
+ content: [
14
+ {
15
+ type: "text",
16
+ text: `text with 'single', "double", & specials – — © ® ™`,
17
+ },
18
+ ],
19
+ },
20
+ ],
21
+ });
22
+ });
23
+
24
+ test("parses accidental HTML paragraph", () => {
25
+ const markdown = `Some <tag looking> text`;
26
+ const result = markdownToProseMirror(markdown);
27
+
28
+ expect(result).toEqual({
29
+ type: "doc",
30
+ content: [
31
+ {
32
+ type: "paragraph",
33
+ content: [
34
+ {
35
+ type: "text",
36
+ text: "Some ",
37
+ },
38
+ {
39
+ type: "text",
40
+ text: "<tag looking>",
41
+ },
42
+ {
43
+ type: "text",
44
+ text: " text",
45
+ },
46
+ ],
47
+ },
48
+ ],
49
+ });
50
+ });
51
+
52
+ test("parses basic formatting", () => {
53
+ const markdown = "Some **bold** and *italic* text";
54
+ const result = markdownToProseMirror(markdown);
55
+
56
+ expect(result).toEqual({
57
+ type: "doc",
58
+ content: [
59
+ {
60
+ type: "paragraph",
61
+ content: [
62
+ { type: "text", text: "Some " },
63
+ { type: "text", text: "bold", marks: [{ type: "bold" }] },
64
+ { type: "text", text: " and " },
65
+ { type: "text", text: "italic", marks: [{ type: "italic" }] },
66
+ { type: "text", text: " text" },
67
+ ],
68
+ },
69
+ ],
70
+ });
71
+ });
72
+
73
+ test("handles nested formatting", () => {
74
+ const markdown = `**_bold italic_** text with **bold *then italic*** and *italic **then bold***`;
75
+ const result = markdownToProseMirror(markdown);
76
+
77
+ expect(result).toEqual({
78
+ type: "doc",
79
+ content: [
80
+ {
81
+ type: "paragraph",
82
+ content: [
83
+ {
84
+ type: "text",
85
+ text: "bold italic",
86
+ marks: [{ type: "italic" }, { type: "bold" }],
87
+ },
88
+ { type: "text", text: " text with " },
89
+ { type: "text", text: "bold ", marks: [{ type: "bold" }] },
90
+ {
91
+ type: "text",
92
+ text: "then italic",
93
+ marks: [{ type: "italic" }, { type: "bold" }],
94
+ },
95
+ { type: "text", text: " and " },
96
+ { type: "text", text: "italic ", marks: [{ type: "italic" }] },
97
+ {
98
+ type: "text",
99
+ text: "then bold",
100
+ marks: [{ type: "bold" }, { type: "italic" }],
101
+ },
102
+ ],
103
+ },
104
+ ],
105
+ });
106
+ });
107
+
108
+ test("triple nested formatting", () => {
109
+ const markdown = `***bold italic both***`;
110
+ const result = markdownToProseMirror(markdown);
111
+
112
+ expect(result).toEqual({
113
+ type: "doc",
114
+ content: [
115
+ {
116
+ type: "paragraph",
117
+ content: [
118
+ {
119
+ type: "text",
120
+ text: "bold italic both",
121
+ marks: [{ type: "bold" }, { type: "italic" }],
122
+ },
123
+ ],
124
+ },
125
+ ],
126
+ });
127
+ });
128
+
129
+ test("parses headings", () => {
130
+ const markdown = "# Main Title\n## Subtitle";
131
+ const result = markdownToProseMirror(markdown);
132
+
133
+ expect(result).toEqual({
134
+ type: "doc",
135
+ content: [
136
+ {
137
+ type: "heading",
138
+ attrs: { level: 1, id: null },
139
+ content: [{ type: "text", text: "Main Title" }],
140
+ },
141
+ {
142
+ type: "heading",
143
+ attrs: { level: 2, id: null },
144
+ content: [{ type: "text", text: "Subtitle" }],
145
+ },
146
+ ],
147
+ });
148
+ });
149
+
150
+ test("parses links", () => {
151
+ const markdown = '[Link text](https://example.com "Title")';
152
+ const result = markdownToProseMirror(markdown);
153
+
154
+ expect(result).toEqual({
155
+ type: "doc",
156
+ content: [
157
+ {
158
+ type: "paragraph",
159
+ content: [
160
+ {
161
+ type: "text",
162
+ text: "Link text",
163
+ marks: [
164
+ {
165
+ type: "link",
166
+ attrs: {
167
+ href: "https://example.com",
168
+ title: "Title",
169
+ },
170
+ },
171
+ ],
172
+ },
173
+ ],
174
+ },
175
+ ],
176
+ });
177
+ });
178
+ });
179
+
180
+ describe("Extended Syntax", () => {
181
+ test("parses images without role", () => {
182
+ const markdown = "![Title](path/to/image.svg)";
183
+ const result = markdownToProseMirror(markdown);
184
+
185
+ expect(result).toEqual({
186
+ type: "doc",
187
+ content: [
188
+ {
189
+ type: "paragraph",
190
+ content: [
191
+ {
192
+ type: "image",
193
+ attrs: {
194
+ src: "path/to/image.svg",
195
+ title: "Title",
196
+ alt: null,
197
+ role: "image",
198
+ },
199
+ },
200
+ ],
201
+ },
202
+ ],
203
+ });
204
+ });
205
+
206
+ test("parses images with roles", () => {
207
+ const markdown = '![Title](icon:path/to/image.svg "Alt text")';
208
+ const result = markdownToProseMirror(markdown);
209
+
210
+ expect(result).toEqual({
211
+ type: "doc",
212
+ content: [
213
+ {
214
+ type: "paragraph",
215
+ content: [
216
+ {
217
+ type: "image",
218
+ attrs: {
219
+ src: "path/to/image.svg",
220
+ title: "Title",
221
+ alt: "Alt text",
222
+ role: "icon",
223
+ },
224
+ },
225
+ ],
226
+ },
227
+ ],
228
+ });
229
+ });
230
+
231
+ test("parses images without URL", () => {
232
+ const markdown = "![Title](https://test.com)";
233
+ const result = markdownToProseMirror(markdown);
234
+
235
+ expect(result).toEqual({
236
+ type: "doc",
237
+ content: [
238
+ {
239
+ type: "paragraph",
240
+ content: [
241
+ {
242
+ type: "image",
243
+ attrs: {
244
+ src: "https://test.com",
245
+ title: "Title",
246
+ alt: null,
247
+ role: "image",
248
+ },
249
+ },
250
+ ],
251
+ },
252
+ ],
253
+ });
254
+ });
255
+
256
+ test("parses button links", () => {
257
+ const markdown = "[Button Text](button:https://example.com)";
258
+ const result = markdownToProseMirror(markdown);
259
+
260
+ expect(result).toEqual({
261
+ type: "doc",
262
+ content: [
263
+ {
264
+ type: "paragraph",
265
+ content: [
266
+ {
267
+ type: "text",
268
+ text: "Button Text",
269
+ marks: [
270
+ {
271
+ type: "button",
272
+ attrs: {
273
+ href: "https://example.com",
274
+ title: null,
275
+ variant: "primary",
276
+ },
277
+ },
278
+ ],
279
+ },
280
+ ],
281
+ },
282
+ ],
283
+ });
284
+ });
285
+
286
+ // test("parses eyebrow headings", () => {
287
+ // const markdown = "### Eyebrow\n# Main Title";
288
+ // const result = markdownToProseMirror(markdown);
289
+
290
+ // expect(result).toEqual({
291
+ // type: "doc",
292
+ // content: [
293
+ // {
294
+ // type: "eyebrowHeading",
295
+ // content: [{ type: "text", text: "Eyebrow" }],
296
+ // },
297
+ // {
298
+ // type: "heading",
299
+ // attrs: { level: 1, id: null },
300
+ // content: [{ type: "text", text: "Main Title" }],
301
+ // },
302
+ // ],
303
+ // });
304
+ // });
305
+
306
+ test("parses dividers", () => {
307
+ const markdown = "Text\n\n---\n\nMore text";
308
+ const result = markdownToProseMirror(markdown);
309
+
310
+ expect(result).toEqual({
311
+ type: "doc",
312
+ content: [
313
+ {
314
+ type: "paragraph",
315
+ content: [{ type: "text", text: "Text" }],
316
+ },
317
+ {
318
+ type: "divider",
319
+ attrs: { style: "line", size: "normal" },
320
+ },
321
+ {
322
+ type: "paragraph",
323
+ content: [{ type: "text", text: "More text" }],
324
+ },
325
+ ],
326
+ });
327
+ });
328
+
329
+ test("ignores HTML comments", () => {
330
+ const markdown = "<!-- Comment -->\nText\n<!-- Another comment -->";
331
+ const result = markdownToProseMirror(markdown);
332
+
333
+ expect(result).toEqual({
334
+ type: "doc",
335
+ content: [
336
+ {
337
+ type: "paragraph",
338
+ content: [{ type: "text", text: "Text" }],
339
+ },
340
+ ],
341
+ });
342
+ });
343
+ });
@@ -0,0 +1,44 @@
1
+ # Heading 1
2
+
3
+ ## Heading 2
4
+
5
+ ### Heading 3
6
+
7
+ Regular paragraph with **bold text**, _italic text_, and `inline code`.
8
+
9
+ > This is a blockquote with some important information
10
+ > It can span multiple lines
11
+
12
+ Unordered list:
13
+
14
+ - Item 1
15
+ - Item 2
16
+ - Nested item
17
+ - Item 3
18
+
19
+ Ordered list:
20
+
21
+ 1. First step
22
+ 2. Second step
23
+ 3. Third step
24
+
25
+ [This is a link](https://example.com)
26
+
27
+ ![Example image alt text](https://app-assets-ca-central-1-live.uniweb.app/uniweb/67cf5c7f80955/js/00a557d90de4fde4b16fbf4f7329a115.png "Party") test
28
+
29
+ [Video that talks about Chinese food in Guangdong](https://www.youtube.com/watch?v=f_jTbGACnJs&t=14s)
30
+
31
+ ---
32
+
33
+ Code block:
34
+
35
+ ```javascript
36
+ function testFunction() {
37
+ console.log("Hello world!");
38
+ return true;
39
+ }
40
+ ```
41
+
42
+ This paragraph has **_bold and italic_** formatting combined.
43
+
44
+ This is ~~strikethrough~~ text (though not in your detection patterns).