-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathreplacements.ts
More file actions
51 lines (40 loc) · 1.51 KB
/
replacements.ts
File metadata and controls
51 lines (40 loc) · 1.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import { stringMatch, stringMatchAll } from '@noeldemartin/utils/helpers/string_helpers';
import { toString } from '@noeldemartin/utils/helpers/object_helpers';
import * as replacementsScope from './replacements-scope';
import type { ReplacementsScope } from './replacements-scope';
function renderReplacement(script: string, scope: ReplacementsScope): string {
try {
return Function(`with (this) { return (${script}).toString() }`).bind({
...replacementsScope,
...scope,
})();
} catch (error) {
const undefinedVariable = stringMatch<2>(toString(error), /([^\s]+) is not defined/)?.[1];
if (!undefinedVariable) {
throw error;
}
return renderReplacement(script, {
...scope,
[undefinedVariable]: undefined,
});
}
}
const REPLACEMENTS_REGEX = /{{([^}]+)}}/gm;
function reduce<TItem, TResult>(
items: IterableIterator<TItem>,
initial: TResult,
callback: (result: TResult, item: TItem) => TResult,
) {
let result = initial;
for (const item of items) {
result = callback(result, item);
}
return result;
}
export type Replacements = Record<string, unknown>;
export function applyReplacements(text: string, replacements: Replacements = {}): string {
const matches = stringMatchAll<2>(text, REPLACEMENTS_REGEX);
return reduce(matches, text, (renderedText, match) => {
return renderedText.replace(match[0], renderReplacement(match[1], replacements));
});
}