From 3929fc3a40586d7c4efe6f62bc28cc2f007c400a Mon Sep 17 00:00:00 2001 From: Hiroki Osame Date: Sun, 15 Feb 2026 11:26:15 +0900 Subject: [PATCH 1/3] util: add fast path to stripVTControlCharacters --- benchmark/util/strip-vt-control-characters.js | 37 +++++++++++++++++++ lib/internal/util/inspect.js | 4 ++ test/parallel/test-util.js | 10 +++++ 3 files changed, 51 insertions(+) create mode 100644 benchmark/util/strip-vt-control-characters.js diff --git a/benchmark/util/strip-vt-control-characters.js b/benchmark/util/strip-vt-control-characters.js new file mode 100644 index 00000000000000..a0e79de853bd65 --- /dev/null +++ b/benchmark/util/strip-vt-control-characters.js @@ -0,0 +1,37 @@ +'use strict'; + +const common = require('../common.js'); + +const { stripVTControlCharacters } = require('node:util'); +const assert = require('node:assert'); + +const bench = common.createBenchmark(main, { + input: ['plain-short', 'plain-long', 'ansi-short', 'ansi-heavy'], + n: [1e6], +}); + +function main({ input, n }) { + let str; + switch (input) { + case 'plain-short': + str = 'Hello, World!'; + break; + case 'plain-long': + str = 'a'.repeat(1000); + break; + case 'ansi-short': + str = '\u001B[31mHello\u001B[39m'; + break; + case 'ansi-heavy': + str = '\u001B[1m\u001B[31m\u001B[4m' + 'x'.repeat(100) + + '\u001B[24m\u001B[39m\u001B[22m'; + break; + } + + bench.start(); + for (let i = 0; i < n; i++) { + const result = stripVTControlCharacters(str); + assert.ok(typeof result === 'string'); + } + bench.end(n); +} diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index 3d0787e8d1a9b7..c39183e6a6c20a 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -3036,6 +3036,10 @@ if (internalBinding('config').hasIntl) { function stripVTControlCharacters(str) { validateString(str, 'str'); + if (!StringPrototypeIncludes(str, '\u001B') && + !StringPrototypeIncludes(str, '\u009B')) + return str; + return RegExpPrototypeSymbolReplace(ansi, str, ''); } diff --git a/test/parallel/test-util.js b/test/parallel/test-util.js index 1ecddc829a0fdb..ad6c8142b27d0b 100644 --- a/test/parallel/test-util.js +++ b/test/parallel/test-util.js @@ -87,3 +87,13 @@ assert.throws(() => { message: 'The "str" argument must be of type string.' + common.invalidArgTypeHelper({}) }); + +// stripVTControlCharacters: fast path returns input when no ANSI codes +assert.strictEqual(util.stripVTControlCharacters('hello'), 'hello'); +assert.strictEqual(util.stripVTControlCharacters(''), ''); + +// stripVTControlCharacters: strips 7-bit ESC sequences +assert.strictEqual(util.stripVTControlCharacters('\u001B[31mfoo\u001B[39m'), 'foo'); + +// stripVTControlCharacters: strips 8-bit CSI sequences +assert.strictEqual(util.stripVTControlCharacters('\u009B31mfoo\u009B39m'), 'foo'); From bf4a28dbaafdec764b251293c35a89cad5961d16 Mon Sep 17 00:00:00 2001 From: Hiroki Osame Date: Sun, 15 Feb 2026 12:02:51 +0900 Subject: [PATCH 2/3] fixup! util: add fast path to stripVTControlCharacters --- benchmark/util/strip-vt-control-characters.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/benchmark/util/strip-vt-control-characters.js b/benchmark/util/strip-vt-control-characters.js index a0e79de853bd65..9bc923663427bd 100644 --- a/benchmark/util/strip-vt-control-characters.js +++ b/benchmark/util/strip-vt-control-characters.js @@ -6,25 +6,24 @@ const { stripVTControlCharacters } = require('node:util'); const assert = require('node:assert'); const bench = common.createBenchmark(main, { - input: ['plain-short', 'plain-long', 'ansi-short', 'ansi-heavy'], + input: ['noAnsi-short', 'noAnsi-long', 'ansi-short', 'ansi-long'], n: [1e6], }); function main({ input, n }) { let str; switch (input) { - case 'plain-short': - str = 'Hello, World!'; + case 'noAnsi-short': + str = 'This is a plain text string without any ANSI codes'; break; - case 'plain-long': - str = 'a'.repeat(1000); + case 'noAnsi-long': + str = 'Long plain text without ANSI. '.repeat(100); break; case 'ansi-short': str = '\u001B[31mHello\u001B[39m'; break; - case 'ansi-heavy': - str = '\u001B[1m\u001B[31m\u001B[4m' + 'x'.repeat(100) + - '\u001B[24m\u001B[39m\u001B[22m'; + case 'ansi-long': + str = ('\u001B[31m' + 'colored text '.repeat(10) + '\u001B[39m').repeat(10); break; } From 003511d1a607ffe1227db3e4ac43d06d041313aa Mon Sep 17 00:00:00 2001 From: Hiroki Osame Date: Sun, 15 Feb 2026 12:12:06 +0900 Subject: [PATCH 3/3] fixup! util: add fast path to stripVTControlCharacters --- benchmark/util/strip-vt-control-characters.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmark/util/strip-vt-control-characters.js b/benchmark/util/strip-vt-control-characters.js index 9bc923663427bd..a83b03bb2bb26d 100644 --- a/benchmark/util/strip-vt-control-characters.js +++ b/benchmark/util/strip-vt-control-characters.js @@ -17,7 +17,7 @@ function main({ input, n }) { str = 'This is a plain text string without any ANSI codes'; break; case 'noAnsi-long': - str = 'Long plain text without ANSI. '.repeat(100); + str = 'Long plain text without ANSI. '.repeat(333); break; case 'ansi-short': str = '\u001B[31mHello\u001B[39m';