(format: string, formatterNames: string[])
| 15 | export type Formatters<T> = Map<string, (a: T, context: IPreviewContext) => string>; |
| 16 | |
| 17 | function tokenizeFormatString(format: string, formatterNames: string[]): FormatToken[] { |
| 18 | if (!format.includes('%')) { |
| 19 | return [{ type: 'string', value: format }]; // happy path, no formatting needed |
| 20 | } |
| 21 | |
| 22 | const tokens: FormatToken[] = []; |
| 23 | |
| 24 | function addStringToken(str: string) { |
| 25 | if (!str) return; |
| 26 | const lastToken = tokens[tokens.length - 1]; |
| 27 | if (lastToken?.type === 'string') lastToken.value += str; |
| 28 | else tokens.push({ type: 'string', value: str }); |
| 29 | } |
| 30 | |
| 31 | function addSpecifierToken( |
| 32 | specifier: string, |
| 33 | precision: number | undefined, |
| 34 | substitutionIndex: number, |
| 35 | ) { |
| 36 | tokens.push({ |
| 37 | type: 'specifier', |
| 38 | specifier: specifier, |
| 39 | precision: precision, |
| 40 | substitutionIndex, |
| 41 | }); |
| 42 | } |
| 43 | |
| 44 | let textStart = 0; |
| 45 | let substitutionIndex = 0; |
| 46 | const re = new RegExp(`%%|%(?:(\\d+)\\$)?(?:\\.(\\d*))?([${formatterNames.join('')}])`, 'g'); |
| 47 | for (let match = re.exec(format); !!match; match = re.exec(format)) { |
| 48 | const matchStart = match.index; |
| 49 | if (matchStart > textStart) addStringToken(format.substring(textStart, matchStart)); |
| 50 | |
| 51 | if (match[0] === '%%') { |
| 52 | addStringToken('%'); |
| 53 | } else { |
| 54 | const [, substitionString, precisionString, specifierString] = match; |
| 55 | if (substitionString && Number(substitionString) > 0) { |
| 56 | substitutionIndex = Number(substitionString) - 1; |
| 57 | } |
| 58 | const precision = precisionString ? Number(precisionString) : undefined; |
| 59 | addSpecifierToken(specifierString, precision, substitutionIndex); |
| 60 | ++substitutionIndex; |
| 61 | } |
| 62 | textStart = matchStart + match[0].length; |
| 63 | } |
| 64 | addStringToken(format.substring(textStart)); |
| 65 | return tokens; |
| 66 | } |
| 67 | |
| 68 | export function formatMessage<T>( |
| 69 | format: string, |
no test coverage detected