From 95bf8fa4eddb76c46c71e594da9bebecff332132 Mon Sep 17 00:00:00 2001 From: adamviktora Date: Thu, 13 Apr 2023 15:12:55 +0200 Subject: [PATCH 1/5] chore(Td): rename disable prop in TdSelectType and TdActionsType --- ...SelectType-TdActionsType-rename-disable.js | 94 +++++++++++++++++++ ...SelectType-TdActionsType-rename-disable.js | 40 ++++++++ test/test.tsx | 5 + 3 files changed, 139 insertions(+) create mode 100644 packages/eslint-plugin-pf-codemods/lib/rules/v5/td-TdSelectType-TdActionsType-rename-disable.js create mode 100644 packages/eslint-plugin-pf-codemods/test/rules/v5/td-TdSelectType-TdActionsType-rename-disable.js diff --git a/packages/eslint-plugin-pf-codemods/lib/rules/v5/td-TdSelectType-TdActionsType-rename-disable.js b/packages/eslint-plugin-pf-codemods/lib/rules/v5/td-TdSelectType-TdActionsType-rename-disable.js new file mode 100644 index 000000000..8d2ee5278 --- /dev/null +++ b/packages/eslint-plugin-pf-codemods/lib/rules/v5/td-TdSelectType-TdActionsType-rename-disable.js @@ -0,0 +1,94 @@ +const { getPackageImports } = require("../../helpers"); + +// https://github.com/patternfly/patternfly-react/pull/8861 +// https://github.com/patternfly/patternfly-react/pull/8904 +module.exports = { + meta: { fixable: "code" }, + create: function (context) { + const tableTdImport = getPackageImports( + context, + "@patternfly/react-table" + ).find((specifier) => "Td" === specifier.imported.name); + + const tdName = tableTdImport?.local.name; + + return !tableTdImport + ? {} + : { + JSXOpeningElement(node) { + if (tdName !== node.name?.name) { + return; + } + + const attributes = node.attributes.filter( + (attr) => + attr.type === "JSXAttribute" && + attr.value.type === "JSXExpressionContainer" && + ["select", "actions"].includes(attr.name?.name) + ); + + const interfaceMap = { + select: "TdSelectType", + actions: "TdActionsType", + }; + + for (const attr of attributes) { + const expr = attr.value.expression; + + const getObjectProp = (expr, propName) => + expr.properties.find( + (prop) => + (prop.key.type === "Identifier" && + prop.key.name === propName) || + (prop.key.type === "Literal" && prop.key.value === propName) + ); + + const doReport = (disableProp) => { + if (disableProp) { + context.report({ + message: `'disable' prop of interface ${ + interfaceMap[attr.name.name] + } has been renamed to 'isDisabled'`, + node, + fix: function (fixer) { + return fixer.replaceText(disableProp.key, "isDisabled"); + }, + }); + } + }; + + if (expr.type === "ObjectExpression") { + doReport(getObjectProp(expr, "disable")); + } + + if (expr.type === "Identifier") { + console.log(expr.loc); + + let scope = context.getScope(); + while (scope !== null) { + const variable = scope.variables.find( + (v) => v.name === expr.name + ); + if (!variable) { + scope = scope.upper; + continue; + } + if ( + variable.references.some( + (r) => r.identifier.loc === expr.loc + ) + ) { + variable.defs.forEach((def) => { + if (def.node.init?.type === "ObjectExpression") { + doReport(getObjectProp(def.node.init, "disable")); + } + }); + break; + } + } + } + } + }, + }; + }, +}; diff --git a/packages/eslint-plugin-pf-codemods/test/rules/v5/td-TdSelectType-TdActionsType-rename-disable.js b/packages/eslint-plugin-pf-codemods/test/rules/v5/td-TdSelectType-TdActionsType-rename-disable.js new file mode 100644 index 000000000..318383d33 --- /dev/null +++ b/packages/eslint-plugin-pf-codemods/test/rules/v5/td-TdSelectType-TdActionsType-rename-disable.js @@ -0,0 +1,40 @@ +const ruleTester = require("../../ruletester"); +const rule = require("../../../lib/rules/v5/td-TdSelectType-TdActionsType-rename-disable"); + +ruleTester.run("td-TdSelectType-TdActionsType-rename-disable", rule, { + valid: [ + { + code: `import { Td } from "@patternfly/react-table"; + const myObj = {isDisabled: true}; + `, + }, + { + // No @patternfly/react-core import + code: ` + const myObj = {"disable": true}; + `, + }, + ], + invalid: [ + { + code: ` + import { Td } from "@patternfly/react-table"; + const myObj = {"disable": true}; + `, + output: ` + import { Td } from "@patternfly/react-table"; + const myObj = {isDisabled: true}; + `, + errors: [ + { + message: `'disable' prop of interface TdSelectType has been renamed to 'isDisabled'`, + type: "JSXOpeningElement", + }, + { + message: `'disable' prop of interface TdActionsType has been renamed to 'isDisabled'`, + type: "JSXOpeningElement", + }, + ], + }, + ], +}); diff --git a/test/test.tsx b/test/test.tsx index 172618a02..ed3768e68 100644 --- a/test/test.tsx +++ b/test/test.tsx @@ -79,6 +79,10 @@ import { } from "@patternfly/react-core"; import { SelectOption, WizardBody as WizardBodyNext, WizardFooter } from "@patternfly/react-core/next"; +import { Td } from "@patternfly/react-table"; + +const tdSelectTypeObj = {"disable": true}; + //following type of import was causing errors for rules that checked specifiers before import package import foo from Bar; @@ -172,6 +176,7 @@ const backgroundImgSrcObj: BackgroundImageSrcMap = {}; + From be01c0501a8860c41693f7d2872da848c64fa0d4 Mon Sep 17 00:00:00 2001 From: adamviktora Date: Thu, 13 Apr 2023 15:58:46 +0200 Subject: [PATCH 2/5] feat(Td): add documentation --- packages/pf-codemods/README.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/packages/pf-codemods/README.md b/packages/pf-codemods/README.md index 883d037e6..e8ea5451d 100644 --- a/packages/pf-codemods/README.md +++ b/packages/pf-codemods/README.md @@ -1768,6 +1768,30 @@ We've restricted the type of elements that can be passed to the `Tabs` component This rule will raise a warning when `Tabs` is imported in a file, even if the children passed to it are already of the appropriate type. It will not make any code changes. +### td-TdSelectType-TdActionsType-rename-disable [(#8861,](https://github.com/patternfly/patternfly-react/pull/8861) [#8904)](https://github.com/patternfly/patternfly-react/pull/8904) + +We've renamed `disable` prop to `isDisabled` inside `TdSelectType` and `TdActionsType` interfaces. These interfaces are used as types of `Td` component's props `select` (`TdSelectType`) and `actions` (`TdActionsType`). + +#### Examples + +In: + +```jsx +const tdSelectTypeObject = {disable: true}; + + + +``` + +Out: + +```jsx +const tdSelectTypeObject = {isDisabled: true}; + + + +``` + ### toggle-remove-isprimary [(#8179)](https://github.com/patternfly/patternfly-react/pull/8179) We've removed the deprecated `isPrimary` prop. This rule wil replace it with the "primary" value on the `toggleVariant` prop. From 43333c32ca659ba14d2a0be3017118fa4f70a4d7 Mon Sep 17 00:00:00 2001 From: adamviktora Date: Wed, 19 Apr 2023 13:31:14 +0200 Subject: [PATCH 3/5] refactor(Td): renaming --- .../v5/td-TdSelectType-TdActionsType-rename-disable.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/eslint-plugin-pf-codemods/lib/rules/v5/td-TdSelectType-TdActionsType-rename-disable.js b/packages/eslint-plugin-pf-codemods/lib/rules/v5/td-TdSelectType-TdActionsType-rename-disable.js index 8d2ee5278..024e1891a 100644 --- a/packages/eslint-plugin-pf-codemods/lib/rules/v5/td-TdSelectType-TdActionsType-rename-disable.js +++ b/packages/eslint-plugin-pf-codemods/lib/rules/v5/td-TdSelectType-TdActionsType-rename-disable.js @@ -20,7 +20,7 @@ module.exports = { return; } - const attributes = node.attributes.filter( + const attributesToUpdate = node.attributes.filter( (attr) => attr.type === "JSXAttribute" && attr.value.type === "JSXExpressionContainer" && @@ -32,7 +32,7 @@ module.exports = { actions: "TdActionsType", }; - for (const attr of attributes) { + for (const attr of attributesToUpdate) { const expr = attr.value.expression; const getObjectProp = (expr, propName) => @@ -62,12 +62,10 @@ module.exports = { } if (expr.type === "Identifier") { - console.log(expr.loc); - let scope = context.getScope(); while (scope !== null) { const variable = scope.variables.find( - (v) => v.name === expr.name + (variable) => variable.name === expr.name ); if (!variable) { scope = scope.upper; @@ -75,7 +73,7 @@ module.exports = { } if ( variable.references.some( - (r) => r.identifier.loc === expr.loc + (ref) => ref.identifier.loc === expr.loc ) ) { variable.defs.forEach((def) => { From 4a9b9b46474b70398c4407dbe7bcf0a9f0a00d39 Mon Sep 17 00:00:00 2001 From: adamviktora Date: Mon, 24 Apr 2023 12:40:59 +0200 Subject: [PATCH 4/5] refactor(Td) --- ...SelectType-TdActionsType-rename-disable.js | 59 ++++++++++--------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/packages/eslint-plugin-pf-codemods/lib/rules/v5/td-TdSelectType-TdActionsType-rename-disable.js b/packages/eslint-plugin-pf-codemods/lib/rules/v5/td-TdSelectType-TdActionsType-rename-disable.js index 024e1891a..1d7d982b8 100644 --- a/packages/eslint-plugin-pf-codemods/lib/rules/v5/td-TdSelectType-TdActionsType-rename-disable.js +++ b/packages/eslint-plugin-pf-codemods/lib/rules/v5/td-TdSelectType-TdActionsType-rename-disable.js @@ -10,13 +10,11 @@ module.exports = { "@patternfly/react-table" ).find((specifier) => "Td" === specifier.imported.name); - const tdName = tableTdImport?.local.name; - return !tableTdImport ? {} : { JSXOpeningElement(node) { - if (tdName !== node.name?.name) { + if (tableTdImport.local.name !== node.name.name) { return; } @@ -32,45 +30,48 @@ module.exports = { actions: "TdActionsType", }; - for (const attr of attributesToUpdate) { - const expr = attr.value.expression; + const getObjectProp = (expr, propName) => + expr.properties.find( + (prop) => + (prop.key.type === "Identifier" && + prop.key.name === propName) || + (prop.key.type === "Literal" && prop.key.value === propName) + ); - const getObjectProp = (expr, propName) => - expr.properties.find( - (prop) => - (prop.key.type === "Identifier" && - prop.key.name === propName) || - (prop.key.type === "Literal" && prop.key.value === propName) - ); + const doReport = (disableProp, attr) => { + if (disableProp) { + context.report({ + message: `'disable' prop of interface ${ + interfaceMap[attr.name.name] + } has been renamed to 'isDisabled'`, + node, + fix: function (fixer) { + return fixer.replaceText(disableProp.key, "isDisabled"); + }, + }); + } + }; - const doReport = (disableProp) => { - if (disableProp) { - context.report({ - message: `'disable' prop of interface ${ - interfaceMap[attr.name.name] - } has been renamed to 'isDisabled'`, - node, - fix: function (fixer) { - return fixer.replaceText(disableProp.key, "isDisabled"); - }, - }); - } - }; + for (const attr of attributesToUpdate) { + const expr = attr.value.expression; if (expr.type === "ObjectExpression") { - doReport(getObjectProp(expr, "disable")); + doReport(getObjectProp(expr, "disable"), attr); } if (expr.type === "Identifier") { - let scope = context.getScope(); + let scope = context.getSourceCode().getScope(node); + while (scope !== null) { const variable = scope.variables.find( - (variable) => variable.name === expr.name + (v) => v.name === expr.name ); + if (!variable) { scope = scope.upper; continue; } + if ( variable.references.some( (ref) => ref.identifier.loc === expr.loc @@ -78,7 +79,7 @@ module.exports = { ) { variable.defs.forEach((def) => { if (def.node.init?.type === "ObjectExpression") { - doReport(getObjectProp(def.node.init, "disable")); + doReport(getObjectProp(def.node.init, "disable"), attr); } }); break; From 28d6e5d47b42efc1062fbbe81f5116786d840a58 Mon Sep 17 00:00:00 2001 From: adamviktora Date: Mon, 24 Apr 2023 18:05:15 +0200 Subject: [PATCH 5/5] fix(Td): apply fix to more use cases (let variable, default property name) --- ...SelectType-TdActionsType-rename-disable.js | 111 ++++++++++++------ ...SelectType-TdActionsType-rename-disable.js | 75 ++++++++++-- packages/pf-codemods/README.md | 30 ++++- 3 files changed, 164 insertions(+), 52 deletions(-) diff --git a/packages/eslint-plugin-pf-codemods/lib/rules/v5/td-TdSelectType-TdActionsType-rename-disable.js b/packages/eslint-plugin-pf-codemods/lib/rules/v5/td-TdSelectType-TdActionsType-rename-disable.js index 1d7d982b8..3ab0c62a5 100644 --- a/packages/eslint-plugin-pf-codemods/lib/rules/v5/td-TdSelectType-TdActionsType-rename-disable.js +++ b/packages/eslint-plugin-pf-codemods/lib/rules/v5/td-TdSelectType-TdActionsType-rename-disable.js @@ -25,11 +25,6 @@ module.exports = { ["select", "actions"].includes(attr.name?.name) ); - const interfaceMap = { - select: "TdSelectType", - actions: "TdActionsType", - }; - const getObjectProp = (expr, propName) => expr.properties.find( (prop) => @@ -38,54 +33,94 @@ module.exports = { (prop.key.type === "Literal" && prop.key.value === propName) ); - const doReport = (disableProp, attr) => { - if (disableProp) { - context.report({ - message: `'disable' prop of interface ${ - interfaceMap[attr.name.name] - } has been renamed to 'isDisabled'`, - node, - fix: function (fixer) { - return fixer.replaceText(disableProp.key, "isDisabled"); - }, - }); + const findVariableDeclaration = (name, scope) => { + while (scope !== null) { + const variable = scope.variables.find((v) => v.name === name); + + if (variable) { + return variable; + } + + scope = scope.upper; + } + return undefined; + }; + + const handleObjectExpression = (expr, toReplace) => { + const disableProp = getObjectProp(expr, "disable"); + disableProp && toReplace.push(disableProp.key); + + if (disableProp?.shorthand) { + const variable = findVariableDeclaration( + "disable", + context.getSourceCode().getScope(expr) + ); + + variable && toReplace.push(variable.defs[0].node.id); } }; for (const attr of attributesToUpdate) { const expr = attr.value.expression; + let toReplace = []; + if (expr.type === "ObjectExpression") { - doReport(getObjectProp(expr, "disable"), attr); + handleObjectExpression(expr, toReplace); } if (expr.type === "Identifier") { - let scope = context.getSourceCode().getScope(node); - - while (scope !== null) { - const variable = scope.variables.find( - (v) => v.name === expr.name - ); - - if (!variable) { - scope = scope.upper; - continue; + const variable = findVariableDeclaration( + expr.name, + context.getSourceCode().getScope(node) + ); + + const variableValue = variable.defs[0].node.init; + + if (variableValue?.type === "ObjectExpression") { + handleObjectExpression(variableValue, toReplace); + } + + const nodesUsingVariable = variable.references.map( + (ref) => ref.identifier.parent + ); + + nodesUsingVariable.forEach((n) => { + if ( + n.type === "MemberExpression" && + (n.property.name === "disable" || + n.property.value === "disable") + ) { + toReplace.push(n.property); } - + if ( - variable.references.some( - (ref) => ref.identifier.loc === expr.loc - ) + n.type === "AssignmentExpression" && + n.right.type === "ObjectExpression" ) { - variable.defs.forEach((def) => { - if (def.node.init?.type === "ObjectExpression") { - doReport(getObjectProp(def.node.init, "disable"), attr); - } - }); - break; + handleObjectExpression(n.right, toReplace); } - } + }); } + + toReplace.length && + context.report({ + message: `'disable' prop of interface ${ + { + select: "TdSelectType", + actions: "TdActionsType", + }[attr.name.name] + } has been renamed to 'isDisabled'`, + node, + fix: function (fixer) { + return toReplace.map((val) => + fixer.replaceText( + val, + val.type === "Literal" ? '"isDisabled"' : "isDisabled" + ) + ); + }, + }); } }, }; diff --git a/packages/eslint-plugin-pf-codemods/test/rules/v5/td-TdSelectType-TdActionsType-rename-disable.js b/packages/eslint-plugin-pf-codemods/test/rules/v5/td-TdSelectType-TdActionsType-rename-disable.js index 318383d33..71925f697 100644 --- a/packages/eslint-plugin-pf-codemods/test/rules/v5/td-TdSelectType-TdActionsType-rename-disable.js +++ b/packages/eslint-plugin-pf-codemods/test/rules/v5/td-TdSelectType-TdActionsType-rename-disable.js @@ -5,26 +5,73 @@ ruleTester.run("td-TdSelectType-TdActionsType-rename-disable", rule, { valid: [ { code: `import { Td } from "@patternfly/react-table"; - const myObj = {isDisabled: true}; - `, + + let myObj = { isDisabled: true }; + myObj["isDisabled"] = false; + myObj.isDisabled = true; + myObj = { isDisabled: false }; + + ; + + const isDisabled = true; + const obj = { isDisabled }; + <> + + + ;`, }, { // No @patternfly/react-core import code: ` - const myObj = {"disable": true}; - `, + let myObj = { disable: true }; + myObj["disable"] = false; + myObj.disable = true; + myObj = { disable: false }; + + ; + + const disable = true; + const obj = { disable }; + <> + + + ;`, }, ], invalid: [ { code: ` import { Td } from "@patternfly/react-table"; - const myObj = {"disable": true}; - `, + + let myObj = { disable: true }; + myObj["disable"] = false; + myObj.disable = true; + myObj = { disable: false }; + + ; + + const disable = true; + const obj = { disable }; + <> + + + ;`, output: ` import { Td } from "@patternfly/react-table"; - const myObj = {isDisabled: true}; - `, + + let myObj = { isDisabled: true }; + myObj["isDisabled"] = false; + myObj.isDisabled = true; + myObj = { isDisabled: false }; + + ; + + const isDisabled = true; + const obj = { isDisabled }; + <> + + + ;`, errors: [ { message: `'disable' prop of interface TdSelectType has been renamed to 'isDisabled'`, @@ -34,6 +81,18 @@ ruleTester.run("td-TdSelectType-TdActionsType-rename-disable", rule, { message: `'disable' prop of interface TdActionsType has been renamed to 'isDisabled'`, type: "JSXOpeningElement", }, + { + message: `'disable' prop of interface TdSelectType has been renamed to 'isDisabled'`, + type: "JSXOpeningElement", + }, + { + message: `'disable' prop of interface TdActionsType has been renamed to 'isDisabled'`, + type: "JSXOpeningElement", + }, + { + message: `'disable' prop of interface TdActionsType has been renamed to 'isDisabled'`, + type: "JSXOpeningElement", + } ], }, ], diff --git a/packages/pf-codemods/README.md b/packages/pf-codemods/README.md index e8ea5451d..c1d8e4422 100644 --- a/packages/pf-codemods/README.md +++ b/packages/pf-codemods/README.md @@ -1777,19 +1777,37 @@ We've renamed `disable` prop to `isDisabled` inside `TdSelectType` and `TdAction In: ```jsx -const tdSelectTypeObject = {disable: true}; +let myObj = { disable: true }; +myObj["disable"] = false; +myObj.disable = true; +myObj = { disable: false }; - - +; + +const disable = true; +const obj = { disable }; +<> + + +; ``` Out: ```jsx -const tdSelectTypeObject = {isDisabled: true}; +let myObj = { isDisabled: true }; +myObj["isDisabled"] = false; +myObj.isDisabled = true; +myObj = { isDisabled: false }; - - +; + +const isDisabled = true; +const obj = { isDisabled }; +<> + + +; ``` ### toggle-remove-isprimary [(#8179)](https://github.com/patternfly/patternfly-react/pull/8179)