fix(markdown): preserve adjacency around inline HTML

This commit is contained in:
Mohamed Boudra
2026-10-15 20:24:56 +02:00
parent 1456d461a9
commit 904fc10d89
2 changed files with 8 additions and 11 deletions
+1 -8
View File
@@ -69,8 +69,7 @@ export function normalizeMarkdownHtml(source: string): string {
if (EMPHASIS[name]) return EMPHASIS[name];
const boundary = layoutBoundary(name, source, raw, offset, closing);
if (boundary !== null) return boundary;
if (name === "a")
return markdownAnchor(raw, closing, links) + inlineBoundary(source, raw, offset, closing);
if (name === "a") return markdownAnchor(raw, closing, links);
if (name === "img" && !closing) {
const { src, alt = "" } = attributes(raw);
return src ? `![${alt.replace(/[[\]\\]/g, "\\$&")}](<${markdownUrl(src)}>)` : alt;
@@ -126,11 +125,6 @@ function markdownAnchor(raw: string, closing: boolean, links: (string | undefine
return href === undefined ? "" : "[";
}
function inlineBoundary(source: string, raw: string, offset: number, closing: boolean): string {
if (!closing) return "";
return /^(?:<[A-Za-z]|[A-Za-z0-9])/.test(source.slice(offset + raw.length)) ? " " : "";
}
function blockBoundary(source: string, raw: string, offset: number, closing: boolean): string {
if (closing) {
const following = source.slice(offset + raw.length);
@@ -160,6 +154,5 @@ function layoutBoundary(
if (BLOCK_TAGS.has(name)) return blockBoundary(source, raw, offset, closing);
if ((name === "td" || name === "th") && closing)
return /^<t[dh]\b/i.test(source.slice(offset + raw.length)) ? " " : "";
if (name === "span") return inlineBoundary(source, raw, offset, closing);
return null;
}
@@ -222,6 +222,10 @@ describe("GitHub comment HTML normalization", () => {
"**Review details**\n\nBody **stays**.",
],
["Before<!-- private -->After", "BeforeAfter"],
["<span>file.</span>ts", "file.ts"],
['<a href="https://example.com">Git</a>Hub', "[Git](<https://example.com>)Hub"],
["<span>Git</span><span>Hub</span>", "GitHub"],
["<span>Git</span> Hub", "Git Hub"],
["<div><span>Keep the content</span></div>", "Keep the content"],
[
'<a href="https://example.com"><strong>Review</strong></a>',
@@ -297,14 +301,14 @@ it("uses the first repeated image reference definition", () => {
);
});
it("keeps Greptile header links and block text separated", () => {
it("preserves authored adjacency in the Greptile header", () => {
const source =
'<h2><a href="https://app.greptile.com/api/retrigger?id=81261069"><picture><source media="(prefers-color-scheme: dark)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/RetriggerDark.svg?v=2"><source media="(prefers-color-scheme: light)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/Retrigger.svg?v=2"><img alt="Retrigger" src="https://greptile-static-assets.s3.amazonaws.com/badges/Retrigger.svg?v=2" align="right"></picture></a><a href="https://app.greptile.com/paseo/github/getpaseo/paseo/-/pull/6973"><picture><source media="(prefers-color-scheme: dark)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewInGreptileDark.svg?v=2"><source media="(prefers-color-scheme: light)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewInGreptile.svg?v=2"><img alt="View in Greptile" src="https://greptile-static-assets.s3.amazonaws.com/badges/ViewInGreptile.svg?v=2" align="right"></picture></a>Confidence Score: 2/5</h2>';
const rendered = prepareMarkdownSource(source, undefined, { supportsSvg: false }).render();
expect(rendered).toContain(
"[Retrigger](<https://app.greptile.com/api/retrigger?id=81261069>) [View in Greptile]",
"[Retrigger](<https://app.greptile.com/api/retrigger?id=81261069>)[View in Greptile]",
);
expect(rendered).toContain(") Confidence Score: 2/5");
expect(rendered).toContain(")Confidence Score: 2/5");
});
it("keeps paragraph, heading and table-row boundaries when stripping HTML", () => {