embedded-typescript
Version:
Type safe TypeScript templates
98 lines (96 loc) • 3.58 kB
JavaScript
import { format } from "prettier";
import { trimLeadingIndentation, getLeadingIndentation, trimLaggingNewline, sanitizeString, removeFinalNewline, } from "./utils/index.js";
import { parse, isParseError } from "../parser/index.js";
const RESULT = "result";
function compile(nodes) {
let compiled = "";
let indent = "";
let hasPreserveIndentation = false;
function write(text) {
compiled += indent + text;
}
nodes.forEach((node, idx) => {
const prevNode = nodes[idx - 1];
const nextNode = nodes[idx + 1];
switch (node.type) {
case "header": {
const props = /(interface|type) Props/.test(node.content)
? "Props"
: "unknown";
write(`${node.content}\n\n`);
write(`export default function (props: ${props}): string {\n`);
indent += " ";
write(`let ${RESULT} = '';\n`);
break;
}
case "text": {
let content = node.content;
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (prevNode?.type === "statement" || prevNode?.type === "header") {
content = trimLaggingNewline(content);
}
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (nextNode?.type === "statement") {
content = trimLeadingIndentation(content);
}
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (!nextNode) {
content = removeFinalNewline(content);
}
if (content) {
write(`${RESULT} += '${sanitizeString(content)}';\n`);
}
break;
}
case "expression": {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
const indentation = getLeadingIndentation(prevNode.content ?? "");
if (!indentation) {
write(`${RESULT} += ${node.content};\n`);
}
else {
hasPreserveIndentation = true;
write(`${RESULT} += preserveIndentation(${node.content}, '${indentation}');\n`);
}
break;
}
case "statement": {
write(`${node.content}\n`);
break;
}
default: {
const exhaust = node.type;
return exhaust;
}
}
});
write(`return ${RESULT};\n`);
indent = "";
write(`}\n`);
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (hasPreserveIndentation) {
write(`\nfunction preserveIndentation(text: string, indentation: string): string {
return text
.split("\\n")
.map((line, idx) => (idx === 0 ? line : indentation + line))
.join("\\n");
}`);
}
return compiled;
}
export function compiler(template, templatePath) {
const parsed = parse(template);
if (isParseError(parsed)) {
return parsed;
}
const heading = `/*
* THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
*
* Run \`npx ets\` or \`yarn ets\` to regenerate this file.
* Source: ${templatePath}
*/
/* eslint-disable */
`;
const file = heading + compile(parsed);
return format(file, { parser: "typescript" });
}