Skip to content

Commit a427a45

Browse files
committed
fix: resolve test failures in boundary cases
- Fix regex in test helper to allow empty src attributes - Improve normalizeImageSrc to correctly resolve relative paths with .. and . - Simplify and unify image path normalization logic
1 parent d8ec95f commit a427a45

File tree

2 files changed

+32
-24
lines changed

2 files changed

+32
-24
lines changed

src/lib/markdown.ts

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -26,32 +26,40 @@ function joinBase(p: unknown) {
2626
export function normalizeImageSrc(src: any) {
2727
if (!src || typeof src !== "string") return src;
2828
if (/^(https?:\/\/|data:)/i.test(src)) return src;
29-
if (src.startsWith("/blog/")) {
30-
const rest = src.slice("/blog/".length);
31-
if (rest.startsWith("assets/images/")) {
32-
const flattened = flattenLegacyAssetsPath(rest);
33-
return joinBase(flattened);
34-
}
35-
return joinBase(rest);
36-
}
37-
if (src.startsWith(BASE)) return src;
38-
if (src.startsWith("/assets/images/")) {
39-
const flattened = flattenLegacyAssetsPath(src.slice(1));
40-
return joinBase(flattened);
29+
30+
let p = src;
31+
if (p.startsWith("/blog/")) {
32+
p = p.slice("/blog/".length);
33+
} else if (p.startsWith(BASE)) {
34+
p = p.slice(BASE.length);
35+
} else if (p.startsWith("/")) {
36+
p = p.slice(1);
4137
}
42-
if (src.startsWith("/images/")) {
43-
return joinBase(src.slice(1));
38+
39+
// Resolve .. and . first (EP-BVA boundary case handling)
40+
p = resolveRelativePath(p);
41+
42+
if (p.startsWith("assets/images/")) {
43+
p = flattenLegacyAssetsPath(p);
44+
} else if (p.startsWith("images/")) {
45+
// already normalized to images/
4446
}
45-
const mRelAssetsImages = src.match(
46-
/(^|\/?)(?:\.\.\/|\.\/)?assets\/images\/(.+)$/,
47-
);
48-
if (mRelAssetsImages) {
49-
const flattened = flattenLegacyAssetsPath(
50-
`assets/images/${mRelAssetsImages[2]}`,
51-
);
52-
return joinBase(flattened);
47+
48+
return joinBase(p);
49+
}
50+
51+
function resolveRelativePath(p: string) {
52+
const parts = p.split("/");
53+
const stack: string[] = [];
54+
for (const part of parts) {
55+
if (part === "..") {
56+
if (stack.length > 0) stack.pop();
57+
} else if (part !== "." && part !== "") {
58+
stack.push(part);
59+
}
5360
}
54-
return joinBase(src);
61+
const result = stack.join("/");
62+
return p.startsWith("/") ? `/${result}` : result;
5563
}
5664

5765
function flattenLegacyAssetsPath(p: string) {

tests/run-tests.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { runDevtoAbsolutizeTests } from "./devto-absolutize.test";
55

66
function extractImgSrcs(html: string) {
77
const out: string[] = [];
8-
const re = /<img\s+[^>]*src=["']([^"']+)["'][^>]*>/g;
8+
const re = /<img\s+[^>]*src=["']([^"']*)["'][^>]*>/g;
99
let m: RegExpExecArray | null;
1010
while ((m = re.exec(html))) out.push(m[1]);
1111
return out;

0 commit comments

Comments
 (0)