Skip to content

Commit 32a3b6c

Browse files
committed
fix(markdown): upgrade marked.js and document inline marks behavior
- Upgrade marked.js from v15.0.12 to v17.0.1 for latest bug fixes - Add comprehensive tests documenting CommonMark behavior for inline marks with punctuation - Clarify that whitespace is required around punctuation-only emphasis per CommonMark spec Fixes #7325
1 parent f592f0a commit 32a3b6c

File tree

4 files changed

+341
-7
lines changed

4 files changed

+341
-7
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@tiptap/markdown": patch
3+
---
4+
5+
Upgrade marked.js from v15.0.12 to v17.0.1. Note that `**)**` requires whitespace when adjacent to alphanumeric text per CommonMark specification.
Lines changed: 329 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,329 @@
1+
import { Bold } from '@tiptap/extension-bold'
2+
import { Document } from '@tiptap/extension-document'
3+
import { Italic } from '@tiptap/extension-italic'
4+
import { Paragraph } from '@tiptap/extension-paragraph'
5+
import { Text } from '@tiptap/extension-text'
6+
import { describe, expect, it } from 'vitest'
7+
8+
import { MarkdownManager } from '../src/MarkdownManager.js'
9+
10+
describe('Inline marks containing only punctuation', () => {
11+
const extensions = [Document, Paragraph, Text, Bold, Italic]
12+
const markdownManager = new MarkdownManager({ extensions })
13+
14+
describe('Bold marks with punctuation - whitespace-separated (CommonMark compliant)', () => {
15+
it('should parse standalone bold punctuation marks', () => {
16+
const markdown = '**)**'
17+
const json = markdownManager.parse(markdown)
18+
19+
expect(json).toEqual({
20+
type: 'doc',
21+
content: [
22+
{
23+
type: 'paragraph',
24+
content: [
25+
{
26+
type: 'text',
27+
text: ')',
28+
marks: [{ type: 'bold' }],
29+
},
30+
],
31+
},
32+
],
33+
})
34+
})
35+
36+
it('should parse bold punctuation when preceded by whitespace', () => {
37+
const markdown = 'text **)**'
38+
const json = markdownManager.parse(markdown)
39+
40+
expect(json).toEqual({
41+
type: 'doc',
42+
content: [
43+
{
44+
type: 'paragraph',
45+
content: [
46+
{
47+
type: 'text',
48+
text: 'text ',
49+
},
50+
{
51+
type: 'text',
52+
text: ')',
53+
marks: [{ type: 'bold' }],
54+
},
55+
],
56+
},
57+
],
58+
})
59+
})
60+
61+
it('should parse bold punctuation when followed by whitespace', () => {
62+
const markdown = '**)** text'
63+
const json = markdownManager.parse(markdown)
64+
65+
expect(json).toEqual({
66+
type: 'doc',
67+
content: [
68+
{
69+
type: 'paragraph',
70+
content: [
71+
{
72+
type: 'text',
73+
text: ')',
74+
marks: [{ type: 'bold' }],
75+
},
76+
{
77+
type: 'text',
78+
text: ' text',
79+
},
80+
],
81+
},
82+
],
83+
})
84+
})
85+
86+
it('should parse bold punctuation when surrounded by whitespace', () => {
87+
const markdown = 'text **)** more'
88+
const json = markdownManager.parse(markdown)
89+
90+
expect(json).toEqual({
91+
type: 'doc',
92+
content: [
93+
{
94+
type: 'paragraph',
95+
content: [
96+
{
97+
type: 'text',
98+
text: 'text ',
99+
},
100+
{
101+
type: 'text',
102+
text: ')',
103+
marks: [{ type: 'bold' }],
104+
},
105+
{
106+
type: 'text',
107+
text: ' more',
108+
},
109+
],
110+
},
111+
],
112+
})
113+
})
114+
115+
it('should parse bold punctuation when preceded by other punctuation', () => {
116+
const markdown = '[**)**]'
117+
const json = markdownManager.parse(markdown)
118+
119+
expect(json).toEqual({
120+
type: 'doc',
121+
content: [
122+
{
123+
type: 'paragraph',
124+
content: [
125+
{
126+
type: 'text',
127+
text: '[',
128+
},
129+
{
130+
type: 'text',
131+
text: ')',
132+
marks: [{ type: 'bold' }],
133+
},
134+
{
135+
type: 'text',
136+
text: ']',
137+
},
138+
],
139+
},
140+
],
141+
})
142+
})
143+
144+
it('should parse multiple bold punctuation marks', () => {
145+
const markdown = '**,** **.**'
146+
const json = markdownManager.parse(markdown)
147+
148+
expect(json).toEqual({
149+
type: 'doc',
150+
content: [
151+
{
152+
type: 'paragraph',
153+
content: [
154+
{
155+
type: 'text',
156+
text: ',',
157+
marks: [{ type: 'bold' }],
158+
},
159+
{
160+
type: 'text',
161+
text: ' ',
162+
},
163+
{
164+
type: 'text',
165+
text: '.',
166+
marks: [{ type: 'bold' }],
167+
},
168+
],
169+
},
170+
],
171+
})
172+
})
173+
})
174+
175+
describe('Bold marks without required whitespace (CommonMark limitation)', () => {
176+
it('should NOT parse bold punctuation when directly preceded by alphanumeric characters', () => {
177+
const markdown = 'text**)**'
178+
const json = markdownManager.parse(markdown)
179+
180+
// Per CommonMark flanking rules, this is not recognized as bold
181+
expect(json).toEqual({
182+
type: 'doc',
183+
content: [
184+
{
185+
type: 'paragraph',
186+
content: [
187+
{
188+
type: 'text',
189+
text: 'text**)**',
190+
},
191+
],
192+
},
193+
],
194+
})
195+
})
196+
197+
it('should NOT parse bold punctuation when directly followed by alphanumeric characters', () => {
198+
const markdown = '**)**text'
199+
const json = markdownManager.parse(markdown)
200+
201+
// Per CommonMark flanking rules, this is not recognized as bold
202+
expect(json).toEqual({
203+
type: 'doc',
204+
content: [
205+
{
206+
type: 'paragraph',
207+
content: [
208+
{
209+
type: 'text',
210+
text: '**)**text',
211+
},
212+
],
213+
},
214+
],
215+
})
216+
})
217+
218+
it('should NOT parse bold punctuation when surrounded by alphanumeric characters', () => {
219+
const markdown = 'text**)**text'
220+
const json = markdownManager.parse(markdown)
221+
222+
// Per CommonMark flanking rules, this is not recognized as bold
223+
expect(json).toEqual({
224+
type: 'doc',
225+
content: [
226+
{
227+
type: 'paragraph',
228+
content: [
229+
{
230+
type: 'text',
231+
text: 'text**)**text',
232+
},
233+
],
234+
},
235+
],
236+
})
237+
})
238+
})
239+
240+
describe('Italic marks with punctuation', () => {
241+
it('should parse standalone italic punctuation', () => {
242+
const markdown = '*)*'
243+
const json = markdownManager.parse(markdown)
244+
245+
expect(json).toEqual({
246+
type: 'doc',
247+
content: [
248+
{
249+
type: 'paragraph',
250+
content: [
251+
{
252+
type: 'text',
253+
text: ')',
254+
marks: [{ type: 'italic' }],
255+
},
256+
],
257+
},
258+
],
259+
})
260+
})
261+
262+
it('should parse italic punctuation with whitespace', () => {
263+
const markdown = 'text *,* more'
264+
const json = markdownManager.parse(markdown)
265+
266+
expect(json).toEqual({
267+
type: 'doc',
268+
content: [
269+
{
270+
type: 'paragraph',
271+
content: [
272+
{
273+
type: 'text',
274+
text: 'text ',
275+
},
276+
{
277+
type: 'text',
278+
text: ',',
279+
marks: [{ type: 'italic' }],
280+
},
281+
{
282+
type: 'text',
283+
text: ' more',
284+
},
285+
],
286+
},
287+
],
288+
})
289+
})
290+
291+
it('should NOT parse italic punctuation without whitespace', () => {
292+
const markdown = 'text*,*text'
293+
const json = markdownManager.parse(markdown)
294+
295+
expect(json).toEqual({
296+
type: 'doc',
297+
content: [
298+
{
299+
type: 'paragraph',
300+
content: [
301+
{
302+
type: 'text',
303+
text: 'text*,*text',
304+
},
305+
],
306+
},
307+
],
308+
})
309+
})
310+
})
311+
312+
describe('Roundtrip serialization', () => {
313+
it('should maintain bold punctuation through parse-serialize cycle', () => {
314+
const markdown = 'text **)** more'
315+
const json = markdownManager.parse(markdown)
316+
const serialized = markdownManager.serialize(json)
317+
318+
expect(serialized).toBe(markdown)
319+
})
320+
321+
it('should maintain italic punctuation through parse-serialize cycle', () => {
322+
const markdown = 'text *,* more'
323+
const json = markdownManager.parse(markdown)
324+
const serialized = markdownManager.serialize(json)
325+
326+
expect(serialized).toBe(markdown)
327+
})
328+
})
329+
})

packages/markdown/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
"dist"
3535
],
3636
"dependencies": {
37-
"marked": "^15.0.12"
37+
"marked": "^17.0.1"
3838
},
3939
"devDependencies": {
4040
"@tiptap/core": "workspace:^",

pnpm-lock.yaml

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)