-
Notifications
You must be signed in to change notification settings - Fork 2
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
Add aphrodite-add-style-variable-name lint rule #1091
Conversation
🦋 Changeset detectedLatest commit: 638d66e The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
55000ef
to
770fc81
Compare
770fc81
to
6811682
Compare
## Creating a new lint rule | ||
|
||
Here are some helpful resources for setting up a new lint rule: | ||
|
||
- [TypeScript Eslint custom rules](https://typescript-eslint.io/developers/custom-rules/) | ||
- [AST Explorer](https://astexplorer.net/): A tool for showing what the abstract syntax tree (AST) looks like based on code | ||
- [ESTree Spec](https://github.com/estree/estree/tree/master): The spec for learning more about the AST node types |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me know if there are other resources to include in this list! This is based on some resources Kevin pointed me to!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea. Thanks for adding these links.
@@ -0,0 +1,35 @@ | |||
# Naming convention for addStyle variable (aphrodite-add-style-variable-name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me know if there are certain conventions around custom lint rules! I tried to follow similar naming and documentation structure as our other custom lint rules
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for following the existing patterns. LGTM :)
defaultOptions: [], | ||
create(context) { | ||
return { | ||
VariableDeclarator(node: TSESTree.VariableDeclarator) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
chatgpt helped a lot with this 😄 I verified the structure with the AST explorer as well!
data: { | ||
variableName, | ||
tagName, | ||
expectedName, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One thing to note is that it doesn't have an auto-fix option. We could potentially provide one since we have the expected name though. Auto-fixing existing places could be tricky I think since renaming a variable would mean we'd also need to update other places it is used. Let me know though if there's another way to approach this though!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's probably good enough for now to ship this without the autofix. addStyle("
appears 127 times across 91 files. Some of those will be invalid cases with this rule but fixing them by hand shouldn't take too long. If you want to add autofixing in the future let me know and we can pair on it.
// Placeholder example for calling addStyle for aphrodite | ||
} | ||
|
||
const div = addStyle("div"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I updated the demo project to include a linting error intentionally. Looks like it's working in CI! Some thoughts/questions:
- I'm not sure why it shows the errors twice in the PR (same with the other errors)
- Should CI be running linting in the demo project since we expect it to fail?
- I followed the steps in the Demo readme locally and was running into an error. The linting error also wasn't being shown in VSCode. The error:
Oops! Something went wrong! :(
ESLint: 8.40.0
TypeError: Failed to load plugin '@khanacademy' declared in '.eslintrc.js': Class extends value undefined is not a constructor or null
@kevinb-khan Have you come across this before?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Try updating eslint
to 8.55.0.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Circling back so I remember what we did:
- We updated eslint versions
- We updated the demo project to use the released
eslint-plugin-khan
package. We'll enable the new addStyle rule in the demo project once these changes are released - We ignored the demo project from lint checks so that CI can pass (it is expected that the demo project has lint errors so we can test that rules are working as expected!)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 🎉 Thanks for adding this rule. I've left a few style suggestions in the comments.
|
||
The variable name when using `addStyle` should be the same as the tag argument in the format `StyledTag`. | ||
|
||
This is useful so that Aphrodite styled elements can be mapped to HTML elements for static code analysis. For example, if the `addStyle` variables are consistently named, we are able to provide custom component mapping to `eslint-plugin-jsx-a11y` so that it can identify linting issues based on the underlying HTML tag. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add a link to the docs for that rule?
## Creating a new lint rule | ||
|
||
Here are some helpful resources for setting up a new lint rule: | ||
|
||
- [TypeScript Eslint custom rules](https://typescript-eslint.io/developers/custom-rules/) | ||
- [AST Explorer](https://astexplorer.net/): A tool for showing what the abstract syntax tree (AST) looks like based on code | ||
- [ESTree Spec](https://github.com/estree/estree/tree/master): The spec for learning more about the AST node types |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea. Thanks for adding these links.
node.init && | ||
node.init.type === "CallExpression" && | ||
node.init.callee.type === "Identifier" && | ||
node.init.callee.name === "addStyle" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style suggestion: some of our other code compares .type
fields to things like AST_NODE_TYPES.Identifier
. AST_NODE_TYPES
can be imported from "@typescript-eslint/utils"
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style suggestion: this could be one or early returns to avoid excessive indentation, e.g.
if (node?.init.type !== "CallExpression") {
return;
}
if (node.init.callee.type !== "Identifier" || node.init.callee.name !== "addStyle) {
return;
}
...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the feedback Kevin! I refactored the function to favour early returns. It is easier to read now 😄
// Get variable name for the addStyle return value | ||
const variableName = | ||
node.id.type === "Identifier" ? node.id.name : null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style suggestion: this check could be an early return
const firstArg = node.init.arguments[0]; | ||
if ( | ||
firstArg && | ||
firstArg.type === "Literal" && | ||
typeof firstArg.value === "string" | ||
) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style suggestion: this check could be an early return
const expectedName = `Styled${tagName | ||
.charAt(0) | ||
.toUpperCase()}${tagName.slice(1)}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style nit: you may want to extract the part that does the capitalization into a helper function call capitalizeString
. This will make it easier for people to grok what's going on here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea! I moved it to a toPascalCase
function to handle the custom html element use case you mentioned!
data: { | ||
variableName, | ||
tagName, | ||
expectedName, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's probably good enough for now to ship this without the autofix. addStyle("
appears 127 times across 91 files. Some of those will be invalid cases with this rule but fixing them by hand shouldn't take too long. If you want to add autofixing in the future let me know and we can pair on it.
{ | ||
code: `const StyledSup = addStyle("sup")`, | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: It might be worth handling things like addStyle("foo-bar")
to enable handling of custom HTML components. I know we don't use any right now, but we might someday in the future or other people/orgs might use this eslint rule (it is open source). Being pro-active is pretty low cost in this situation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea! Instead of capitalizing the tag name for the expected variable name, I updated it to turn the string into PascalCase. I added a test for this (and other types of text casing like foo_bar
or FooBar
just in case!)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome. Thank you!
{ | ||
code: `const StyledDiv = addStyle("div")`, | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this rule ignore things like const MyCompWithStyle = addStyle(MyComp)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question, yes! It will return early if the addStyle argument is not a string:
wonder-stuff/packages/eslint-plugin-khan/src/rules/aphrodite-add-style-variable-name.ts
Lines 79 to 85 in fc49725
// Return early if the argument is not a string literal | |
if ( | |
firstArg?.type !== AST_NODE_TYPES.Literal || | |
typeof firstArg.value !== "string" | |
) { | |
return; | |
} |
I updated the tests and docs to explain that the naming convention only applies to string arguments for html tags!
(I didn't know addStyle could be used with components, thank you for bringing this up!)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see any uses of it in our code. I usually tell people to manually add a style
prop. Since that's what all of our code does I guess people just copy/pasting that now which is good.
…on't automatically lint files in the demo folder
Size Change: 0 B Total Size: 4.63 kB ℹ️ View Unchanged
|
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #1091 +/- ##
=======================================
Coverage 99.85% 99.85%
=======================================
Files 97 97
Lines 1392 1392
Branches 358 351 -7
=======================================
Hits 1390 1390
Misses 1 1
Partials 1 1 Continue to review full report in Codecov by Sentry.
|
… doesn't enforce the linting convention
module.exports = { | ||
root: true, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We set this so that the demo eslint config isn't merged with parent eslint config files!
// Placeholder example for calling addStyle for aphrodite | ||
} | ||
|
||
const div = addStyle("div"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Circling back so I remember what we did:
- We updated eslint versions
- We updated the demo project to use the released
eslint-plugin-khan
package. We'll enable the new addStyle rule in the demo project once these changes are released - We ignored the demo project from lint checks so that CI can pass (it is expected that the demo project has lint errors so we can test that rules are working as expected!)
GeraldRequired Reviewers
Don't want to be involved in this pull request? Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for all of the changes. This looks great. 🚀
Summary:
Adding a custom lint rule to ensure consistent naming for when we use
addStyle
for aphrodite styling. Variable naming should be in the formatStyledTag
.By having predictable names for styled elements, we are able to use component mapping more effectively for
eslint-plugin-jsx-a11y
so it can use the underlying HTML for accessibility checks.Issue: FEI-5952
Related ADR: ADR-781
Test plan: