Skip to content

Commit

Permalink
fix(EmptyState): components no longer exported (#642)
Browse files Browse the repository at this point in the history
* fix(EmptyState): components no longer exported

* PR feedback
  • Loading branch information
thatblindgeye authored May 31, 2024
1 parent f9bbb92 commit f6f3028
Show file tree
Hide file tree
Showing 5 changed files with 288 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
### emptyState-nonExported-components [(#10364)](https://github.com/patternfly/patternfly-react/pull/10364)

EmptyStateHeader and EmptyStateIcon are no longer exported by PatternFly. This rule will only apply fixes for exports of these components, as our rule for unused imports will handle applying fixes for imports.

#### Examples

In:

```jsx
%inputExample%
```

Out:

```jsx
%outputExample%
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
const ruleTester = require("../../ruletester");
import * as rule from "./emptyState-nonExported-components";

ruleTester.run("emptyState-nonExported-components", rule, {
valid: [
{
code: `import { EmptyStateHeader } from '@some/other/package';`,
},
{
code: `import { EmptyStateIcon } from '@some/other/package';`,
},
],
invalid: [
{
code: `import { EmptyStateHeader } from '@patternfly/react-core';`,
output: `import { EmptyStateHeader } from '@patternfly/react-core';`,
errors: [
{
message: `EmptyStateHeader is no longer exported by PatternFly. This rule will not fix any imports, as our cleanup rule handles removal of unused imports.`,
type: "ImportSpecifier",
},
],
},
{
code: `import { EmptyStateHeader as CustomHeader } from '@patternfly/react-core';`,
output: `import { EmptyStateHeader as CustomHeader } from '@patternfly/react-core';`,
errors: [
{
message: `EmptyStateHeader is no longer exported by PatternFly. This rule will not fix any imports, as our cleanup rule handles removal of unused imports.`,
type: "ImportSpecifier",
},
],
},
{
code: `export { EmptyStateHeader } from '@patternfly/react-core';`,
output: ``,
errors: [
{
message: `EmptyStateHeader is no longer exported by PatternFly.`,
type: "ExportNamedDeclaration",
},
],
},
{
code: `export { EmptyStateHeader as CustomHeader } from '@patternfly/react-core';`,
output: ``,
errors: [
{
message: `EmptyStateHeader is no longer exported by PatternFly.`,
type: "ExportNamedDeclaration",
},
],
},
{
code: `export { EmptyStateHeader, EmptyStateIcon } from '@patternfly/react-core';`,
output: ``,
errors: [
{
message: `EmptyStateHeader and EmptyStateIcon are no longer exported by PatternFly.`,
type: "ExportNamedDeclaration",
},
],
},
{
code: `export { EmptyStateHeader, Button, EmptyStateIcon } from '@patternfly/react-core';`,
output: `export { Button } from '@patternfly/react-core';`,
errors: [
{
message: `EmptyStateHeader is no longer exported by PatternFly.`,
type: "ExportNamedDeclaration",
},
{
message: `EmptyStateIcon is no longer exported by PatternFly.`,
type: "ExportNamedDeclaration",
},
],
},
{
code: `import { EmptyStateHeader } from '@patternfly/react-core';export { EmptyStateHeader };`,
output: `import { EmptyStateHeader } from '@patternfly/react-core';`,
errors: [
{
message: `EmptyStateHeader is no longer exported by PatternFly. This rule will not fix any imports, as our cleanup rule handles removal of unused imports.`,
type: "ImportSpecifier",
},
{
message: `EmptyStateHeader is no longer exported by PatternFly.`,
type: "ExportNamedDeclaration",
},
],
},
{
code: `import { EmptyStateHeader as CustomHeader } from '@patternfly/react-core';export { CustomHeader };`,
output: `import { EmptyStateHeader as CustomHeader } from '@patternfly/react-core';`,
errors: [
{
message: `EmptyStateHeader is no longer exported by PatternFly. This rule will not fix any imports, as our cleanup rule handles removal of unused imports.`,
type: "ImportSpecifier",
},
{
message: `CustomHeader is no longer exported by PatternFly.`,
type: "ExportNamedDeclaration",
},
],
},
{
code: `import { EmptyStateHeader } from '@patternfly/react-core';export { EmptyStateHeader as CustomHeader };`,
output: `import { EmptyStateHeader } from '@patternfly/react-core';`,
errors: [
{
message: `EmptyStateHeader is no longer exported by PatternFly. This rule will not fix any imports, as our cleanup rule handles removal of unused imports.`,
type: "ImportSpecifier",
},
{
message: `EmptyStateHeader is no longer exported by PatternFly.`,
type: "ExportNamedDeclaration",
},
],
},
{
code: `import { EmptyStateHeader } from '@patternfly/react-core';export default EmptyStateHeader;`,
output: `import { EmptyStateHeader } from '@patternfly/react-core';`,
errors: [
{
message: `EmptyStateHeader is no longer exported by PatternFly. This rule will not fix any imports, as our cleanup rule handles removal of unused imports.`,
type: "ImportSpecifier",
},
{
message: `EmptyStateHeader is no longer exported by PatternFly.`,
type: "ExportDefaultDeclaration",
},
],
},
{
code: `import { EmptyStateHeader as CustomHeader } from '@patternfly/react-core';export default CustomHeader;`,
output: `import { EmptyStateHeader as CustomHeader } from '@patternfly/react-core';`,
errors: [
{
message: `EmptyStateHeader is no longer exported by PatternFly. This rule will not fix any imports, as our cleanup rule handles removal of unused imports.`,
type: "ImportSpecifier",
},
{
message: `CustomHeader is no longer exported by PatternFly.`,
type: "ExportDefaultDeclaration",
},
],
},
],
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { Rule } from "eslint";
import {
ImportSpecifier,
ExportNamedDeclaration,
ExportDefaultDeclaration,
} from "estree-jsx";
import { getFromPackage } from "../../helpers";

// https://github.com/patternfly/patternfly-react/pull/10364
module.exports = {
meta: { fixable: "code" },
create: function (context: Rule.RuleContext) {
const { imports, exports } = getFromPackage(
context,
"@patternfly/react-core"
);
const emptyStateComponents = ["EmptyStateHeader", "EmptyStateIcon"];
const emptyStateImports = imports.filter((specifier) =>
emptyStateComponents.includes(specifier.imported.name)
);
const emptyStateExports = exports.filter((specifier) =>
emptyStateComponents.includes(specifier.local.name)
);

if (!emptyStateImports.length && !emptyStateExports.length) {
return {};
}

return {
ImportSpecifier(node: ImportSpecifier) {
if (!emptyStateImports.length) {
return;
}

emptyStateImports.forEach((esImport) => {
context.report({
node,
message: `${esImport.imported.name} is no longer exported by PatternFly. This rule will not fix any imports, as our cleanup rule handles removal of unused imports.`,
});
});
},
ExportNamedDeclaration(node: ExportNamedDeclaration) {
const exportsToRemove = node.specifiers.filter(
(spec) =>
(emptyStateComponents.includes(spec.local.name) &&
node.source?.value === "@patternfly/react-core") ||
emptyStateImports.some((imp) => imp.local.name === spec.local.name)
);
if (!exportsToRemove.length) {
return;
}

if (exportsToRemove.length === node.specifiers.length) {
const validExportNames = exportsToRemove
.map((exportToRemove) => exportToRemove.local.name)
.join(" and ");
context.report({
node,
message: `${validExportNames} ${
exportsToRemove.length > 1 ? "are" : "is"
} no longer exported by PatternFly.`,
fix(fixer) {
return fixer.remove(node);
},
});

return;
}

exportsToRemove.forEach((exportToRemove) => {
const { range } = exportToRemove;
const tokenBefore = context
.getSourceCode()
.getTokenBefore(exportToRemove);
const isCommaBefore = tokenBefore?.value === ",";
const tokenAfter = context
.getSourceCode()
.getTokenAfter(exportToRemove);
const isCommaAfter = tokenAfter?.value === ",";
const rangeStartValue = isCommaBefore
? tokenBefore.range[0]
: range?.[0];
const rangeEndValue = isCommaAfter ? tokenAfter.range[1] : range?.[1];
context.report({
node,
message: `${exportToRemove.local.name} is no longer exported by PatternFly.`,
fix(fixer) {
return rangeStartValue && rangeEndValue
? fixer.removeRange([rangeStartValue, rangeEndValue])
: [];
},
});
});
},
ExportDefaultDeclaration(node: ExportDefaultDeclaration) {
const exportName =
node.declaration.type === "Identifier" && node.declaration.name;
const isEmptyStateDefaultExport = emptyStateImports.some(
(imp) => imp.local.name === exportName
);

if (isEmptyStateDefaultExport) {
context.report({
node,
message: `${exportName} is no longer exported by PatternFly.`,
fix(fixer) {
return fixer.remove(node);
},
});
}
},
};
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { EmptyStateHeader, EmptyStateIcon } from "@patternfly/react-core";

export { EmptyStateHeader, EmptyStateIcon };
export {
EmptyStateHeader as CustomESHeader,
EmptyStateIcon as CustomESIcon,
} from "@patternfly/react-core";
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import { EmptyStateHeader, EmptyStateIcon } from "@patternfly/react-core";

0 comments on commit f6f3028

Please sign in to comment.