Skip to content

Commit 8cb2b34

Browse files
committed
Add helper for substituting config variables
1 parent 7da3740 commit 8cb2b34

File tree

2 files changed

+136
-0
lines changed

2 files changed

+136
-0
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Based on https://github.com/microsoft/vscode/blob/edfd5b8ba54d50f3f5c2ebee877af088803def88/src/vs/base/common/labels.ts#L316C1-L400
2+
3+
/**
4+
* Helper to insert values for specific template variables into the string. E.g. "this ${is} a ${template}" can be
5+
* passed to this function together with an object that maps "is" and "template" to strings to have them replaced.
6+
*
7+
* @param template string to which template is applied
8+
* @param values the values of the templates to use
9+
*/
10+
export function substituteConfigVariables(
11+
template: string,
12+
values: {
13+
[key: string]: string | undefined | null;
14+
},
15+
): string {
16+
const segments: string[] = [];
17+
18+
let inVariable = false;
19+
let currentValue = "";
20+
for (const char of template) {
21+
// Beginning of variable
22+
if (char === "$" || (inVariable && char === "{")) {
23+
if (currentValue) {
24+
segments.push(currentValue);
25+
}
26+
27+
currentValue = "";
28+
inVariable = true;
29+
}
30+
31+
// End of variable
32+
else if (char === "}" && inVariable) {
33+
const resolved = values[currentValue];
34+
35+
// Variable
36+
if (resolved && resolved.length > 0) {
37+
segments.push(resolved);
38+
}
39+
// If the variable, doesn't exist, we discard it (i.e. replace it by the empty string)
40+
41+
currentValue = "";
42+
inVariable = false;
43+
}
44+
45+
// Text or Variable Name
46+
else {
47+
currentValue += char;
48+
}
49+
}
50+
51+
// Tail
52+
if (currentValue && !inVariable) {
53+
segments.push(currentValue);
54+
}
55+
56+
return segments.join("");
57+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { substituteConfigVariables } from "../../../src/common/config-template";
2+
3+
describe("substituteConfigVariables", () => {
4+
const values = {
5+
userHome: "/home/your-username",
6+
workspaceFolder: "/home/your-username/your-project",
7+
workspaceFolderBasename: "your-project",
8+
pathSeparator: "/",
9+
owner: "github",
10+
name: "vscode-codeql",
11+
language: "java",
12+
};
13+
14+
const testCases = [
15+
{
16+
template: ".github/codeql/extensions/${name}-${language}",
17+
expected: ".github/codeql/extensions/vscode-codeql-java",
18+
},
19+
{
20+
template: "${owner}/${name}-${language}",
21+
expected: "github/vscode-codeql-java",
22+
},
23+
{
24+
template: "models/${group}.model.yml",
25+
expected: "models/.model.yml",
26+
},
27+
{
28+
template:
29+
"${workspaceFolder}${pathSeparator}.github/workflows/codeql-analysis.yml",
30+
expected:
31+
"/home/your-username/your-project/.github/workflows/codeql-analysis.yml",
32+
},
33+
{
34+
template:
35+
"${workspaceFolder/.github/codeql/extensions/${name}-${language}",
36+
expected: "workspaceFolder/.github/codeql/extensions/vscode-codeql-java",
37+
},
38+
{
39+
template: "}${workspaceFolder}/.github/workflows/codeql-analysis.yml",
40+
expected:
41+
"}/home/your-username/your-project/.github/workflows/codeql-analysis.yml",
42+
},
43+
{
44+
template: "Foo Bar",
45+
expected: "Foo Bar",
46+
},
47+
{
48+
template: "Foo${}Bar",
49+
expected: "FooBar",
50+
},
51+
{
52+
template: "$FooBar",
53+
expected: "",
54+
},
55+
{
56+
template: "}FooBar",
57+
expected: "}FooBar",
58+
},
59+
{
60+
template: "Foo ${name} Bar",
61+
expected: "Foo vscode-codeql Bar",
62+
},
63+
{
64+
template: "Foo ${name} Bar ${owner}",
65+
expected: "Foo vscode-codeql Bar github",
66+
},
67+
{
68+
template: "Foo ${nmae} Bar ${owner}",
69+
expected: "Foo Bar github",
70+
},
71+
];
72+
73+
test.each(testCases)(
74+
"result of $template is $expected",
75+
({ template, expected }) => {
76+
expect(substituteConfigVariables(template, values)).toEqual(expected);
77+
},
78+
);
79+
});

0 commit comments

Comments
 (0)