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

fix: type getVariableValue helper #796

Open
wants to merge 7 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
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
JSXEmptyExpression,
JSXFragment,
JSXOpeningElement,
MemberExpression,
} from "estree-jsx";

export function getAttribute(
Expand Down Expand Up @@ -58,7 +57,7 @@ export function getAttributeValue(
return getVariableValue(node.expression.name, variableScope, context);
}
if (node.expression.type === "MemberExpression") {
return getMemberExpression(node.expression);
return node.expression;
}
if (node.expression.type === "Literal") {
return node.expression.value;
Expand All @@ -78,15 +77,6 @@ export function getExpression(node?: JSXAttribute["value"]) {
}
}

function getMemberExpression(node: MemberExpression) {
if (!node) {
return;
}
const { object, property } = node;

return { object, property };
}

export function getVariableDeclaration(
name: string,
scope: Scope.Scope | null
Expand All @@ -103,19 +93,29 @@ export function getVariableDeclaration(
return undefined;
}

export function getVariableInit(
variableDeclaration: Scope.Variable | undefined
) {
if (!variableDeclaration || !variableDeclaration.defs.length) {
return;
}

const variableDefinition = variableDeclaration.defs[0];

if (variableDefinition.type !== "Variable") {
return;
}

return variableDefinition.node.init;
}

export function getVariableValue(
name: string,
scope: Scope.Scope | null,
context: Rule.RuleContext
) {
const variableDeclaration = getVariableDeclaration(name, scope);
if (!variableDeclaration) {
return;
}

const variableInit = variableDeclaration.defs.length
? variableDeclaration.defs[0].node.init
: undefined;
const variableInit = getVariableInit(variableDeclaration);

if (!variableInit) {
return;
Expand All @@ -131,7 +131,7 @@ export function getVariableValue(
return variableInit.value;
}
if (variableInit.type === "MemberExpression") {
return getMemberExpression(variableInit);
return variableInit;
}
if (variableInit.type === "ObjectExpression") {
return variableInit.properties;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Rule } from "eslint";
import { MemberExpression } from "estree-jsx";
import { getVariableValue } from ".";

/** Used to get a property name on an enum (MemberExpression). */
export function getEnumPropertyName(
context: Rule.RuleContext,
enumNode: MemberExpression
) {
if (enumNode.property.type === "Identifier") {
// E.g. const key = "key"; someEnum[key]
if (enumNode.computed) {
const scope = context.getSourceCode().getScope(enumNode);
const propertyName = enumNode.property.name;

return getVariableValue(propertyName, scope, context)?.toString();
}
// E.g. someEnum.key
return enumNode.property.name;
}

// E.g. someEnum["key"]
if (enumNode.property.type === "Literal") {
return enumNode.property.value?.toString();
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Rule } from "eslint";
import { JSXAttribute } from "estree-jsx";
import { getVariableDeclaration } from "./JSXAttributes";
import { getVariableDeclaration, getVariableInit } from "./JSXAttributes";

/** Used to find the node where a prop value is initially assigned, to then be passed
* as a fixer function's nodeOrToken argument. Useful for when a prop may have an inline value, e.g. `<Comp prop="value" />`, or
Expand Down Expand Up @@ -41,6 +41,6 @@ function getJSXExpressionContainerValue(
scope
);

return variableDeclaration && variableDeclaration.defs[0].node.init;
return getVariableInit(variableDeclaration);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Rule } from "eslint";
import { Property, Identifier } from "estree-jsx";
import { getVariableDeclaration } from "./JSXAttributes";
import { Property } from "estree-jsx";
import { propertyNameMatches } from "./propertyNameMatches";

/** Can be used to run logic on the specified property of an ObjectExpression or
* only if the specified property exists.
Expand All @@ -14,26 +14,9 @@ export function getObjectProperty(
return;
}

const matchedProperty = properties.find((property) => {
const isIdentifier = property.key.type === "Identifier";
const { computed } = property;

// E.g. const key = "key"; {[key]: value}
if (isIdentifier && computed) {
const scope = context.getSourceCode().getScope(property);
const propertyName = (property.key as Identifier).name;
const propertyVariable = getVariableDeclaration(propertyName, scope);
return propertyVariable?.defs[0].node.init.value === name;
}
// E.g. {key: value}
if (isIdentifier && !computed) {
return (property.key as Identifier).name === name;
}
// E.g. {"key": value} or {["key"]: value}
if (property.key.type === "Literal") {
return property.key.value === name;
}
});
const matchedProperty = properties.find((property) =>
propertyNameMatches(context, property.key, property.computed, name)
);

return matchedProperty;
}
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 @@ -8,6 +8,7 @@ export * from "./getChildJSXElementByName";
export * from "./getCodeModDataTag";
export * from "./getComponentImportName";
export * from "./getEndRange";
export * from "./getEnumPropertyName";
export * from "./getFromPackage";
export * from "./getImportedName";
export * from "./getImportPath";
Expand All @@ -22,12 +23,14 @@ export * from "./helpers";
export * from "./importAndExport";
export * from "./includesImport";
export * from "./interfaces";
export * from "./isEnumValue";
export * from "./isReactIcon";
export * from "./JSXAttributes";
export * from "./makeJSXElementSelfClosing";
export * from "./nodeMatches/checkMatchingImportDeclaration";
export * from "./nodeMatches/checkMatchingJSXOpeningElement";
export * from "./pfPackageMatches";
export * from "./propertyNameMatches";
export * from "./removeElement";
export * from "./removeEmptyLineAfter";
export * from "./removePropertiesFromObjectExpression";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { MemberExpression } from "estree-jsx";
import { propertyNameMatches } from "./propertyNameMatches";
import { Rule } from "eslint";

/** Checks whether an enum node (MemberExpression), e.g. ButtonVariant["primary"]
* has a given enumName ("ButtonVariant") and a given propertyName ("primary"), or one of given property names. */
export function isEnumValue(
context: Rule.RuleContext,
enumExpression: MemberExpression,
enumName: string,
propertyName: string | string[]
) {
if (
enumExpression?.object?.type === "Identifier" &&
enumExpression?.object?.name === enumName
) {
const nameMatches = (name: string) =>
propertyNameMatches(
context,
enumExpression.property,
enumExpression.computed,
name
);

if (Array.isArray(propertyName)) {
return propertyName.some((name) => nameMatches(name));
}

return nameMatches(propertyName);
}

return false;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Rule } from "eslint";
import { Expression, PrivateIdentifier } from "estree-jsx";
import { getVariableValue } from "./JSXAttributes";

/** Check whether a property name is of a given value.
* Property can either be of an ObjectExpression - {propName: "value"} or MemberExpression - someObject.propName */
export function propertyNameMatches(
context: Rule.RuleContext,
key: Expression | PrivateIdentifier,
computed: boolean,
name: string
) {
if (key.type === "Identifier") {
// E.g. const key = "key"; {[key]: value}; someObject[key]
if (computed) {
const scope = context.getSourceCode().getScope(key);
const propertyName = key.name;
const propertyVariableValue = getVariableValue(
propertyName,
scope,
context
);

return propertyVariableValue === name;
}
// E.g. {key: value}; someObject.key
return key.name === name;
}

// E.g. {"key": value} or {["key"]: value}; someObject["key"]
if (key.type === "Literal") {
return key.value === name;
}

return false;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Rule } from "eslint";
import { JSXElement, JSXFragment } from "estree-jsx";
import { JSXElement, JSXFragment, MemberExpression } from "estree-jsx";
import {
childrenIsEmpty,
getFromPackage,
Expand All @@ -10,6 +10,8 @@ import {
getChildJSXElementByName,
isReactIcon,
makeJSXElementSelfClosing,
propertyNameMatches,
isEnumValue,
} from "../../helpers";

// https://github.com/patternfly/patternfly-react/pull/10663
Expand Down Expand Up @@ -47,11 +49,16 @@ module.exports = {
variantProp?.value
);

const variantValueAsEnum = variantValue as MemberExpression;

const isEnumValuePlain =
buttonVariantEnumImport &&
variantValue?.object?.name ===
buttonVariantEnumImport.local.name &&
variantValue?.property.name === "plain";
!!buttonVariantEnumImport &&
isEnumValue(
context,
variantValueAsEnum,
buttonVariantEnumImport.local.name,
"plain"
);

const isPlain = variantValue === "plain" || isEnumValuePlain;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Rule } from "eslint";
import { JSXElement, Property, Literal } from "estree-jsx";
import { JSXElement, ObjectExpression, Property } from "estree-jsx";
import {
getAllImportsFromPackage,
getFromPackage,
checkMatchingJSXOpeningElement,
getAttribute,
Expand Down Expand Up @@ -54,19 +53,23 @@ module.exports = {
const selectableActionsValue = getAttributeValue(
context,
selectableActionsProp.value
);
) as ObjectExpression["properties"]; // selectableActions prop on CardHeader accepts an object
if (!selectableActionsValue) {
return;
}

const selectableActionsProperties = selectableActionsValue.filter(
(val) => val.type === "Property"
) as Property[];

const nameProperty = getObjectProperty(
context,
selectableActionsValue,
selectableActionsProperties,
"name"
);
const idProperty = getObjectProperty(
context,
selectableActionsValue,
selectableActionsProperties,
"selectableActionId"
);

Expand All @@ -92,11 +95,11 @@ module.exports = {
return [];
}
const propertiesToKeep = removePropertiesFromObjectExpression(
selectableActionsValue,
selectableActionsProperties,
validPropertiesToRemove
);
const replacementProperties = propertiesToKeep
.map((property: Property) =>
.map((property) =>
context.getSourceCode().getText(property)
)
.join(", ");
Expand All @@ -105,6 +108,11 @@ module.exports = {
context,
selectableActionsProp
);

if (!nodeToUpdate) {
return [];
}

return fixer.replaceText(
nodeToUpdate,
propertiesToKeep.length
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ module.exports = {
return;
}

const colorValue = getAttributeValue(context, colorProp.value);
const colorValue = getAttributeValue(
context,
colorProp.value
) as string;
if (Object.keys(replacementMap).includes(colorValue)) {
const newColorValue = replacementMap[colorValue];
const message = `The \`color\` prop on ${node.name.name} has been updated to replace "${colorValue}" with "${newColorValue}".`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@ module.exports = {
node,
"statusPageLinkText"
);
const statusPageLinkTextString: string =
getAttributeValue(context, statusPageLinkTextProp?.value) ??
"status page";
const statusPageLinkTextString =
(getAttributeValue(
context,
statusPageLinkTextProp?.value
) as string) ?? "status page";

if (statusPageLinkTextProp && statusPageLinkTextString.length) {
const firstChar = statusPageLinkTextString.charAt(0);
Expand Down
Loading
Loading