diff --git a/tools/src/hwpod-node-lib.ts b/tools/src/hwpod-node-lib.ts index 2802550b..683b8f93 100644 --- a/tools/src/hwpod-node-lib.ts +++ b/tools/src/hwpod-node-lib.ts @@ -419,7 +419,7 @@ async function workspaceReplace(args: any) { assertExpectedSha(before, args.expectedSha, relativePath); const find = requiredRawString(args.find, "find"); const replace = rawString(args.replace ?? args.replacement); - const normalizedFind = normalizeContentLineEndings(find, "\n", false); + const normalizedFind = normalizeContentLineEndings(find, "\n", false, true); const normalizedReplace = normalizeContentLineEndings(replace, "\n", false); const normalizedContent = before.normalizedContent; const occurrences = countOccurrences(normalizedContent, normalizedFind); @@ -443,7 +443,7 @@ async function workspaceInsertAfter(args: any) { assertExpectedSha(before, args.expectedSha, relativePath); const anchor = requiredRawString(args.anchor, "anchor"); const insertText = requiredRawString(args.line ?? args.insert ?? args.text, "line"); - const normalizedAnchor = normalizeContentLineEndings(anchor, "\n", false); + const normalizedAnchor = normalizeContentLineEndings(anchor, "\n", false, true); const normalizedInsert = normalizeContentLineEndings(insertText, "\n", false); const lines = splitNormalizedLines(before.normalizedContent, before.hadFinalNewline); const anchorLines = splitNormalizedLines(normalizedAnchor, false); @@ -611,7 +611,7 @@ function splitNormalizedLines(content: string, hadFinalNewline: boolean) { } function diagnosticDetails(fileState: any, expected: string, relativePath: string) { - const expectedLines = splitNormalizedLines(normalizeContentLineEndings(expected, "\n", false), false).filter((line) => line.length > 0); + const expectedLines = splitNormalizedLines(normalizeContentLineEndings(expected, "\n", false, true), false).filter((line) => line.length > 0); const normalizedLines = splitNormalizedLines(fileState.normalizedContent, fileState.hadFinalNewline); const candidates = expectedLines.slice(0, 4).flatMap((line) => candidateLines(normalizedLines, line)).slice(0, 12); return { @@ -700,9 +700,9 @@ function lineEndingFromArgs(args: any, fallback: string) { throw cliError("invalid_line_ending", "lineEnding must be preserve, lf, or crlf", { lineEnding: value }); } -function normalizeContentLineEndings(content: string, lineEnding: string, finalNewline: boolean) { - const unescaped = String(content).replace(/\\r\\n/gu, "\r\n").replace(/\\n/gu, "\n").replace(/\\r(?!\n)/gu, "\r"); - const normalized = unescaped.replace(/\r\n?/gu, "\n"); +function normalizeContentLineEndings(content: string, lineEnding: string, finalNewline: boolean, unescapeEscapeSequences = false) { + const base = unescapeEscapeSequences ? String(content).replace(/\\r\\n/gu, "\r\n").replace(/\\n/gu, "\n").replace(/\\r(?!\n)/gu, "\r") : String(content); + const normalized = base.replace(/\r\n?/gu, "\n"); const withFinal = finalNewline && !normalized.endsWith("\n") ? `${normalized}\n` : normalized; return lineEnding === "\r\n" ? withFinal.replace(/\n/gu, "\r\n") : withFinal; }