From 52140cc6ad09c042c866df6eb0d8ace025cf5307 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Wed, 11 Jan 2023 11:22:44 +0800 Subject: [PATCH] Improve internal ESLint rule (#14112) --- .../prefer-create-type-check-function.js | 74 ++++++++++++++++--- .../test.js | 19 +++++ src/language-js/print/statement.js | 18 +++-- 3 files changed, 94 insertions(+), 17 deletions(-) diff --git a/scripts/tools/eslint-plugin-prettier-internal-rules/prefer-create-type-check-function.js b/scripts/tools/eslint-plugin-prettier-internal-rules/prefer-create-type-check-function.js index fd59e3aed..e67511d83 100644 --- a/scripts/tools/eslint-plugin-prettier-internal-rules/prefer-create-type-check-function.js +++ b/scripts/tools/eslint-plugin-prettier-internal-rules/prefer-create-type-check-function.js @@ -17,24 +17,32 @@ const isTypeAccess = (node, parameterName) => { ); }; -const isTypeCheck = (node, parameterName) => +const isEqualCheck = (node) => node.type === "BinaryExpression" && node.operator === "===" && - isTypeAccess(node.left, parameterName) && node.right.type === "Literal" && typeof node.right.value === "string"; -function getTypes(node, parameterName) { - if (isTypeCheck(node, parameterName)) { + +const isTypeIdentifierCheck = (node) => + isEqualCheck(node) && + node.left.type === "Identifier" && + node.left.name === "type"; + +const isTypeAccessCheck = (node, parameterName) => + isEqualCheck(node) && isTypeAccess(node.left, parameterName); + +function getTypesFromNodeParameter(node, parameterName) { + if (isTypeAccessCheck(node, parameterName)) { return [node.right.value]; } if (node.type === "LogicalExpression" && node.operator === "||") { - const left = getTypes(node.left, parameterName); + const left = getTypesFromNodeParameter(node.left, parameterName); if (!left) { return; } - const right = getTypes(node.right, parameterName); + const right = getTypesFromNodeParameter(node.right, parameterName); if (!right) { return; @@ -44,6 +52,54 @@ function getTypes(node, parameterName) { } } +function getTypesFromTypeParameter(node) { + if (isTypeIdentifierCheck(node)) { + return [node.right.value]; + } + + if (node.type === "LogicalExpression" && node.operator === "||") { + const left = getTypesFromTypeParameter(node.left); + + if (!left) { + return; + } + const right = getTypesFromTypeParameter(node.right); + + if (!right) { + return; + } + + return [...left, ...right]; + } +} + +function getTypes(node, parameter) { + // `function(node) {}` + if (parameter.type === "Identifier") { + return getTypesFromNodeParameter(node, parameter.name); + } + + // `function({type}) {}` + if ( + parameter.type === "ObjectPattern" && + parameter.properties.length === 1 && + parameter.properties[0].type === "Property" + ) { + const [{ shorthand, computed, key, value }] = parameter.properties; + + if ( + shorthand && + !computed && + key.type === "Identifier" && + key.name === "type" && + value.type === "Identifier" && + value.name === "type" + ) { + return getTypesFromTypeParameter(node); + } + } +} + function isTopLevelFunction(node) { return ( node.parent.type === "Program" || @@ -57,7 +113,6 @@ function isTopLevelFunction(node) { const selector = [ ":function", "[params.length=1]", - '[params.0.type="Identifier"]', "[async!=true]", "[generator!=true]", ].join(""); @@ -116,8 +171,9 @@ module.exports = { returnStatementArgument = body.body[0].argument; } - const parameterName = functionNode.params[0].name; - const types = getTypes(returnStatementArgument, parameterName); + const [parameter] = functionNode.params; + const types = getTypes(returnStatementArgument, parameter); + if (!types) { return; } diff --git a/scripts/tools/eslint-plugin-prettier-internal-rules/test.js b/scripts/tools/eslint-plugin-prettier-internal-rules/test.js index b539ccc01..eac730119 100644 --- a/scripts/tools/eslint-plugin-prettier-internal-rules/test.js +++ b/scripts/tools/eslint-plugin-prettier-internal-rules/test.js @@ -423,6 +423,11 @@ test("prefer-create-type-check-function", { return node.kind === "get" || node.kind === "set"; } `, + outdent` + const isClassProperty = ({ notType }) => + notType === "ClassProperty" || + notType === "PropertyDefinition"; + `, ], invalid: [ { @@ -541,6 +546,20 @@ test("prefer-create-type-check-function", { `, errors: 1, }, + { + code: outdent` + const isClassProperty = ({ type }) => + type === "ClassProperty" || + type === "PropertyDefinition"; + `, + output: outdent` + const isClassProperty = createTypeCheckFunction([ + "ClassProperty", + "PropertyDefinition" + ]); + `, + errors: 1, + }, ].map((testCase) => ({ ...testCase, parserOptions: { sourceType: "module" }, diff --git a/src/language-js/print/statement.js b/src/language-js/print/statement.js index bf41b4ccc..f6ea3d4ac 100644 --- a/src/language-js/print/statement.js +++ b/src/language-js/print/statement.js @@ -8,6 +8,7 @@ import { hasComment, CommentCheckFlags, isNextLineEmpty, + createTypeCheckFunction, } from "../utils/index.js"; import { shouldPrintParamsWithoutParens } from "./function.js"; @@ -157,14 +158,15 @@ function printSwitchCaseConsequent(path, options, print) { return printStatementSequence(path, options, print, "consequent"); } -const isClassProperty = ({ type }) => - type === "ClassProperty" || - type === "PropertyDefinition" || - type === "ClassPrivateProperty" || - type === "ClassAccessorProperty" || - type === "AccessorProperty" || - type === "TSAbstractPropertyDefinition" || - type === "TSAbstractAccessorProperty"; +const isClassProperty = createTypeCheckFunction([ + "ClassProperty", + "PropertyDefinition", + "ClassPrivateProperty", + "ClassAccessorProperty", + "AccessorProperty", + "TSAbstractPropertyDefinition", + "TSAbstractAccessorProperty", +]); /** * @returns {boolean} */