Skip to content

Commit 445dd2c

Browse files
committed
fix: interpolation should ignore strings outside variables with same
names
1 parent d3c4a00 commit 445dd2c

File tree

3 files changed

+55
-27
lines changed

3 files changed

+55
-27
lines changed

.changeset/cold-experts-work.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"typed-string-interpolation": patch
3+
---
4+
5+
Fixes issue #8 where variable keys were also replaced outside variables
Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,35 @@
1-
import { stringInterpolation } from "../index"
1+
import { stringInterpolation } from "../index";
22

33
describe("stringInterpolation()", () => {
44
test("Empty", () => {
55
expect(() => {
66
stringInterpolation("", {
77
world: "world",
8-
})
9-
}).toThrow("Empty string")
10-
})
8+
});
9+
}).toThrow("Empty string");
10+
});
1111
test("Incorrect variable count", () => {
1212
expect(() => {
1313
stringInterpolation("Hello {{world}}", {
1414
world: "world with varialbe",
1515
extraVariable: "this is unnecessary",
16-
})
17-
}).toThrow("Variable count mismatch")
18-
})
16+
});
17+
}).toThrow("Variable count mismatch");
18+
});
1919
test("Variable not found", () => {
2020
expect(() =>
2121
stringInterpolation("Hello {{world}}", {
2222
wrongVariable: "world",
2323
})
24-
).toThrow("Variable 'world' not found")
25-
})
24+
).toThrow("Variable 'world' not found");
25+
});
2626
test("Interpolate single variable", () => {
2727
expect(
2828
stringInterpolation("Hello {{world}}", {
2929
world: "world with variable",
3030
})
31-
).toBe("Hello world with variable")
32-
})
31+
).toBe("Hello world with variable");
32+
});
3333
test("Interpolate single variable and return raw result with passed in option", () => {
3434
expect(
3535
stringInterpolation(
@@ -39,16 +39,16 @@ describe("stringInterpolation()", () => {
3939
},
4040
{ raw: true }
4141
)
42-
).toStrictEqual(["Hello ", "world with variable"])
43-
})
42+
).toStrictEqual(["Hello ", "world with variable"]);
43+
});
4444
test("Interpolate two variables", () => {
4545
expect(
4646
stringInterpolation("Hello {{world}} and {{anotherVariable}}", {
4747
world: "world with variable",
4848
anotherVariable: "another variable",
4949
})
50-
).toBe("Hello world with variable and another variable")
51-
})
50+
).toBe("Hello world with variable and another variable");
51+
});
5252
test("Interpolation variable contains a function", () => {
5353
expect(
5454
stringInterpolation("Hello {{world}} and {{anotherVariable}}", {
@@ -60,6 +60,11 @@ describe("stringInterpolation()", () => {
6060
"world with variable",
6161
" and ",
6262
expect.any(Function),
63-
])
64-
})
65-
})
63+
]);
64+
});
65+
test("Interpolation string contains a variable name which should remain the same", () => {
66+
expect(stringInterpolation("foo{{foo}}", { foo: "bar" })).toStrictEqual(
67+
"foobar" // todo: fix - returns "barbar"
68+
);
69+
});
70+
});

src/index.ts

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -76,15 +76,33 @@ export function stringInterpolation<
7676
}
7777
}
7878

79-
// Create raw interpolation result
80-
const rawInterpolation = string
81-
.split(pattern)
82-
// Trim empty string from array end (Could propably be done with regex as well)
83-
.filter(Boolean)
84-
// Match parsed variables with passed in variables
85-
.map((splitItem) => {
86-
return variables[splitItem] ? variables[splitItem] : splitItem
87-
})
79+
// Create raw interpolation result using match positions to avoid
80+
// replacing plain text that happens to equal a variable name
81+
const rawInterpolation: (string | VariableValue)[] = []
82+
let lastIndex = 0
83+
84+
for (const regExpMatchArray of stringVariables as RegExpExecArray[]) {
85+
const matchIndex = regExpMatchArray.index ?? 0
86+
const fullMatch = regExpMatchArray[0]
87+
const variableKeyInString = regExpMatchArray[1]
88+
89+
// Push literal substring before the match
90+
if (matchIndex > lastIndex) {
91+
rawInterpolation.push(string.slice(lastIndex, matchIndex))
92+
}
93+
94+
// Push the variable value for the matched key
95+
rawInterpolation.push(
96+
variables[variableKeyInString as unknown as PropertyKey]
97+
)
98+
99+
lastIndex = matchIndex + fullMatch.length
100+
}
101+
102+
// Push any trailing literal substring after the last match
103+
if (lastIndex < string.length) {
104+
rawInterpolation.push(string.slice(lastIndex))
105+
}
88106

89107
// Checks if raw interpolation can be joined or not.
90108
// i.e. avoid printing [object Object | Array | Function | ...] within returned string.

0 commit comments

Comments
 (0)