diff --git a/doc/api/util.md b/doc/api/util.md index fdb90505b1c0ec..ba6cc8ee3827b3 100644 --- a/doc/api/util.md +++ b/doc/api/util.md @@ -181,28 +181,27 @@ The `--throw-deprecation` command line flag and `process.throwDeprecation` property take precedence over `--trace-deprecation` and `process.traceDeprecation`. -## util.format(format\[, ...args\]) +## util.format(\[format\]\[, ...args\]) * `format` {string} A `printf`-like format string. +* `...args` {any} +* Returns: {string} The formatted string -The `util.format()` method returns a formatted string using the first argument -as a `printf`-like format string which can contain zero or more format -specifiers. Each specifier is replaced with the converted value from the -corresponding argument. Supported specifiers are: +If the first argument `format` is a string and `args` is not empty, the +`util.format()` method returns a formatted string using the first argument as a +`printf`-like format string which can contain zero or more format specifiers. +Each specifier is replaced with the converted value from the corresponding +argument. Supported specifiers are: * `%s` - `String` will be used to convert all values except `BigInt`, `Object` and `-0`. `BigInt` values will be represented with an `n` and Objects that @@ -246,7 +248,6 @@ corresponding argument. Supported specifiers are: * `%c` - `CSS`. This specifier is currently ignored, and will skip any CSS passed in. * `%%` - single percent sign (`'%'`). This does not consume an argument. -* Returns: {string} The formatted string If a specifier does not have a corresponding argument, it is not replaced: @@ -255,45 +256,51 @@ util.format('%s:%s', 'foo'); // Returns: 'foo:%s' ``` -Values that are not part of the format string are formatted using -`util.inspect()` if their type is not `string`. - -If there are more arguments passed to the `util.format()` method than the -number of specifiers, the extra arguments are concatenated to the returned -string, separated by spaces: +If there are more arguments than the number of specifiers, the extra arguments +are concatenated to the returned string, separated by spaces: ```js util.format('%s:%s', 'foo', 'bar', 'baz'); // Returns: 'foo:bar baz' ``` -If the first argument does not contain a valid format specifier, `util.format()` -returns a string that is the concatenation of all arguments separated by spaces: +Values that are not consumed by the format string are formatted using +`util.inspect()` if their type is not `string`: ```js -util.format(1, 2, 3); -// Returns: '1 2 3' +util.format('string', 5n, 'string'); +// Returns 'string 5n string' ``` -If only one argument is passed to `util.format()`, it is returned as it is -without any formatting: +All values are formatted using `util.inspect()`, if the first argument's type is +not `string`: ```js -util.format('%% %s'); -// Returns: '%% %s' +util.format(5n, 'string \n line 1', [1]); +// Returns: "5n 'string \\n line 1' [ 1 ]" +``` + +If only a single argument of type `string` is passed to `util.format()`, it is +returned as it is without any formatting: + +```js +util.format('%% %s \n line 2'); +// Returns: '%% %s \n line 2' ``` `util.format()` is a synchronous method that is intended as a debugging tool. Some input values can have a significant performance overhead that can block the event loop. Use this function with care and never in a hot code path. -## util.formatWithOptions(inspectOptions, format\[, ...args\]) +## util.formatWithOptions(inspectOptions\[, format\]\[, ...args\]) * `inspectOptions` {Object} * `format` {string} +* `...args` {any} +* Returns: {string} This function is identical to [`util.format()`][], except in that it takes an `inspectOptions` argument which specifies options that are passed along to diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index 2f922f6659931c..8bbd16dc5c8079 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -1700,7 +1700,11 @@ function formatWithOptionsInternal(inspectOptions, ...args) { while (a < args.length) { const value = args[a]; str += join; - str += typeof value !== 'string' ? inspect(value, inspectOptions) : value; + if (typeof value !== 'string' || typeof first !== 'string') { + str += inspect(value, inspectOptions); + } else { + str += value; + } join = ' '; a++; } diff --git a/test/parallel/test-util-format.js b/test/parallel/test-util-format.js index 2ef05284902995..d359f9a4f8e9b2 100644 --- a/test/parallel/test-util-format.js +++ b/test/parallel/test-util-format.js @@ -367,17 +367,17 @@ Object.setPrototypeOf(BadCustomError, Error); assert.strictEqual(util.format(new BadCustomError('foo')), '[BadCustomError: foo]'); -// The format of arguments should not depend on type of the first argument +// The argument formatting depends on the type of the first argument. assert.strictEqual(util.format('1', '1'), '1 1'); -assert.strictEqual(util.format(1, '1'), '1 1'); +assert.strictEqual(util.format(1, '1'), "1 '1'"); assert.strictEqual(util.format('1', 1), '1 1'); assert.strictEqual(util.format(1, -0), '1 -0'); assert.strictEqual(util.format('1', () => {}), '1 [Function (anonymous)]'); assert.strictEqual(util.format(1, () => {}), '1 [Function (anonymous)]'); assert.strictEqual(util.format('1', "'"), "1 '"); -assert.strictEqual(util.format(1, "'"), "1 '"); +assert.strictEqual(util.format(1, "'"), '1 "\'"'); assert.strictEqual(util.format('1', 'number'), '1 number'); -assert.strictEqual(util.format(1, 'number'), '1 number'); +assert.strictEqual(util.format(1, 'number'), "1 'number'"); assert.strictEqual(util.format(5n), '5n'); assert.strictEqual(util.format(5n, 5n), '5n 5n'); @@ -393,7 +393,15 @@ assert.strictEqual( '\u001b[33m1\u001b[39m ' + '\u001b[33m5n\u001b[39m ' + '\u001b[1mnull\u001b[22m ' + - 'foobar' + "\u001b[32m'foobar'\u001b[39m" +); + +assert.strictEqual( + util.formatWithOptions( + { colors: true }, + 'string', true, 'foobar' + ), + 'string \u001b[33mtrue\u001b[39m foobar' ); assert.strictEqual(