Files
payloadcms/packages/eslint-plugin/customRules/proper-payload-logger-usage.js
Sasha 6be665f887 chore(eslint): payload-logger proper usage works with template literals (#8660)
Improves this https://github.com/payloadcms/payload/pull/8578 to work
with template literals as well,

This is invalid:
```ts
this.payload.logger.error('Failed to create database', err)
```

But this is valid:
```ts
this.payload.logger.error(`Failed to create database ${dbName}`, err)
```

This PR fixes that

<img width="577" alt="image"
src="https://github.com/user-attachments/assets/b867da53-4f81-4b1b-8423-598e0419946a">
2024-10-13 19:10:47 -04:00

90 lines
3.2 KiB
JavaScript

export const rule = {
meta: {
type: 'problem',
docs: {
description: 'Disallow improper usage of payload.logger.error',
recommended: 'error',
},
messages: {
improperUsage: 'Improper logger usage. Pass { msg, err } so full error stack is logged.',
wrongErrorField: 'Improper usage. Use { err } instead of { error }.',
wrongMessageField: 'Improper usage. Use { msg } instead of { message }.',
},
schema: [],
},
create(context) {
return {
CallExpression(node) {
const callee = node.callee
// Function to check if the expression ends with `payload.logger.error`
function isPayloadLoggerError(expression) {
return (
expression.type === 'MemberExpression' &&
expression.property.name === 'error' && // must be `.error`
expression.object.type === 'MemberExpression' &&
expression.object.property.name === 'logger' && // must be `.logger`
(expression.object.object.name === 'payload' || // handles just `payload`
(expression.object.object.type === 'MemberExpression' &&
expression.object.object.property.name === 'payload')) // handles `*.payload`
)
}
// Check if the function being called is `payload.logger.error` or `*.payload.logger.error`
if (isPayloadLoggerError(callee)) {
const args = node.arguments
// Case 1: Single string / templated string is passed as the argument
if (
args.length === 1 &&
((args[0].type === 'Literal' && typeof args[0].value === 'string') ||
args[0].type === 'TemplateLiteral')
) {
return // Valid: single string argument
}
// Case 2: Object is passed as the first argument
if (args.length > 0 && args[0].type === 'ObjectExpression') {
const properties = args[0].properties
// Ensure no { error } key, only { err } is allowed
properties.forEach((prop) => {
if (prop.key.type === 'Identifier' && prop.key.name === 'error') {
context.report({
node: prop,
messageId: 'wrongErrorField',
})
}
// Ensure no { message } key, only { msg } is allowed
if (prop.key.type === 'Identifier' && prop.key.name === 'message') {
context.report({
node: prop,
messageId: 'wrongMessageField',
})
}
})
return // Valid object, checked for 'err'/'error' keys
}
// Case 3: Improper usage (string / templated string + error or additional err/error)
if (
args.length > 1 &&
((args[0].type === 'Literal' && typeof args[0].value === 'string') ||
args[0].type === 'TemplateLiteral') &&
args[1].type === 'Identifier' &&
(args[1].name === 'err' || args[1].name === 'error')
) {
context.report({
node,
messageId: 'improperUsage',
})
}
}
},
}
},
}
export default rule