Skip to content

Commit e37547a

Browse files
committed
chore: process characters with indexOf insead of one-by-one
1 parent dba6bf4 commit e37547a

File tree

2 files changed

+57
-32
lines changed

2 files changed

+57
-32
lines changed

packages/util-endpoints/src/utils/evaluateTemplate.spec.ts

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,14 @@ describe(evaluateTemplate.name, () => {
1313
jest.clearAllMocks();
1414
});
1515

16-
it("should escape tilde while processing expression", () => {
17-
const template = "foo `bar` baz";
18-
// This test verifies that tilde is unescaped after processing.
19-
expect(evaluateTemplate(template, mockOptions)).toBe(template);
20-
expect(getAttr).not.toHaveBeenCalled();
16+
it("should not escape template with empty parameter", () => {
17+
const templateWithEmptyParam = "foo {} bar";
18+
expect(evaluateTemplate(templateWithEmptyParam, mockOptions)).toEqual(templateWithEmptyParam);
19+
});
20+
21+
it("should not escape template without braces", () => {
22+
const templateWithoutBraces = "foo bar baz";
23+
expect(evaluateTemplate(templateWithoutBraces, mockOptions)).toEqual(templateWithoutBraces);
2124
});
2225

2326
describe("should replace `{parameterName}` with value", () => {
@@ -33,7 +36,7 @@ describe(evaluateTemplate.name, () => {
3336
});
3437
});
3538

36-
it("should not replace string escaped {{value}}", () => {
39+
it("should escape values within double braces like {{value}}", () => {
3740
const value = "bar";
3841
expect(evaluateTemplate("foo {{value1}} bar {{value2}} baz", { ...mockOptions, endpointParams: { value } })).toBe(
3942
"foo {value1} bar {value2} baz"
@@ -56,4 +59,15 @@ describe(evaluateTemplate.name, () => {
5659
expect(getAttr).toHaveBeenNthCalledWith(1, ref1, "key1");
5760
expect(getAttr).toHaveBeenNthCalledWith(2, ref2, "key2");
5861
});
62+
63+
describe("should not change template with incomplete braces", () => {
64+
it.each([
65+
"incomplete opening bracket '{' in template",
66+
"incomplete closing bracket '}' in template",
67+
"incomplete opening escape '{{' in template",
68+
"incomplete closing escape '}}' in template",
69+
])("%s", (template) => {
70+
expect(evaluateTemplate(template, mockOptions)).toEqual(template);
71+
});
72+
});
5973
});

packages/util-endpoints/src/utils/evaluateTemplate.ts

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,43 +2,54 @@ import { getAttr } from "../lib";
22
import { EvaluateOptions } from "../types";
33

44
export const evaluateTemplate = (template: string, options: EvaluateOptions) => {
5-
const evaluatedTemplateArr: Array<String> = [];
5+
const evaluatedTemplateArr: string[] = [];
66

77
const templateContext = {
88
...options.endpointParams,
99
...options.referenceRecord,
1010
} as Record<string, string>;
1111

12-
for (let i = 0; i < template.length; i++) {
13-
const char = template[i];
14-
const nextChar = template[i + 1];
12+
let currentIndex = 0;
13+
while (true) {
14+
const openingBraceIndex = template.indexOf("{", currentIndex);
1515

16-
if (char === "{") {
17-
if (nextChar === "{") {
18-
// Escaped expression, skip next char
19-
i++;
20-
evaluatedTemplateArr.push(char);
21-
} else {
22-
const closingBraceIndex = template.indexOf("}", i);
23-
const parameterName = template.substring(i + 1, closingBraceIndex);
16+
if (openingBraceIndex === -1) {
17+
// No more opening braces, add the rest of the template and break
18+
evaluatedTemplateArr.push(template.slice(currentIndex));
19+
break;
20+
} else {
21+
evaluatedTemplateArr.push(template.slice(currentIndex, openingBraceIndex));
22+
const closingBraceIndex = template.indexOf("}", openingBraceIndex);
2423

25-
if (parameterName.includes("#")) {
26-
const [refName, attrName] = parameterName.split("#");
27-
evaluatedTemplateArr.push(getAttr(templateContext[refName], attrName) as string);
28-
} else {
29-
evaluatedTemplateArr.push(templateContext[parameterName]);
30-
}
24+
if (closingBraceIndex === -1) {
25+
// Invalid template, but pass as it is.
26+
evaluatedTemplateArr.push(template.slice(openingBraceIndex));
27+
break;
28+
}
3129

32-
i = closingBraceIndex;
30+
if (closingBraceIndex === openingBraceIndex + 1) {
31+
// Empty parameter, pass as it is.
32+
evaluatedTemplateArr.push(template.slice(openingBraceIndex, closingBraceIndex + 1));
33+
currentIndex = closingBraceIndex + 1;
34+
continue;
3335
}
34-
} else if (char === "}") {
35-
if (nextChar === "}") {
36-
// Escaped expression, skip next char
37-
i++;
36+
37+
if (template[openingBraceIndex + 1] === "{" && template[closingBraceIndex + 1] === "}") {
38+
// Escaped expression. Do not evaluate.
39+
evaluatedTemplateArr.push(template.slice(openingBraceIndex + 1, closingBraceIndex));
40+
currentIndex = closingBraceIndex + 2;
3841
}
39-
evaluatedTemplateArr.push(char);
40-
} else {
41-
evaluatedTemplateArr.push(char);
42+
43+
const parameterName = template.substring(openingBraceIndex + 1, closingBraceIndex);
44+
45+
if (parameterName.includes("#")) {
46+
const [refName, attrName] = parameterName.split("#");
47+
evaluatedTemplateArr.push(getAttr(templateContext[refName], attrName) as string);
48+
} else {
49+
evaluatedTemplateArr.push(templateContext[parameterName]);
50+
}
51+
52+
currentIndex = closingBraceIndex + 1;
4253
}
4354
}
4455

0 commit comments

Comments
 (0)