| |
| |
| |
|
|
| export interface SubstituteResult { |
| text: string; |
| bound: string[]; |
| unbound: string[]; |
| } |
|
|
| const PLACEHOLDER_RE = /\{\{([A-Z][A-Z0-9_]*)\}\}/g; |
|
|
| export function substitute(text: string, inputs: Record<string, string>): string { |
| return text.replace(PLACEHOLDER_RE, (match, key: string) => { |
| if (inputs[key] !== undefined) return inputs[key]; |
| const lc = key.toLowerCase(); |
| if (inputs[lc] !== undefined) return inputs[lc]; |
| return match; |
| }); |
| } |
|
|
| export function substituteWithReport( |
| text: string, |
| inputs: Record<string, string>, |
| ): SubstituteResult { |
| const bound = new Set<string>(); |
| const unbound = new Set<string>(); |
| const out = text.replace(PLACEHOLDER_RE, (match, key: string) => { |
| if (inputs[key] !== undefined) { |
| bound.add(`{{${key}}}`); |
| return inputs[key]; |
| } |
| const lc = key.toLowerCase(); |
| if (inputs[lc] !== undefined) { |
| bound.add(`{{${key}}}`); |
| return inputs[lc]; |
| } |
| unbound.add(`{{${key}}}`); |
| return match; |
| }); |
| return { text: out, bound: [...bound], unbound: [...unbound] }; |
| } |
|
|
| export function findPlaceholders(text: string): string[] { |
| const found = new Set<string>(); |
| for (const m of text.matchAll(PLACEHOLDER_RE)) { |
| found.add(`{{${m[1]}}}`); |
| } |
| return [...found]; |
| } |
|
|