in packages/jinja/src/format.ts [212:318]
function formatExpression(node: Expression, parentPrec: number = -1): string {
switch (node.type) {
case "SpreadExpression": {
const n = node as SpreadExpression;
return `*${formatExpression(n.argument)}`;
}
case "Identifier":
return (node as Identifier).value;
case "IntegerLiteral":
return `${(node as IntegerLiteral).value}`;
case "FloatLiteral":
return `${(node as FloatLiteral).value}`;
case "StringLiteral":
return JSON.stringify((node as StringLiteral).value);
case "BinaryExpression": {
const n = node as BinaryExpression;
const thisPrecedence = getBinaryOperatorPrecedence(n);
const left = formatExpression(n.left, thisPrecedence);
const right = formatExpression(n.right, thisPrecedence + 1);
const expr = `${left} ${n.operator.value} ${right}`;
return thisPrecedence < parentPrec ? `(${expr})` : expr;
}
case "UnaryExpression": {
const n = node as UnaryExpression;
const val = n.operator.value + (n.operator.value === "not" ? " " : "") + formatExpression(n.argument, Infinity);
return val;
}
case "CallExpression": {
const n = node as CallExpression;
const args = n.args.map(formatExpression).join(", ");
return `${formatExpression(n.callee)}(${args})`;
}
case "MemberExpression": {
const n = node as MemberExpression;
let obj = formatExpression(n.object);
// only wrap if it's not a simple or chained access/call
if (
![
"Identifier",
"MemberExpression",
"CallExpression",
"StringLiteral",
"IntegerLiteral",
"FloatLiteral",
"ArrayLiteral",
"TupleLiteral",
"ObjectLiteral",
].includes(n.object.type)
) {
obj = `(${obj})`;
}
let prop = formatExpression(n.property);
if (!n.computed && n.property.type !== "Identifier") {
prop = `(${prop})`;
}
return n.computed ? `${obj}[${prop}]` : `${obj}.${prop}`;
}
case "FilterExpression": {
const n = node as FilterExpression;
const operand = formatExpression(n.operand, Infinity);
if (n.filter.type === "CallExpression") {
return `${operand} | ${formatExpression(n.filter)}`;
}
return `${operand} | ${(n.filter as Identifier).value}`;
}
case "SelectExpression": {
const n = node as SelectExpression;
return `${formatExpression(n.lhs)} if ${formatExpression(n.test)}`;
}
case "TestExpression": {
const n = node as TestExpression;
return `${formatExpression(n.operand)} is${n.negate ? " not" : ""} ${n.test.value}`;
}
case "ArrayLiteral":
case "TupleLiteral": {
const elems = ((node as ArrayLiteral | TupleLiteral).value as Expression[]).map(formatExpression);
const brackets = node.type === "ArrayLiteral" ? "[]" : "()";
return `${brackets[0]}${elems.join(", ")}${brackets[1]}`;
}
case "ObjectLiteral": {
const entries = Array.from((node as ObjectLiteral).value.entries()).map(
([k, v]) => `${formatExpression(k)}: ${formatExpression(v)}`
);
return `{${entries.join(", ")}}`;
}
case "SliceExpression": {
const n = node as SliceExpression;
const s = n.start ? formatExpression(n.start) : "";
const t = n.stop ? formatExpression(n.stop) : "";
const st = n.step ? `:${formatExpression(n.step)}` : "";
return `${s}:${t}${st}`;
}
case "KeywordArgumentExpression": {
const n = node as KeywordArgumentExpression;
return `${n.key.value}=${formatExpression(n.value)}`;
}
case "Ternary": {
const n = node as Ternary;
const expr = `${formatExpression(n.trueExpr)} if ${formatExpression(n.condition, 0)} else ${formatExpression(
n.falseExpr
)}`;
return parentPrec > -1 ? `(${expr})` : expr;
}
default:
throw new Error(`Unknown expression type: ${node.type}`);
}
}