.eslintrc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. {
  2. "root": true,
  3. "parser": "@typescript-eslint/parser",
  4. "extends": [
  5. "eslint:recommended",
  6. "plugin:@typescript-eslint/recommended",
  7. "standard",
  8. "prettier"
  9. ],
  10. "plugins": ["@overleaf"],
  11. "env": {
  12. "es2020": true
  13. },
  14. "settings": {
  15. // Tell eslint-plugin-react to detect which version of React we are using
  16. "react": {
  17. "version": "detect"
  18. }
  19. },
  20. "rules": {
  21. "no-constant-binary-expression": "error",
  22. // do not allow importing of implicit dependencies.
  23. "import/no-extraneous-dependencies": "error",
  24. // disable some TypeScript rules
  25. "@typescript-eslint/no-var-requires": "off",
  26. "@typescript-eslint/no-unused-vars": "off",
  27. "@typescript-eslint/no-empty-function": "off",
  28. "@typescript-eslint/no-explicit-any": "off",
  29. "@typescript-eslint/no-this-alias": "off",
  30. "@typescript-eslint/no-non-null-assertion": "off",
  31. "@typescript-eslint/ban-ts-comment": "off"
  32. },
  33. "overrides": [
  34. // NOTE: changing paths may require updating them in the Makefile too.
  35. {
  36. // Node
  37. "files": [
  38. "**/app/src/**/*.js",
  39. "app.js",
  40. "i18next-scanner.config.js",
  41. "scripts/**/*.js",
  42. "webpack.config*.js"
  43. ],
  44. "env": {
  45. "node": true
  46. }
  47. },
  48. {
  49. // Test specific rules
  50. "files": ["**/test/**/*.*"],
  51. "plugins": [
  52. "mocha",
  53. "chai-expect",
  54. "chai-friendly"
  55. ],
  56. "env": {
  57. "mocha": true
  58. },
  59. "rules": {
  60. // mocha-specific rules
  61. "mocha/handle-done-callback": "error",
  62. "mocha/no-exclusive-tests": "error",
  63. "mocha/no-global-tests": "error",
  64. "mocha/no-identical-title": "error",
  65. "mocha/no-nested-tests": "error",
  66. "mocha/no-pending-tests": "error",
  67. "mocha/no-skipped-tests": "error",
  68. "mocha/no-mocha-arrows": "error",
  69. // Swap the no-unused-expressions rule with a more chai-friendly one
  70. "no-unused-expressions": "off",
  71. "chai-friendly/no-unused-expressions": "error",
  72. // chai-specific rules
  73. "chai-expect/missing-assertion": "error",
  74. "chai-expect/terminating-properties": "error",
  75. // prefer-arrow-callback applies to all callbacks, not just ones in mocha tests.
  76. // we don't enforce this at the top-level - just in tests to manage `this` scope
  77. // based on mocha's context mechanism
  78. "mocha/prefer-arrow-callback": "error"
  79. }
  80. },
  81. {
  82. // Backend specific rules
  83. "files": ["**/app/src/**/*.js", "app.js"],
  84. "rules": {
  85. // do not allow importing of implicit dependencies.
  86. "import/no-extraneous-dependencies": ["error", {
  87. // do not allow importing of devDependencies.
  88. "devDependencies": false
  89. }],
  90. // do not allow node-fetch in backend code
  91. "no-restricted-syntax": [
  92. "error",
  93. {
  94. "selector": "CallExpression[callee.name='require'] > .arguments[value='node-fetch']",
  95. "message": "Requiring node-fetch is not allowed in production services, please use fetch-utils."
  96. }
  97. ]
  98. }
  99. },
  100. {
  101. // Backend + backend tests specific rules
  102. "files": ["**/app/src/**/*.js", "app.js", "**/test/**/*.*", "**/scripts/*.*"],
  103. "rules": {
  104. // do not allow node-fetch in backend code
  105. "no-restricted-syntax": [
  106. "error",
  107. // Require `new` when constructing ObjectId (For mongo + mongoose upgrade)
  108. {
  109. "selector": "CallExpression[callee.name='ObjectId'], CallExpression[callee.property.name='ObjectId']",
  110. "message": "Construct ObjectId with `new ObjectId()` instead of `ObjectId()`"
  111. },
  112. // Require `new` when mapping a list of ids to a list of ObjectId (For mongo + mongoose upgrade)
  113. {
  114. "selector": "CallExpression[callee.property.name='map'] Identifier[name='ObjectId']:first-child, CallExpression[callee.property.name='map'] MemberExpression[property.name='ObjectId']:first-child",
  115. "message": "Don't map ObjectId directly. Use `id => new ObjectId(id)` instead"
  116. }
  117. ]
  118. }
  119. },
  120. {
  121. // Cypress specific rules
  122. "files": ["cypress/**/*.{js,jsx,ts,tsx}", "**/test/frontend/**/*.spec.{js,jsx,ts,tsx}"],
  123. "extends": [
  124. "plugin:cypress/recommended"
  125. ]
  126. },
  127. {
  128. // React component specific rules
  129. //
  130. "files": ["**/frontend/js/**/components/**/*.{js,jsx,ts,tsx}", "**/frontend/js/**/hooks/**/*.{js,jsx,ts,tsx}"],
  131. "rules": {
  132. "@overleaf/no-unnecessary-trans": "error",
  133. "@overleaf/should-unescape-trans": "error",
  134. // https://astexplorer.net/
  135. "no-restricted-syntax": [
  136. "error",
  137. // prohibit direct calls to methods of window.location
  138. {
  139. "selector": "CallExpression[callee.object.object.name='window'][callee.object.property.name='location']",
  140. "message": "Modify location via useLocation instead of calling window.location methods directly"
  141. },
  142. // prohibit assignment to window.location
  143. {
  144. "selector": "AssignmentExpression[left.object.name='window'][left.property.name='location']",
  145. "message": "Modify location via useLocation instead of calling window.location methods directly"
  146. },
  147. // prohibit assignment to window.location.href
  148. {
  149. "selector": "AssignmentExpression[left.object.object.name='window'][left.object.property.name='location'][left.property.name='href']",
  150. "message": "Modify location via useLocation instead of calling window.location methods directly"
  151. },
  152. // prohibit using lookbehinds due to incidents with Safari simply crashing when the script is parsed
  153. {
  154. "selector": "Literal[regex.pattern=/\\(\\?<[!=]/]",
  155. "message": "Lookbehind is not supported in older Safari versions."
  156. }
  157. ]
  158. }
  159. },
  160. {
  161. // Frontend specific rules
  162. "files": ["**/frontend/js/**/*.{js,jsx,ts,tsx}", "**/frontend/stories/**/*.{js,jsx,ts,tsx}", "**/*.stories.{js,jsx,ts,tsx}", "**/test/frontend/**/*.{js,jsx,ts,tsx}", "**/test/frontend/components/**/*.spec.{js,jsx,ts,tsx}"],
  163. "env": {
  164. "browser": true
  165. },
  166. "parserOptions": {
  167. "sourceType": "module"
  168. },
  169. "plugins": [
  170. "jsx-a11y"
  171. ],
  172. "extends": [
  173. "plugin:react/recommended",
  174. "plugin:react-hooks/recommended",
  175. "plugin:jsx-a11y/recommended",
  176. "standard-jsx",
  177. "prettier"
  178. ],
  179. "globals": {
  180. "__webpack_public_path__": true,
  181. "$": true,
  182. "angular": true,
  183. "ga": true,
  184. // Injected in layout.pug
  185. "user_id": true,
  186. "ExposedSettings": true
  187. },
  188. "rules": {
  189. // TODO: remove once https://github.com/standard/eslint-config-standard-react/issues/68 (support eslint@8) is fixed.
  190. // START: inline standard-react rules
  191. // "react/jsx-no-bind": ["error", {
  192. // "allowArrowFunctions": true,
  193. // "allowBind": false,
  194. // "ignoreRefs": true
  195. // },],
  196. "react/no-did-update-set-state": "error",
  197. "react/no-unused-prop-types": "error",
  198. "react/prop-types": "error",
  199. // "react/react-in-jsx-scope": "error",
  200. // END: inline standard-react rules
  201. "react/no-unknown-property": ["error", {
  202. "ignore": ["dnd-container", "dropdown-toggle"]
  203. }],
  204. "react/jsx-no-target-blank": ["error", {
  205. "allowReferrer": true
  206. }],
  207. // Prevent usage of legacy string refs
  208. "react/no-string-refs": "error",
  209. // Prevent curly braces around strings (as they're unnecessary)
  210. "react/jsx-curly-brace-presence": ["error", {
  211. "props": "never",
  212. "children": "never"
  213. }],
  214. // Don't import React for JSX; the JSX runtime is added by a Babel plugin
  215. "react/react-in-jsx-scope": "off",
  216. "react/jsx-uses-react": "off",
  217. // Allow functions as JSX props
  218. "react/jsx-no-bind": "off", // TODO: fix occurrences and re-enable this
  219. // Fix conflict between prettier & standard by overriding to prefer
  220. // double quotes
  221. "jsx-quotes": ["error", "prefer-double"],
  222. // Override weird behaviour of jsx-a11y label-has-for (says labels must be
  223. // nested *and* have for/id attributes)
  224. "jsx-a11y/label-has-for": [
  225. "error",
  226. {
  227. "required": {
  228. "some": [
  229. "nesting",
  230. "id"
  231. ]
  232. }
  233. }
  234. ],
  235. // Require .jsx or .tsx file extension when using JSX
  236. "react/jsx-filename-extension": ["error", {
  237. "extensions": [".jsx", ".tsx"]
  238. }],
  239. "no-restricted-syntax": [
  240. "error",
  241. // Begin: Make sure angular can withstand minification
  242. {
  243. "selector": "CallExpression[callee.object.name='App'][callee.property.name=/run|directive|config|controller/] > :function[params.length > 0]",
  244. "message": "Wrap the function in an array with the parameter names, to withstand minifcation. E.g. App.controller('MyController', ['param1', function(param1) {}]"
  245. },
  246. {
  247. "selector": "CallExpression[callee.object.name='App'][callee.property.name=/run|directive|config|controller/] > ArrayExpression > ArrowFunctionExpression",
  248. "message": "Use standard function syntax instead of arrow function syntax in angular components. E.g. function(param1) {}"
  249. },
  250. {
  251. "selector": "CallExpression[callee.object.name='App'][callee.property.name=/run|directive|config|controller/] > ArrowFunctionExpression",
  252. "message": "Use standard function syntax instead of arrow function syntax in angular components. E.g. function(param1) {}"
  253. },
  254. {
  255. "selector": "CallExpression[callee.object.name='App'][callee.property.name=/run|directive|config|controller/] > ArrayExpression > :not(:function, Identifier):last-child",
  256. "message": "Last element of the array must be a function. E.g ['param1', function(param1) {}]"
  257. },
  258. {
  259. "selector": "CallExpression[callee.object.name='App'][callee.property.name=/run|directive|config|controller/] > ArrayExpression[elements.length=0]",
  260. "message": "Array must not be empty. Add parameters and a function. E.g ['param1', function(param1) {}]"
  261. }
  262. // End: Make sure angular can withstand minification
  263. ]
  264. }
  265. },
  266. // React + TypeScript-specific rules
  267. {
  268. "files": ["**/*.tsx"],
  269. "rules": {
  270. "react/prop-types": "off",
  271. "no-undef": "off"
  272. }
  273. },
  274. // TypeScript-specific rules
  275. {
  276. "files": ["**/*.ts"],
  277. "rules": {
  278. "no-undef": "off"
  279. }
  280. },
  281. {
  282. "files": ["scripts/ukamf/*.js"],
  283. "rules": {
  284. // Do not allow importing of any dependencies unless specified in either
  285. // - web/package.json
  286. // - web/scripts/ukamf/package.json
  287. "import/no-extraneous-dependencies": ["error", {"packageDir": [".", "scripts/ukamf"]}]
  288. }
  289. },
  290. {
  291. "files": ["scripts/learn/checkSanitize/*.js"],
  292. "rules": {
  293. // The checkSanitize script is used in the dev-env only.
  294. "import/no-extraneous-dependencies": ["error", {
  295. "devDependencies": true,
  296. "packageDir": [".", "../../"]
  297. }]
  298. }
  299. },
  300. {
  301. "files": [
  302. // Backend: Use @overleaf/logger
  303. // Docs: https://manual.dev-overleaf.com/development/code/logging/#structured-logging
  304. "**/app/**/*.{js,cjs,mjs}", "app.js", "modules/*/*.js",
  305. // Frontend: Prefer debugConsole over bare console
  306. // Docs: https://manual.dev-overleaf.com/development/code/logging/#frontend
  307. "**/frontend/**/*.{js,jsx,ts,tsx}",
  308. // Tests
  309. "**/test/**/*.{js,cjs,mjs,jsx,ts,tsx}"
  310. ],
  311. "excludedFiles": [
  312. // Allow console logs in scripts
  313. "**/scripts/**/*.js",
  314. // Allow console logs in stories
  315. "**/stories/**/*.{js,jsx,ts,tsx}",
  316. // Workers do not have access to the search params for enabling ?debug=true.
  317. // self.location.url is the URL of the worker script.
  318. "*.worker.{js,ts}"
  319. ],
  320. "rules": {
  321. "no-console": "error"
  322. }
  323. }
  324. ]
  325. }