Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions extensions/ql-vscode/src/common/config-template.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Based on https://github.com/microsoft/vscode/blob/edfd5b8ba54d50f3f5c2ebee877af088803def88/src/vs/base/common/labels.ts#L316C1-L400

/**
* Helper to insert values for specific template variables into the string. E.g. "this ${is} a ${template}" can be
* passed to this function together with an object that maps "is" and "template" to strings to have them replaced.
*
* @param template string to which template is applied
* @param values the values of the templates to use
*/
export function substituteConfigVariables(
template: string,
values: {
[key: string]: string | undefined | null;
},
): string {
const segments: string[] = [];

let inVariable = false;
let currentValue = "";
for (const char of template) {
// Beginning of variable
if (char === "$" || (inVariable && char === "{")) {
if (currentValue) {
segments.push(currentValue);
}

currentValue = "";
inVariable = true;
}

// End of variable
else if (char === "}" && inVariable) {
const resolved = values[currentValue];

// Variable
if (resolved && resolved.length > 0) {
segments.push(resolved);
}
// If the variable, doesn't exist, we discard it (i.e. replace it by the empty string)

currentValue = "";
inVariable = false;
}

// Text or Variable Name
else {
currentValue += char;
}
}

// Tail
if (currentValue && !inVariable) {
segments.push(currentValue);
}

return segments.join("");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { substituteConfigVariables } from "../../../src/common/config-template";

describe("substituteConfigVariables", () => {
const values = {
userHome: "/home/your-username",
workspaceFolder: "/home/your-username/your-project",
workspaceFolderBasename: "your-project",
pathSeparator: "/",
owner: "github",
name: "vscode-codeql",
language: "java",
};

const testCases = [
{
template: ".github/codeql/extensions/${name}-${language}",
expected: ".github/codeql/extensions/vscode-codeql-java",
},
{
template: "${owner}/${name}-${language}",
expected: "github/vscode-codeql-java",
},
{
template: "models/${group}.model.yml",
expected: "models/.model.yml",
},
{
template:
"${workspaceFolder}${pathSeparator}.github/workflows/codeql-analysis.yml",
expected:
"/home/your-username/your-project/.github/workflows/codeql-analysis.yml",
},
{
template:
"${workspaceFolder/.github/codeql/extensions/${name}-${language}",
expected: "workspaceFolder/.github/codeql/extensions/vscode-codeql-java",
},
{
template: "}${workspaceFolder}/.github/workflows/codeql-analysis.yml",
expected:
"}/home/your-username/your-project/.github/workflows/codeql-analysis.yml",
},
{
template: "Foo Bar",
expected: "Foo Bar",
},
{
template: "Foo${}Bar",
expected: "FooBar",
},
{
template: "$FooBar",
expected: "",
},
{
template: "}FooBar",
expected: "}FooBar",
},
{
template: "Foo ${name} Bar",
expected: "Foo vscode-codeql Bar",
},
{
template: "Foo ${name} Bar ${owner}",
expected: "Foo vscode-codeql Bar github",
},
{
template: "Foo ${nmae} Bar ${owner}",
expected: "Foo Bar github",
},
];

test.each(testCases)(
"result of $template is $expected",
({ template, expected }) => {
expect(substituteConfigVariables(template, values)).toEqual(expected);
},
);
});