( format: string, substitutions: ReadonlyArray<T>, formatters: Formatters<T>, )
| 66 | } |
| 67 | |
| 68 | export function formatMessage<T>( |
| 69 | format: string, |
| 70 | substitutions: ReadonlyArray<T>, |
| 71 | formatters: Formatters<T>, |
| 72 | ): { result: string; usedAllSubs: boolean } { |
| 73 | const tokens = tokenizeFormatString(format, Array.from(formatters.keys())); |
| 74 | const usedSubstitutionIndexes = new Set<number>(); |
| 75 | const defaultFormatter = formatters.get(''); |
| 76 | if (!defaultFormatter) { |
| 77 | throw new Error('Expected to hav a default formatter'); |
| 78 | } |
| 79 | |
| 80 | const builder = new BudgetStringBuilder(maxMessageFormatLength); |
| 81 | let cssFormatApplied = false; |
| 82 | for (let i = 0; builder.checkBudget() && i < tokens.length; ++i) { |
| 83 | const token = tokens[i]; |
| 84 | if (token.type === 'string') { |
| 85 | builder.append(token.value); |
| 86 | continue; |
| 87 | } |
| 88 | |
| 89 | const index = token.substitutionIndex; |
| 90 | if (index >= substitutions.length) { |
| 91 | // If there are not enough substitutions for the current substitutionIndex |
| 92 | // just output the format specifier literally and move on. |
| 93 | builder.append('%' + (token.precision || '') + token.specifier); |
| 94 | continue; |
| 95 | } |
| 96 | usedSubstitutionIndexes.add(index); |
| 97 | if (token.specifier === 'c') cssFormatApplied = true; |
| 98 | const formatter = formatters.get(token.specifier) || defaultFormatter; |
| 99 | builder.append( |
| 100 | formatter(substitutions[index], { budget: builder.budget(), quoted: false, ansi: true }), |
| 101 | ); |
| 102 | } |
| 103 | |
| 104 | if (cssFormatApplied) builder.append('\x1b[0m'); // clear format |
| 105 | |
| 106 | for (let i = 0; builder.checkBudget() && i < substitutions.length; ++i) { |
| 107 | if (usedSubstitutionIndexes.has(i)) continue; |
| 108 | usedSubstitutionIndexes.add(i); |
| 109 | if (format || i) { |
| 110 | // either we are second argument or we had format. |
| 111 | builder.append(' '); |
| 112 | } |
| 113 | builder.append( |
| 114 | defaultFormatter(substitutions[i], { budget: builder.budget(), quoted: false, ansi: true }), |
| 115 | ); |
| 116 | } |
| 117 | |
| 118 | return { |
| 119 | result: builder.build(), |
| 120 | usedAllSubs: usedSubstitutionIndexes.size === substitutions.length, |
| 121 | }; |
| 122 | } |
| 123 | |
| 124 | function escapeAnsiColor(colorString: string): number | undefined { |
| 125 | try { |
no test coverage detected