| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341 |
- {
- "root": true,
- "parser": "@typescript-eslint/parser",
- "extends": [
- "eslint:recommended",
- "plugin:@typescript-eslint/recommended",
- "standard",
- "prettier"
- ],
- "plugins": ["@overleaf"],
- "env": {
- "es2020": true
- },
- "settings": {
- // Tell eslint-plugin-react to detect which version of React we are using
- "react": {
- "version": "detect"
- }
- },
- "rules": {
- "no-constant-binary-expression": "error",
- // do not allow importing of implicit dependencies.
- "import/no-extraneous-dependencies": "error",
- // disable some TypeScript rules
- "@typescript-eslint/no-var-requires": "off",
- "@typescript-eslint/no-unused-vars": "off",
- "@typescript-eslint/no-empty-function": "off",
- "@typescript-eslint/no-explicit-any": "off",
- "@typescript-eslint/no-this-alias": "off",
- "@typescript-eslint/no-non-null-assertion": "off",
- "@typescript-eslint/ban-ts-comment": "off"
- },
- "overrides": [
- // NOTE: changing paths may require updating them in the Makefile too.
- {
- // Node
- "files": [
- "**/app/src/**/*.js",
- "app.js",
- "i18next-scanner.config.js",
- "scripts/**/*.js",
- "webpack.config*.js"
- ],
- "env": {
- "node": true
- }
- },
- {
- // Test specific rules
- "files": ["**/test/**/*.*"],
- "plugins": [
- "mocha",
- "chai-expect",
- "chai-friendly"
- ],
- "env": {
- "mocha": true
- },
- "rules": {
- // mocha-specific rules
- "mocha/handle-done-callback": "error",
- "mocha/no-exclusive-tests": "error",
- "mocha/no-global-tests": "error",
- "mocha/no-identical-title": "error",
- "mocha/no-nested-tests": "error",
- "mocha/no-pending-tests": "error",
- "mocha/no-skipped-tests": "error",
- "mocha/no-mocha-arrows": "error",
- // Swap the no-unused-expressions rule with a more chai-friendly one
- "no-unused-expressions": "off",
- "chai-friendly/no-unused-expressions": "error",
- // chai-specific rules
- "chai-expect/missing-assertion": "error",
- "chai-expect/terminating-properties": "error",
- // prefer-arrow-callback applies to all callbacks, not just ones in mocha tests.
- // we don't enforce this at the top-level - just in tests to manage `this` scope
- // based on mocha's context mechanism
- "mocha/prefer-arrow-callback": "error"
- }
- },
- {
- // Backend specific rules
- "files": ["**/app/src/**/*.js", "app.js"],
- "rules": {
- // do not allow importing of implicit dependencies.
- "import/no-extraneous-dependencies": ["error", {
- // do not allow importing of devDependencies.
- "devDependencies": false
- }],
- // do not allow node-fetch in backend code
- "no-restricted-syntax": [
- "error",
- {
- "selector": "CallExpression[callee.name='require'] > .arguments[value='node-fetch']",
- "message": "Requiring node-fetch is not allowed in production services, please use fetch-utils."
- }
- ]
- }
- },
- {
- // Backend + backend tests specific rules
- "files": ["**/app/src/**/*.js", "app.js", "**/test/**/*.*", "**/scripts/*.*"],
- "rules": {
- // do not allow node-fetch in backend code
- "no-restricted-syntax": [
- "error",
- // Require `new` when constructing ObjectId (For mongo + mongoose upgrade)
- {
- "selector": "CallExpression[callee.name='ObjectId'], CallExpression[callee.property.name='ObjectId']",
- "message": "Construct ObjectId with `new ObjectId()` instead of `ObjectId()`"
- },
- // Require `new` when mapping a list of ids to a list of ObjectId (For mongo + mongoose upgrade)
- {
- "selector": "CallExpression[callee.property.name='map'] Identifier[name='ObjectId']:first-child, CallExpression[callee.property.name='map'] MemberExpression[property.name='ObjectId']:first-child",
- "message": "Don't map ObjectId directly. Use `id => new ObjectId(id)` instead"
- }
- ]
- }
- },
- {
- // Cypress specific rules
- "files": ["cypress/**/*.{js,jsx,ts,tsx}", "**/test/frontend/**/*.spec.{js,jsx,ts,tsx}"],
- "extends": [
- "plugin:cypress/recommended"
- ]
- },
- {
- // React component specific rules
- //
- "files": ["**/frontend/js/**/components/**/*.{js,jsx,ts,tsx}", "**/frontend/js/**/hooks/**/*.{js,jsx,ts,tsx}"],
- "rules": {
- "@overleaf/no-unnecessary-trans": "error",
- "@overleaf/should-unescape-trans": "error",
- // https://astexplorer.net/
- "no-restricted-syntax": [
- "error",
- // prohibit direct calls to methods of window.location
- {
- "selector": "CallExpression[callee.object.object.name='window'][callee.object.property.name='location']",
- "message": "Modify location via useLocation instead of calling window.location methods directly"
- },
- // prohibit assignment to window.location
- {
- "selector": "AssignmentExpression[left.object.name='window'][left.property.name='location']",
- "message": "Modify location via useLocation instead of calling window.location methods directly"
- },
- // prohibit assignment to window.location.href
- {
- "selector": "AssignmentExpression[left.object.object.name='window'][left.object.property.name='location'][left.property.name='href']",
- "message": "Modify location via useLocation instead of calling window.location methods directly"
- },
- // prohibit using lookbehinds due to incidents with Safari simply crashing when the script is parsed
- {
- "selector": "Literal[regex.pattern=/\\(\\?<[!=]/]",
- "message": "Lookbehind is not supported in older Safari versions."
- }
- ]
- }
- },
- {
- // Frontend specific rules
- "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}"],
- "env": {
- "browser": true
- },
- "parserOptions": {
- "sourceType": "module"
- },
- "plugins": [
- "jsx-a11y"
- ],
- "extends": [
- "plugin:react/recommended",
- "plugin:react-hooks/recommended",
- "plugin:jsx-a11y/recommended",
- "standard-jsx",
- "prettier"
- ],
- "globals": {
- "__webpack_public_path__": true,
- "$": true,
- "angular": true,
- "ga": true,
- // Injected in layout.pug
- "user_id": true,
- "ExposedSettings": true
- },
- "rules": {
- // TODO: remove once https://github.com/standard/eslint-config-standard-react/issues/68 (support eslint@8) is fixed.
- // START: inline standard-react rules
- // "react/jsx-no-bind": ["error", {
- // "allowArrowFunctions": true,
- // "allowBind": false,
- // "ignoreRefs": true
- // },],
- "react/no-did-update-set-state": "error",
- "react/no-unused-prop-types": "error",
- "react/prop-types": "error",
- // "react/react-in-jsx-scope": "error",
- // END: inline standard-react rules
- "react/no-unknown-property": ["error", {
- "ignore": ["dnd-container", "dropdown-toggle"]
- }],
- "react/jsx-no-target-blank": ["error", {
- "allowReferrer": true
- }],
- // Prevent usage of legacy string refs
- "react/no-string-refs": "error",
- // Prevent curly braces around strings (as they're unnecessary)
- "react/jsx-curly-brace-presence": ["error", {
- "props": "never",
- "children": "never"
- }],
- // Don't import React for JSX; the JSX runtime is added by a Babel plugin
- "react/react-in-jsx-scope": "off",
- "react/jsx-uses-react": "off",
- // Allow functions as JSX props
- "react/jsx-no-bind": "off", // TODO: fix occurrences and re-enable this
- // Fix conflict between prettier & standard by overriding to prefer
- // double quotes
- "jsx-quotes": ["error", "prefer-double"],
- // Override weird behaviour of jsx-a11y label-has-for (says labels must be
- // nested *and* have for/id attributes)
- "jsx-a11y/label-has-for": [
- "error",
- {
- "required": {
- "some": [
- "nesting",
- "id"
- ]
- }
- }
- ],
- // Require .jsx or .tsx file extension when using JSX
- "react/jsx-filename-extension": ["error", {
- "extensions": [".jsx", ".tsx"]
- }],
- "no-restricted-syntax": [
- "error",
- // Begin: Make sure angular can withstand minification
- {
- "selector": "CallExpression[callee.object.name='App'][callee.property.name=/run|directive|config|controller/] > :function[params.length > 0]",
- "message": "Wrap the function in an array with the parameter names, to withstand minifcation. E.g. App.controller('MyController', ['param1', function(param1) {}]"
- },
- {
- "selector": "CallExpression[callee.object.name='App'][callee.property.name=/run|directive|config|controller/] > ArrayExpression > ArrowFunctionExpression",
- "message": "Use standard function syntax instead of arrow function syntax in angular components. E.g. function(param1) {}"
- },
- {
- "selector": "CallExpression[callee.object.name='App'][callee.property.name=/run|directive|config|controller/] > ArrowFunctionExpression",
- "message": "Use standard function syntax instead of arrow function syntax in angular components. E.g. function(param1) {}"
- },
- {
- "selector": "CallExpression[callee.object.name='App'][callee.property.name=/run|directive|config|controller/] > ArrayExpression > :not(:function, Identifier):last-child",
- "message": "Last element of the array must be a function. E.g ['param1', function(param1) {}]"
- },
- {
- "selector": "CallExpression[callee.object.name='App'][callee.property.name=/run|directive|config|controller/] > ArrayExpression[elements.length=0]",
- "message": "Array must not be empty. Add parameters and a function. E.g ['param1', function(param1) {}]"
- }
- // End: Make sure angular can withstand minification
- ]
- }
- },
- // React + TypeScript-specific rules
- {
- "files": ["**/*.tsx"],
- "rules": {
- "react/prop-types": "off",
- "no-undef": "off"
- }
- },
- // TypeScript-specific rules
- {
- "files": ["**/*.ts"],
- "rules": {
- "no-undef": "off"
- }
- },
- {
- "files": ["scripts/ukamf/*.js"],
- "rules": {
- // Do not allow importing of any dependencies unless specified in either
- // - web/package.json
- // - web/scripts/ukamf/package.json
- "import/no-extraneous-dependencies": ["error", {"packageDir": [".", "scripts/ukamf"]}]
- }
- },
- {
- "files": ["scripts/learn/checkSanitize/*.js"],
- "rules": {
- // The checkSanitize script is used in the dev-env only.
- "import/no-extraneous-dependencies": ["error", {
- "devDependencies": true,
- "packageDir": [".", "../../"]
- }]
- }
- },
- {
- "files": [
- // Backend: Use @overleaf/logger
- // Docs: https://manual.dev-overleaf.com/development/code/logging/#structured-logging
- "**/app/**/*.{js,cjs,mjs}", "app.js", "modules/*/*.js",
- // Frontend: Prefer debugConsole over bare console
- // Docs: https://manual.dev-overleaf.com/development/code/logging/#frontend
- "**/frontend/**/*.{js,jsx,ts,tsx}",
- // Tests
- "**/test/**/*.{js,cjs,mjs,jsx,ts,tsx}"
- ],
- "excludedFiles": [
- // Allow console logs in scripts
- "**/scripts/**/*.js",
- // Allow console logs in stories
- "**/stories/**/*.{js,jsx,ts,tsx}",
- // Workers do not have access to the search params for enabling ?debug=true.
- // self.location.url is the URL of the worker script.
- "*.worker.{js,ts}"
- ],
- "rules": {
- "no-console": "error"
- }
- }
- ]
- }
|