diff --git a/packages/eslint-plugin-pf-codemods/src/ruleCuration.ts b/packages/eslint-plugin-pf-codemods/src/ruleCuration.ts index f7694e5d1..4f9a3a62c 100644 --- a/packages/eslint-plugin-pf-codemods/src/ruleCuration.ts +++ b/packages/eslint-plugin-pf-codemods/src/ruleCuration.ts @@ -1,4 +1,3 @@ -import { version } from "typescript"; import { betaRuleNames, warningRules } from "./ruleCustomization"; import { join } from "path"; @@ -27,13 +26,14 @@ const createListOfRules = (version: string, includeBeta = false) => { } } }); - + return rules; }; export const v6rules = createListOfRules("6"); export const v5rules = createListOfRules("5"); export const v4rules = createListOfRules("4"); +export const betaV6Rules = createListOfRules("6", true); export const betaV5Rules = createListOfRules("5", true); const createRules = (rules: Rules) => { @@ -52,7 +52,13 @@ export const mappedRules = { ...createRules(v4rules), }; -export const rules = { ...v6rules, ...v5rules, ...v4rules, ...betaV5Rules }; +export const rules = { + ...v6rules, + ...v5rules, + ...v4rules, + ...betaV6Rules, + ...betaV5Rules, +}; export const ruleVersionMapping = { v4: Object.keys(v4rules), diff --git a/packages/eslint-plugin-pf-codemods/src/ruleCustomization.ts b/packages/eslint-plugin-pf-codemods/src/ruleCustomization.ts index 03873479f..fcc12a5da 100644 --- a/packages/eslint-plugin-pf-codemods/src/ruleCustomization.ts +++ b/packages/eslint-plugin-pf-codemods/src/ruleCustomization.ts @@ -2,7 +2,10 @@ * add the rule name to the below array. Do not add rules here if the React PR is * not yet merged; instead add a "do not merge" label to the codemod PR. */ -export const betaRuleNames: string[] = ["kebabToggle-replace-with-menuToggle"]; +export const betaRuleNames: string[] = [ + "data-codemods-cleanup", + "kebabToggle-replace-with-menuToggle", +]; // if you want a rule to have a severity that defaults to warning rather than error, add the rule name to the below array export const warningRules = [ diff --git a/packages/eslint-plugin-pf-codemods/src/rules/v6/dataCodemodsCleanup/data-codemods-cleanup.md b/packages/eslint-plugin-pf-codemods/src/rules/v6/dataCodemodsCleanup/data-codemods-cleanup.md new file mode 100644 index 000000000..169909031 --- /dev/null +++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/dataCodemodsCleanup/data-codemods-cleanup.md @@ -0,0 +1,20 @@ +### data-codemods-cleanup + +This rule will remove `data-codemods` attributes and comments, which were introduced by our codemods in order to work correctly. +You should run this rule only once, after you finish running the codemods. + +This rule can only run using the `--only data-codemods-cleanup` option. + +#### Examples + +In: + +```jsx +%inputExample% +``` + +Out: + +```jsx +%outputExample% +``` diff --git a/packages/eslint-plugin-pf-codemods/src/rules/v6/dataCodemodsCleanup/data-codemods-cleanup.test.ts b/packages/eslint-plugin-pf-codemods/src/rules/v6/dataCodemodsCleanup/data-codemods-cleanup.test.ts new file mode 100644 index 000000000..703a163c7 --- /dev/null +++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/dataCodemodsCleanup/data-codemods-cleanup.test.ts @@ -0,0 +1,112 @@ +const ruleTester = require("../../ruletester"); +import * as rule from "./data-codemods-cleanup"; + +const message = `This rule will remove data-codemods attributes and comments, which were introduced by our codemods in order to work correctly.`; + +ruleTester.run("data-codemods-cleanup", rule, { + valid: [ + { + code: `import { DualListSelector /* data-codemods */ } from 'somewhereElse';`, + }, + { + code: `import { LoginMainFooterLinksItem } from 'somewhereElse'; `, + }, + ], + invalid: [ + { + code: `import { DualListSelector /* data-codemods */ } from '@patternfly/react-core';`, + output: `import { DualListSelector } from '@patternfly/react-core';`, + errors: [ + { + message, + type: "ImportSpecifier", + }, + ], + }, + // aliased import + { + code: `import { DualListSelector as DLS /* data-codemods */ } from '@patternfly/react-core';`, + output: `import { DualListSelector as DLS } from '@patternfly/react-core';`, + errors: [ + { + message, + type: "ImportSpecifier", + }, + ], + }, + { + code: `import { LoginMainFooterLinksItem } from '@patternfly/react-core'; `, + output: `import { LoginMainFooterLinksItem } from '@patternfly/react-core'; `, + errors: [ + { + message, + type: "JSXOpeningElement", + }, + ], + }, + { + code: `import { MastheadLogo } from '@patternfly/react-core'; `, + output: `import { MastheadLogo } from '@patternfly/react-core'; `, + errors: [ + { + message, + type: "JSXOpeningElement", + }, + ], + }, + // dist imports + { + code: `import { MastheadLogo } from '@patternfly/react-core/dist/esm/components/MastheadLogo'; `, + output: `import { MastheadLogo } from '@patternfly/react-core/dist/esm/components/MastheadLogo'; `, + errors: [ + { + message, + type: "JSXOpeningElement", + }, + ], + }, + { + code: `import { MastheadLogo } from '@patternfly/react-core/dist/js/components/MastheadLogo'; `, + output: `import { MastheadLogo } from '@patternfly/react-core/dist/js/components/MastheadLogo'; `, + errors: [ + { + message, + type: "JSXOpeningElement", + }, + ], + }, + // with alias + { + code: `import { MastheadLogo as ML } from '@patternfly/react-core'; `, + output: `import { MastheadLogo as ML } from '@patternfly/react-core'; `, + errors: [ + { + message, + type: "JSXOpeningElement", + }, + ], + }, + // comment including other text and data-codemods + { + code: `import { DualListSelector /* This has been passed by data-codemods */ } from '@patternfly/react-core';`, + output: `import { DualListSelector } from '@patternfly/react-core';`, + errors: [ + { + message, + type: "ImportSpecifier", + }, + ], + }, + // data-codemods attribute including other value than true + { + code: `import { LoginMainFooterLinksItem } from '@patternfly/react-core'; `, + output: `import { LoginMainFooterLinksItem } from '@patternfly/react-core'; `, + errors: [ + { + message, + type: "JSXOpeningElement", + }, + ], + }, + ], +}); diff --git a/packages/eslint-plugin-pf-codemods/src/rules/v6/dataCodemodsCleanup/data-codemods-cleanup.ts b/packages/eslint-plugin-pf-codemods/src/rules/v6/dataCodemodsCleanup/data-codemods-cleanup.ts new file mode 100644 index 000000000..21979176b --- /dev/null +++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/dataCodemodsCleanup/data-codemods-cleanup.ts @@ -0,0 +1,60 @@ +import { Rule } from "eslint"; +import { ImportSpecifier, JSXOpeningElement } from "estree-jsx"; +import { + checkMatchingJSXOpeningElement, + getAttribute, + getFromPackage, +} from "../../helpers"; + +module.exports = { + meta: { fixable: "code" }, + create: function (context: Rule.RuleContext) { + const { imports: coreImports } = getFromPackage( + context, + "@patternfly/react-core" + ); + const { imports: tableImports } = getFromPackage( + context, + "@patternfly/react-table" + ); + + const imports = [...coreImports, ...tableImports]; + + const message = + "This rule will remove data-codemods attributes and comments, which were introduced by our codemods in order to work correctly."; + + return { + JSXOpeningElement(node: JSXOpeningElement) { + if (checkMatchingJSXOpeningElement(node, imports)) { + const dataCodemodsAttribute = getAttribute(node, "data-codemods"); + if (dataCodemodsAttribute) { + context.report({ + node, + message, + fix(fixer) { + return fixer.remove(dataCodemodsAttribute); + }, + }); + } + } + }, + ImportSpecifier(node: ImportSpecifier) { + if (imports.some((specifier) => specifier === node)) { + const comments = context.getSourceCode().getCommentsAfter(node); + const dataCodemodsComment = comments.find((comment) => + comment.value.includes("data-codemods") + ); + if (dataCodemodsComment) { + context.report({ + node, + message, + fix(fixer) { + return fixer.removeRange(dataCodemodsComment.range!); + }, + }); + } + } + }, + }; + }, +}; diff --git a/packages/eslint-plugin-pf-codemods/src/rules/v6/dataCodemodsCleanup/dataCodemodsCleanupInput.tsx b/packages/eslint-plugin-pf-codemods/src/rules/v6/dataCodemodsCleanup/dataCodemodsCleanupInput.tsx new file mode 100644 index 000000000..54e6d6c17 --- /dev/null +++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/dataCodemodsCleanup/dataCodemodsCleanupInput.tsx @@ -0,0 +1,13 @@ +import { + DualListSelector /* data-codemods */, + LoginMainFooterLinksItem, + MastheadLogo, +} from "@patternfly/react-core"; + +export const DataCodemodsCleanupInput = () => ( + <> + + + + +); diff --git a/packages/eslint-plugin-pf-codemods/src/rules/v6/dataCodemodsCleanup/dataCodemodsCleanupOutput.tsx b/packages/eslint-plugin-pf-codemods/src/rules/v6/dataCodemodsCleanup/dataCodemodsCleanupOutput.tsx new file mode 100644 index 000000000..9bed8c708 --- /dev/null +++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/dataCodemodsCleanup/dataCodemodsCleanupOutput.tsx @@ -0,0 +1,13 @@ +import { + DualListSelector , + LoginMainFooterLinksItem, + MastheadLogo, +} from "@patternfly/react-core"; + +export const DataCodemodsCleanupInput = () => ( + <> + + + + +); diff --git a/packages/pf-codemods/README.md b/packages/pf-codemods/README.md index 39cd1ddb8..f1c52fb19 100644 --- a/packages/pf-codemods/README.md +++ b/packages/pf-codemods/README.md @@ -54,7 +54,7 @@ Options: These rules are based off the breaking change notes for React. Each rule links the breaking change patternfly-react PR in case you want to better understand the change. Also, each rule makes sure you're using a PatternFly component before running. Some rules will add either a comment (`/* data-codemods */`) or data attribute (`data-codemods="true"`) in order to prevent certain other rules from applying an unnecessary fix. - +These `data-codemods` attributes and comments can be removed by our `data-codemods-cleanup` rule. You should run this rule only once, after you finish running the general codemods, by adding the `--only data-codemods-cleanup` option. ### accordionContent-remove-isHidden-prop [(#9876)](https://github.com/patternfly/patternfly-react/pull/9876) diff --git a/packages/pf-codemods/index.js b/packages/pf-codemods/index.js index bf6b745bb..63e4bdb9c 100755 --- a/packages/pf-codemods/index.js +++ b/packages/pf-codemods/index.js @@ -5,7 +5,7 @@ const { configs, ruleVersionMapping, setupRules, - cleanupRules + cleanupRules, } = require("@patternfly/eslint-plugin-pf-codemods/dist/js"); const { Command } = require("commander"); const program = new Command(); @@ -97,7 +97,12 @@ async function runCodemods(path, otherPaths, options) { const rulesToRemove = getRulesToRemove(options); - rulesToRemove.forEach((rule) => delete configs.recommended.rules[prefix + rule]); + rulesToRemove.forEach((rule) => { + // data-codemods-cleanup rule should exist for any version of codemods + if (rule !== "data-codemods-cleanup") { + delete configs.recommended.rules[prefix + rule]; + } + }); const eslintBaseConfig = { extensions: [".js", ".jsx", ".ts", ".tsx"], baseConfig: configs.recommended, diff --git a/packages/pf-codemods/scripts/baseReadMe.md b/packages/pf-codemods/scripts/baseReadMe.md index 93568a5f8..5cab0762d 100644 --- a/packages/pf-codemods/scripts/baseReadMe.md +++ b/packages/pf-codemods/scripts/baseReadMe.md @@ -16,6 +16,12 @@ Requires Node.js >= 10. npx @patternfly/pf-codemods ./path-to-src ``` +Note: when updating from PatternFly 5 to 6, add the `--v6` flag. + +```sh +npx @patternfly/pf-codemods --v6 ./path-to-src +``` + Giving node more RAM can help for large codebases. ```sh @@ -37,6 +43,9 @@ Options: --exclude Run recommended rules EXCLUDING this comma-seperated list --fix Whether to run fixer --format What eslint report format to use (default: "stylish") + --no-cache Disables eslint caching + --v4 Run v3 to v4 codemods + --v6 Run v5 to v6 codemods -h, --help display help for command ``` @@ -45,4 +54,5 @@ Options: These rules are based off the breaking change notes for React. Each rule links the breaking change patternfly-react PR in case you want to better understand the change. Also, each rule makes sure you're using a PatternFly component before running. Some rules will add either a comment (`/* data-codemods */`) or data attribute (`data-codemods="true"`) in order to prevent certain other rules from applying an unnecessary fix. +These `data-codemods` attributes and comments can be removed by our `data-codemods-cleanup` rule. You should run this rule only once, after you finish running the general codemods, by adding the `--only data-codemods-cleanup` option.