Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion packages/eslint-plugin/src/configs/all.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,10 @@
// DO NOT EDIT THIS CODE BY HAND
// YOU CAN REGENERATE IT USING yarn generate:configs

export = { extends: ['./configs/base'], rules: { '@sourcegraph/sourcegraph/check-help-links': 'error' } }
export = {
extends: ['./configs/base'],
rules: {
'@sourcegraph/sourcegraph/check-help-links': 'error',
'@sourcegraph/sourcegraph/no-unexplained-console-error': 'error',
},
}
2 changes: 2 additions & 0 deletions packages/eslint-plugin/src/rules/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// This file is used by `scripts/generate-configs.ts` for rules extraction.
import { checkHelpLinks } from './check-help-links'
import { noUnexplainedConsoleError } from './no-unexplained-console-error'

// eslint-disable-next-line import/no-default-export
export default {
'check-help-links': checkHelpLinks,
'no-unexplained-console-error': noUnexplainedConsoleError,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { getFixturesRootDir, RuleTester } from '../../../testing/RuleTester'
import { noUnexplainedConsoleError } from '../no-unexplained-console-error'

const ruleTester = new RuleTester({
parserOptions: {
tsconfigRootDir: getFixturesRootDir(),
project: './tsconfig.json',
},
parser: '@typescript-eslint/parser',
})

ruleTester.run('no-unexplained-console-error', noUnexplainedConsoleError, {
valid: [
{
code: `
try {}
catch (error) {
// This comment explains why we need to do this
console.error(error)
}
`,
},
],
invalid: [
{ code: 'console.error(err)' },
{
code: `
try {} catch (err) {
console.error(err)
}
`,
},
].map(test => {
return {
...test,
errors: [{ messageId: 'noUnexplainedConsoleError' }],
}
}),
})
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './no-unexplained-console-error'
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Ban unexplained `console.error` calls

## Rule details

This rule bans logging errors directly to the console using `console.error()` calls, unless supported with a comment explaining why it's required. Otherwise, it's recommended to pass the error through `Sentry.captureException()`.

### Bad code

```ts
try {
// do something
} catch (error) {
console.error(error)
}
```

### Okay code

```ts
try {
// do something
} catch (error) {
// We need to log this to the browser console because XYZ
console.error(error)
}
```

### (Bonus) Awesome code

```ts
try {
// do something
} catch (error) {
Sentry.captureException(error)
}
```

## How to Use

```jsonc
{
"@sourcegraph/sourcegraph/no-unexplained-console-error": "error"
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { createRule } from '../../utils'

export const messages = {
noUnexplainedConsoleError:
'Directly logging through `console.error()` is discouraged. If required, please add a comment explaining why so, otherwise consider passing the error through `Sentry.captureException()` instead.',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Depending on the outcome of the discussion in the follow-up PR, we would need to update the message here. Using Sentry.captureException directly is not safe because it's not always available. We would want to point consumers to something more generic that encapsulates Sentry and other output channels.

}

export const noUnexplainedConsoleError = createRule<[], keyof typeof messages>({
name: 'no-unexplained-console-error',
meta: {
docs: {
description:
"Bans usage of the console.error() calls, unless supported by a comment explain why it's required.",
recommended: 'error',
},
messages,
schema: [],
type: 'problem',
},
defaultOptions: [],
create(context) {
const sourceCode = context.getSourceCode()

return {
CallExpression(node) {
const { callee } = node
if (
callee.type === 'MemberExpression' &&
callee.object.type === 'Identifier' &&
callee.object.name === 'console' &&
callee.property.type === 'Identifier' &&
callee.property.name === 'error'
) {
const comments = sourceCode.getCommentsBefore(node)
if (comments.length === 0) {
context.report({
node,
messageId: 'noUnexplainedConsoleError',
})
}
}
},
}
},
})