rcgit

/ karakeep

Commit 3408e6e4

SHA 3408e6e4854dc79b963eef455e9a69231de3cd28
Author Mohamed Bassem <me at mbassem dot com>
Author Date 2025-12-24 12:10 +0200
Committer GitHub <noreply at github dot com>
Commit Date 2025-12-24 10:10 +0000
Parent(s) e336513fad5b (diff)
Tree 0f5c9b218bac

patch snapshot

fix: handle empty folder names in HTML bookmark imports (#2300)
When importing bookmarks from an HTML file, empty H3 tags (folder names)
are now replaced with "Unnamed" to prevent import failures.

Fixes #2299

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Mohamed Bassem <MohamedBassem@users.noreply.github.com>
File + - Graph
M packages/shared/import-export/importer.test.ts +82 -0
M packages/shared/import-export/parsers.ts +3 -1
2 file(s) changed, 85 insertions(+), 1 deletions(-)

packages/shared/import-export/importer.test.ts

diff --git a/packages/shared/import-export/importer.test.ts b/packages/shared/import-export/importer.test.ts
index 48cd1204..7e381f20 100644
--- a/packages/shared/import-export/importer.test.ts
+++ b/packages/shared/import-export/importer.test.ts
@@ -402,6 +402,88 @@ describe("importBookmarksFromFile", () => {
     expect(updateBookmarkTags).toHaveBeenCalledTimes(2);
   });
 
+  it("handles HTML bookmarks with empty folder names", async () => {
+    const htmlContent = `<!DOCTYPE NETSCAPE-Bookmark-file-1>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
+<TITLE>Bookmarks</TITLE>
+<H1>Bookmarks</H1>
+<DL><p>
+    <DT><H3 ADD_DATE="1765995928" LAST_MODIFIED="1765995928">Bluetooth Fernbedienung</H3>
+    <DL><p>
+        <DT><H3 ADD_DATE="1765995928" LAST_MODIFIED="0"></H3>
+        <DL><p>
+            <DT><A HREF="https://www.example.com/product.html" ADD_DATE="1593444456">Example Product</A>
+        </DL><p>
+    </DL><p>
+</DL><p>`;
+
+    const mockFile = {
+      text: vi.fn().mockResolvedValue(htmlContent),
+    } as unknown as File;
+
+    const createdLists: { name: string; icon: string; parentId?: string }[] =
+      [];
+    const createList = vi.fn(
+      async (input: { name: string; icon: string; parentId?: string }) => {
+        createdLists.push(input);
+        return {
+          id: `${input.parentId ? input.parentId + "/" : ""}${input.name}`,
+        };
+      },
+    );
+
+    const createdBookmarks: ParsedBookmark[] = [];
+    const createBookmark = vi.fn(async (bookmark: ParsedBookmark) => {
+      createdBookmarks.push(bookmark);
+      return {
+        id: `bookmark-${createdBookmarks.length}`,
+        alreadyExists: false,
+      };
+    });
+
+    const res = await importBookmarksFromFile({
+      file: mockFile,
+      source: "html",
+      rootListName: "HTML Import",
+      deps: {
+        createList,
+        createBookmark,
+        addBookmarkToLists: vi.fn(),
+        updateBookmarkTags: vi.fn(),
+        createImportSession: vi.fn(async () => ({ id: "session-1" })),
+      },
+    });
+
+    expect(res.counts).toEqual({
+      successes: 1,
+      failures: 0,
+      alreadyExisted: 0,
+      total: 1,
+    });
+
+    // Verify that the empty folder name was replaced with "Unnamed"
+    expect(createdLists).toEqual([
+      { name: "HTML Import", icon: "⬆️" },
+      { name: "Bluetooth Fernbedienung", parentId: "HTML Import", icon: "📁" },
+      {
+        name: "Unnamed",
+        parentId: "HTML Import/Bluetooth Fernbedienung",
+        icon: "📁",
+      },
+    ]);
+
+    // Verify the bookmark was created and assigned to the correct path
+    expect(createdBookmarks).toHaveLength(1);
+    expect(createdBookmarks[0]).toMatchObject({
+      title: "Example Product",
+      content: {
+        type: "link",
+        url: "https://www.example.com/product.html",
+      },
+      tags: [],
+    });
+  });
+
   it("parses mymind CSV export correctly", async () => {
     const mymindCsv = `id,type,title,url,content,note,tags,created
 1pYm0O0hY4WnmKN,WebPage,mymind,https://access.mymind.com/everything,,,"Wellness,Self-Improvement,Psychology",2024-12-04T23:02:10Z

packages/shared/import-export/parsers.ts

diff --git a/packages/shared/import-export/parsers.ts b/packages/shared/import-export/parsers.ts
index f4d3f862..5b8b01e8 100644
--- a/packages/shared/import-export/parsers.ts
+++ b/packages/shared/import-export/parsers.ts
@@ -55,7 +55,9 @@ function parseNetscapeBookmarkFile(textContent: string): ParsedBookmark[] {
       while (current && current.length > 0) {
         const h3 = current.find("> h3").first();
         if (h3.length > 0) {
-          path.unshift(h3.text());
+          const folderName = h3.text().trim();
+          // Use "Unnamed" for empty folder names
+          path.unshift(folderName || "Unnamed");
         }
         current = current.parent();
       }