.eslintrc.js 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039
  1. /* eslint-env node */
  2. const detectDeprecations = !!process.env.SENTRY_DETECT_DEPRECATIONS;
  3. const baseRules = {
  4. /**
  5. * Strict mode
  6. */
  7. // https://eslint.org/docs/rules/strict
  8. strict: ['error', 'global'],
  9. /**
  10. * Variables
  11. */
  12. // https://eslint.org/docs/rules/no-shadow
  13. 'no-shadow': ['error'],
  14. // https://eslint.org/docs/rules/no-shadow-restricted-names
  15. 'no-shadow-restricted-names': ['error'],
  16. // https://eslint.org/docs/rules/no-undef
  17. 'no-undef': ['error'],
  18. // https://eslint.org/docs/rules/no-unused-vars
  19. 'no-unused-vars': [
  20. 'error',
  21. {
  22. vars: 'all',
  23. args: 'none',
  24. // Ignore vars that start with an underscore
  25. // e.g. if you want to omit a property using object spread:
  26. //
  27. // const {name: _name, ...props} = this.props;
  28. //
  29. varsIgnorePattern: '^_',
  30. argsIgnorePattern: '^_',
  31. },
  32. ],
  33. // https://eslint.org/docs/rules/no-use-before-define
  34. 'no-use-before-define': ['error', {functions: false}],
  35. /**
  36. * Possible errors
  37. */
  38. // https://eslint.org/docs/rules/no-cond-assign
  39. 'no-cond-assign': ['error', 'always'],
  40. // https://eslint.org/docs/rules/no-console
  41. 'no-console': ['warn'],
  42. // https://eslint.org/docs/rules/no-alert
  43. 'no-alert': ['error'],
  44. // https://eslint.org/docs/rules/no-constant-condition
  45. 'no-constant-condition': ['warn'],
  46. // https://eslint.org/docs/rules/no-empty
  47. 'no-empty': ['error'],
  48. // https://eslint.org/docs/rules/no-ex-assign
  49. 'no-ex-assign': ['error'],
  50. // https://eslint.org/docs/rules/no-extra-boolean-cast
  51. 'no-extra-boolean-cast': ['error'],
  52. // https://eslint.org/docs/rules/no-func-assign
  53. 'no-func-assign': ['error'],
  54. // https://eslint.org/docs/rules/no-inner-declarations
  55. 'no-inner-declarations': ['error'],
  56. // https://eslint.org/docs/rules/no-invalid-regexp
  57. 'no-invalid-regexp': ['error'],
  58. // https://eslint.org/docs/rules/no-irregular-whitespace
  59. 'no-irregular-whitespace': ['error'],
  60. // https://eslint.org/docs/rules/no-obj-calls
  61. 'no-obj-calls': ['error'],
  62. // https://eslint.org/docs/rules/no-sparse-arrays
  63. 'no-sparse-arrays': ['error'],
  64. // https://eslint.org/docs/rules/block-scoped-var
  65. 'block-scoped-var': ['error'],
  66. /**
  67. * Best practices
  68. */
  69. // https://eslint.org/docs/rules/consistent-return
  70. 'consistent-return': ['error'],
  71. // https://eslint.org/docs/rules/default-case
  72. 'default-case': ['error'],
  73. // https://eslint.org/docs/rules/dot-notation
  74. 'dot-notation': [
  75. 'error',
  76. {
  77. allowKeywords: true,
  78. },
  79. ],
  80. // https://eslint.org/docs/rules/guard-for-in [REVISIT ME]
  81. 'guard-for-in': ['off'],
  82. // https://eslint.org/docs/rules/no-caller
  83. 'no-caller': ['error'],
  84. // https://eslint.org/docs/rules/no-eval
  85. 'no-eval': ['error'],
  86. // https://eslint.org/docs/rules/no-extend-native
  87. 'no-extend-native': ['error'],
  88. // https://eslint.org/docs/rules/no-extra-bind
  89. 'no-extra-bind': ['error'],
  90. // https://eslint.org/docs/rules/no-fallthrough
  91. 'no-fallthrough': ['error'],
  92. // https://eslint.org/docs/rules/no-floating-decimal
  93. 'no-floating-decimal': ['error'],
  94. // https://eslint.org/docs/rules/no-implied-eval
  95. 'no-implied-eval': ['error'],
  96. // https://eslint.org/docs/rules/no-lone-blocks
  97. 'no-lone-blocks': ['error'],
  98. // https://eslint.org/docs/rules/no-loop-func
  99. 'no-loop-func': ['error'],
  100. // https://eslint.org/docs/rules/no-multi-str
  101. 'no-multi-str': ['error'],
  102. // https://eslint.org/docs/rules/no-native-reassign
  103. 'no-native-reassign': ['error'],
  104. // https://eslint.org/docs/rules/no-new
  105. 'no-new': ['error'],
  106. // https://eslint.org/docs/rules/no-new-func
  107. 'no-new-func': ['error'],
  108. // https://eslint.org/docs/rules/no-new-wrappers
  109. 'no-new-wrappers': ['error'],
  110. // https://eslint.org/docs/rules/no-octal
  111. 'no-octal': ['error'],
  112. // https://eslint.org/docs/rules/no-octal-escape
  113. 'no-octal-escape': ['error'],
  114. // https://eslint.org/docs/rules/no-param-reassign [REVISIT ME]
  115. 'no-param-reassign': ['off'],
  116. // https://eslint.org/docs/rules/no-proto
  117. 'no-proto': ['error'],
  118. // https://eslint.org/docs/rules/no-return-assign
  119. 'no-return-assign': ['error'],
  120. // https://eslint.org/docs/rules/no-script-url
  121. 'no-script-url': ['error'],
  122. // https://eslint.org/docs/rules/no-self-compare
  123. 'no-self-compare': ['error'],
  124. // https://eslint.org/docs/rules/no-sequences
  125. 'no-sequences': ['error'],
  126. // https://eslint.org/docs/rules/no-throw-literal
  127. 'no-throw-literal': ['error'],
  128. // https://eslint.org/docs/rules/no-with
  129. 'no-with': ['error'],
  130. // https://eslint.org/docs/rules/radix
  131. radix: ['error'],
  132. // https://eslint.org/docs/rules/space-in-brackets.html
  133. 'computed-property-spacing': ['error', 'never'],
  134. // https://eslint.org/docs/rules/space-in-brackets.html
  135. 'array-bracket-spacing': ['error', 'never'],
  136. // https://eslint.org/docs/rules/space-in-brackets.html
  137. 'object-curly-spacing': ['error', 'never'],
  138. // https://eslint.org/docs/rules/object-shorthand
  139. 'object-shorthand': ['error', 'properties'],
  140. // https://eslint.org/docs/rules/space-infix-ops.html
  141. 'space-infix-ops': ['error'],
  142. // https://eslint.org/docs/rules/vars-on-top
  143. 'vars-on-top': ['off'],
  144. // https://eslint.org/docs/rules/wrap-iife
  145. 'wrap-iife': ['error', 'any'],
  146. // https://eslint.org/docs/rules/array-callback-return
  147. 'array-callback-return': ['error'],
  148. // https://eslint.org/docs/rules/yoda
  149. yoda: ['error'],
  150. // https://eslint.org/docs/rules/no-else-return
  151. 'no-else-return': ['error', {allowElseIf: false}],
  152. // https://eslint.org/docs/rules/require-await
  153. 'require-await': ['error'],
  154. // https://eslint.org/docs/rules/multiline-comment-style
  155. 'multiline-comment-style': ['error', 'separate-lines'],
  156. // https://eslint.org/docs/rules/spaced-comment
  157. 'spaced-comment': [
  158. 'error',
  159. 'always',
  160. {
  161. line: {markers: ['/'], exceptions: ['-', '+']},
  162. block: {exceptions: ['*'], balanced: true},
  163. },
  164. ],
  165. };
  166. const reactReactRules = {
  167. // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/display-name.md
  168. 'react/display-name': ['off'],
  169. // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-multi-comp.md
  170. 'react/no-multi-comp': [
  171. 'off',
  172. {
  173. ignoreStateless: true,
  174. },
  175. ],
  176. // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-fragments.md
  177. 'react/jsx-fragments': ['error', 'element'],
  178. // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-handler-names.md
  179. // Ensures that any component or prop methods used to handle events are correctly prefixed.
  180. 'react/jsx-handler-names': [
  181. 'off',
  182. {
  183. eventHandlerPrefix: 'handle',
  184. eventHandlerPropPrefix: 'on',
  185. },
  186. ],
  187. // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-key.md
  188. 'react/jsx-key': ['error'],
  189. // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-undef.md
  190. 'react/jsx-no-undef': ['error'],
  191. // Disabled as we use the newer JSX transform babel plugin.
  192. // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-uses-react.md
  193. 'react/jsx-uses-react': ['off'],
  194. // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-uses-vars.md
  195. 'react/jsx-uses-vars': ['error'],
  196. /**
  197. * Deprecation related rules
  198. */
  199. // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-deprecated.md
  200. 'react/no-deprecated': ['error'],
  201. // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-is-mounted.md
  202. 'react/no-is-mounted': ['warn'],
  203. // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-find-dom-node.md
  204. // Recommended to use callback refs instead
  205. // TODO: Upgrade sentry to use callback refs
  206. 'react/no-find-dom-node': ['warn'],
  207. // Prevent usage of the return value of React.render
  208. // deprecation: https://facebook.github.io/react/docs/react-dom.html#render
  209. // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-render-return-value.md
  210. 'react/no-render-return-value': ['error'],
  211. // Children should always be actual children, not passed in as a prop.
  212. // When using JSX, the children should be nested between the opening and closing tags. When not using JSX, the children should be passed as additional arguments to React.createElement.
  213. // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-children-prop.md
  214. 'react/no-children-prop': ['error'],
  215. // This rule helps prevent problems caused by using children and the dangerouslySetInnerHTML prop at the same time.
  216. // React will throw a warning if this rule is ignored.
  217. // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-danger-with-children.md
  218. 'react/no-danger-with-children': ['error'],
  219. // Prevent direct mutation of this.state
  220. // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-direct-mutation-state.md
  221. 'react/no-direct-mutation-state': ['error'],
  222. // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-did-mount-set-state.md
  223. 'react/no-did-mount-set-state': ['error'],
  224. // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-did-update-set-state.md"
  225. 'react/no-did-update-set-state': ['error'],
  226. // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-redundant-should-component-update.md
  227. 'react/no-redundant-should-component-update': ['error'],
  228. // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-typos.md
  229. 'react/no-typos': ['error'],
  230. // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-string-refs.md
  231. // This is now considered legacy, callback refs preferred
  232. 'react/no-string-refs': ['warn'],
  233. // Prevent invalid characters from appearing in markup
  234. // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-unescaped-entities.md
  235. 'react/no-unescaped-entities': ['off'],
  236. // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-unknown-property.md
  237. 'react/no-unknown-property': ['error', {ignore: ['css']}],
  238. // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-unused-prop-types.md
  239. // Disabled since this currently fails to correctly detect a lot of
  240. // typescript prop type usage.
  241. 'react/no-unused-prop-types': ['off'],
  242. // We do not need proptypes since we're using typescript
  243. 'react/prop-types': ['off'],
  244. // When writing the render method in a component it is easy to forget to return the JSX content.
  245. // This rule will warn if the return statement is missing.
  246. // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/require-render-return.md
  247. 'react/require-render-return': ['error'],
  248. // Disabled as we are using the newer JSX transform babel plugin.
  249. // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/react-in-jsx-scope.md
  250. 'react/react-in-jsx-scope': ['off'],
  251. // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/self-closing-comp.md
  252. 'react/self-closing-comp': ['error'],
  253. // This also causes issues with typescript
  254. // See: https://github.com/yannickcr/eslint-plugin-react/issues/2066
  255. //
  256. // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/sort-comp.md
  257. 'react/sort-comp': ['warn'],
  258. // Disabled because of prettier
  259. // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/wrap-multilines.md
  260. 'react/jsx-wrap-multilines': ['off'],
  261. // Consistent <Component booleanProp /> (never add ={true})
  262. // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-boolean-value.md
  263. 'react/jsx-boolean-value': ['error', 'never'],
  264. // Consistent function component declaration styles
  265. // https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/function-component-definition.md
  266. 'react/function-component-definition': [
  267. 'error',
  268. {namedComponents: 'function-declaration'},
  269. ],
  270. };
  271. const reactImportRules = {
  272. // Not recommended to be enabled with typescript-eslint
  273. // https://typescript-eslint.io/linting/troubleshooting/performance-troubleshooting/#eslint-plugin-import
  274. 'import/no-unresolved': ['off'],
  275. 'import/named': ['off'],
  276. 'import/default': ['off'],
  277. 'import/export': ['off'],
  278. 'import/no-named-as-default-member': ['off'],
  279. // Redflags
  280. // do not allow a default import name to match a named export (airbnb: error)
  281. // Issue with `DefaultIssuePlugin` and `app/plugins/index`
  282. // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-named-as-default.md
  283. 'import/no-named-as-default': ['off'],
  284. // disallow use of jsdoc-marked-deprecated imports
  285. // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-deprecated.md
  286. 'import/no-deprecated': ['off'],
  287. // Forbid mutable exports (airbnb: error)
  288. // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-mutable-exports.md
  289. // TODO: enable?
  290. 'import/no-mutable-exports': ['off'],
  291. // disallow require()
  292. // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-commonjs.md
  293. 'import/no-commonjs': ['off'],
  294. // disallow AMD require/define
  295. // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-amd.md
  296. 'import/no-amd': ['error'],
  297. // disallow duplicate imports
  298. // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-duplicates.md
  299. 'import/no-duplicates': ['error'],
  300. // disallow namespace imports
  301. // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-namespace.md
  302. 'import/no-namespace': ['off'],
  303. // Ensure consistent use of file extension within the import path
  304. // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/extensions.md
  305. // TODO this fucks up getsentry
  306. 'import/extensions': [
  307. 'off',
  308. 'always',
  309. {
  310. js: 'never',
  311. jsx: 'never',
  312. },
  313. ],
  314. // Enforce a convention in module import order
  315. // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/order.md
  316. 'import/order': [
  317. 'error',
  318. {
  319. groups: ['builtin', 'external', 'internal', ['parent', 'sibling', 'index']],
  320. 'newlines-between': 'always',
  321. },
  322. ],
  323. // Require a newline after the last import/require in a group
  324. // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/newline-after-import.md
  325. 'import/newline-after-import': ['error'],
  326. // Require modules with a single export to use a default export (airbnb: error)
  327. // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/prefer-default-export.md
  328. 'import/prefer-default-export': ['off'],
  329. // Restrict which files can be imported in a given folder
  330. // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-restricted-paths.md
  331. 'import/no-restricted-paths': ['off'],
  332. // Forbid modules to have too many dependencies
  333. // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/max-dependencies.md
  334. 'import/max-dependencies': ['off', {max: 10}],
  335. // Forbid import of modules using absolute paths
  336. // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-absolute-path.md
  337. 'import/no-absolute-path': ['error'],
  338. // Forbid require() calls with expressions (airbnb: error)
  339. // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-dynamic-require.md
  340. 'import/no-dynamic-require': ['off'],
  341. // Use webpack default chunk names
  342. 'import/dynamic-import-chunkname': ['off'],
  343. // prevent importing the submodules of other modules
  344. // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-internal-modules.md
  345. 'import/no-internal-modules': [
  346. 'off',
  347. {
  348. allow: [],
  349. },
  350. ],
  351. // Warn if a module could be mistakenly parsed as a script by a consumer
  352. // leveraging Unambiguous JavaScript Grammar
  353. // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/unambiguous.md
  354. // this should not be enabled until this proposal has at least been *presented* to TC39.
  355. // At the moment, it"s not a thing.
  356. 'import/unambiguous': ['off'],
  357. // Forbid Webpack loader syntax in imports
  358. // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-webpack-loader-syntax.md
  359. 'import/no-webpack-loader-syntax': ['error'],
  360. // Prevent unassigned imports
  361. // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-unassigned-import.md
  362. // importing for side effects is perfectly acceptable, if you need side effects.
  363. 'import/no-unassigned-import': ['off'],
  364. // Prevent importing the default as if it were named
  365. // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-named-default.md
  366. 'import/no-named-default': ['error'],
  367. // Reports if a module"s default export is unnamed
  368. // https://github.com/benmosher/eslint-plugin-import/blob/d9b712ac7fd1fddc391f7b234827925c160d956f/docs/rules/no-anonymous-default-export.md
  369. 'import/no-anonymous-default-export': [
  370. 'error',
  371. {
  372. allowArray: false,
  373. allowArrowFunction: false,
  374. allowAnonymousClass: false,
  375. allowAnonymousFunction: false,
  376. allowCallExpression: true,
  377. allowLiteral: false,
  378. allowObject: false,
  379. },
  380. ],
  381. };
  382. const reactJestRules = {
  383. 'jest/no-large-snapshots': ['warn', {maxSize: 2000}],
  384. 'jest/no-disabled-tests': 'error',
  385. };
  386. const reactRules = {
  387. ...reactReactRules,
  388. ...reactImportRules,
  389. ...reactJestRules,
  390. /**
  391. * React hooks
  392. */
  393. 'react-hooks/exhaustive-deps': 'error',
  394. // Biome not yet enforcing all parts of this rule https://github.com/biomejs/biome/issues/1984
  395. 'react-hooks/rules-of-hooks': 'error',
  396. /**
  397. * Custom
  398. */
  399. // highlights literals in JSX components w/o translation tags
  400. 'getsentry/jsx-needs-il8n': ['off'],
  401. 'testing-library/render-result-naming-convention': 'off',
  402. 'testing-library/no-unnecessary-act': 'off',
  403. // Disabled as we have many tests which render as simple validations
  404. 'jest/expect-expect': 'off',
  405. // Disabled as we have some comment out tests that cannot be
  406. // uncommented due to typescript errors.
  407. 'jest/no-commented-out-tests': 'off',
  408. // Disabled as we do sometimes have conditional expects
  409. 'jest/no-conditional-expect': 'off',
  410. // Useful for exporting some test utilities
  411. 'jest/no-export': 'off',
  412. 'typescript-sort-keys/interface': [
  413. 'error',
  414. 'asc',
  415. {caseSensitive: true, natural: false, requiredFirst: true},
  416. ],
  417. // Disallow importing `import React from 'react'`. This is not needed since
  418. // React 17. We prefer the named imports for potential tree-shaking gains
  419. // in the future.
  420. 'no-restricted-imports': [
  421. 'error',
  422. {
  423. paths: [
  424. {
  425. name: 'react',
  426. importNames: ['default'],
  427. message: 'Prefer named React imports (React types DO NOT need imported!)',
  428. },
  429. ],
  430. },
  431. ],
  432. };
  433. const appRules = {
  434. /**
  435. * emotion rules for v10
  436. *
  437. * This probably aren't as necessary anymore, but let's remove when we move to v11
  438. */
  439. '@emotion/jsx-import': 'off',
  440. '@emotion/no-vanilla': 'error',
  441. '@emotion/import-from-emotion': 'error',
  442. '@emotion/styled-import': 'error',
  443. // no-undef is redundant with typescript as tsc will complain
  444. // A downside is that we won't get eslint errors about it, but your editors should
  445. // support tsc errors so....
  446. 'no-undef': 'off',
  447. // Let formatter handle this
  448. 'arrow-body-style': 'off',
  449. /**
  450. * Need to use typescript version of these rules
  451. */
  452. 'no-shadow': 'off',
  453. '@typescript-eslint/no-shadow': 'error',
  454. // This only override the `args` rule (which is "none"). There are too many errors and it's difficult to manually
  455. // fix them all, so we'll have to incrementally update.
  456. 'no-unused-vars': 'off',
  457. '@typescript-eslint/no-unused-vars': [
  458. 'error',
  459. {
  460. vars: 'all',
  461. args: 'all',
  462. // TODO(scttcper): We could enable this to enforce catch (error)
  463. // https://eslint.org/docs/latest/rules/no-unused-vars#caughterrors
  464. caughtErrors: 'none',
  465. // Ignore vars that start with an underscore
  466. // e.g. if you want to omit a property using object spread:
  467. //
  468. // const {name: _name, ...props} = this.props;
  469. //
  470. varsIgnorePattern: '^_',
  471. argsIgnorePattern: '^_',
  472. destructuredArrayIgnorePattern: '^_',
  473. },
  474. ],
  475. 'no-use-before-define': 'off',
  476. // This seems to have been turned on while previously it had been off
  477. '@typescript-eslint/no-use-before-define': ['off'],
  478. /**
  479. * Restricted imports, e.g. deprecated libraries, etc
  480. *
  481. * See: https://eslint.org/docs/rules/no-restricted-imports
  482. */
  483. 'no-restricted-imports': [
  484. 'error',
  485. {
  486. paths: [
  487. {
  488. name: 'enzyme',
  489. message:
  490. 'Please import from `sentry-test/enzyme` instead. See: https://github.com/getsentry/frontend-handbook#undefined-theme-properties-in-tests for more information',
  491. },
  492. {
  493. name: '@testing-library/react',
  494. message:
  495. 'Please import from `sentry-test/reactTestingLibrary` instead so that we can ensure consistency throughout the codebase',
  496. },
  497. {
  498. name: '@testing-library/react-hooks',
  499. message:
  500. 'Please import from `sentry-test/reactTestingLibrary` instead so that we can ensure consistency throughout the codebase',
  501. },
  502. {
  503. name: '@testing-library/user-event',
  504. message:
  505. 'Please import from `sentry-test/reactTestingLibrary` instead so that we can ensure consistency throughout the codebase',
  506. },
  507. {
  508. name: '@sentry/browser',
  509. message:
  510. 'Please import from `@sentry/react` to ensure consistency throughout the codebase.',
  511. },
  512. {
  513. name: 'marked',
  514. message:
  515. "Please import marked from 'app/utils/marked' so that we can ensure sanitation of marked output",
  516. },
  517. {
  518. name: 'lodash',
  519. message:
  520. "Please import lodash utilities individually. e.g. `import isEqual from 'lodash/isEqual';`. See https://github.com/getsentry/frontend-handbook#lodash from for information",
  521. },
  522. {
  523. name: 'lodash/get',
  524. message:
  525. 'Optional chaining `?.` and nullish coalescing operators `??` are available and preferred over using `lodash/get`. See https://github.com/getsentry/frontend-handbook#new-syntax for more information',
  526. },
  527. {
  528. name: 'react-bootstrap',
  529. message:
  530. 'Avoid usage of any react-bootstrap components as it will soon be removed',
  531. },
  532. {
  533. name: 'sentry/utils/theme',
  534. importNames: ['lightColors', 'darkColors'],
  535. message:
  536. "'lightColors' and 'darkColors' exports intended for use in Storybook only. Instead, use theme prop from emotion or the useTheme hook.",
  537. },
  538. {
  539. name: 'react-router',
  540. importNames: ['withRouter'],
  541. message:
  542. "Use 'useLocation', 'useParams', 'useNavigate', 'useRoutes' from sentry/utils instead.",
  543. },
  544. {
  545. name: 'sentry/utils/withSentryRouter',
  546. importNames: ['withSentryRouter'],
  547. message:
  548. "Use 'useLocation', 'useParams', 'useNavigate', 'useRoutes' from sentry/utils instead.",
  549. },
  550. ],
  551. },
  552. ],
  553. /**
  554. * Better import sorting
  555. */
  556. 'sort-imports': 'off',
  557. 'import/order': 'off',
  558. 'simple-import-sort/imports': [
  559. 'error',
  560. {
  561. groups: [
  562. // Side effect imports.
  563. ['^\\u0000'],
  564. // Node.js builtins.
  565. // biome-ignore lint/correctness/noNodejsModules: Need to get the list of things!
  566. [`^(${require('node:module').builtinModules.join('|')})(/|$)`],
  567. // Packages. `react` related packages come first.
  568. ['^react', '^@?\\w'],
  569. // Test should be separate from the app
  570. ['^(sentry-test|getsentry-test)(/.*|$)'],
  571. // Internal packages.
  572. ['^(sentry-locale|sentry-images)(/.*|$)'],
  573. ['^(getsentry-images)(/.*|$)'],
  574. ['^(app|sentry)(/.*|$)'],
  575. // Getsentry packages.
  576. ['^(admin|getsentry)(/.*|$)'],
  577. // Style imports.
  578. ['^.+\\.less$'],
  579. // Parent imports. Put `..` last.
  580. ['^\\.\\.(?!/?$)', '^\\.\\./?$'],
  581. // Other relative imports. Put same-folder imports and `.` last.
  582. ['^\\./(?=.*/)(?!/?$)', '^\\.(?!/?$)', '^\\./?$'],
  583. ],
  584. },
  585. ],
  586. 'sentry/no-digits-in-tn': ['error'],
  587. 'sentry/no-dynamic-translations': ['error'],
  588. // https://github.com/xojs/eslint-config-xo-typescript/blob/9791a067d6a119a21a4db72c02f1da95e25ffbb6/index.js#L95
  589. '@typescript-eslint/no-restricted-types': [
  590. 'error',
  591. {
  592. types: {
  593. // TODO(scttcper): Turn object on to make our types more strict
  594. // object: {
  595. // message: 'The `object` type is hard to use. Use `Record<string, unknown>` instead. See: https://github.com/typescript-eslint/typescript-eslint/pull/848',
  596. // fixWith: 'Record<string, unknown>'
  597. // },
  598. Buffer: {
  599. message:
  600. 'Use Uint8Array instead. See: https://sindresorhus.com/blog/goodbye-nodejs-buffer',
  601. suggest: ['Uint8Array'],
  602. },
  603. '[]': "Don't use the empty array type `[]`. It only allows empty arrays. Use `SomeType[]` instead.",
  604. '[[]]':
  605. "Don't use `[[]]`. It only allows an array with a single element which is an empty array. Use `SomeType[][]` instead.",
  606. '[[[]]]': "Don't use `[[[]]]`. Use `SomeType[][][]` instead.",
  607. },
  608. },
  609. ],
  610. // TODO(scttcper): Turn no-empty-object-type on to make our types more strict
  611. // '@typescript-eslint/no-empty-object-type': 'error',
  612. // TODO(scttcper): Turn no-function on to make our types more strict
  613. // '@typescript-eslint/no-unsafe-function-type': 'error',
  614. '@typescript-eslint/no-wrapper-object-types': 'error',
  615. // Naming convention enforcements
  616. '@typescript-eslint/naming-convention': [
  617. 'error',
  618. {
  619. selector: 'typeLike',
  620. format: ['PascalCase'],
  621. leadingUnderscore: 'allow',
  622. },
  623. {
  624. selector: 'enumMember',
  625. format: ['UPPER_CASE'],
  626. },
  627. ],
  628. // Don't allow lookbehind expressions in regexp as they crash safari
  629. // We've accidentally used lookbehinds a few times and caused problems.
  630. 'no-lookahead-lookbehind-regexp/no-lookahead-lookbehind-regexp': [
  631. 'error',
  632. 'no-lookbehind',
  633. 'no-negative-lookbehind',
  634. ],
  635. };
  636. const strictRules = {
  637. 'no-console': ['error'],
  638. // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-is-mounted.md
  639. 'react/no-is-mounted': ['error'],
  640. // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-find-dom-node.md
  641. // Recommended to use callback refs instead
  642. 'react/no-find-dom-node': ['error'],
  643. // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-string-refs.md
  644. // This is now considered legacy, callback refs preferred
  645. 'react/no-string-refs': ['error'],
  646. 'jest/no-large-snapshots': ['error', {maxSize: 2000}],
  647. 'sentry/no-styled-shortcut': ['error'],
  648. };
  649. const extendsList = [
  650. 'plugin:jest/recommended',
  651. 'plugin:jest-dom/recommended',
  652. 'plugin:import/typescript',
  653. ];
  654. if (detectDeprecations) {
  655. extendsList.push('plugin:deprecation/recommended');
  656. }
  657. module.exports = {
  658. root: true,
  659. extends: extendsList,
  660. plugins: [
  661. 'jest-dom',
  662. 'testing-library',
  663. 'typescript-sort-keys',
  664. 'react-hooks',
  665. '@typescript-eslint',
  666. '@emotion',
  667. 'import',
  668. 'react',
  669. 'sentry',
  670. 'simple-import-sort',
  671. 'no-lookahead-lookbehind-regexp',
  672. ],
  673. parser: '@typescript-eslint/parser',
  674. parserOptions: detectDeprecations
  675. ? {
  676. warnOnUnsupportedTypeScriptVersion: false,
  677. ecmaVersion: 6,
  678. sourceType: 'module',
  679. ecmaFeatures: {
  680. jsx: true,
  681. modules: true,
  682. legacyDecorators: true,
  683. },
  684. project: './tsconfig.json',
  685. }
  686. : {
  687. warnOnUnsupportedTypeScriptVersion: false,
  688. ecmaVersion: 6,
  689. sourceType: 'module',
  690. ecmaFeatures: {
  691. jsx: true,
  692. modules: true,
  693. legacyDecorators: true,
  694. },
  695. },
  696. env: {
  697. browser: true,
  698. es6: true,
  699. jest: true,
  700. jquery: true, // hard-loaded into vendor.js
  701. },
  702. globals: {
  703. require: false,
  704. expect: false,
  705. MockApiClient: true,
  706. tick: true,
  707. jest: true,
  708. },
  709. settings: {
  710. react: {
  711. version: '17.0.2', // React version, can not `detect` because of getsentry
  712. },
  713. 'import/parsers': {
  714. '@typescript-eslint/parser': ['.ts', '.tsx'],
  715. },
  716. 'import/resolver': {
  717. typescript: {},
  718. },
  719. 'import/extensions': ['.js', '.jsx'],
  720. },
  721. rules: {
  722. ...baseRules,
  723. ...reactRules,
  724. ...appRules,
  725. ...strictRules,
  726. 'react-hooks/rules-of-hooks': 'error',
  727. 'react-hooks/exhaustive-deps': [
  728. 'error',
  729. {additionalHooks: '(useEffectAfterFirstRender|useMemoWithPrevious)'},
  730. ],
  731. 'no-restricted-imports': [
  732. 'error',
  733. {
  734. patterns: [
  735. {
  736. group: ['sentry/components/devtoolbar/*'],
  737. message: 'Do not depend on toolbar internals',
  738. },
  739. ],
  740. paths: [
  741. {
  742. name: '@testing-library/react',
  743. message:
  744. 'Please import from `sentry-test/reactTestingLibrary` instead so that we can ensure consistency throughout the codebase',
  745. },
  746. {
  747. name: '@testing-library/react-hooks',
  748. message:
  749. 'Please import from `sentry-test/reactTestingLibrary` instead so that we can ensure consistency throughout the codebase',
  750. },
  751. {
  752. name: '@testing-library/user-event',
  753. message:
  754. 'Please import from `sentry-test/reactTestingLibrary` instead so that we can ensure consistency throughout the codebase',
  755. },
  756. {
  757. name: '@sentry/browser',
  758. message:
  759. 'Please import from `@sentry/react` to ensure consistency throughout the codebase.',
  760. },
  761. {
  762. name: 'marked',
  763. message:
  764. "Please import marked from 'app/utils/marked' so that we can ensure sanitation of marked output",
  765. },
  766. {
  767. name: 'lodash',
  768. message:
  769. "Please import lodash utilities individually. e.g. `import isEqual from 'lodash/isEqual';`. See https://github.com/getsentry/frontend-handbook#lodash from for information",
  770. },
  771. {
  772. name: 'lodash/get',
  773. message:
  774. 'Optional chaining `?.` and nullish coalescing operators `??` are available and preferred over using `lodash/get`. See https://github.com/getsentry/frontend-handbook#new-syntax for more information',
  775. },
  776. {
  777. name: 'sentry/utils/theme',
  778. importNames: ['lightColors', 'darkColors'],
  779. message:
  780. "'lightColors' and 'darkColors' exports intended for use in Storybook only. Instead, use theme prop from emotion or the useTheme hook.",
  781. },
  782. {
  783. name: 'react-router',
  784. importNames: ['withRouter'],
  785. message:
  786. "Use 'useLocation', 'useParams', 'useNavigate', 'useRoutes' from sentry/utils instead.",
  787. },
  788. {
  789. name: 'sentry/utils/withSentryRouter',
  790. importNames: ['withSentryRouter'],
  791. message:
  792. "Use 'useLocation', 'useParams', 'useNavigate', 'useRoutes' from sentry/utils instead.",
  793. },
  794. {
  795. name: 'qs',
  796. message: 'Please use query-string instead of qs',
  797. },
  798. {
  799. name: 'moment',
  800. message: 'Please import moment-timezone instead of moment',
  801. },
  802. ],
  803. },
  804. ],
  805. // TODO(@anonrig): Remove this from eslint-sentry-config
  806. 'space-infix-ops': 'off',
  807. 'object-shorthand': 'off',
  808. 'object-curly-spacing': 'off',
  809. 'import/no-amd': 'off',
  810. 'no-danger-with-children': 'off',
  811. 'no-fallthrough': 'off',
  812. 'no-obj-calls': 'off',
  813. 'array-bracket-spacing': 'off',
  814. 'computed-property-spacing': 'off',
  815. 'react/no-danger-with-children': 'off',
  816. 'jest/no-disabled-tests': 'off',
  817. },
  818. // JSON file formatting is handled by Biome. ESLint should not be linting
  819. // and formatting these files.
  820. ignorePatterns: ['*.json'],
  821. overrides: [
  822. {
  823. files: ['static/app/components/devtoolbar/**/*.{ts,tsx}'],
  824. rules: {
  825. 'no-restricted-imports': [
  826. 'error',
  827. {
  828. paths: [
  829. {
  830. name: 'sentry/utils/queryClient',
  831. message:
  832. 'Import from `@tanstack/react-query` and `./hooks/useFetchApiData` or `./hooks/useFetchInfiniteApiData` instead.',
  833. },
  834. ],
  835. },
  836. ],
  837. },
  838. },
  839. {
  840. files: ['static/**/*.spec.{ts,js}', 'tests/js/**/*.{ts,js}'],
  841. extends: ['plugin:testing-library/react', ...extendsList],
  842. rules: {
  843. ...baseRules,
  844. ...reactRules,
  845. ...appRules,
  846. ...strictRules,
  847. // TODO(@anonrig): Remove this from eslint-sentry-config
  848. 'space-infix-ops': 'off',
  849. 'object-shorthand': 'off',
  850. 'object-curly-spacing': 'off',
  851. 'import/no-amd': 'off',
  852. 'no-danger-with-children': 'off',
  853. 'no-fallthrough': 'off',
  854. 'no-obj-calls': 'off',
  855. 'array-bracket-spacing': 'off',
  856. 'computed-property-spacing': 'off',
  857. 'react/no-danger-with-children': 'off',
  858. 'jest/no-disabled-tests': 'off',
  859. },
  860. },
  861. {
  862. // We specify rules explicitly for the sdk-loader here so we do not have
  863. // eslint ignore comments included in the source file, which is consumed
  864. // by users.
  865. files: ['**/js-sdk-loader.ts'],
  866. rules: {
  867. 'no-console': 'off',
  868. },
  869. },
  870. ],
  871. };