diff --git a/src/jsc/bindings/wrapAnsi.cpp b/src/jsc/bindings/wrapAnsi.cpp index 968705d87c1b..20dfc630e375 100644 --- a/src/jsc/bindings/wrapAnsi.cpp +++ b/src/jsc/bindings/wrapAnsi.cpp @@ -435,6 +435,8 @@ static std::optional getCloseCode(uint32_t code) return 28; case 9: return 29; + case 53: + return 55; } if (code >= 30 && code <= 37) @@ -511,8 +513,9 @@ static void joinRowsWithAnsiPreservation(const Vector>& rows, StringBu } } } else if (c == '\n') { - // Restore styles after newline - if (escapeCode) { + // Restore styles after newline (only open codes; close/unknown codes + // have no close mapping and are not re-emitted, matching npm wrap-ansi) + if (escapeCode && getCloseCode(*escapeCode)) { result.append("\x1b["_s); result.append(String::number(*escapeCode)); result.append('m'); diff --git a/test/js/bun/util/wrapAnsi.test.ts b/test/js/bun/util/wrapAnsi.test.ts index db2ab9c88fef..035d7711132c 100644 --- a/test/js/bun/util/wrapAnsi.test.ts +++ b/test/js/bun/util/wrapAnsi.test.ts @@ -95,6 +95,31 @@ describe("Bun.wrapAnsi", () => { // "ab" is 2 chars, should fit in width 2 expect(Bun.wrapAnsi(input, 2)).toBe(input); }); + + // SGR close codes (22-29, 49, 55) and unknown codes have no close mapping + // in ansi-styles' codes map, so npm wrap-ansi never re-emits them after a + // line break. Only open codes with a known close code are closed-then-reopened. + test.each([22, 23, 24, 25, 27, 28, 29, 49, 55, 39, 0, 200])( + "does not re-open SGR close/unknown code %p after line break", + code => { + expect(Bun.wrapAnsi(`\x1b[${code}mabc def`, 3)).toBe(`\x1b[${code}mabc\ndef`); + }, + ); + + test.each([ + [1, 22], + [4, 24], + [31, 39], + [42, 49], + [53, 55], + [100, 49], + ])("re-opens SGR open code %p after line break", (open, close) => { + expect(Bun.wrapAnsi(`\x1b[${open}mabc def`, 3)).toBe(`\x1b[${open}mabc\x1b[${close}m\n\x1b[${open}mdef`); + }); + + test("close code following an open code is not carried across line break", () => { + expect(Bun.wrapAnsi("\x1b[42mab\x1b[49mcd ef", 4)).toBe("\x1b[42mab\x1b[49mcd\nef"); + }); }); describe("Unicode support", () => {