| 1 | /** |
| 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. |
| 3 | * |
| 4 | * This source code is licensed under the MIT license found in the |
| 5 | * LICENSE file in the root directory of this source tree. |
| 6 | * |
| 7 | * @flow |
| 8 | */ |
| 9 | |
| 10 | // Do not add / import anything to this file. |
| 11 | // This function could be used from multiple places, including hook. |
| 12 | |
| 13 | // Formats an array of args with a style for console methods, using |
| 14 | // the following algorithm: |
| 15 | // 1. The first param is a string that contains %c |
| 16 | // - Bail out and return the args without modifying the styles. |
| 17 | // We don't want to affect styles that the developer deliberately set. |
| 18 | // 2. The first param is a string that doesn't contain %c but contains |
| 19 | // string formatting |
| 20 | // - [`%c${args[0]}`, style, ...args.slice(1)] |
| 21 | // - Note: we assume that the string formatting that the developer uses |
| 22 | // is correct. |
| 23 | // 3. The first param is a string that doesn't contain string formatting |
| 24 | // OR is not a string |
| 25 | // - Create a formatting string where: |
| 26 | // boolean, string, symbol -> %s |
| 27 | // number -> %f OR %i depending on if it's an int or float |
| 28 | // default -> %o |
| 29 | export default function formatWithStyles( |
| 30 | inputArgs: $ReadOnlyArray<any>, |
| 31 | style?: string, |
| 32 | ): $ReadOnlyArray<any> { |
| 33 | if ( |
| 34 | inputArgs === undefined || |
| 35 | // $FlowFixMe[invalid-compare] |
| 36 | inputArgs === null || |
| 37 | inputArgs.length === 0 || |
| 38 | // Matches any of %c but not %%c |
| 39 | (typeof inputArgs[0] === 'string' && inputArgs[0].match(/([^%]|^)(%c)/g)) || |
| 40 | style === undefined |
| 41 | ) { |
| 42 | return inputArgs; |
| 43 | } |
| 44 | |
| 45 | // Matches any of %(o|O|d|i|s|f), but not %%(o|O|d|i|s|f) |
| 46 | const REGEXP = /([^%]|^)((%%)*)(%([oOdisf]))/g; |
| 47 | if (typeof inputArgs[0] === 'string' && inputArgs[0].match(REGEXP)) { |
| 48 | return [`%c${inputArgs[0]}`, style, ...inputArgs.slice(1)]; |
| 49 | } else { |
| 50 | const firstArg = inputArgs.reduce((formatStr, elem, i) => { |
| 51 | if (i > 0) { |
| 52 | formatStr += ' '; |
| 53 | } |
| 54 | switch (typeof elem) { |
| 55 | case 'string': |
| 56 | case 'boolean': |
| 57 | case 'symbol': |
| 58 | return (formatStr += '%s'); |
| 59 | case 'number': |
| 60 | const formatting = Number.isInteger(elem) ? '%i' : '%f'; |
| 61 | return (formatStr += formatting); |
| 62 | default: |
| 63 | return (formatStr += '%o'); |
| 64 | } |
| 65 | }, '%c'); |
| 66 | return [firstArg, style, ...inputArgs]; |
| 67 | } |
| 68 | } |