Skip to content

Commit 1452dc9

Browse files
authored
feat: Add suggestions to no-console (#17680)
* feat: Add suggestions to no-console Fixes #17493 * Fix method of when to show safe suggestions and fix tests. * improved formatting of tests with suggestions by breaking them into multiple lines * Updated function name to canProvideSuggestions. Added better description for the function. * Fixed code that fails when AST is not deep enough. * Added suggestions:null for test cases that will not provide a suggestion. * test to make sure console statement with semicolon is removed via suggestion * dont provide suggestions if removing console.log() will lead to ASI breaking * missing period * renamed regexps variable names for better understanding * updated passing in expressionstatement node instead of memberexpression node to maybeAsiHazard * ++ or -- in the token before is not always safe.
1 parent 6fb8805 commit 1452dc9

File tree

2 files changed

+451
-15
lines changed

2 files changed

+451
-15
lines changed

lib/rules/no-console.js

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,11 @@ module.exports = {
4343
}
4444
],
4545

46+
hasSuggestions: true,
47+
4648
messages: {
47-
unexpected: "Unexpected console statement."
49+
unexpected: "Unexpected console statement.",
50+
removeConsole: "Remove the console.{{ propertyName }}()."
4851
}
4952
},
5053

@@ -94,6 +97,64 @@ module.exports = {
9497
);
9598
}
9699

100+
/**
101+
* Checks if removing the ExpressionStatement node will cause ASI to
102+
* break.
103+
* eg.
104+
* foo()
105+
* console.log();
106+
* [1, 2, 3].forEach(a => doSomething(a))
107+
*
108+
* Removing the console.log(); statement should leave two statements, but
109+
* here the two statements will become one because [ causes continuation after
110+
* foo().
111+
* @param {ASTNode} node The ExpressionStatement node to check.
112+
* @returns {boolean} `true` if ASI will break after removing the ExpressionStatement
113+
* node.
114+
*/
115+
function maybeAsiHazard(node) {
116+
const SAFE_TOKENS_BEFORE = /^[:;{]$/u; // One of :;{
117+
const UNSAFE_CHARS_AFTER = /^[-[(/+`]/u; // One of [(/+-`
118+
119+
const tokenBefore = sourceCode.getTokenBefore(node);
120+
const tokenAfter = sourceCode.getTokenAfter(node);
121+
122+
return (
123+
Boolean(tokenAfter) &&
124+
UNSAFE_CHARS_AFTER.test(tokenAfter.value) &&
125+
tokenAfter.value !== "++" &&
126+
tokenAfter.value !== "--" &&
127+
Boolean(tokenBefore) &&
128+
!SAFE_TOKENS_BEFORE.test(tokenBefore.value)
129+
);
130+
}
131+
132+
/**
133+
* Checks if the MemberExpression node's parent.parent.parent is a
134+
* Program, BlockStatement, StaticBlock, or SwitchCase node. This check
135+
* is necessary to avoid providing a suggestion that might cause a syntax error.
136+
*
137+
* eg. if (a) console.log(b), removing console.log() here will lead to a
138+
* syntax error.
139+
* if (a) { console.log(b) }, removing console.log() here is acceptable.
140+
*
141+
* Additionally, it checks if the callee of the CallExpression node is
142+
* the node itself.
143+
*
144+
* eg. foo(console.log), cannot provide a suggestion here.
145+
* @param {ASTNode} node The MemberExpression node to check.
146+
* @returns {boolean} `true` if a suggestion can be provided for a node.
147+
*/
148+
function canProvideSuggestions(node) {
149+
return (
150+
node.parent.type === "CallExpression" &&
151+
node.parent.callee === node &&
152+
node.parent.parent.type === "ExpressionStatement" &&
153+
astUtils.STATEMENT_LIST_PARENTS.has(node.parent.parent.parent.type) &&
154+
!maybeAsiHazard(node.parent.parent)
155+
);
156+
}
157+
97158
/**
98159
* Reports the given reference as a violation.
99160
* @param {eslint-scope.Reference} reference The reference to report.
@@ -102,10 +163,21 @@ module.exports = {
102163
function report(reference) {
103164
const node = reference.identifier.parent;
104165

166+
const propertyName = astUtils.getStaticPropertyName(node);
167+
105168
context.report({
106169
node,
107170
loc: node.loc,
108-
messageId: "unexpected"
171+
messageId: "unexpected",
172+
suggest: canProvideSuggestions(node)
173+
? [{
174+
messageId: "removeConsole",
175+
data: { propertyName },
176+
fix(fixer) {
177+
return fixer.remove(node.parent.parent);
178+
}
179+
}]
180+
: []
109181
});
110182
}
111183

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy