diff --git a/.eslintrc.js b/.eslintrc.js index da8fe6a7b3c..bdfdd01710f 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -12,6 +12,7 @@ module.exports = { "testing-library", "unused-imports", "no-autofix", + "artsy", ], extends: [ "eslint:recommended", @@ -41,7 +42,7 @@ module.exports = { /** * Errors */ - + "artsy/no-uselazyloadquery-inside-suspense": ERR, "import/order": [ ERR, { diff --git a/eslint-rules/index.js b/eslint-rules/index.js new file mode 100644 index 00000000000..8bacc0b3fda --- /dev/null +++ b/eslint-rules/index.js @@ -0,0 +1,5 @@ +module.exports = { + rules: { + "no-uselazyloadquery-inside-suspense": require("./no-uselazyloadquery-inside-suspense"), + }, +} diff --git a/eslint-rules/no-uselazyloadquery-inside-suspense.js b/eslint-rules/no-uselazyloadquery-inside-suspense.js new file mode 100644 index 00000000000..e3ae2a2dbc4 --- /dev/null +++ b/eslint-rules/no-uselazyloadquery-inside-suspense.js @@ -0,0 +1,85 @@ +// yarn upgrade eslint-plugin-artsy in order to refresh the rule +// restart eslint server + +const { ESLintUtils } = require("@typescript-eslint/utils") + +const createRule = ESLintUtils.RuleCreator( + (name) => `https://github.com/artsy/eigen/blob/main/eslint-rules/${name}.js` +) + +module.exports = createRule({ + name: "no-uselazyloadquery-inside-suspense", + meta: { + type: "problem", + hasSuggestions: true, + docs: { + description: + "When we use `useLazyLoadQuery` inside a component that has `` in react-native it causes a crash, this rule is for preventing that" + recommended: "error", + }, + messages: { + noUseLazyLoadQueryInsideSuspenseError: + "Do not use useLazyLoadQuery inside a component that has a Suspense component inside, it will cause a crash.", + }, + schema: [], + }, + defaultOptions: [], + + create: (context) => { + return { + CallExpression: (node) => { + if (node.callee.name !== "useLazyLoadQuery") { + return + } + + let parent = node.parent + while (parent) { + if (parent.type === "BlockStatement") { + break + } + parent = parent.parent + } + + const body = parent.body + + const returnStatement = body.find((node) => node.type === "ReturnStatement") + + if ( + returnStatement.argument.type !== "JSXElement" && + returnStatement.argument.type !== "JSXFragment" + ) { + return + } + + const jsxElement = returnStatement.argument + + if (!amIsuspenseOrMyChildrenAreSuspense(jsxElement)) { + return + } + + context.report({ + node: returnStatement, + messageId: "noUseLazyLoadQueryInsideSuspenseError", + }) + }, + } + }, +}) + +const amIsuspenseOrMyChildrenAreSuspense = (node) => { + let result = node.type === "JSXElement" && node.openingElement.name.name === "Suspense" + + if (!result && node.type === "JSXFragment") { + for (const child of node.children) { + result = result || amIsuspenseOrMyChildrenAreSuspense(child) + } + } + + if (!result) { + for (const child of node.children || []) { + result = result || amIsuspenseOrMyChildrenAreSuspense(child) + } + } + + return result +} diff --git a/package.json b/package.json index bb1c9fcf0d6..6a8d3986081 100644 --- a/package.json +++ b/package.json @@ -261,6 +261,7 @@ "@types/yup": "0.29.13", "@typescript-eslint/eslint-plugin": "5.57.0", "@typescript-eslint/parser": "5.57.0", + "@typescript-eslint/utils": "5.51.0", "argparse": "2.0.1", "auto": "7.12.3", "awesome-typescript-loader": "3.4.1", @@ -278,6 +279,7 @@ "eslint": "8.32.0", "eslint-config-prettier": "8.6.0", "eslint-import-resolver-typescript": "3.5.3", + "eslint-plugin-artsy": "file:./eslint-rules", "eslint-plugin-import": "2.27.5", "eslint-plugin-jest": "27.2.1", "eslint-plugin-no-autofix": "1.2.3", diff --git a/yarn.lock b/yarn.lock index af7fa0b0c97..302b2e41a08 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5294,6 +5294,14 @@ "@typescript-eslint/types" "5.48.2" "@typescript-eslint/visitor-keys" "5.48.2" +"@typescript-eslint/scope-manager@5.51.0": + version "5.51.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.51.0.tgz#ad3e3c2ecf762d9a4196c0fbfe19b142ac498990" + integrity sha512-gNpxRdlx5qw3yaHA0SFuTjW4rxeYhpHxt491PEcKF8Z6zpq0kMhe0Tolxt0qjlojS+/wArSDlj/LtE69xUJphQ== + dependencies: + "@typescript-eslint/types" "5.51.0" + "@typescript-eslint/visitor-keys" "5.51.0" + "@typescript-eslint/scope-manager@5.57.0": version "5.57.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.57.0.tgz#79ccd3fa7bde0758059172d44239e871e087ea36" @@ -5317,6 +5325,11 @@ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.48.2.tgz#635706abb1ec164137f92148f06f794438c97b8e" integrity sha512-hE7dA77xxu7ByBc6KCzikgfRyBCTst6dZQpwaTy25iMYOnbNljDT4hjhrGEJJ0QoMjrfqrx+j1l1B9/LtKeuqA== +"@typescript-eslint/types@5.51.0": + version "5.51.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.51.0.tgz#e7c1622f46c7eea7e12bbf1edfb496d4dec37c90" + integrity sha512-SqOn0ANn/v6hFn0kjvLwiDi4AzR++CBZz0NV5AnusT2/3y32jdc0G4woXPWHCumWtUXZKPAS27/9vziSsC9jnw== + "@typescript-eslint/types@5.57.0": version "5.57.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.57.0.tgz#727bfa2b64c73a4376264379cf1f447998eaa132" @@ -5335,6 +5348,19 @@ semver "^7.3.7" tsutils "^3.21.0" +"@typescript-eslint/typescript-estree@5.51.0": + version "5.51.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.51.0.tgz#0ec8170d7247a892c2b21845b06c11eb0718f8de" + integrity sha512-TSkNupHvNRkoH9FMA3w7TazVFcBPveAAmb7Sz+kArY6sLT86PA5Vx80cKlYmd8m3Ha2SwofM1KwraF24lM9FvA== + dependencies: + "@typescript-eslint/types" "5.51.0" + "@typescript-eslint/visitor-keys" "5.51.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + "@typescript-eslint/typescript-estree@5.57.0": version "5.57.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.57.0.tgz#ebcd0ee3e1d6230e888d88cddf654252d41e2e40" @@ -5348,6 +5374,20 @@ semver "^7.3.7" tsutils "^3.21.0" +"@typescript-eslint/utils@5.51.0": + version "5.51.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.51.0.tgz#074f4fabd5b12afe9c8aa6fdee881c050f8b4d47" + integrity sha512-76qs+5KWcaatmwtwsDJvBk4H76RJQBFe+Gext0EfJdC3Vd2kpY2Pf//OHHzHp84Ciw0/rYoGTDnIAr3uWhhJYw== + dependencies: + "@types/json-schema" "^7.0.9" + "@types/semver" "^7.3.12" + "@typescript-eslint/scope-manager" "5.51.0" + "@typescript-eslint/types" "5.51.0" + "@typescript-eslint/typescript-estree" "5.51.0" + eslint-scope "^5.1.1" + eslint-utils "^3.0.0" + semver "^7.3.7" + "@typescript-eslint/utils@5.57.0": version "5.57.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.57.0.tgz#eab8f6563a2ac31f60f3e7024b91bf75f43ecef6" @@ -5384,6 +5424,14 @@ "@typescript-eslint/types" "5.48.2" eslint-visitor-keys "^3.3.0" +"@typescript-eslint/visitor-keys@5.51.0": + version "5.51.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.51.0.tgz#c0147dd9a36c0de758aaebd5b48cae1ec59eba87" + integrity sha512-Oh2+eTdjHjOFjKA27sxESlA87YPSOJafGCR0md5oeMdh1ZcCfAGCIOL216uTBAkAIptvLIfKQhl7lHxMJet4GQ== + dependencies: + "@typescript-eslint/types" "5.51.0" + eslint-visitor-keys "^3.3.0" + "@typescript-eslint/visitor-keys@5.57.0": version "5.57.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.57.0.tgz#e2b2f4174aff1d15eef887ce3d019ecc2d7a8ac1" @@ -8183,6 +8231,9 @@ eslint-module-utils@^2.7.4: dependencies: debug "^3.2.7" +"eslint-plugin-artsy@file:./eslint-rules": + version "0.0.0" + eslint-plugin-import@2.27.5: version "2.27.5" resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz#876a6d03f52608a3e5bb439c2550588e51dd6c65"