.eslintrc.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661
  1. const _ = require('lodash')
  2. const confusingBrowserGlobals = require('confusing-browser-globals')
  3. const globals = require('globals')
  4. module.exports = {
  5. root: true,
  6. parser: '@typescript-eslint/parser',
  7. extends: [
  8. 'eslint:recommended',
  9. 'plugin:@typescript-eslint/recommended',
  10. 'standard',
  11. 'prettier',
  12. ],
  13. plugins: ['@overleaf'],
  14. env: {
  15. es2020: true,
  16. },
  17. settings: {
  18. // Tell eslint-plugin-react to detect which version of React we are using
  19. react: {
  20. version: 'detect',
  21. },
  22. },
  23. rules: {
  24. 'no-constant-binary-expression': 'error',
  25. 'no-restricted-globals': ['error', ...confusingBrowserGlobals],
  26. // do not allow importing of implicit dependencies.
  27. 'import/no-extraneous-dependencies': 'error',
  28. '@overleaf/prefer-kebab-url': 'error',
  29. // disable some TypeScript rules
  30. '@typescript-eslint/no-var-requires': 'off',
  31. '@typescript-eslint/no-unused-vars': 'off',
  32. '@typescript-eslint/no-empty-function': 'off',
  33. '@typescript-eslint/no-explicit-any': 'off',
  34. '@typescript-eslint/no-this-alias': 'off',
  35. '@typescript-eslint/no-non-null-assertion': 'off',
  36. '@typescript-eslint/ban-ts-comment': 'off',
  37. 'no-use-before-define': 'off',
  38. '@typescript-eslint/no-use-before-define': [
  39. 'error',
  40. { functions: false, classes: false, variables: false },
  41. ],
  42. 'react-hooks/exhaustive-deps': [
  43. 'warn',
  44. {
  45. additionalHooks: '(useCommandProvider)',
  46. },
  47. ],
  48. },
  49. overrides: [
  50. // NOTE: changing paths may require updating them in the Makefile too.
  51. {
  52. // Node
  53. files: [
  54. '**/app/src/**/*.{js,mjs}',
  55. 'app.{js,mjs}',
  56. 'i18next-scanner.config.js',
  57. 'scripts/**/*.{js,mjs}',
  58. 'webpack.config*.js',
  59. ],
  60. env: {
  61. node: true,
  62. },
  63. },
  64. {
  65. // Test specific rules
  66. files: ['**/test/**/*.*'],
  67. excludedFiles: [
  68. '**/test/unit/src/**/*.test.mjs',
  69. 'test/unit/vitest_bootstrap.mjs',
  70. ], // exclude vitest files
  71. plugins: ['mocha', 'chai-expect', 'chai-friendly'],
  72. env: {
  73. mocha: true,
  74. },
  75. rules: {
  76. // mocha-specific rules
  77. 'mocha/handle-done-callback': 'error',
  78. 'mocha/no-exclusive-tests': 'error',
  79. 'mocha/no-global-tests': 'error',
  80. 'mocha/no-identical-title': 'error',
  81. 'mocha/no-nested-tests': 'error',
  82. 'mocha/no-pending-tests': 'error',
  83. 'mocha/no-skipped-tests': 'error',
  84. 'mocha/no-mocha-arrows': 'error',
  85. // Swap the no-unused-expressions rule with a more chai-friendly one
  86. 'no-unused-expressions': 'off',
  87. 'chai-friendly/no-unused-expressions': 'error',
  88. // chai-specific rules
  89. 'chai-expect/missing-assertion': 'error',
  90. 'chai-expect/terminating-properties': 'error',
  91. // prefer-arrow-callback applies to all callbacks, not just ones in mocha tests.
  92. // we don't enforce this at the top-level - just in tests to manage `this` scope
  93. // based on mocha's context mechanism
  94. 'mocha/prefer-arrow-callback': 'error',
  95. '@typescript-eslint/no-unused-expressions': 'off',
  96. },
  97. },
  98. {
  99. files: [
  100. '**/test/unit/src/**/*.test.mjs',
  101. 'test/unit/vitest_bootstrap.mjs',
  102. ],
  103. env: {
  104. jest: true, // best match for vitest API etc.
  105. },
  106. plugins: ['@vitest', 'chai-expect', 'chai-friendly'], // still using chai for now
  107. rules: {
  108. // vitest-specific rules
  109. '@vitest/no-focused-tests': 'error',
  110. '@vitest/no-disabled-tests': 'error',
  111. // Swap the no-unused-expressions rule with a more chai-friendly one
  112. 'no-unused-expressions': 'off',
  113. 'chai-friendly/no-unused-expressions': 'error',
  114. // chai-specific rules
  115. 'chai-expect/missing-assertion': 'error',
  116. 'chai-expect/terminating-properties': 'error',
  117. '@typescript-eslint/no-unused-expressions': 'off',
  118. '@overleaf/require-vi-doMock-valid-path': 'error',
  119. },
  120. },
  121. {
  122. // ES specific rules
  123. files: [
  124. '**/app/src/**/*.mjs',
  125. 'modules/*/index.mjs',
  126. 'app.mjs',
  127. 'scripts/**/*.mjs',
  128. 'migrations/**/*.mjs',
  129. ],
  130. excludedFiles: [
  131. // migration template file
  132. 'migrations/lib/template.mjs',
  133. ],
  134. parserOptions: {
  135. sourceType: 'module',
  136. },
  137. plugins: ['unicorn'],
  138. rules: {
  139. 'import/no-unresolved': [
  140. 'error',
  141. {
  142. // eslint-plugin-import does not support exports directive in package.json
  143. // https://github.com/import-js/eslint-plugin-import/issues/1810
  144. ignore: ['^p-queue$'],
  145. },
  146. ],
  147. 'import/extensions': [
  148. 'error',
  149. 'ignorePackages',
  150. {
  151. js: 'always',
  152. mjs: 'always',
  153. },
  154. ],
  155. 'unicorn/prefer-module': 'error',
  156. 'unicorn/prefer-node-protocol': 'error',
  157. },
  158. },
  159. {
  160. // Backend specific rules
  161. files: ['**/app/src/**/*.{js,mjs}', 'app.{js,mjs}'],
  162. parserOptions: {
  163. tsconfigRootDir: __dirname,
  164. project: './tsconfig.backend.json',
  165. },
  166. rules: {
  167. // do not allow importing of implicit dependencies.
  168. 'import/no-extraneous-dependencies': [
  169. 'error',
  170. {
  171. // do not allow importing of devDependencies.
  172. devDependencies: false,
  173. },
  174. ],
  175. 'no-restricted-syntax': [
  176. 'error',
  177. // do not allow node-fetch in backend code
  178. {
  179. selector:
  180. "CallExpression[callee.name='require'] > .arguments[value='node-fetch']",
  181. message:
  182. 'Requiring node-fetch is not allowed in production services, please use fetch-utils.',
  183. },
  184. // mongoose populate must set fields to populate
  185. {
  186. selector:
  187. "CallExpression[callee.property.name='populate'][arguments.length<2]",
  188. message:
  189. "Populate without a second argument returns the whole document. Use populate('field',['prop1','prop2']) instead",
  190. },
  191. // Require `new` when constructing ObjectId (For mongo + mongoose upgrade)
  192. {
  193. selector:
  194. "CallExpression[callee.name='ObjectId'], CallExpression[callee.property.name='ObjectId']",
  195. message:
  196. 'Construct ObjectId with `new ObjectId()` instead of `ObjectId()`',
  197. },
  198. // Require `new` when mapping a list of ids to a list of ObjectId (For mongo + mongoose upgrade)
  199. {
  200. selector:
  201. "CallExpression[callee.property.name='map'] Identifier[name='ObjectId']:first-child, CallExpression[callee.property.name='map'] MemberExpression[property.name='ObjectId']:first-child",
  202. message:
  203. "Don't map ObjectId directly. Use `id => new ObjectId(id)` instead",
  204. },
  205. // Catch incorrect usage of `await db.collection.find()`
  206. {
  207. selector:
  208. "AwaitExpression > CallExpression > MemberExpression[property.name='find'][object.object.name='db']",
  209. message:
  210. 'Mongo find returns a cursor not a promise, use `for await (const result of cursor)` or `.toArray()` instead.',
  211. },
  212. ],
  213. '@typescript-eslint/no-floating-promises': [
  214. 'error',
  215. { checkThenables: true },
  216. ],
  217. },
  218. },
  219. {
  220. // Backend scripts specific rules
  221. files: ['**/scripts/**/*.{js,mjs}'],
  222. rules: {
  223. 'no-restricted-syntax': [
  224. 'error',
  225. // Require `new` when constructing ObjectId (For mongo + mongoose upgrade)
  226. {
  227. selector:
  228. "CallExpression[callee.name='ObjectId'], CallExpression[callee.property.name='ObjectId']",
  229. message:
  230. 'Construct ObjectId with `new ObjectId()` instead of `ObjectId()`',
  231. },
  232. // Require `new` when mapping a list of ids to a list of ObjectId (For mongo + mongoose upgrade)
  233. {
  234. selector:
  235. "CallExpression[callee.property.name='map'] Identifier[name='ObjectId']:first-child, CallExpression[callee.property.name='map'] MemberExpression[property.name='ObjectId']:first-child",
  236. message:
  237. "Don't map ObjectId directly. Use `id => new ObjectId(id)` instead",
  238. },
  239. // Catch incorrect usage of `await db.collection.find()`
  240. {
  241. selector:
  242. "AwaitExpression > CallExpression > MemberExpression[property.name='find'][object.object.name='db']",
  243. message:
  244. 'Mongo find returns a cursor not a promise, use `for await (const result of cursor)` or `.toArray()` instead.',
  245. },
  246. ],
  247. },
  248. },
  249. {
  250. // Insist on using Script Runner for new scripts. Old scripts should be
  251. // converted to use Script Runner in future, but are excluded for now
  252. rules: {
  253. '@overleaf/require-script-runner': 'error',
  254. },
  255. files: ['**/scripts/**/*.mjs'], // ESM only
  256. excludedFiles: [
  257. 'modules/admin-roles/scripts/import_admin_role_assignments.mjs',
  258. 'modules/admin-roles/scripts/remove_admin_role_from_user.mjs',
  259. 'modules/admin-roles/scripts/remove_admin_roles_from_non_admins.mjs',
  260. 'modules/admin-roles/scripts/utils.mjs',
  261. 'modules/institutions/scripts/apply_policy_to_institution.mjs',
  262. 'modules/server-ce-scripts/scripts/change-compile-timeout.mjs',
  263. 'modules/server-ce-scripts/scripts/check-mongodb.mjs',
  264. 'modules/server-ce-scripts/scripts/check-redis.mjs',
  265. 'modules/server-ce-scripts/scripts/check-texlive-images.mjs',
  266. 'modules/server-ce-scripts/scripts/create-user.mjs',
  267. 'modules/server-ce-scripts/scripts/delete-user.mjs',
  268. 'modules/server-ce-scripts/scripts/export-user-projects.mjs',
  269. 'modules/server-ce-scripts/scripts/migrate-user-emails.mjs',
  270. 'modules/server-ce-scripts/scripts/rename-tag.mjs',
  271. 'modules/server-ce-scripts/scripts/transfer-all-projects-to-user.mjs',
  272. 'modules/server-ce-scripts/scripts/upgrade-user-features.mjs',
  273. 'modules/subscriptions/scripts/backfill_user_last_trial.mjs',
  274. 'scripts/add_feature_override.mjs',
  275. 'scripts/add_subscription_members_csv.mjs',
  276. 'scripts/analytics/helpers/GoogleBigQueryHelper.mjs',
  277. 'scripts/attach_dangling_comments_to_doc.mjs',
  278. 'scripts/back_fill_doc_rev.mjs',
  279. 'scripts/backfill_mixpanel_user_properties.mjs',
  280. 'scripts/backfill_project_image_name.mjs',
  281. 'scripts/backfill_project_invites_token_hmac.mjs',
  282. 'scripts/backfill_user_properties.mjs',
  283. 'scripts/backfill_users_sso_attribute.mjs',
  284. 'scripts/bench_bcrypt.mjs',
  285. 'scripts/check_institution_users.mjs',
  286. 'scripts/check_overleafModuleImports.mjs',
  287. 'scripts/check_saml_emails.mjs',
  288. 'scripts/clear_feedback_collection.mjs',
  289. 'scripts/clear_sessions_set_must_reconfirm.mjs',
  290. 'scripts/count_files_in_projects.mjs',
  291. 'scripts/count_project_size.mjs',
  292. 'scripts/create_oauth_personal_access_token.mjs',
  293. 'scripts/create_project.mjs',
  294. 'scripts/deactivate_projects.mjs',
  295. 'scripts/delete-duplicate-splittest-versions/delete_test_dupes.mjs',
  296. 'scripts/delete-orphaned-docs/delete-orphaned-docs.mjs',
  297. 'scripts/delete_dangling_comments.mjs',
  298. 'scripts/delete_orphaned_chat_threads.mjs',
  299. 'scripts/delete_orphaned_data_helper.mjs',
  300. 'scripts/delete_subscriptions.mjs',
  301. 'scripts/e2e_test_setup.mjs',
  302. 'scripts/ensure_affiliations.mjs',
  303. 'scripts/esm-check-migration.mjs',
  304. 'scripts/example/script_for_migration.mjs',
  305. 'scripts/fix_collaborator_refs_null.mjs',
  306. 'scripts/fix_comment_id.mjs',
  307. 'scripts/helpers/chunkArray.mjs',
  308. 'scripts/helpers/env_variable_helper.mjs',
  309. 'scripts/inst_table.mjs',
  310. 'scripts/invalidate_tokens.mjs',
  311. 'scripts/ip_matcher_ranges.mjs',
  312. 'scripts/learn/checkSanitize/checkSanitizeOptions.mjs',
  313. 'scripts/learn/checkSanitize/scrape.mjs',
  314. 'scripts/lezer-latex/benchmark.mjs',
  315. 'scripts/lezer-latex/print-tree.mjs',
  316. 'scripts/lezer-latex/random.mjs',
  317. 'scripts/lezer-latex/run.mjs',
  318. 'scripts/lezer-latex/test-incremental-parser.mjs',
  319. 'scripts/mark_migration.mjs',
  320. 'scripts/marketing-exports/error-assistant-export.mjs',
  321. 'scripts/marketing-exports/export.mjs',
  322. 'scripts/marketing-exports/linked-papers-users.mjs',
  323. 'scripts/marketing-exports/papers-export.mjs',
  324. 'scripts/marketing-exports/writefull-export.mjs',
  325. 'scripts/oauth/upgrade_token_scopes.mjs',
  326. 'scripts/plan-prices/plans.mjs',
  327. 'scripts/process_lapsed_reconfirmations.mjs',
  328. 'scripts/purge_non_logged_in_sessions.mjs',
  329. 'scripts/recurly/generate_recurly_prices.mjs',
  330. 'scripts/recurly/get_paypal_accounts_csv.mjs',
  331. 'scripts/recurly/recurly_prices.mjs',
  332. 'scripts/recurly/resync_recurly_state_single_subscription.mjs',
  333. 'scripts/recurly/resync_subscriptions.mjs',
  334. 'scripts/recurly/set_manually_collected_subscriptions.mjs',
  335. 'scripts/refresh_features.mjs',
  336. 'scripts/regenerate_duplicate_referral_ids.mjs',
  337. 'scripts/remove_deleted_users_from_token_access_refs.mjs',
  338. 'scripts/remove_email.mjs',
  339. 'scripts/remove_user_enrollment.mjs',
  340. 'scripts/sso_id_migration_check.mjs',
  341. 'scripts/stress_test.mjs',
  342. 'scripts/suspend_users.mjs',
  343. 'scripts/sync-user-entitlements/sync-user-entitlements.mjs',
  344. 'scripts/update_project_image_name.mjs',
  345. 'scripts/user-export/fs.mjs',
  346. 'scripts/user-export/http.mjs',
  347. 'scripts/user-export/observer.mjs',
  348. 'scripts/user-export/options.mjs',
  349. 'scripts/user-export/project.mjs',
  350. 'scripts/user-export/scrubber.mjs',
  351. 'scripts/user-export/stream.mjs',
  352. 'scripts/user-export/user.mjs',
  353. 'scripts/validate-data-of-model.mjs',
  354. ],
  355. },
  356. {
  357. // Cypress specific rules
  358. files: [
  359. 'cypress/**/*.{js,jsx,ts,tsx}',
  360. '**/test/frontend/**/*.spec.{js,jsx,ts,tsx}',
  361. ],
  362. extends: ['plugin:cypress/recommended'],
  363. },
  364. {
  365. // Frontend test specific rules
  366. files: ['**/frontend/**/*.test.{js,jsx,ts,tsx}'],
  367. plugins: ['testing-library'],
  368. extends: ['plugin:testing-library/react'],
  369. rules: {
  370. 'testing-library/no-await-sync-events': 'off',
  371. 'testing-library/no-await-sync-queries': 'off',
  372. 'testing-library/no-container': 'off',
  373. 'testing-library/no-node-access': 'off',
  374. 'testing-library/no-render-in-lifecycle': 'off',
  375. 'testing-library/no-wait-for-multiple-assertions': 'off',
  376. 'testing-library/no-wait-for-side-effects': 'off',
  377. 'testing-library/prefer-query-by-disappearance': 'off',
  378. 'testing-library/prefer-screen-queries': 'off',
  379. 'testing-library/render-result-naming-convention': 'off',
  380. },
  381. },
  382. {
  383. // Frontend specific rules
  384. files: [
  385. '**/frontend/js/**/*.{js,jsx,ts,tsx}',
  386. '**/frontend/stories/**/*.{js,jsx,ts,tsx}',
  387. '**/*.stories.{js,jsx,ts,tsx}',
  388. '**/test/frontend/**/*.{js,jsx,ts,tsx}',
  389. '**/test/frontend/components/**/*.spec.{js,jsx,ts,tsx}',
  390. ],
  391. env: {
  392. browser: true,
  393. },
  394. parserOptions: {
  395. sourceType: 'module',
  396. },
  397. plugins: ['jsx-a11y'],
  398. extends: [
  399. 'plugin:react/recommended',
  400. 'plugin:react-hooks/recommended',
  401. 'plugin:jsx-a11y/recommended',
  402. 'standard-jsx',
  403. 'prettier',
  404. ],
  405. globals: {
  406. __webpack_public_path__: true,
  407. $: true,
  408. ga: true,
  409. },
  410. rules: {
  411. // TODO: remove once https://github.com/standard/eslint-config-standard-react/issues/68 (support eslint@8) is fixed.
  412. // START: inline standard-react rules
  413. // "react/jsx-no-bind": ["error", {
  414. // "allowArrowFunctions": true,
  415. // "allowBind": false,
  416. // "ignoreRefs": true
  417. // },],
  418. 'react/no-did-update-set-state': 'error',
  419. 'react/no-unused-prop-types': 'error',
  420. 'react/prop-types': 'error',
  421. '@overleaf/no-generated-editor-themes': 'error',
  422. // "react/react-in-jsx-scope": "error",
  423. // END: inline standard-react rules
  424. 'react/no-unknown-property': [
  425. 'error',
  426. {
  427. ignore: ['dnd-container', 'dropdown-toggle'],
  428. },
  429. ],
  430. 'react/jsx-no-target-blank': [
  431. 'error',
  432. {
  433. allowReferrer: true,
  434. },
  435. ],
  436. // Prevent usage of legacy string refs
  437. 'react/no-string-refs': 'error',
  438. // Prevent curly braces around strings (as they're unnecessary)
  439. 'react/jsx-curly-brace-presence': [
  440. 'error',
  441. {
  442. props: 'never',
  443. children: 'never',
  444. },
  445. ],
  446. // Don't import React for JSX; the JSX runtime is added by a Babel plugin
  447. 'react/react-in-jsx-scope': 'off',
  448. 'react/jsx-uses-react': 'off',
  449. // Allow functions as JSX props
  450. 'react/jsx-no-bind': 'off', // TODO: fix occurrences and re-enable this
  451. // Fix conflict between prettier & standard by overriding to prefer
  452. // double quotes
  453. 'jsx-quotes': ['error', 'prefer-double'],
  454. // Override weird behaviour of jsx-a11y label-has-for (says labels must be
  455. // nested *and* have for/id attributes)
  456. 'jsx-a11y/label-has-for': [
  457. 'error',
  458. {
  459. required: {
  460. some: ['nesting', 'id'],
  461. },
  462. },
  463. ],
  464. // Require .jsx or .tsx file extension when using JSX
  465. 'react/jsx-filename-extension': [
  466. 'error',
  467. {
  468. extensions: ['.jsx', '.tsx'],
  469. },
  470. ],
  471. 'no-restricted-syntax': [
  472. 'error',
  473. // prohibit direct calls to methods of window.localStorage
  474. {
  475. selector:
  476. "CallExpression[callee.object.object.name='window'][callee.object.property.name='localStorage']",
  477. message:
  478. 'Modify location via customLocalStorage instead of calling window.localStorage methods directly',
  479. },
  480. ],
  481. 'no-unused-vars': 'off',
  482. '@typescript-eslint/no-unused-vars': [
  483. 'error',
  484. {
  485. args: 'after-used',
  486. argsIgnorePattern: '^_',
  487. ignoreRestSiblings: false,
  488. caughtErrors: 'none',
  489. vars: 'all',
  490. varsIgnorePattern: '^_',
  491. },
  492. ],
  493. },
  494. },
  495. {
  496. // Sorting for Meta
  497. files: ['frontend/js/utils/meta.ts'],
  498. rules: {
  499. '@typescript-eslint/member-ordering': [
  500. 'error',
  501. { interfaces: { order: 'alphabetically' } },
  502. ],
  503. },
  504. },
  505. {
  506. // React component specific rules
  507. //
  508. files: [
  509. '**/frontend/js/**/components/**/*.{js,jsx,ts,tsx}',
  510. '**/frontend/js/**/hooks/**/*.{js,jsx,ts,tsx}',
  511. ],
  512. rules: {
  513. '@overleaf/no-unnecessary-trans': 'error',
  514. '@overleaf/should-unescape-trans': 'error',
  515. // https://astexplorer.net/
  516. 'no-restricted-syntax': [
  517. 'error',
  518. // prohibit direct calls to methods of window.location
  519. {
  520. selector:
  521. "CallExpression[callee.object.object.name='window'][callee.object.property.name='location']",
  522. message:
  523. 'Modify location via useLocation instead of calling window.location methods directly',
  524. },
  525. // prohibit assignment to window.location
  526. {
  527. selector:
  528. "AssignmentExpression[left.object.name='window'][left.property.name='location']",
  529. message:
  530. 'Modify location via useLocation instead of calling window.location methods directly',
  531. },
  532. // prohibit assignment to window.location.href
  533. {
  534. selector:
  535. "AssignmentExpression[left.object.object.name='window'][left.object.property.name='location'][left.property.name='href']",
  536. message:
  537. 'Modify location via useLocation instead of calling window.location methods directly',
  538. },
  539. // prohibit using lookbehinds due to incidents with Safari simply crashing when the script is parsed
  540. {
  541. selector: 'Literal[regex.pattern=/\\(\\?<[!=]/]',
  542. message: 'Lookbehind is not supported in older Safari versions.',
  543. },
  544. // prohibit direct calls to methods of window.localStorage
  545. // NOTE: this rule is also defined for all frontend files, but those rules are overriden by the React component-specific config
  546. {
  547. selector:
  548. "CallExpression[callee.object.object.name='window'][callee.object.property.name='localStorage']",
  549. message:
  550. 'Modify location via customLocalStorage instead of calling window.localStorage methods directly',
  551. },
  552. ],
  553. },
  554. },
  555. // React + TypeScript-specific rules
  556. {
  557. files: ['**/*.tsx'],
  558. rules: {
  559. 'react/prop-types': 'off',
  560. 'no-undef': 'off',
  561. },
  562. },
  563. // TypeScript-specific rules
  564. {
  565. files: ['**/*.ts'],
  566. rules: {
  567. 'no-undef': 'off',
  568. },
  569. },
  570. // JavaScript-specific rules
  571. {
  572. files: ['**/*.js'],
  573. rules: {
  574. '@typescript-eslint/no-require-imports': 'off',
  575. },
  576. },
  577. {
  578. files: ['scripts/ukamf/*.js'],
  579. rules: {
  580. // Do not allow importing of any dependencies unless specified in either
  581. // - web/package.json
  582. // - web/scripts/ukamf/package.json
  583. 'import/no-extraneous-dependencies': [
  584. 'error',
  585. { packageDir: ['.', 'scripts/ukamf'] },
  586. ],
  587. },
  588. },
  589. {
  590. files: ['scripts/learn/checkSanitize/*.js'],
  591. rules: {
  592. // The checkSanitize script is used in the dev-env only.
  593. 'import/no-extraneous-dependencies': [
  594. 'error',
  595. {
  596. devDependencies: true,
  597. packageDir: ['.', '../../'],
  598. },
  599. ],
  600. },
  601. },
  602. {
  603. files: [
  604. // Backend: Use @overleaf/logger
  605. // Docs: https://manual.dev-overleaf.com/development/code/logging/#structured-logging
  606. '**/app/**/*.{js,cjs,mjs}',
  607. 'app.{js,mjs}',
  608. 'modules/*/*.{js,mjs}',
  609. // Frontend: Prefer debugConsole over bare console
  610. // Docs: https://manual.dev-overleaf.com/development/code/logging/#frontend
  611. '**/frontend/**/*.{js,jsx,ts,tsx}',
  612. // Tests
  613. '**/test/**/*.{js,cjs,mjs,jsx,ts,tsx}',
  614. ],
  615. excludedFiles: [
  616. // Allow console logs in scripts
  617. '**/scripts/**/*.js',
  618. // Allow console logs in stories
  619. '**/stories/**/*.{js,jsx,ts,tsx}',
  620. // Workers do not have access to the search params for enabling ?debug=true.
  621. // self.location.url is the URL of the worker script.
  622. '*.worker.{js,ts}',
  623. ],
  624. rules: {
  625. 'no-console': 'error',
  626. },
  627. },
  628. {
  629. files: ['**/*.worker.{js,ts}'],
  630. rules: {
  631. 'no-restricted-globals': [
  632. 'error',
  633. ..._.difference(
  634. Object.keys({ ...globals.browser, ...globals.node }),
  635. Object.keys(globals.worker)
  636. ),
  637. ],
  638. },
  639. },
  640. ],
  641. }