diff --git a/packages/eslint-plugin-pf-codemods/src/rules/v6/textReplaceWithContent/text-replace-with-content.test.ts b/packages/eslint-plugin-pf-codemods/src/rules/v6/textReplaceWithContent/text-replace-with-content.test.ts
index 33294b28b..d921301fa 100644
--- a/packages/eslint-plugin-pf-codemods/src/rules/v6/textReplaceWithContent/text-replace-with-content.test.ts
+++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/textReplaceWithContent/text-replace-with-content.test.ts
@@ -1,17 +1,21 @@
-const ruleTester = require('../../ruletester');
-import * as rule from './text-replace-with-content';
+const ruleTester = require("../../ruletester");
+import * as rule from "./text-replace-with-content";
const errorMessage = `We have replaced Text, TextContent, TextList and TextListItem with one Content component. Running this fix will change all of those components names to Content and add a \`component\` prop where necessary.`;
const importDeclarationError = {
message: errorMessage,
- type: 'ImportDeclaration',
+ type: "ImportDeclaration",
};
const jsxElementError = {
message: errorMessage,
- type: 'JSXElement',
+ type: "JSXElement",
+};
+const identifierError = {
+ message: errorMessage,
+ type: "Identifier",
};
-ruleTester.run('text-replace-with-content', rule, {
+ruleTester.run("text-replace-with-content", rule, {
valid: [
{
code: ``,
@@ -97,6 +101,16 @@ ruleTester.run('text-replace-with-content', rule, {
output: `import { Content } from '@patternfly/react-core'; `,
errors: [importDeclarationError, jsxElementError],
},
+ {
+ code: `import { TextVariants } from '@patternfly/react-core'; const foo = TextVariants.h1`,
+ output: `import { ContentVariants } from '@patternfly/react-core'; const foo = ContentVariants.h1`,
+ errors: [importDeclarationError, identifierError],
+ },
+ {
+ code: `import { TextProps } from '@patternfly/react-core'; interface Foo extends TextProps {}`,
+ output: `import { ContentProps } from '@patternfly/react-core'; interface Foo extends ContentProps {}`,
+ errors: [importDeclarationError, identifierError],
+ },
// with alias
{
code: `import { Text as PFText } from '@patternfly/react-core'; Abc`,
diff --git a/packages/eslint-plugin-pf-codemods/src/rules/v6/textReplaceWithContent/text-replace-with-content.ts b/packages/eslint-plugin-pf-codemods/src/rules/v6/textReplaceWithContent/text-replace-with-content.ts
index 64137fa89..30dfca8e1 100644
--- a/packages/eslint-plugin-pf-codemods/src/rules/v6/textReplaceWithContent/text-replace-with-content.ts
+++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/textReplaceWithContent/text-replace-with-content.ts
@@ -1,47 +1,66 @@
-import { Rule } from 'eslint';
+import { Rule } from "eslint";
import {
ImportDeclaration,
ImportSpecifier,
JSXElement,
JSXIdentifier,
-} from 'estree-jsx';
-import { getAttribute, getFromPackage, pfPackageMatches } from '../../helpers';
+} from "estree-jsx";
+import {
+ getAttribute,
+ getFromPackage,
+ IdentifierWithParent,
+ pfPackageMatches,
+} from "../../helpers";
// https://github.com/patternfly/patternfly-react/pull/10643
module.exports = {
- meta: { fixable: 'code' },
+ meta: { fixable: "code" },
create: function (context: Rule.RuleContext) {
- const { imports } = getFromPackage(context, '@patternfly/react-core');
+ const { imports } = getFromPackage(context, "@patternfly/react-core");
+
+ const textComponents = ["Text", "TextContent", "TextList", "TextListItem"];
- const textComponents = ['Text', 'TextContent', 'TextList', 'TextListItem'];
+ const nonComponentTextIdentifiers = ["TextProps", "TextVariants"];
- const componentImports = imports.filter((specifier) =>
- textComponents.includes(specifier.imported.name)
+ const allTextIdentifiers = [
+ ...textComponents,
+ ...nonComponentTextIdentifiers,
+ ];
+
+ const textImports = imports.filter((specifier) =>
+ allTextIdentifiers.includes(specifier.imported.name)
);
const errorMessage =
- 'We have replaced Text, TextContent, TextList and TextListItem with one Content component. Running this fix will change all of those components names to Content and add a `component` prop where necessary.';
+ "We have replaced Text, TextContent, TextList and TextListItem with one Content component. Running this fix will change all of those components names to Content and add a `component` prop where necessary.";
- return !componentImports.length
+ return !textImports.length
? {}
: {
ImportDeclaration(node: ImportDeclaration) {
- if (pfPackageMatches('@patternfly/react-core', node.source.value)) {
+ if (pfPackageMatches("@patternfly/react-core", node.source.value)) {
const specifierToReplace = node.specifiers.find(
(specifier) =>
- specifier.type === 'ImportSpecifier' &&
- textComponents.includes(specifier.imported.name)
+ specifier.type === "ImportSpecifier" &&
+ allTextIdentifiers.includes(specifier.imported.name)
) as ImportSpecifier;
if (!specifierToReplace) {
return;
}
+ const specifierName = specifierToReplace.imported.name;
+ const newText = nonComponentTextIdentifiers.includes(
+ specifierName
+ )
+ ? specifierName.replace("Text", "Content")
+ : "Content";
+
context.report({
node,
message: errorMessage,
fix(fixer) {
- return fixer.replaceText(specifierToReplace, 'Content');
+ return fixer.replaceText(specifierToReplace, newText);
},
});
}
@@ -50,8 +69,8 @@ module.exports = {
const openingElement = node.openingElement;
const closingElement = node.closingElement;
- if (openingElement.name.type === 'JSXIdentifier') {
- const componentImport = componentImports.find(
+ if (openingElement.name.type === "JSXIdentifier") {
+ const componentImport = textImports.find(
(imp) =>
imp.local.name === (openingElement.name as JSXIdentifier).name
);
@@ -61,12 +80,12 @@ module.exports = {
}
const componentName = componentImport.imported.name as
- | 'Text'
- | 'TextContent'
- | 'TextList'
- | 'TextListItem';
+ | "Text"
+ | "TextContent"
+ | "TextList"
+ | "TextListItem";
- const componentAttribute = getAttribute(node, 'component');
+ const componentAttribute = getAttribute(node, "component");
context.report({
node,
@@ -74,11 +93,11 @@ module.exports = {
fix(fixer) {
const fixes = [];
- if (!componentAttribute && componentName !== 'TextContent') {
+ if (!componentAttribute && componentName !== "TextContent") {
const componentMap = {
- Text: 'p',
- TextList: 'ul',
- TextListItem: 'li',
+ Text: "p",
+ TextList: "ul",
+ TextListItem: "li",
};
fixes.push(
@@ -89,31 +108,31 @@ module.exports = {
);
}
- if (componentName === 'TextContent') {
- const isVisitedAttribute = getAttribute(node, 'isVisited');
+ if (componentName === "TextContent") {
+ const isVisitedAttribute = getAttribute(node, "isVisited");
if (isVisitedAttribute) {
fixes.push(
fixer.replaceText(
isVisitedAttribute.name,
- 'isVisitedLink'
+ "isVisitedLink"
)
);
}
}
- if (componentName === 'TextList') {
- const isPlainAttribute = getAttribute(node, 'isPlain');
+ if (componentName === "TextList") {
+ const isPlainAttribute = getAttribute(node, "isPlain");
if (isPlainAttribute) {
fixes.push(
- fixer.replaceText(isPlainAttribute.name, 'isPlainList')
+ fixer.replaceText(isPlainAttribute.name, "isPlainList")
);
}
}
- fixes.push(fixer.replaceText(openingElement.name, 'Content'));
+ fixes.push(fixer.replaceText(openingElement.name, "Content"));
if (closingElement) {
fixes.push(
- fixer.replaceText(closingElement.name, 'Content')
+ fixer.replaceText(closingElement.name, "Content")
);
}
@@ -122,6 +141,37 @@ module.exports = {
});
}
},
+ Identifier(node: IdentifierWithParent) {
+ const textImport = textImports.find(
+ (imp) => imp.local.name === node.name
+ );
+
+ if (!textImport) {
+ return;
+ }
+
+ const isComponent = textComponents.includes(node.name);
+
+ // the JSXElement fixer will handle the text components
+ if (isComponent) {
+ return;
+ }
+
+ // imports are handled by the ImportDeclaration fixer
+ if (node.parent && node.parent.type === "ImportSpecifier") {
+ return;
+ }
+
+ const newText = node.name.replace("Text", "Content");
+
+ context.report({
+ node,
+ message: errorMessage,
+ fix(fixer) {
+ return [fixer.replaceText(node, newText)];
+ },
+ });
+ },
};
},
};
diff --git a/packages/eslint-plugin-pf-codemods/src/rules/v6/textReplaceWithContent/textReplaceWithContentInput.tsx b/packages/eslint-plugin-pf-codemods/src/rules/v6/textReplaceWithContent/textReplaceWithContentInput.tsx
index 4a71d68be..e1d5be78a 100644
--- a/packages/eslint-plugin-pf-codemods/src/rules/v6/textReplaceWithContent/textReplaceWithContentInput.tsx
+++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/textReplaceWithContent/textReplaceWithContentInput.tsx
@@ -3,23 +3,30 @@ import {
TextContent,
TextList,
TextListItem,
+ TextProps,
+ TextVariants,
} from "@patternfly/react-core";
-export const TextReplaceWithContentInput = () => (
- <>
- Abc
- Abc
- Abc
- Abc
- Abc
- Abc
- Abc
- Abc
- Abc
-
- A
- B
- C
-
- >
-);
+export const TextReplaceWithContentInput = () => {
+ interface Foo extends TextProps {}
+
+ return (
+ <>
+ Abc
+ Abc
+ Abc
+ Abc
+ Abc
+ Abc
+ Abc
+ Abc
+ Abc
+ Abc
+
+ A
+ B
+ C
+
+ >
+ );
+};
diff --git a/packages/eslint-plugin-pf-codemods/src/rules/v6/textReplaceWithContent/textReplaceWithContentOutput.tsx b/packages/eslint-plugin-pf-codemods/src/rules/v6/textReplaceWithContent/textReplaceWithContentOutput.tsx
index aa099bb56..745919aa0 100644
--- a/packages/eslint-plugin-pf-codemods/src/rules/v6/textReplaceWithContent/textReplaceWithContentOutput.tsx
+++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/textReplaceWithContent/textReplaceWithContentOutput.tsx
@@ -3,23 +3,30 @@ import {
Content,
Content,
Content,
+ ContentProps,
+ ContentVariants,
} from "@patternfly/react-core";
-export const TextReplaceWithContentInput = () => (
- <>
- Abc
- Abc
- Abc
- Abc
- Abc
- Abc
- Abc
- Abc
- Abc
-
- A
- B
- C
-
- >
-);
+export const TextReplaceWithContentInput = () => {
+ interface Foo extends ContentProps {}
+
+ return (
+ <>
+ Abc
+ Abc
+ Abc
+ Abc
+ Abc
+ Abc
+ Abc
+ Abc
+ Abc
+ Abc
+
+ A
+ B
+ C
+
+ >
+ );
+};