From ea26a64b253bee9271c77c390304e540e340e444 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E4=B8=AB=E8=AE=B2=E6=A2=B5?= Date: Mon, 27 Jul 2026 22:52:31 +0800 Subject: [PATCH] fix(nacos): normalize config diff line endings --- .../lib/__tests__/nacos/nacosAdmin.spec.ts | 34 ++++++++++++++ apps/desktop/src/lib/nacos/nacosAdmin.ts | 46 ++++++++----------- 2 files changed, 53 insertions(+), 27 deletions(-) diff --git a/apps/desktop/src/lib/__tests__/nacos/nacosAdmin.spec.ts b/apps/desktop/src/lib/__tests__/nacos/nacosAdmin.spec.ts index 9e570c1aa4..5485ad5ea7 100644 --- a/apps/desktop/src/lib/__tests__/nacos/nacosAdmin.spec.ts +++ b/apps/desktop/src/lib/__tests__/nacos/nacosAdmin.spec.ts @@ -124,6 +124,12 @@ describe("nacosAdmin helpers", () => { expect(diff.preview).toContain("+ c"); }); + it("uses the same terminal newline semantics for diff summaries", () => { + expect(summarizeNacosConfigDiff("aa", "aa\nbb")).toMatchObject({ changed: true, addedLines: 1, removedLines: 0 }); + expect(summarizeNacosConfigDiff("aa", "aa\n")).toEqual({ changed: false, addedLines: 0, removedLines: 0, preview: "No content changes." }); + expect(summarizeNacosConfigDiff("aa\r\n", "aa\n")).toEqual({ changed: false, addedLines: 0, removedLines: 0, preview: "No content changes." }); + }); + it("builds side-by-side config diff rows with inline segments", () => { const rows = buildNacosSideBySideDiff('cloud:\n secret: "aaa"\n', 'cloud:\n secret: "aaa1"\n enabled: true\n'); expect(rows[0]).toMatchObject({ leftLineNumber: 1, rightLineNumber: 1, leftType: "equal", rightType: "equal" }); @@ -132,6 +138,34 @@ describe("nacosAdmin helpers", () => { expect(rows[2]).toMatchObject({ leftLineNumber: null, rightLineNumber: 3, leftType: "padding", rightType: "insert" }); }); + it("keeps unchanged lines when appending without a trailing newline", () => { + const rows = buildNacosSideBySideDiff("aa", "aa\nbb"); + expect(rows).toHaveLength(2); + expect(rows[0]).toMatchObject({ leftLineNumber: 1, rightLineNumber: 1, leftContent: "aa", rightContent: "aa", leftType: "equal", rightType: "equal" }); + expect(rows[1]).toMatchObject({ leftLineNumber: null, rightLineNumber: 2, leftContent: "", rightContent: "bb", leftType: "padding", rightType: "insert" }); + + const inlineRows = buildNacosInlineDiff("aa", "aa\nbb"); + expect(inlineRows).toMatchObject([ + { lineNumber: 1, content: "aa", type: "equal" }, + { lineNumber: 2, content: "bb", type: "insert" }, + ]); + expect(inlineRows.some((row) => row.type === "delete")).toBe(false); + }); + + it("normalizes line endings and ignores terminal newline-only differences", () => { + expect(buildNacosSideBySideDiff("aa\n", "aa\nbb\n")).toMatchObject([ + { leftContent: "aa", rightContent: "aa", leftType: "equal", rightType: "equal" }, + { leftLineNumber: null, rightContent: "bb", leftType: "padding", rightType: "insert" }, + ]); + expect(buildNacosSideBySideDiff("aa\r\n", "aa\n")).toMatchObject([{ leftContent: "aa", rightContent: "aa", leftType: "equal", rightType: "equal" }]); + expect(buildNacosSideBySideDiff("aa\n", "aa")).toMatchObject([{ leftContent: "aa", rightContent: "aa", leftType: "equal", rightType: "equal" }]); + }); + + it("handles empty config content as line insertions", () => { + expect(buildNacosSideBySideDiff("", "bb")).toMatchObject([{ leftLineNumber: null, rightLineNumber: 1, rightContent: "bb", leftType: "padding", rightType: "insert" }]); + expect(buildNacosSideBySideDiff("", "")).toEqual([]); + }); + it("builds inline config diff rows with character-level changed segments", () => { const rows = buildNacosInlineDiff('secretId: "aaa1"\n', 'secretId: "aaa2"\n'); expect(rows).toHaveLength(2); diff --git a/apps/desktop/src/lib/nacos/nacosAdmin.ts b/apps/desktop/src/lib/nacos/nacosAdmin.ts index 20a9ebfdca..07724156ce 100644 --- a/apps/desktop/src/lib/nacos/nacosAdmin.ts +++ b/apps/desktop/src/lib/nacos/nacosAdmin.ts @@ -1,5 +1,5 @@ import type { NacosConfigHistoryItem, NacosConfigItem, NacosConfigKey, NacosContentMatch, NacosImplementation, NacosInstanceInfo, NacosRawRequest, NacosServiceInfo, NacosVersionMode } from "@/types/nacos"; -import { diffChars, diffLines } from "diff"; +import { diffArrays, diffChars } from "diff"; export type NacosRawTemplateKey = "serverState" | "namespaceList" | "configDetail" | "serviceList" | "instanceList"; @@ -502,32 +502,24 @@ export interface NacosInlineDiffRow { } export function summarizeNacosConfigDiff(before: string, after: string, maxPreviewLines = 40): NacosDiffSummary { - if (before === after) { - return { changed: false, addedLines: 0, removedLines: 0, preview: "No content changes." }; - } - const beforeLines = before.split(/\r?\n/); - const afterLines = after.split(/\r?\n/); - const max = Math.max(beforeLines.length, afterLines.length); + const changes = diffArrays(splitDiffLines(before), splitDiffLines(after)); const lines: string[] = []; let addedLines = 0; let removedLines = 0; - for (let index = 0; index < max; index += 1) { - const left = beforeLines[index]; - const right = afterLines[index]; - if (left === right) continue; - if (left !== undefined) { - removedLines += 1; - lines.push(`- ${left}`); - } - if (right !== undefined) { - addedLines += 1; - lines.push(`+ ${right}`); - } - if (lines.length >= maxPreviewLines) { - lines.push("..."); - break; + + for (const change of changes) { + if (!change.added && !change.removed) continue; + const prefix = change.added ? "+" : "-"; + for (const line of change.value) { + if (change.added) addedLines += 1; + else removedLines += 1; + if (lines.length < maxPreviewLines) lines.push(`${prefix} ${line}`); } } + + const changed = addedLines > 0 || removedLines > 0; + if (!changed) return { changed: false, addedLines: 0, removedLines: 0, preview: "No content changes." }; + if (lines.length < addedLines + removedLines) lines.push("..."); return { changed: true, addedLines, removedLines, preview: lines.join("\n") }; } @@ -581,7 +573,7 @@ function pairChangedLines(leftLines: string[], rightLines: string[], leftStart: } export function buildNacosSideBySideDiff(before: string, after: string): NacosSideBySideDiffRow[] { - const changes = diffLines(normalizeNacosDiffText(before), normalizeNacosDiffText(after), { newlineIsToken: false }); + const changes = diffArrays(splitDiffLines(before), splitDiffLines(after)); const rows: NacosSideBySideDiffRow[] = []; let leftLineNumber = 1; let rightLineNumber = 1; @@ -591,7 +583,7 @@ export function buildNacosSideBySideDiff(before: string, after: string): NacosSi for (let index = 0; index < changes.length; index += 1) { const change = changes[index]; if (!change.added && !change.removed) { - for (const line of splitDiffLines(change.value)) { + for (const line of change.value) { rows.push({ id: nextId(), leftLineNumber, @@ -610,10 +602,10 @@ export function buildNacosSideBySideDiff(before: string, after: string): NacosSi } if (change.removed) { - const leftLines = splitDiffLines(change.value); + const leftLines = change.value; const next = changes[index + 1]; if (next?.added) { - const rightLines = splitDiffLines(next.value); + const rightLines = next.value; pairChangedLines(leftLines, rightLines, leftLineNumber, rightLineNumber, rows, nextId); leftLineNumber += leftLines.length; rightLineNumber += rightLines.length; @@ -626,7 +618,7 @@ export function buildNacosSideBySideDiff(before: string, after: string): NacosSi } if (change.added) { - const rightLines = splitDiffLines(change.value); + const rightLines = change.value; pairChangedLines([], rightLines, leftLineNumber, rightLineNumber, rows, nextId); rightLineNumber += rightLines.length; }