Add flat-ast-path-call rule (#10354)

This commit is contained in:
fisker Cheung authored and GitHub committed 2021-02-26 09:26:50 +08:00
1 parent 64a3906571
commit 95c8086c2c
6 files changed
+159 -7

No files matched your search

@@ -49,7 +49,7 @@ module.exports = {
type: "suggestion",
docs: {
url:
"https://github.com/prettier/prettier/blob/main/scripts/eslint-plugin-prettier-internal-rules/better-parent-property-check-in-needs-parens.js",
"https://github.com/prettier/prettier/blob/main/scripts/tools/eslint-plugin-prettier-internal-rules/better-parent-property-check-in-needs-parens.js",
},
messages: {
[MESSAGE_ID_PREFER_NAME_CHECK]:
@@ -0,0 +1,128 @@
"use strict";
// This rule only work for nested `AstPath#call()` for now
function astPathCallSelector(path) {
const prefix = path ? `${path}.` : "";
return [
`[${prefix}type="CallExpression"]`,
`[${prefix}callee.type="MemberExpression"]`,
`[${prefix}callee.property.type="Identifier"]`,
`[${prefix}callee.property.name="call"]`,
`[${prefix}arguments.length>1]`,
`[${prefix}arguments.0.type!="SpreadElement"]`,
].join("");
}
// Matches:
// ```
// path.call((childPath) => childPath.call(print, "b"), "a")
// ```
const selector = [
astPathCallSelector(),
'[arguments.0.type="ArrowFunctionExpression"]',
"[arguments.0.params.length=1]",
'[arguments.0.params.0.type="Identifier"]',
astPathCallSelector("arguments.0.body"),
'[arguments.0.body.callee.object.type="Identifier"]',
].join("");
const MESSAGE_ID = "flat-ast-path-call";
module.exports = {
meta: {
type: "suggestion",
docs: {
url:
"https://github.com/prettier/prettier/blob/main/scripts/tools/eslint-plugin-prettier-internal-rules/flat-ast-path-call.js",
},
messages: {
[MESSAGE_ID]: "Do not use nested `AstPath#{{method}}(…)`.",
},
fixable: "code",
},
create(context) {
const sourceCode = context.getSourceCode();
return {
[selector](outerCall) {
// path.call((childPath) => childPath.call(print, "b"), "a")
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
const outerCallback = outerCall.arguments[0];
// path.call((childPath) => childPath.call(print, "b"), "a")
// ^^^^^^^^^
const outerCallbackParameterName = outerCallback.params[0].name;
// path.call((childPath) => childPath.call(print, "b"), "a")
// ^^^^^^^^^^^^^^^^^^^^^^^^^^
const innerCall = outerCallback.body;
// path.call((childPath) => childPath.call(print, "b"), "a")
// ^^^^^^^^^
const innerCallCalleeObjectName = innerCall.callee.object.name;
if (outerCallbackParameterName !== innerCallCalleeObjectName) {
return;
}
context.report({
node: innerCall,
messageId: MESSAGE_ID,
data: { method: "call" },
*fix(fixer) {
// path.call((childPath) => childPath.call(print, "b"), "a")
// ^^^^^
const innerCallback = innerCall.arguments[0];
yield fixer.replaceTextRange(
[
// path.call((childPath) => childPath.call(print, "b"), "a")
// ^
outerCallback.range[0],
// path.call((childPath) => childPath.call(print, "b"), "a")
// ^
innerCallback.range[0],
],
""
);
// path.call((childPath) => childPath.call(print, "b"), "a")
// ^
const innerNamesStart = innerCallback.range[1];
// path.call((childPath) => childPath.call(print, "b"), "a")
// ^
const innerNamesEnd = innerCall.range[1] - 1;
let innerNamesText = sourceCode.text.slice(
innerNamesStart,
innerNamesEnd
);
yield fixer.replaceTextRange(
[innerNamesStart, innerNamesEnd + 1],
""
);
const [penultimateToken, lastToken] = sourceCode.getLastTokens(
outerCall,
2
);
// `outer` call has `trailing comma`
if (
penultimateToken &&
penultimateToken.type === "Punctuator" &&
penultimateToken.value === ","
) {
innerNamesText = innerNamesText.slice(1);
}
yield fixer.insertTextBefore(lastToken, innerNamesText);
},
});
},
};
},
};
@@ -4,6 +4,7 @@ module.exports = {
rules: {
"better-parent-property-check-in-needs-parens": require("./better-parent-property-check-in-needs-parens"),
"directly-loc-start-end": require("./directly-loc-start-end"),
"flat-ast-path-call": require("./flat-ast-path-call"),
"jsx-identifier-case": require("./jsx-identifier-case"),
"no-doc-builder-concat": require("./no-doc-builder-concat"),
"no-empty-flat-contents-for-if-break": require("./no-empty-flat-contents-for-if-break"),
@@ -73,6 +73,33 @@ test("directly-loc-start-end", {
],
});
test("flat-ast-path-call", {
valid: [
'path.call((childPath) => childPath.notCall(print, "b"), "a")',
'path.notCall((childPath) => childPath.call(print, "b"), "a")',
'path.call((childPath) => childPath.call(print, "b"))',
'path.call((childPath) => childPath.call(print), "a")',
'path.call((childPath) => notChildPath.call(print), "a")',
'path.call(functionReference, "a")',
// Only check `arrow function`
'path.call((childPath) => {return childPath.call(print, "b")}, "a")',
'path.call(function(childPath) {return childPath.call(print, "b")}, "a")',
],
invalid: [
{
code: 'path.call((childPath) => childPath.call(print, "b"), "a")',
output: 'path.call(print, "a", "b")',
errors: [{ message: "Do not use nested `AstPath#call(…)`." }],
},
{
// Trailing comma
code: 'path.call((childPath) => childPath.call(print, "b"), "a",)',
output: 'path.call(print, "a", "b")',
errors: 1,
},
],
});
test("jsx-identifier-case", {
valid: [
{