Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(componentGroups): rename InvalidObjectProps to MissingPageProps #799

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/eslint-plugin-pf-codemods/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
},
"devDependencies": {
"@types/eslint": "^8.56.0",
"@types/estree-jsx": "^1.0.4"
"@types/estree-jsx": "^1.0.4",
"@typescript-eslint/utils": "^8.12.2"
}
}
3 changes: 3 additions & 0 deletions packages/eslint-plugin-pf-codemods/src/rules/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,12 @@ export * from "./isReactIcon";
export * from "./JSXAttributes";
export * from "./makeJSXElementSelfClosing";
export * from "./nodeMatches/checkMatchingImportDeclaration";
export * from "./nodeMatches/checkMatchingImportSpecifier";
export * from "./nodeMatches/checkMatchingJSXOpeningElement";
export * from "./pfPackageMatches";
export * from "./removeElement";
export * from "./removeEmptyLineAfter";
export * from "./removePropertiesFromObjectExpression";
export * from "./renameComponent";
export * from "./renameInterface";
export * from "./renameProps";
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { ImportSpecifier } from "estree-jsx";

/** Used to check whether the current ImportSpecifier node matches at least 1 of the import specifiers. */
export function checkMatchingImportSpecifier(
node: ImportSpecifier,
imports: ImportSpecifier | ImportSpecifier[]
) {
if (Array.isArray(imports)) {
return imports.some(
(specifier) => specifier.imported.name === node.imported.name
);
}

return imports.imported.name === node.imported.name;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { Rule } from "eslint";
import { TSESTree } from "@typescript-eslint/utils";
import { Identifier, ImportSpecifier } from "estree-jsx";
import { getFromPackage } from "./getFromPackage";
import { checkMatchingImportSpecifier } from "./nodeMatches/checkMatchingImportSpecifier";

interface InterfaceRenames {
[currentName: string]: string;
}

function formatDefaultMessage(oldName: string, newName: string) {
return `${oldName} has been renamed to ${newName}.`;
}
Comment on lines +11 to +13
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a blocker, but wondering if we should add a directory to helpers for these sort of default message functions. defaultMessages as the directory -> renameIdentifier.tsx as this helper for example. Or just yanking this out as its own individual helper to be reused elsewhere.


export function renameInterface(
renames: InterfaceRenames,
packageName = "@patternfly/react-core"
) {
return function (context: Rule.RuleContext) {
const oldNames = Object.keys(renames);
const { imports } = getFromPackage(context, packageName, oldNames);

if (imports.length === 0) {
return {};
}

const shouldRenameIdentifier = (identifier: Identifier) => {
const matchingImport = imports.find(
(specifier) => specifier.local.name === identifier.name
);

if (!matchingImport) {
return false;
}

return matchingImport.local.name === matchingImport.imported.name;
};

const replaceIdentifier = (identifier: Identifier) => {
const oldName = identifier.name;
const newName = renames[oldName];

context.report({
node: identifier,
message: formatDefaultMessage(oldName, newName),
fix(fixer) {
return fixer.replaceText(identifier, newName);
},
});
};

return {
ImportSpecifier(node: ImportSpecifier) {
if (!checkMatchingImportSpecifier(node, imports)) {
return;
}

const oldName = node.imported.name;
const newName = renames[oldName];

context.report({
node,
message: formatDefaultMessage(oldName, newName),
fix(fixer) {
return fixer.replaceText(node.imported, newName);
},
});
},
TSTypeReference(node: TSESTree.TSTypeReference) {
if (node.typeName.type === "Identifier") {
shouldRenameIdentifier(node.typeName) &&
replaceIdentifier(node.typeName);
}
},
TSInterfaceHeritage(node: TSESTree.TSInterfaceHeritage) {
if (node.expression.type === "Identifier") {
shouldRenameIdentifier(node.expression) &&
replaceIdentifier(node.expression);
}
},
};
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
### component-groups-invalidObjectProps-rename-to-missingPageProps [(react-component-groups/#313)](https://github.com/patternfly/react-component-groups/pull/313)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't a blocker for this PR, but sorta wondering if this should really be its own separate rule or included as part of the pre-existing one (with a rename maybe). Really the question is, "should there be a single rule dealing with anything InvalidObject rename related?"

Doing so would require a little refactoring both to the existing rule and the new helper here possibly, but also depends whether doing so would just bloat the existing rule too much. Having them separate may be more clear and understandable than trying to combine them, but curious what you think.

Copy link
Contributor Author

@adamviktora adamviktora Nov 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"should there be a single rule dealing with anything InvalidObject rename related?"

I think ideally there should be just a single rule, but...

Doing so would require a little refactoring

I am afraid it could end up being "a lot of" refactoring, given that the current renameComponent helper already returns a function. But now that I am thinking about it, we could combine it and do something like:

create: (context: Rule.RuleContext) => {...renameComponent(args)(context), ...renameInterface(args)(context)}

But maybe it will not be that easy


In react-component-groups, we've renamed InvalidObjectProps interface to MissingPageProps

#### Examples

In:

```jsx
%inputExample%
```

Out:

```jsx
%outputExample%
```

Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
const ruleTester = require("../../ruletester");
import * as rule from "./component-groups-invalidObjectProps-rename-to-missingPageProps";

const message = `InvalidObjectProps has been renamed to MissingPageProps.`;

ruleTester.run(
"component-groups-invalidObjectProps-rename-to-missingPageProps",
rule,
{
valid: [
// missing import
{
code: `const props: InvalidObjectProps;`,
},
// import from wrong package
{
code: `import { InvalidObjectProps } from '@patternfly/react-core';`,
},
// import of other props
{
code: `import { SomeOtherProps } from '@patternfly/react-component-groups';`,
},
],
invalid: [
{
code: `import { InvalidObjectProps, SomethingElse } from '@patternfly/react-component-groups';
const props: InvalidObjectProps;
const otherProps = props as InvalidObjectProps;
interface CustomProps extends InvalidObjectProps {};`,
output: `import { MissingPageProps, SomethingElse } from '@patternfly/react-component-groups';
const props: MissingPageProps;
const otherProps = props as MissingPageProps;
interface CustomProps extends MissingPageProps {};`,
errors: [
{
message,
type: "ImportSpecifier",
},
{
message,
type: "Identifier",
},
{
message,
type: "Identifier",
},
{
message,
type: "Identifier",
},
],
},
// named import with alias
{
code: `import { InvalidObjectProps as InvObjProps } from '@patternfly/react-component-groups';
const props: InvObjProps;`,
output: `import { MissingPageProps as InvObjProps } from '@patternfly/react-component-groups';
const props: InvObjProps;`,
errors: [
{
message,
type: "ImportSpecifier",
},
],
},
// imports from dist
{
code: `import { InvalidObjectProps } from '@patternfly/react-component-groups/dist/cjs/InvalidObject';`,
output: `import { MissingPageProps } from '@patternfly/react-component-groups/dist/cjs/InvalidObject';`,
errors: [
{
message,
type: "ImportSpecifier",
},
],
},
{
code: `import { InvalidObjectProps } from '@patternfly/react-component-groups/dist/esm/InvalidObject';`,
output: `import { MissingPageProps } from '@patternfly/react-component-groups/dist/esm/InvalidObject';`,
errors: [
{
message,
type: "ImportSpecifier",
},
],
},
{
code: `import { InvalidObjectProps } from '@patternfly/react-component-groups/dist/dynamic/InvalidObject';`,
output: `import { MissingPageProps } from '@patternfly/react-component-groups/dist/dynamic/InvalidObject';`,
errors: [
{
message,
type: "ImportSpecifier",
},
],
},
],
}
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { renameInterface } from "../../helpers";

// https://github.com/patternfly/react-component-groups/pull/313
module.exports = {
meta: { fixable: "code" },
create: renameInterface(
{
InvalidObjectProps: "MissingPageProps",
},
"@patternfly/react-component-groups"
),
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { InvalidObjectProps } from "@patternfly/react-component-groups";

const props: InvalidObjectProps;
interface CustomProps extends InvalidObjectProps {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { MissingPageProps } from "@patternfly/react-component-groups";

const props: MissingPageProps;
interface CustomProps extends MissingPageProps {}
Loading
Loading