function formatExpression()

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}`);
	}
}