|
7 | 7 | import { parseMarkdown } from "./parser.ts"; |
8 | 8 | import { extractHashtag } from "../../plug-api/lib/tags.ts"; |
9 | 9 | import { renderHashtag } from "../../plugs/index/tags.ts"; |
| 10 | +import { mdLinkRegex } from "./constants.ts"; |
10 | 11 |
|
11 | 12 | const sample1 = `--- |
12 | 13 | type: page |
@@ -309,3 +310,74 @@ test("Test table parser does not treat {[...]} specially", () => { |
309 | 310 | const row1Cells = collectNodesOfType(rows[0], "TableCell"); |
310 | 311 | expect(row1Cells.length).toBe(2); |
311 | 312 | }); |
| 313 | + |
| 314 | +// Links with escaped square brackets |
| 315 | +test("Test markdown links with escaped square brackets", () => { |
| 316 | + // Parser should produce a Link node for escaped brackets |
| 317 | + const tree = parseMarkdown(`[\\[link\\]](address)`); |
| 318 | + const links = collectNodesOfType(tree, "Link"); |
| 319 | + expect(links.length).toBe(1); |
| 320 | + |
| 321 | + // Should contain Escape nodes for the brackets |
| 322 | + const escapes = collectNodesOfType(links[0], "Escape"); |
| 323 | + expect(escapes.length).toBe(2); |
| 324 | + expect(escapes[0].children![0].text).toBe("\\["); |
| 325 | + expect(escapes[1].children![0].text).toBe("\\]"); |
| 326 | + |
| 327 | + // Should have a URL node |
| 328 | + const urlNode = findNodeOfType(links[0], "URL"); |
| 329 | + expect(urlNode).not.toBeUndefined(); |
| 330 | + expect(urlNode!.children![0].text).toBe("address"); |
| 331 | + |
| 332 | + // Full roundtrip |
| 333 | + expect(renderToText(tree)).toBe(`[\\[link\\]](address)`); |
| 334 | +}); |
| 335 | + |
| 336 | +test("Test mdLinkRegex with escaped square brackets", () => { |
| 337 | + // Normal link |
| 338 | + mdLinkRegex.lastIndex = 0; |
| 339 | + let match = mdLinkRegex.exec("[link](address)"); |
| 340 | + expect(match).not.toBeNull(); |
| 341 | + expect(match!.groups!.title).toBe("link"); |
| 342 | + expect(match!.groups!.url).toBe("address"); |
| 343 | + |
| 344 | + // Escaped brackets in link text (issue #1896) |
| 345 | + mdLinkRegex.lastIndex = 0; |
| 346 | + match = mdLinkRegex.exec("[\\[link\\]](address)"); |
| 347 | + expect(match).not.toBeNull(); |
| 348 | + expect(match!.groups!.title).toBe("\\[link\\]"); |
| 349 | + expect(match!.groups!.url).toBe("address"); |
| 350 | + |
| 351 | + // Escaped brackets with other text |
| 352 | + mdLinkRegex.lastIndex = 0; |
| 353 | + match = mdLinkRegex.exec("[see \\[ref\\] here](http://example.com)"); |
| 354 | + expect(match).not.toBeNull(); |
| 355 | + expect(match!.groups!.title).toBe("see \\[ref\\] here"); |
| 356 | + expect(match!.groups!.url).toBe("http://example.com"); |
| 357 | + |
| 358 | + // Image with escaped brackets |
| 359 | + mdLinkRegex.lastIndex = 0; |
| 360 | + match = mdLinkRegex.exec("![\\[img\\]](image.png)"); |
| 361 | + expect(match).not.toBeNull(); |
| 362 | + expect(match!.groups!.title).toBe("\\[img\\]"); |
| 363 | + expect(match!.groups!.url).toBe("image.png"); |
| 364 | + |
| 365 | + // Other escaped characters (backslash itself) |
| 366 | + mdLinkRegex.lastIndex = 0; |
| 367 | + match = mdLinkRegex.exec("[a\\\\b](url)"); |
| 368 | + expect(match).not.toBeNull(); |
| 369 | + expect(match!.groups!.title).toBe("a\\\\b"); |
| 370 | + expect(match!.groups!.url).toBe("url"); |
| 371 | + |
| 372 | + // Normal link still works (no regressions) |
| 373 | + mdLinkRegex.lastIndex = 0; |
| 374 | + match = mdLinkRegex.exec("[simple text](http://example.com)"); |
| 375 | + expect(match).not.toBeNull(); |
| 376 | + expect(match!.groups!.title).toBe("simple text"); |
| 377 | + |
| 378 | + // Empty title still works |
| 379 | + mdLinkRegex.lastIndex = 0; |
| 380 | + match = mdLinkRegex.exec("[](url)"); |
| 381 | + expect(match).not.toBeNull(); |
| 382 | + expect(match!.groups!.title).toBe(""); |
| 383 | +}); |
0 commit comments