Convert node types Set to type check function (#18149)
This commit is contained in:
1 parent
e51ff9ad41
commit
e5118b2650
6 files changed
+161
-18
No files matched your search
+117
-2
@@ -126,13 +126,27 @@ function isTopLevelFunction(node) {
|
||||
);
|
||||
}
|
||||
|
||||
const selector = [
|
||||
const functionSelector = [
|
||||
":function",
|
||||
"[params.length=1]",
|
||||
"[async!=true]",
|
||||
"[generator!=true]",
|
||||
].join("");
|
||||
|
||||
const setDeclareSelector = [
|
||||
"VariableDeclaration",
|
||||
'[kind="const"]',
|
||||
"[declarations.length=1]",
|
||||
" > ",
|
||||
"VariableDeclarator.declarations",
|
||||
'[init.type="NewExpression"]',
|
||||
'[init.callee.type="Identifier"]',
|
||||
'[init.callee.name="Set"]',
|
||||
"[init.arguments.length=1]",
|
||||
'[init.arguments.0.type="ArrayExpression"]',
|
||||
" > Identifier.id",
|
||||
].join("");
|
||||
|
||||
export default {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
@@ -165,7 +179,7 @@ export default {
|
||||
const sourceCode = context.getSourceCode();
|
||||
|
||||
return {
|
||||
[selector](functionNode) {
|
||||
[functionSelector](functionNode) {
|
||||
if (onlyTopLevelFunctions && !isTopLevelFunction(functionNode)) {
|
||||
return;
|
||||
}
|
||||
@@ -252,6 +266,107 @@ export default {
|
||||
|
||||
context.report(problem);
|
||||
},
|
||||
[setDeclareSelector](identifier) {
|
||||
const variable = sourceCode
|
||||
.getDeclaredVariables(identifier.parent)
|
||||
.find(
|
||||
(variable) =>
|
||||
variable.identifiers.length === 1 &&
|
||||
variable.identifiers[0] === identifier,
|
||||
);
|
||||
|
||||
if (!variable) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { references } = variable;
|
||||
|
||||
if (references.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nodes = [];
|
||||
|
||||
for (const { identifier: referenceIdentifier } of references) {
|
||||
if (referenceIdentifier === identifier) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
!(
|
||||
referenceIdentifier.parent.type === "MemberExpression" &&
|
||||
referenceIdentifier.parent.object === referenceIdentifier &&
|
||||
!referenceIdentifier.parent.optional &&
|
||||
!referenceIdentifier.parent.computed &&
|
||||
referenceIdentifier.parent.property.type === "Identifier" &&
|
||||
referenceIdentifier.parent.property.name === "has" &&
|
||||
referenceIdentifier.parent.parent.type === "CallExpression" &&
|
||||
referenceIdentifier.parent.parent.callee ===
|
||||
referenceIdentifier.parent &&
|
||||
!referenceIdentifier.parent.parent.optional &&
|
||||
referenceIdentifier.parent.parent.arguments.length === 1
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const [typeNode] = referenceIdentifier.parent.parent.arguments;
|
||||
const memberExpression =
|
||||
typeNode.type === "ChainExpression"
|
||||
? typeNode.expression
|
||||
: typeNode;
|
||||
|
||||
if (
|
||||
!(
|
||||
memberExpression.type === "MemberExpression" &&
|
||||
!memberExpression.computed &&
|
||||
memberExpression.property.type === "Identifier" &&
|
||||
memberExpression.property.name === "type"
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
nodes.push({
|
||||
setHasNode: referenceIdentifier.parent,
|
||||
typeNode,
|
||||
nodeNode: memberExpression.object,
|
||||
});
|
||||
}
|
||||
|
||||
if (nodes.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
context.report({
|
||||
node: identifier,
|
||||
messageId: MESSAGE_ID,
|
||||
*fix(fixer) {
|
||||
const { name } = identifier;
|
||||
const replacement = `__please_rename_this_function_${name}`;
|
||||
|
||||
// Rename variable
|
||||
yield fixer.replaceText(identifier, replacement);
|
||||
|
||||
// `new Set()` -> `createTypeCheckFunction()`
|
||||
const newSet = identifier.parent.init;
|
||||
const newToken = sourceCode.getFirstToken(newSet);
|
||||
yield fixer.remove(newToken);
|
||||
yield fixer.replaceText(newSet.callee, "createTypeCheckFunction");
|
||||
|
||||
for (const { setHasNode, typeNode, nodeNode } of nodes) {
|
||||
// `set.has` -> `typeCheckFunction`
|
||||
yield fixer.replaceText(setHasNode, replacement);
|
||||
|
||||
// `foo.type` -> `foo`
|
||||
yield fixer.replaceText(
|
||||
typeNode,
|
||||
`(${sourceCode.getText(nodeNode)})`,
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -570,6 +570,19 @@ test("prefer-create-type-check-function", {
|
||||
'const isClassProperty = createTypeCheckFunction(["ClassProperty", "PropertyDefinition"]);',
|
||||
errors: 1,
|
||||
},
|
||||
{
|
||||
code: outdent`
|
||||
const types = new Set(["ClassProperty", "PropertyDefinition"]);
|
||||
if (types.has(node?.type)) {}
|
||||
if (types.has((0, foo).type)) {}
|
||||
`,
|
||||
output: outdent`
|
||||
const __please_rename_this_function_types = createTypeCheckFunction(["ClassProperty", "PropertyDefinition"]);
|
||||
if (__please_rename_this_function_types((node))) {}
|
||||
if (__please_rename_this_function_types((0, foo))) {}
|
||||
`,
|
||||
errors: 1,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
|
||||
@@ -446,7 +446,7 @@ function handleConditionalExpressionComments({
|
||||
return false;
|
||||
}
|
||||
|
||||
const classLikeNodeTypes = new Set([
|
||||
const isClassLikeNode = createTypeCheckFunction([
|
||||
"ClassDeclaration",
|
||||
"ClassExpression",
|
||||
"DeclareClass",
|
||||
@@ -460,7 +460,7 @@ function handleClassComments({
|
||||
enclosingNode,
|
||||
followingNode,
|
||||
}) {
|
||||
if (classLikeNodeTypes.has(enclosingNode?.type)) {
|
||||
if (isClassLikeNode(enclosingNode)) {
|
||||
if (
|
||||
isNonEmptyArray(enclosingNode.decorators) &&
|
||||
!(followingNode?.type === "Decorator")
|
||||
@@ -508,7 +508,7 @@ function handleClassComments({
|
||||
return false;
|
||||
}
|
||||
|
||||
const propertyLikeNodeTypes = new Set([
|
||||
const isPropertyLikeNode = createTypeCheckFunction([
|
||||
"ClassMethod",
|
||||
"ClassProperty",
|
||||
"PropertyDefinition",
|
||||
@@ -552,7 +552,7 @@ function handleMethodNameComments({
|
||||
// on the decorator node instead of the method node
|
||||
if (
|
||||
precedingNode?.type === "Decorator" &&
|
||||
propertyLikeNodeTypes.has(enclosingNode?.type) &&
|
||||
isPropertyLikeNode(enclosingNode) &&
|
||||
(isLineComment(comment) || comment.placement === "ownLine")
|
||||
) {
|
||||
addTrailingComment(precedingNode, comment);
|
||||
@@ -562,7 +562,7 @@ function handleMethodNameComments({
|
||||
return false;
|
||||
}
|
||||
|
||||
const functionLikeNodeTypes = new Set([
|
||||
const isFunctionLikeNode = createTypeCheckFunction([
|
||||
"FunctionDeclaration",
|
||||
"FunctionExpression",
|
||||
"ClassMethod",
|
||||
@@ -578,7 +578,7 @@ function handleFunctionNameComments({
|
||||
if (getNextNonSpaceNonCommentCharacter(text, locEnd(comment)) !== "(") {
|
||||
return false;
|
||||
}
|
||||
if (precedingNode && functionLikeNodeTypes.has(enclosingNode?.type)) {
|
||||
if (precedingNode && isFunctionLikeNode(enclosingNode)) {
|
||||
addTrailingComment(precedingNode, comment);
|
||||
return true;
|
||||
}
|
||||
@@ -891,13 +891,13 @@ function handleAssignmentPatternComments({ comment, enclosingNode }) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const assignmentLikeNodeTypes = new Set([
|
||||
const isAssignmentLikeNode = createTypeCheckFunction([
|
||||
"VariableDeclarator",
|
||||
"AssignmentExpression",
|
||||
"TypeAlias",
|
||||
"TSTypeAliasDeclaration",
|
||||
]);
|
||||
const complexExprNodeTypes = new Set([
|
||||
const isComplexExprNode = createTypeCheckFunction([
|
||||
"ObjectExpression",
|
||||
"ArrayExpression",
|
||||
"TemplateLiteral",
|
||||
@@ -911,9 +911,9 @@ function handleVariableDeclaratorComments({
|
||||
followingNode,
|
||||
}) {
|
||||
if (
|
||||
assignmentLikeNodeTypes.has(enclosingNode?.type) &&
|
||||
isAssignmentLikeNode(enclosingNode) &&
|
||||
followingNode &&
|
||||
(complexExprNodeTypes.has(followingNode.type) || isBlockComment(comment))
|
||||
(isComplexExprNode(followingNode) || isBlockComment(comment))
|
||||
) {
|
||||
addLeadingComment(followingNode, comment);
|
||||
return true;
|
||||
|
||||
@@ -28,6 +28,7 @@ import pathNeedsParens from "../needs-parens.js";
|
||||
import getRaw from "../utils/get-raw.js";
|
||||
import {
|
||||
CommentCheckFlags,
|
||||
createTypeCheckFunction,
|
||||
hasComment,
|
||||
hasNodeIgnoreComment,
|
||||
isArrayExpression,
|
||||
@@ -485,7 +486,7 @@ function separatorWithWhitespace(
|
||||
return hardline;
|
||||
}
|
||||
|
||||
const NO_WRAP_PARENTS = new Set([
|
||||
const isNoWrapParent = createTypeCheckFunction([
|
||||
"ArrayExpression",
|
||||
"JSXAttribute",
|
||||
"JSXElement",
|
||||
@@ -501,7 +502,7 @@ const NO_WRAP_PARENTS = new Set([
|
||||
function maybeWrapJsxElementInParens(path, elem, options) {
|
||||
const { parent } = path;
|
||||
|
||||
if (NO_WRAP_PARENTS.has(parent.type)) {
|
||||
if (isNoWrapParent(parent)) {
|
||||
return elem;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { indent, line } from "../../document/builders.js";
|
||||
import {
|
||||
CommentCheckFlags,
|
||||
createTypeCheckFunction,
|
||||
hasComment,
|
||||
isCallExpression,
|
||||
isMemberExpression,
|
||||
@@ -51,7 +52,7 @@ function printDefiniteToken(path) {
|
||||
: "";
|
||||
}
|
||||
|
||||
const flowDeclareNodeTypes = new Set([
|
||||
const isFlowDeclareNode = createTypeCheckFunction([
|
||||
"DeclareClass",
|
||||
"DeclareComponent",
|
||||
"DeclareFunction",
|
||||
@@ -64,6 +65,7 @@ const flowDeclareNodeTypes = new Set([
|
||||
"DeclareEnum",
|
||||
"DeclareInterface",
|
||||
]);
|
||||
|
||||
/**
|
||||
* @param {AstPath} path
|
||||
* @returns {Doc}
|
||||
@@ -75,24 +77,25 @@ function printDeclareToken(path) {
|
||||
// TypeScript
|
||||
node.declare ||
|
||||
// Flow
|
||||
(flowDeclareNodeTypes.has(node.type) &&
|
||||
(isFlowDeclareNode(node) &&
|
||||
path.parent.type !== "DeclareExportDeclaration")
|
||||
? "declare "
|
||||
: ""
|
||||
);
|
||||
}
|
||||
|
||||
const tsAbstractNodeTypes = new Set([
|
||||
const isTsAbstractNode = createTypeCheckFunction([
|
||||
"TSAbstractMethodDefinition",
|
||||
"TSAbstractPropertyDefinition",
|
||||
"TSAbstractAccessorProperty",
|
||||
]);
|
||||
|
||||
/**
|
||||
* @param {AstPath} param0
|
||||
* @returns {Doc}
|
||||
*/
|
||||
function printAbstractToken({ node }) {
|
||||
return node.abstract || tsAbstractNodeTypes.has(node.type) ? "abstract " : "";
|
||||
return node.abstract || isTsAbstractNode(node) ? "abstract " : "";
|
||||
}
|
||||
|
||||
function printBindExpressionCallee(path, options, print) {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import * as assert from "#universal/assert";
|
||||
|
||||
/** @import {Node, Comment} from "../types/estree.js" */
|
||||
|
||||
/**
|
||||
@@ -6,6 +8,15 @@
|
||||
*/
|
||||
function createTypeCheckFunction(typesArray) {
|
||||
const types = new Set(typesArray);
|
||||
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
assert.equal(
|
||||
typesArray.length,
|
||||
types.size,
|
||||
"'typesArray' should be unique.",
|
||||
);
|
||||
}
|
||||
|
||||
return (node) => types.has(node?.type);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user