Skip to content

Commit 05f3e13

Browse files
adamviktoraEric Olkowski
andauthored
feat(componentGroups): rename InvalidObjectProps to MissingPageProps (#799)
* chore: add @typescript-eslint/utils package * feat(helpers): renameInterface helper * feat(componentGroups): InvalidObjectProps rename to MissingPageProps * fix(renameInterface helper): add TSInterfaceHeritage * feat(componentGroups mod): add interface example * fix: PR review * fix(renameInterface): rename the component directory too * Missed rebase files --------- Co-authored-by: Eric Olkowski <[email protected]>
1 parent 6c8752a commit 05f3e13

10 files changed

+409
-1
lines changed

packages/eslint-plugin-pf-codemods/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"devDependencies": {
2929
"@types/eslint": "^8.56.0",
3030
"@types/estree-jsx": "^1.0.4",
31+
"@typescript-eslint/utils": "^8.12.2",
3132
"typescript": "^5.4.2"
3233
}
3334
}

packages/eslint-plugin-pf-codemods/src/rules/helpers/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,13 @@ export * from "./isReactIcon";
2828
export * from "./JSXAttributes";
2929
export * from "./makeJSXElementSelfClosing";
3030
export * from "./nodeMatches/checkMatchingImportDeclaration";
31+
export * from "./nodeMatches/checkMatchingImportSpecifier";
3132
export * from "./nodeMatches/checkMatchingJSXOpeningElement";
3233
export * from "./pfPackageMatches";
3334
export * from "./propertyNameMatches";
3435
export * from "./removeElement";
3536
export * from "./removeEmptyLineAfter";
3637
export * from "./removePropertiesFromObjectExpression";
38+
export * from "./renameComponent";
39+
export * from "./renameInterface";
3740
export * from "./renameProps";
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { ImportSpecifier } from "estree-jsx";
2+
3+
/** Used to check whether the current ImportSpecifier node matches at least 1 of the import specifiers. */
4+
export function checkMatchingImportSpecifier(
5+
node: ImportSpecifier,
6+
imports: ImportSpecifier | ImportSpecifier[]
7+
) {
8+
if (Array.isArray(imports)) {
9+
return imports.some(
10+
(specifier) => specifier.imported.name === node.imported.name
11+
);
12+
}
13+
14+
return imports.imported.name === node.imported.name;
15+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { Rule } from "eslint";
2+
import { TSESTree } from "@typescript-eslint/utils";
3+
import { Identifier, ImportDeclaration, ImportSpecifier } from "estree-jsx";
4+
import {
5+
checkMatchingImportSpecifier,
6+
getFromPackage,
7+
pfPackageMatches,
8+
} from ".";
9+
10+
interface Renames {
11+
[currentName: string]: string;
12+
}
13+
14+
function formatDefaultMessage(oldName: string, newName: string) {
15+
return `${oldName} has been renamed to ${newName}.`;
16+
}
17+
18+
export function renameInterface(
19+
interfaceRenames: Renames,
20+
componentRenames: Renames,
21+
packageName = "@patternfly/react-core"
22+
) {
23+
return function (context: Rule.RuleContext) {
24+
const oldNames = Object.keys(interfaceRenames);
25+
const { imports } = getFromPackage(context, packageName, oldNames);
26+
27+
if (imports.length === 0) {
28+
return {};
29+
}
30+
31+
const shouldRenameIdentifier = (identifier: Identifier) => {
32+
const matchingImport = imports.find(
33+
(specifier) => specifier.local.name === identifier.name
34+
);
35+
36+
if (!matchingImport) {
37+
return false;
38+
}
39+
40+
return matchingImport.local.name === matchingImport.imported.name;
41+
};
42+
43+
const replaceIdentifier = (identifier: Identifier) => {
44+
const oldName = identifier.name;
45+
const newName = interfaceRenames[oldName];
46+
47+
context.report({
48+
node: identifier,
49+
message: formatDefaultMessage(oldName, newName),
50+
fix(fixer) {
51+
return fixer.replaceText(identifier, newName);
52+
},
53+
});
54+
};
55+
56+
return {
57+
ImportDeclaration(node: ImportDeclaration) {
58+
if (!pfPackageMatches(packageName, node.source.value)) {
59+
return;
60+
}
61+
for (const oldName of Object.keys(componentRenames)) {
62+
const newName = componentRenames[oldName];
63+
const importSource = node.source.raw;
64+
const importSourceHasComponentName = importSource?.includes(oldName);
65+
const newImportDeclaration = importSource?.replace(oldName, newName);
66+
67+
if (newImportDeclaration && importSourceHasComponentName) {
68+
context.report({
69+
node,
70+
message: formatDefaultMessage(oldName, newName),
71+
fix: (fixer) =>
72+
fixer.replaceText(node.source, newImportDeclaration),
73+
});
74+
}
75+
}
76+
},
77+
ImportSpecifier(node: ImportSpecifier) {
78+
if (!checkMatchingImportSpecifier(node, imports)) {
79+
return;
80+
}
81+
82+
const oldName = node.imported.name;
83+
const newName = interfaceRenames[oldName];
84+
85+
context.report({
86+
node,
87+
message: formatDefaultMessage(oldName, newName),
88+
fix(fixer) {
89+
return fixer.replaceText(node.imported, newName);
90+
},
91+
});
92+
},
93+
TSTypeReference(node: TSESTree.TSTypeReference) {
94+
if (node.typeName.type === "Identifier") {
95+
shouldRenameIdentifier(node.typeName) &&
96+
replaceIdentifier(node.typeName);
97+
}
98+
},
99+
TSInterfaceHeritage(node: TSESTree.TSInterfaceHeritage) {
100+
if (node.expression.type === "Identifier") {
101+
shouldRenameIdentifier(node.expression) &&
102+
replaceIdentifier(node.expression);
103+
}
104+
},
105+
};
106+
};
107+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
### component-groups-invalidObjectProps-rename-to-missingPageProps [(react-component-groups/#313)](https://github.com/patternfly/react-component-groups/pull/313)
2+
3+
In react-component-groups, we've renamed InvalidObjectProps interface to MissingPageProps
4+
5+
#### Examples
6+
7+
In:
8+
9+
```jsx
10+
%inputExample%
11+
```
12+
13+
Out:
14+
15+
```jsx
16+
%outputExample%
17+
```
18+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
const ruleTester = require("../../ruletester");
2+
import * as rule from "./component-groups-invalidObjectProps-rename-to-missingPageProps";
3+
4+
const message = `InvalidObjectProps has been renamed to MissingPageProps.`;
5+
const componentMessage = `InvalidObject has been renamed to MissingPage.`;
6+
7+
ruleTester.run(
8+
"component-groups-invalidObjectProps-rename-to-missingPageProps",
9+
rule,
10+
{
11+
valid: [
12+
// missing import
13+
{
14+
code: `const props: InvalidObjectProps;`,
15+
},
16+
// import from wrong package
17+
{
18+
code: `import { InvalidObjectProps } from '@patternfly/react-core';`,
19+
},
20+
// import of other props
21+
{
22+
code: `import { SomeOtherProps } from '@patternfly/react-component-groups';`,
23+
},
24+
],
25+
invalid: [
26+
{
27+
code: `import { InvalidObjectProps, SomethingElse } from '@patternfly/react-component-groups';
28+
const props: InvalidObjectProps;
29+
const otherProps = props as InvalidObjectProps;
30+
interface CustomProps extends InvalidObjectProps {};`,
31+
output: `import { MissingPageProps, SomethingElse } from '@patternfly/react-component-groups';
32+
const props: MissingPageProps;
33+
const otherProps = props as MissingPageProps;
34+
interface CustomProps extends MissingPageProps {};`,
35+
errors: [
36+
{
37+
message,
38+
type: "ImportSpecifier",
39+
},
40+
{
41+
message,
42+
type: "Identifier",
43+
},
44+
{
45+
message,
46+
type: "Identifier",
47+
},
48+
{
49+
message,
50+
type: "Identifier",
51+
},
52+
],
53+
},
54+
// named import with alias
55+
{
56+
code: `import { InvalidObjectProps as InvObjProps } from '@patternfly/react-component-groups';
57+
const props: InvObjProps;`,
58+
output: `import { MissingPageProps as InvObjProps } from '@patternfly/react-component-groups';
59+
const props: InvObjProps;`,
60+
errors: [
61+
{
62+
message,
63+
type: "ImportSpecifier",
64+
},
65+
],
66+
},
67+
// imports from dist
68+
{
69+
code: `import { InvalidObjectProps } from '@patternfly/react-component-groups/dist/cjs/InvalidObject';`,
70+
output: `import { MissingPageProps } from '@patternfly/react-component-groups/dist/cjs/MissingPage';`,
71+
errors: [
72+
{
73+
message: componentMessage,
74+
type: "ImportDeclaration",
75+
},
76+
{
77+
message,
78+
type: "ImportSpecifier",
79+
},
80+
],
81+
},
82+
{
83+
code: `import { InvalidObjectProps } from '@patternfly/react-component-groups/dist/esm/InvalidObject';`,
84+
output: `import { MissingPageProps } from '@patternfly/react-component-groups/dist/esm/MissingPage';`,
85+
errors: [
86+
{
87+
message: componentMessage,
88+
type: "ImportDeclaration",
89+
},
90+
{
91+
message,
92+
type: "ImportSpecifier",
93+
},
94+
],
95+
},
96+
{
97+
code: `import { InvalidObjectProps } from '@patternfly/react-component-groups/dist/dynamic/InvalidObject';`,
98+
output: `import { MissingPageProps } from '@patternfly/react-component-groups/dist/dynamic/MissingPage';`,
99+
errors: [
100+
{
101+
message: componentMessage,
102+
type: "ImportDeclaration",
103+
},
104+
{
105+
message,
106+
type: "ImportSpecifier",
107+
},
108+
],
109+
},
110+
],
111+
}
112+
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { renameInterface } from "../../helpers";
2+
3+
// https://github.com/patternfly/react-component-groups/pull/313
4+
module.exports = {
5+
meta: { fixable: "code" },
6+
create: renameInterface(
7+
{
8+
InvalidObjectProps: "MissingPageProps",
9+
},
10+
{
11+
InvalidObject: "MissingPage",
12+
},
13+
"@patternfly/react-component-groups"
14+
),
15+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { InvalidObjectProps } from "@patternfly/react-component-groups";
2+
3+
const props: InvalidObjectProps;
4+
interface CustomProps extends InvalidObjectProps {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { MissingPageProps } from "@patternfly/react-component-groups";
2+
3+
const props: MissingPageProps;
4+
interface CustomProps extends MissingPageProps {}

0 commit comments

Comments
 (0)