Skip to content

Commit 554c6a3

Browse files
authored
fix: remove lookbehind regex for browser compat (#1827)
1 parent 906a455 commit 554c6a3

File tree

2 files changed

+214
-2
lines changed

2 files changed

+214
-2
lines changed
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
import { describe, expect, it } from "vitest";
2+
import { isMarkdown } from "./detectMarkdown.js";
3+
4+
describe("isMarkdown", () => {
5+
describe("Headings (H1-H6)", () => {
6+
it("should detect H1 headings", () => {
7+
expect(isMarkdown("# Heading 1\n\nContent")).toBe(true);
8+
expect(isMarkdown(" # Heading 1\n\nContent")).toBe(true);
9+
expect(isMarkdown(" # Heading 1\n\nContent")).toBe(true);
10+
});
11+
12+
it("should detect H2-H6 headings", () => {
13+
expect(isMarkdown("## Heading 2\n\nContent")).toBe(true);
14+
expect(isMarkdown("### Heading 3\n\nContent")).toBe(true);
15+
expect(isMarkdown("#### Heading 4\n\nContent")).toBe(true);
16+
expect(isMarkdown("##### Heading 5\n\nContent")).toBe(true);
17+
expect(isMarkdown("###### Heading 6\n\nContent")).toBe(true);
18+
});
19+
20+
it("should not detect invalid headings", () => {
21+
expect(isMarkdown("####### Heading 7\n\nContent")).toBe(false);
22+
expect(isMarkdown("#Heading without space\n\nContent")).toBe(false);
23+
expect(isMarkdown("# \n\nContent")).toBe(false);
24+
expect(
25+
isMarkdown(
26+
"# Very long heading that exceeds the character limit and should not be detected as a valid markdown heading\n\nContent",
27+
),
28+
).toBe(false);
29+
});
30+
});
31+
32+
describe("Bold, italic, underline, strikethrough, highlight", () => {
33+
it("should detect bold text", () => {
34+
expect(isMarkdown("**bold text**")).toBe(true);
35+
expect(isMarkdown("__bold text__")).toBe(true);
36+
expect(isMarkdown(" *bold text* ")).toBe(true);
37+
expect(isMarkdown(" _bold text_ ")).toBe(true);
38+
});
39+
40+
it("should detect italic text", () => {
41+
expect(isMarkdown("*italic text*")).toBe(true);
42+
expect(isMarkdown("_italic text_")).toBe(true);
43+
});
44+
45+
it("should detect strikethrough text", () => {
46+
expect(isMarkdown("~~strikethrough text~~")).toBe(true);
47+
});
48+
49+
it("should detect highlighted text", () => {
50+
expect(isMarkdown("==highlighted text==")).toBe(true);
51+
expect(isMarkdown("++highlighted text++")).toBe(true);
52+
});
53+
});
54+
55+
describe("Links", () => {
56+
it("should detect basic links", () => {
57+
expect(isMarkdown("[Link text](https://example.com)")).toBe(true);
58+
expect(isMarkdown("[Link text](http://example.com)")).toBe(true);
59+
expect(isMarkdown("[Short](https://ex.com)")).toBe(true);
60+
});
61+
62+
it("should detect image links", () => {
63+
expect(isMarkdown("![Alt text](https://example.com/image.jpg)")).toBe(
64+
true,
65+
);
66+
});
67+
});
68+
69+
describe("Inline code", () => {
70+
it("should detect inline code", () => {
71+
expect(isMarkdown("`code`")).toBe(true);
72+
expect(isMarkdown(" `code` ")).toBe(true);
73+
expect(isMarkdown("`const x = 1;`")).toBe(true);
74+
});
75+
76+
it("should not detect invalid inline code", () => {
77+
expect(isMarkdown("` code `")).toBe(false); // spaces around content
78+
expect(isMarkdown("``")).toBe(false); // empty
79+
expect(isMarkdown("` `")).toBe(false); // only space
80+
});
81+
});
82+
83+
describe("Unordered lists", () => {
84+
it("should detect unordered lists", () => {
85+
expect(isMarkdown("- Item 1\n- Item 2")).toBe(true);
86+
expect(isMarkdown(" - Item 1\n - Item 2")).toBe(true);
87+
expect(isMarkdown(" - Item 1\n - Item 2")).toBe(true);
88+
expect(isMarkdown(" - Item 1\n - Item 2")).toBe(true);
89+
expect(isMarkdown(" - Item 1\n - Item 2")).toBe(true);
90+
expect(isMarkdown(" - Item 1\n - Item 2")).toBe(true);
91+
});
92+
93+
it("should not detect invalid unordered lists", () => {
94+
expect(isMarkdown("- Item 1")).toBe(false); // single item
95+
expect(isMarkdown("-- Item 1\n-- Item 2")).toBe(false); // wrong marker
96+
expect(isMarkdown("-Item 1\n-Item 2")).toBe(false); // no space after marker
97+
});
98+
});
99+
100+
describe("Ordered lists", () => {
101+
it("should detect ordered lists", () => {
102+
expect(isMarkdown("1. Item 1\n2. Item 2")).toBe(true);
103+
expect(isMarkdown(" 1. Item 1\n 2. Item 2")).toBe(true);
104+
expect(isMarkdown(" 1. Item 1\n 2. Item 2")).toBe(true);
105+
expect(isMarkdown(" 1. Item 1\n 2. Item 2")).toBe(true);
106+
expect(isMarkdown(" 1. Item 1\n 2. Item 2")).toBe(true);
107+
expect(isMarkdown(" 1. Item 1\n 2. Item 2")).toBe(true);
108+
});
109+
110+
it("should not detect invalid ordered lists", () => {
111+
expect(isMarkdown("1. Item 1")).toBe(false); // single item
112+
expect(isMarkdown("1 Item 1\n2 Item 2")).toBe(false); // no dot
113+
expect(isMarkdown("1.Item 1\n2.Item 2")).toBe(false); // no space after dot
114+
});
115+
});
116+
117+
describe("Horizontal rules", () => {
118+
it("should detect horizontal rules", () => {
119+
expect(isMarkdown("\n\n ---\n\n")).toBe(true);
120+
expect(isMarkdown("\n\n ----\n\n")).toBe(true);
121+
expect(isMarkdown("\n\n ---\n\n")).toBe(true);
122+
expect(isMarkdown("\n\n ---\n\n")).toBe(true);
123+
});
124+
});
125+
126+
describe("Fenced code blocks", () => {
127+
it("should detect fenced code blocks", () => {
128+
expect(isMarkdown("```\ncode block\n```")).toBe(true);
129+
expect(isMarkdown("~~~\ncode block\n~~~")).toBe(true);
130+
expect(isMarkdown("```javascript\nconst x = 1;\n```")).toBe(true);
131+
expect(isMarkdown("```js\nconst x = 1;\n```")).toBe(true);
132+
});
133+
});
134+
135+
describe("Classical underlined headings", () => {
136+
it("should detect H1 with equals", () => {
137+
expect(isMarkdown("Heading\n===\n\nContent")).toBe(true);
138+
expect(isMarkdown("Heading\n====\n\nContent")).toBe(true);
139+
});
140+
141+
it("should detect H2 with dashes", () => {
142+
expect(isMarkdown("Heading\n---\n\nContent")).toBe(true);
143+
expect(isMarkdown("Heading\n----\n\nContent")).toBe(true);
144+
});
145+
});
146+
147+
describe("Blockquotes", () => {
148+
it("should detect blockquotes", () => {
149+
expect(isMarkdown("> This is a blockquote\n\nContent")).toBe(true);
150+
expect(isMarkdown(" > This is a blockquote\n\nContent")).toBe(true);
151+
expect(isMarkdown(" > This is a blockquote\n\nContent")).toBe(true);
152+
expect(isMarkdown(" > This is a blockquote\n\nContent")).toBe(true);
153+
});
154+
155+
it("should detect multi-line blockquotes", () => {
156+
expect(isMarkdown("> Line 1\n> Line 2\n\nContent")).toBe(true);
157+
expect(isMarkdown("> Line 1\n> Line 2\n> Line 3\n\nContent")).toBe(true);
158+
});
159+
});
160+
161+
describe("Tables", () => {
162+
it("should detect table headers", () => {
163+
expect(isMarkdown("| Header 1 | Header 2 |\n")).toBe(true);
164+
expect(isMarkdown("| Header 1 | Header 2 | Header 3 |\n")).toBe(true);
165+
});
166+
167+
it("should detect table dividers", () => {
168+
expect(isMarkdown("| --- | --- |\n")).toBe(true);
169+
expect(isMarkdown("| :--- | ---: |\n")).toBe(true);
170+
expect(isMarkdown("| :---: | --- |\n")).toBe(true);
171+
});
172+
173+
it("should detect table rows", () => {
174+
expect(isMarkdown("| Cell 1 | Cell 2 |\n")).toBe(true);
175+
expect(isMarkdown("| Cell 1 | Cell 2 | Cell 3 |\n")).toBe(true);
176+
});
177+
178+
it("should detect complete tables", () => {
179+
const table =
180+
"| Header 1 | Header 2 |\n| --- | --- |\n| Cell 1 | Cell 2 |\n";
181+
expect(isMarkdown(table)).toBe(true);
182+
});
183+
184+
it("should not detect invalid tables", () => {
185+
expect(isMarkdown("| Header 1 | Header 2\n")).toBe(false); // missing closing pipe
186+
expect(isMarkdown("Header 1 | Header 2 |\n")).toBe(false); // missing opening pipe
187+
});
188+
});
189+
190+
describe("Edge cases and combinations", () => {
191+
it("should detect mixed markdown content", () => {
192+
const mixedContent =
193+
"# Heading\n\nThis is **bold** and *italic* text with a [link](https://example.com).\n\n- List item 1\n- List item 2\n\n> Blockquote\n\n```\ncode block\n```";
194+
expect(isMarkdown(mixedContent)).toBe(true);
195+
});
196+
197+
it("should not detect plain text", () => {
198+
expect(
199+
isMarkdown("This is just plain text without any markdown formatting."),
200+
).toBe(false);
201+
expect(isMarkdown("")).toBe(false);
202+
expect(isMarkdown(" \n \n ")).toBe(false); // only whitespace
203+
});
204+
205+
it("should handle special characters", () => {
206+
expect(isMarkdown("**text with `backticks`**")).toBe(true);
207+
expect(isMarkdown("**text with [brackets]**")).toBe(true);
208+
expect(isMarkdown("**text with (parentheses)**")).toBe(true);
209+
});
210+
});
211+
});

packages/core/src/api/parsers/markdown/detectMarkdown.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22
const h1 = /(^|\n) {0,3}#{1,6} {1,8}[^\n]{1,64}\r?\n\r?\n\s{0,32}\S/;
33

44
// Bold, italic, underline, strikethrough, highlight.
5-
const bold = /(?:\s|^)(_|__|\*|\*\*|~~|==|\+\+)(?!\s).{1,64}(?<!\s)(?=\1)/;
5+
const bold =
6+
/(_|__|\*|\*\*|~~|==|\+\+)(?!\s)(?:[^\s](?:.{0,62}[^\s])?|\S)(?=\1)/;
67

78
// Basic inline link (also captures images).
89
const link = /\[[^\]]{1,128}\]\(https?:\/\/\S{1,999}\)/;
910

1011
// Inline code.
11-
const code = /(?:\s|^)`(?!\s)[^`]{1,48}(?<!\s)`([^\w]|$)/;
12+
const code = /(?:\s|^)`(?!\s)(?:[^\s`](?:[^`]{0,46}[^\s`])?|[^\s`])`([^\w]|$)/;
1213

1314
// Unordered list.
1415
const ul = /(?:^|\n)\s{0,5}-\s{1}[^\n]+\n\s{0,15}-\s/;

0 commit comments

Comments
 (0)