Files
renovate/tools/lint/rules/logger-static-message.ts
Sebastian Poxhofer b2d3b64c48 refactor(lint): enforce err key for errors in logger metadata (#45759)
* refactor(lint): enforce err key for errors in logger metadata

* refactor(lint): flag error message and stack strings in logger-err-key
2026-09-09 13:55:14 +00:00

72 lines
2.1 KiB
TypeScript

import type { ESTree } from '@oxlint/plugins';
import { defineRule } from '@oxlint/plugins';
import { getLoggerLevel } from '../utils/logger.ts';
/**
* Log levels whose messages are used in metrics or error catching services
* and therefore must have a static `msg` component.
* See docs/development/best-practices.md ("Logging").
*/
const flaggedLevels = new Set(['warn', 'error', 'fatal']);
/**
* Whether the expression is (or contains, in a `+` chain) a string literal or
* template literal, i.e. builds the message string dynamically.
*/
function hasStringOperand(node: ESTree.Node): boolean {
if (node.type === 'Literal') {
return typeof node.value === 'string';
}
if (node.type === 'TemplateLiteral') {
return true;
}
if (node.type === 'BinaryExpression' && node.operator === '+') {
return hasStringOperand(node.left) || hasStringOperand(node.right);
}
return false;
}
export default defineRule({
meta: {
type: 'problem',
messages: {
staticMessage:
"WARN, ERROR and FATAL messages must have a static msg component so they can be grouped in metrics; move interpolated values into the metadata object, e.g. logger.{{level}}({ url }, 'Failed to fetch').",
},
},
createOnce(context) {
return {
CallExpression(node) {
const level = getLoggerLevel(node.callee);
if (!level || !flaggedLevels.has(level)) {
return;
}
const [first, second] = node.arguments;
if (!first) {
return;
}
const messageArg: ESTree.Argument | undefined =
first.type === 'ObjectExpression' ? second : first;
if (!messageArg) {
return;
}
if (
(messageArg.type === 'TemplateLiteral' &&
messageArg.expressions.length > 0) ||
(messageArg.type === 'BinaryExpression' &&
messageArg.operator === '+' &&
hasStringOperand(messageArg))
) {
context.report({
node: messageArg,
messageId: 'staticMessage',
data: { level },
});
}
},
};
},
});