Skip to content

Commit

Permalink
Completely audit React rules
Browse files Browse the repository at this point in the history
  • Loading branch information
Josh-Cena committed Dec 3, 2023
1 parent 2475565 commit 0c4eb52
Show file tree
Hide file tree
Showing 7 changed files with 276 additions and 122 deletions.
3 changes: 3 additions & 0 deletions packages/eslint-config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ module.exports = {
"./rules/typescript.js",
"./rules/import.js",
"./rules/react.js",
// Not included by default
// "./rules/react-class-comps.js",
// "./rules/react-prop-types.js",
"./rules/jsx.js",
],
};
2 changes: 2 additions & 0 deletions packages/eslint-config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
"./import": "./rules/import.js",
"./jsx": "./rules/jsx.js",
"./react": "./rules/react.js",
"./react-class-comps": "./rules/react-class-comps.js",
"./react-prop-types": "./rules/react-prop-types.js",
"./typescript": "./rules/typescript.js",
"./typescript-typecheck": "./rules/typescript-typecheck.js"
},
Expand Down
202 changes: 167 additions & 35 deletions packages/eslint-config/rules/jsx.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module.exports = {
plugins: ["jsx-a11y"],
plugins: ["jsx-a11y", "react"],
rules: {
// JSX a11y rules are taken from airbnb, not adjusted yet
"jsx-a11y/alt-text": [
Expand All @@ -13,7 +13,22 @@ module.exports = {
},
],

"jsx-a11y/anchor-has-content": ["error", { components: [] }],
"jsx-a11y/anchor-ambiguous-text": [
"warn",
{
// Default
words: ["click here", "here", "link", "a link", "learn more"],
},
],

"jsx-a11y/anchor-has-content": [
"error",
{
aspects: ["noHref", "invalidHref", "preferButton"],
components: ["Link"],
specialLink: ["to"],
},
],

"jsx-a11y/anchor-is-valid": [
"error",
Expand All @@ -34,12 +49,7 @@ module.exports = {

"jsx-a11y/aria-unsupported-elements": "error",

"jsx-a11y/autocomplete-valid": [
"off",
{
inputComponents: [],
},
],
"jsx-a11y/autocomplete-valid": ["off", { inputComponents: [] }],

"jsx-a11y/click-events-have-key-events": "error",

Expand Down Expand Up @@ -73,15 +83,28 @@ module.exports = {
},
],

"jsx-a11y/heading-has-content": ["error", { components: [""] }],
"jsx-a11y/heading-has-content": ["error", { components: [] }],

"jsx-a11y/html-has-lang": "error",

"jsx-a11y/iframe-has-title": "error",

"jsx-a11y/img-redundant-alt": "error",

"jsx-a11y/interactive-supports-focus": "error",
"jsx-a11y/interactive-supports-focus": [
"error",
{
tabbable: [
"button",
"checkbox",
"link",
"searchbox",
"spinbutton",
"switch",
"textbox",
],
},
],

"jsx-a11y/label-has-associated-control": [
"error",
Expand All @@ -105,24 +128,38 @@ module.exports = {
},
],

"jsx-a11y/mouse-events-have-key-events": "error",
"jsx-a11y/mouse-events-have-key-events": [
"error",
{
hoverInHandlers: [
"onMouseOver",
"onMouseEnter",
"onPointerOver",
"onPointerEnter",
],
hoverOutHandlers: [
"onMouseOut",
"onMouseLeave",
"onPointerOut",
"onPointerLeave",
],
},
],

"jsx-a11y/no-access-key": "error",

"jsx-a11y/no-autofocus": ["error", { ignoreNonDOM: true }],
"jsx-a11y/no-aria-hidden-on-focusable": "error",

"jsx-a11y/no-autofocus": ["error", { ignoreNonDOM: false }],

"jsx-a11y/no-distracting-elements": [
"error",
{
elements: ["marquee", "blink"],
},
{ elements: ["marquee", "blink"] },
],

"jsx-a11y/no-interactive-element-to-noninteractive-role": [
"error",
{
tr: ["none", "presentation"],
},
{ tr: ["none", "presentation"] },
],

"jsx-a11y/no-noninteractive-element-interactions": [
Expand Down Expand Up @@ -169,18 +206,25 @@ module.exports = {
"jsx-a11y/no-noninteractive-tabindex": [
"error",
{
allowExpressionValues: true,
roles: ["tabpanel"],
tags: [],
},
],

"jsx-a11y/no-onchange": "off",

"jsx-a11y/no-redundant-roles": "error",
"jsx-a11y/no-redundant-roles": [
"error",
{
nav: ["navigation"],
},
],

"jsx-a11y/no-static-element-interactions": [
"error",
{
allowExpressionValues: true,
handlers: [
"onClick",
"onMouseDown",
Expand All @@ -192,6 +236,8 @@ module.exports = {
},
],

"jsx-a11y/prefer-tag-over-role": "error",

"jsx-a11y/role-has-required-aria-props": "error",

"jsx-a11y/role-supports-aria-props": "error",
Expand All @@ -200,29 +246,69 @@ module.exports = {

"jsx-a11y/tabindex-no-positive": "error",

"react/button-has-type": [
"error",
{
button: true,
reset: false, // MDN: This behavior tends to annoy users
submit: true,
},
],

"react/iframe-missing-sandbox": "error",

"react/jsx-boolean-value": ["error", "never"],

"react/jsx-curly-brace-presence": "error",
"react/jsx-child-element-spacing": "warn",

// Formatting
"react/jsx-closing-bracket-location": "off",

// Formatting
"react/jsx-closing-tag-location": "off",

"react/jsx-curly-brace-presence": [
"error",
// Maybe one day we can use propElementValues: "never"...
{ children: "never", propElementValues: "ignore", props: "never" },
],

// Formatting
"react/jsx-curly-newline": "off",

// Formatting
"react/jsx-curly-spacing": "off",

// Js, jsx, tsx are all acceptable
// Formatting
"react/jsx-equals-spacing": "off",

// .js, .jsx, .tsx are all acceptable
"react/jsx-filename-extension": "off",

"react/jsx-fragments": "error",
// Formatting
"react/jsx-first-prop-new-line": "off",

// Too stylistic
"react/jsx-handler-names": "off",

// Airbnb says there can be false positives
"react/jsx-key": "error",
// Formatting
"react/jsx-indent": "off",

// Formatting
"react/jsx-indent-props": "off",

// Too stylistic
"react/jsx-max-depth": "off",

"react/jsx-no-bind": ["warn", { ignoreDOMComponents: true }],
// Formatting
"react/jsx-max-props-per-line": "off",

"react/jsx-no-comment-textnodes": "error",
// Formatting
"react/jsx-newline": "off",

"react/jsx-no-constructed-context-values": "error",
"react/jsx-no-comment-textnodes": "error",

"react/jsx-no-duplicate-props": "error",
"react/jsx-no-duplicate-props": ["error", { ignoreCase: false }],

// We'll use strict-boolean-expressions instead
"react/jsx-no-leaked-render": "off",
Expand All @@ -231,23 +317,69 @@ module.exports = {

"react/jsx-no-script-url": "error",

"react/jsx-no-target-blank": "error",
"react/jsx-no-target-blank": [
"error",
{
allowReferrer: false,
enforceDynamicLinks: "always",
forms: true,
links: true,
warnOnSpreadAttributes: true,
},
],

// Also checked by TypeScript
"react/jsx-no-undef": "error",
"react/jsx-no-undef": ["error", { allowGlobals: true }],

"react/jsx-no-useless-fragment": ["error", { allowExpressions: true }],

"react/jsx-pascal-case": ["error", { allowAllCaps: true }],
// Formatting
"react/jsx-one-expression-per-line": "off",

"react/jsx-props-no-spreading": "off",
"react/jsx-pascal-case": [
"error",
{
allowAllCaps: true,
allowLeadingUnderscore: false,
allowNamespace: true,
},
],

"react/jsx-sort-default-props": "off",
// Formatting
"react/jsx-props-no-multi-spaces": "off",

"react/jsx-sort-props": "off",
// We like spreading
"react/jsx-props-no-spreading": "off",

"react/jsx-uses-react": "warn",
"react/jsx-sort-props": 0,

// Formatting
"react/jsx-tag-spacing": "off",

"react/jsx-uses-vars": "warn",

// Formatting
"react/jsx-wrap-multilines": "off",

// Too stylistic
"react/no-adjacent-inline-elements": "off",

"react/no-children-prop": ["error", { allowFunctions: false }],

"react/no-invalid-html-attribute": "error",

// The original text allows for things like spellchecking and searching
"react/no-unescaped-entities": "off",

"react/no-unknown-property": [
"error",
{
// TODO: not released: requireDataLowercase: true,
},
],

"react/self-closing-comp": ["error", { component: true, html: true }],

"react/void-dom-elements-no-children": "error",
},
};
41 changes: 41 additions & 0 deletions packages/eslint-config/rules/react-class-comps.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
module.exports = {
rules: {
"react/no-access-state-in-setstate": "error",

"react/no-arrow-function-lifecycle": "error",

"react/no-did-mount-set-state": "error",

"react/no-did-update-set-state": "error",

"react/no-direct-mutation-state": "error",

"react/no-redundant-should-component-update": "error",

// ???
"react/no-set-state": "off",

"react/no-unsafe": "error",

"react/no-unused-class-component-methods": "error",

"react/no-unused-state": "error",

"react/no-will-update-set-state": "error",

"react/prefer-es6-class": "error",

"react/prefer-stateless-function": "error",

// We mostly use function components, and this optimization is not critical
"react/require-optimization": "off",

"react/require-render-return": "error",

"react/sort-comp": 0,

"react/state-in-constructor": ["error", "never"],

"react/static-property-placement": ["error", "static public field"],
},
};
21 changes: 21 additions & 0 deletions packages/eslint-config/rules/react-prop-types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module.exports = {
rules: {
"react/default-props-match-prop-types": "off",

"react/forbid-foreign-prop-types": "off",

"react/forbid-prop-types": "off",

"react/no-unused-prop-types": "off",

"react/prefer-exact-props": "off",

"react/prop-types": "off",

"react/require-default-props": "off",

"react/sort-default-props": "off",

"react/sort-prop-types": "off",
},
};
Loading

0 comments on commit 0c4eb52

Please sign in to comment.