From f050906171e0afbecd305f03a42c4e49d96a3409 Mon Sep 17 00:00:00 2001 From: tomolld Date: Tue, 17 Dec 2024 08:15:28 +0900 Subject: [PATCH 1/2] =?UTF-8?q?=E3=82=BF=E3=82=A4=E3=83=88=E3=83=AB?= =?UTF-8?q?=E3=82=92=E5=87=A6=E7=90=86=E3=81=99=E3=82=8B=E9=9A=9B=E3=81=AB?= =?UTF-8?q?=E3=80=81source=5Ftexts=E3=81=AE=E7=B6=AD=E6=8C=81=E3=82=92?= =?UTF-8?q?=E7=A2=BA=E8=AA=8D=E3=81=99=E3=82=8B=E3=83=86=E3=82=B9=E3=83=88?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0=E3=81=97=E3=80=81rehypeAddDataId?= =?UTF-8?q?=E9=96=A2=E6=95=B0=E3=81=AB=E3=82=BF=E3=82=A4=E3=83=88=E3=83=AB?= =?UTF-8?q?=E5=BC=95=E6=95=B0=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../$slug+/edit/functions/mutations.server.ts | 17 ----- .../edit/utils/processHtmlContent.test.ts | 64 +++++++++++++++++++ .../$slug+/edit/utils/processHtmlContent.ts | 13 +++- web/scripts/processMarkdownContent.ts | 2 +- 4 files changed, 75 insertions(+), 21 deletions(-) diff --git a/web/app/routes/$userName+/page+/$slug+/edit/functions/mutations.server.ts b/web/app/routes/$userName+/page+/$slug+/edit/functions/mutations.server.ts index 7736c1ad..1dd817c6 100644 --- a/web/app/routes/$userName+/page+/$slug+/edit/functions/mutations.server.ts +++ b/web/app/routes/$userName+/page+/$slug+/edit/functions/mutations.server.ts @@ -1,5 +1,4 @@ import { prisma } from "~/utils/prisma"; -import { generateHashForText } from "../utils/generateHashForText"; export async function upsertPageWithHtml( pageSlug: string, @@ -24,26 +23,10 @@ export async function upsertPageWithHtml( export async function upsertTitle(pageSlug: string, title: string) { const page = await prisma.page.findUnique({ where: { slug: pageSlug } }); if (!page) return; - const titleHash = generateHashForText(title, 0); await prisma.page.update({ where: { id: page.id }, data: { title: title }, }); - return await prisma.sourceText.upsert({ - where: { - pageId_textAndOccurrenceHash: { - pageId: page.id, - textAndOccurrenceHash: titleHash, - }, - }, - update: { text: title }, - create: { - pageId: page.id, - textAndOccurrenceHash: titleHash, - text: title, - number: 0, - }, - }); } export async function upsertTags(tags: string[], pageId: number) { diff --git a/web/app/routes/$userName+/page+/$slug+/edit/utils/processHtmlContent.test.ts b/web/app/routes/$userName+/page+/$slug+/edit/utils/processHtmlContent.test.ts index 8ddf8866..1056e5b1 100644 --- a/web/app/routes/$userName+/page+/$slug+/edit/utils/processHtmlContent.test.ts +++ b/web/app/routes/$userName+/page+/$slug+/edit/utils/processHtmlContent.test.ts @@ -239,4 +239,68 @@ describe("processHtmlContent", () => { // 各タイトル occurrence が異なる ID を持つことを確認 expect(titleOccurrences[0].id).not.toBe(titleOccurrences[1].id); }); + + test("同一HTMLを再度処理した場合に、編集していない箇所のsource_textsが維持されるか確認", async () => { + const pageSlug = "html-no-edit-test-page"; + const title = "No Edit Title"; + const htmlInput = ` +

Line A

+

Line B

+

Line C

+ `; + + const user = await prisma.user.upsert({ + where: { id: 13 }, + create: { + id: 13, + userName: "noedit", + email: "noedit@example.com", + displayName: "noedit", + icon: "noedit", + }, + update: {}, + }); + + // 初回処理 + await processHtmlContent(title, htmlInput, pageSlug, user.id, "en", true); + + const dbPage1 = await prisma.page.findUnique({ + where: { slug: pageSlug }, + include: { sourceTexts: true }, + }); + expect(dbPage1).not.toBeNull(); + if (!dbPage1) return; + + // 初回処理時のIDを記憶 + const originalTextIdMap = new Map(); + for (const st of dbPage1.sourceTexts) { + originalTextIdMap.set(st.text, st.id); + } + expect(originalTextIdMap.size).toBeGreaterThanOrEqual(3); + + // 変更なしで再度同一HTMLを処理 + await processHtmlContent(title, htmlInput, pageSlug, user.id, "en", true); + + const dbPage2 = await prisma.page.findUnique({ + where: { slug: pageSlug }, + include: { sourceTexts: true }, + }); + expect(dbPage2).not.toBeNull(); + if (!dbPage2) return; + + // 再処理後のIDマッピングを取得 + const afterTextIdMap = new Map(); + for (const st of dbPage2.sourceTexts) { + afterTextIdMap.set(st.text, st.id); + } + + // 全てのテキストでIDが変わっていないことを確認 + for (const [text, originalId] of originalTextIdMap.entries()) { + console.log(text, originalId); + expect(afterTextIdMap.get(text)).toBe(originalId); + } + + // source_textsの数が増減していないこと(無駄な消去がないこと) + expect(dbPage2.sourceTexts.length).toBe(dbPage1.sourceTexts.length); + }); }); diff --git a/web/app/routes/$userName+/page+/$slug+/edit/utils/processHtmlContent.ts b/web/app/routes/$userName+/page+/$slug+/edit/utils/processHtmlContent.ts index bcc21f37..6e0950a7 100644 --- a/web/app/routes/$userName+/page+/$slug+/edit/utils/processHtmlContent.ts +++ b/web/app/routes/$userName+/page+/$slug+/edit/utils/processHtmlContent.ts @@ -42,7 +42,7 @@ function extractTextFromHAST(node: Parent): string { }); return result; } -export function rehypeAddDataId(pageId: number): Plugin<[], Root> { +export function rehypeAddDataId(pageId: number, title: string): Plugin<[], Root> { return function attacher() { return async (tree: Root, file: VFile) => { const textOccurrenceMap = new Map(); @@ -74,11 +74,18 @@ export function rehypeAddDataId(pageId: number): Plugin<[], Root> { } }); + const allTextsForDb = blocks.map((block, index) => ({ text: block.text, textAndOccurrenceHash: block.textAndOccurrenceHash, number: index + 1, - })); + })) + + allTextsForDb.push({ + text: title, + textAndOccurrenceHash: generateHashForText(title, 0), + number: 0, + }); const hashToId = await synchronizePageSourceTexts(pageId, allTextsForDb); @@ -127,7 +134,7 @@ export async function processHtmlContent( .use(rehypeRemark) // HAST→MDAST .use(remarkGfm) // GFM拡張 .use(remarkRehype, { allowDangerousHtml: true }) // MDAST→HAST - .use(rehypeAddDataId(page.id)) + .use(rehypeAddDataId(page.id, title)) .use(rehypeRaw) // 生HTMLを処理 .use(rehypeStringify, { allowDangerousHtml: true }) // HAST→HTML .process(htmlInput); diff --git a/web/scripts/processMarkdownContent.ts b/web/scripts/processMarkdownContent.ts index 831847b3..c6ae97ad 100644 --- a/web/scripts/processMarkdownContent.ts +++ b/web/scripts/processMarkdownContent.ts @@ -28,7 +28,7 @@ export async function processMarkdownContent( const file = await remark() .use(remarkGfm) .use(remarkRehype) - .use(rehypeAddDataId(page.id)) + .use(rehypeAddDataId(page.id, title)) .use(rehypeRaw) .use(rehypeStringify, { allowDangerousHtml: true }) .process(body); From 55ed8d7f81d3da07dc6c1569ca0d0a38b093cd38 Mon Sep 17 00:00:00 2001 From: tomolld Date: Tue, 17 Dec 2024 08:15:37 +0900 Subject: [PATCH 2/2] f --- .../edit/utils/processHtmlContent.test.ts | 18 +++++++++--------- .../$slug+/edit/utils/processHtmlContent.ts | 10 ++++++---- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/web/app/routes/$userName+/page+/$slug+/edit/utils/processHtmlContent.test.ts b/web/app/routes/$userName+/page+/$slug+/edit/utils/processHtmlContent.test.ts index 1056e5b1..19d91f12 100644 --- a/web/app/routes/$userName+/page+/$slug+/edit/utils/processHtmlContent.test.ts +++ b/web/app/routes/$userName+/page+/$slug+/edit/utils/processHtmlContent.test.ts @@ -248,7 +248,7 @@ describe("processHtmlContent", () => {

Line B

Line C

`; - + const user = await prisma.user.upsert({ where: { id: 13 }, create: { @@ -260,46 +260,46 @@ describe("processHtmlContent", () => { }, update: {}, }); - + // 初回処理 await processHtmlContent(title, htmlInput, pageSlug, user.id, "en", true); - + const dbPage1 = await prisma.page.findUnique({ where: { slug: pageSlug }, include: { sourceTexts: true }, }); expect(dbPage1).not.toBeNull(); if (!dbPage1) return; - + // 初回処理時のIDを記憶 const originalTextIdMap = new Map(); for (const st of dbPage1.sourceTexts) { originalTextIdMap.set(st.text, st.id); } expect(originalTextIdMap.size).toBeGreaterThanOrEqual(3); - + // 変更なしで再度同一HTMLを処理 await processHtmlContent(title, htmlInput, pageSlug, user.id, "en", true); - + const dbPage2 = await prisma.page.findUnique({ where: { slug: pageSlug }, include: { sourceTexts: true }, }); expect(dbPage2).not.toBeNull(); if (!dbPage2) return; - + // 再処理後のIDマッピングを取得 const afterTextIdMap = new Map(); for (const st of dbPage2.sourceTexts) { afterTextIdMap.set(st.text, st.id); } - + // 全てのテキストでIDが変わっていないことを確認 for (const [text, originalId] of originalTextIdMap.entries()) { console.log(text, originalId); expect(afterTextIdMap.get(text)).toBe(originalId); } - + // source_textsの数が増減していないこと(無駄な消去がないこと) expect(dbPage2.sourceTexts.length).toBe(dbPage1.sourceTexts.length); }); diff --git a/web/app/routes/$userName+/page+/$slug+/edit/utils/processHtmlContent.ts b/web/app/routes/$userName+/page+/$slug+/edit/utils/processHtmlContent.ts index 6e0950a7..fa958acf 100644 --- a/web/app/routes/$userName+/page+/$slug+/edit/utils/processHtmlContent.ts +++ b/web/app/routes/$userName+/page+/$slug+/edit/utils/processHtmlContent.ts @@ -42,7 +42,10 @@ function extractTextFromHAST(node: Parent): string { }); return result; } -export function rehypeAddDataId(pageId: number, title: string): Plugin<[], Root> { +export function rehypeAddDataId( + pageId: number, + title: string, +): Plugin<[], Root> { return function attacher() { return async (tree: Root, file: VFile) => { const textOccurrenceMap = new Map(); @@ -74,13 +77,12 @@ export function rehypeAddDataId(pageId: number, title: string): Plugin<[], Root> } }); - const allTextsForDb = blocks.map((block, index) => ({ text: block.text, textAndOccurrenceHash: block.textAndOccurrenceHash, number: index + 1, - })) - + })); + allTextsForDb.push({ text: title, textAndOccurrenceHash: generateHashForText(title, 0),