From 3408e6e4854dc79b963eef455e9a69231de3cd28 Mon Sep 17 00:00:00 2001 From: Mohamed Bassem Date: Wed, 24 Dec 2025 12:10:02 +0200 Subject: 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 --- packages/shared/import-export/importer.test.ts | 82 ++++++++++++++++++++++++++ packages/shared/import-export/parsers.ts | 4 +- 2 files changed, 85 insertions(+), 1 deletion(-) (limited to 'packages/shared') 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 = ` + +Bookmarks +

Bookmarks

+

+

Bluetooth Fernbedienung

+

+

+

+

Example Product +

+

+

`; + + 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 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(); } -- cgit v1.2.3-70-g09d2