Skip to content

Commit

Permalink
Allow any variable for interpolation
Browse files Browse the repository at this point in the history
  • Loading branch information
blakeembrey committed Mar 7, 2022
1 parent 1815430 commit f54e993
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 6 deletions.
18 changes: 18 additions & 0 deletions src/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,22 @@ describe("string-template", () => {

expect(fn({ test: "are" })).toEqual("\"Some things\" are 'quoted'");
});

it("should escape backslashes", () => {
const fn = template("test\\");

expect(fn({})).toEqual("test\\");
});

it("should allow functions", () => {
const fn = template("{{test()}}");

expect(fn({ test: () => "help" })).toEqual("help");
});

it("should allow bracket syntax reference", () => {
const fn = template("{{['test']}}");

expect(fn({ test: "hello" })).toEqual("hello");
});
});
50 changes: 44 additions & 6 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,55 @@
const DATA_VAR_NAME = "data";
const INPUT_VAR_NAME = "it";
const QUOTE_CHAR = '"';

export type Template<T extends object> = (data: T) => string;

/**
* Stringify a template into a function.
*/
export function compile(value: string, displayName = "template") {
const str = value.replace(/"|{{[^{]+}}/g, (prop) => {
if (prop === '"') return '\\"';
return `" + ${DATA_VAR_NAME}.${prop.slice(2, -2).trim()} + "`;
});
let result = QUOTE_CHAR;
for (let i = 0; i < value.length; i++) {
const char = value[i];

return `function ${displayName}(${DATA_VAR_NAME}) { return "${str}"; }`;
// Escape special characters due to quoting.
if (char === QUOTE_CHAR || char === "\\") {
result += "\\";
}

// Process template param.
if (char === "{" && value[i + 1] === "{") {
const start = i + 2;
let end = 0;
let withinString = "";

for (let j = start; j < value.length; j++) {
const char = value[j];
if (withinString) {
if (char === "\\") j++;
else if (char === withinString) withinString = "";
continue;
} else if (char === "}" && value[j + 1] === "}") {
i = j + 1;
end = j;
break;
} else if (char === '"' || char === "'" || char === "`") {
withinString = char;
}
}

if (!end) throw new TypeError(`Template parameter not closed at ${i}`);

const param = value.slice(start, end).trim();
const sep = `+-/*.?:![]()%&|;={}<>,`.indexOf(param[0]) > -1 ? "" : ".";
result += `${QUOTE_CHAR} + (${INPUT_VAR_NAME}${sep}${param}) + ${QUOTE_CHAR}`;
continue;
}

result += char;
}
result += QUOTE_CHAR;

return `function ${displayName}(${INPUT_VAR_NAME}) { return ${result}; }`;
}

/**
Expand Down

0 comments on commit f54e993

Please sign in to comment.