diff --git a/.eslintrc.ts.json b/.eslintrc.ts.json new file mode 100644 index 0000000000..6d3ea05721 --- /dev/null +++ b/.eslintrc.ts.json @@ -0,0 +1,52 @@ +{ + "extends": [ + "airbnb", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended" + ], + "env": { + "amd": true, + "browser": true, + "mocha": true + }, + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 6, + "ecmaFeatures": { + "jsx": true + } + }, + "plugins": [ + "react-hooks", + "no-only-tests" + ], + "settings": { + "import/resolver": "webpack" + }, + "rules": { + // Typrescript + "react/jsx-filename-extension": 0, + "@typescript-eslint/explicit-function-return-type": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/camelcase": "off", + // Hooks + "react-hooks/rules-of-hooks": "error", + "react-hooks/exhaustive-deps": "warn", + // Exceptions + "arrow-parens": ["error", "always"], + "comma-dangle": ["error", "always-multiline"], + "no-only-tests/no-only-tests": 2, + "no-script-url": 0, + "import/extensions": 0, // we wouldn't be able to import jQuery without this line + "react/destructuring-assignment": 0, // that would be too many changes to fix this one + "prefer-destructuring": 0, // that would be too many changes to fix this one + "jsx-a11y/label-has-for": [2, { + "required": {"some": ["nesting", "id"]} // some of our labels are hidden and we cannot nest those + }], + "jsx-a11y/anchor-is-valid": 0, // cannot fix this one, it would break wprdpress themes + "jsx-a11y/label-has-associated-control": [ 2, { + "either": "either" // control has to be either nested or associated via htmlFor + }] + } +} + diff --git a/RoboFile.php b/RoboFile.php index 575fef80eb..1491c71dc7 100644 --- a/RoboFile.php +++ b/RoboFile.php @@ -445,11 +445,22 @@ class RoboFile extends \Robo\Tasks { ) ->run(); } + if (substr($filePath, -4) === '.tsx' || substr($filePath, -3) === '.ts') { + // fix ESLint using TS rules + return $this->collectionBuilder() + ->taskExec( + 'npx eslint -c .eslintrc.ts.json ' . + '--max-warnings 0 ' . + '--fix ' . + $filePath + ) + ->run(); + } if (substr($filePath, -8) === '.spec.js') { // fix ESLint using tests rules return $this->collectionBuilder() ->taskExec( - 'npx eslint -c .eslintrc.tests.json ' . + 'npx eslint -c .eslintrc.tests_newsletter_editor.json ' . '--max-warnings 0 ' . '--fix ' . $filePath diff --git a/assets/js/src/notices/notice.tsx b/assets/js/src/notices/notice.tsx index db1271a046..26b0fdbbbf 100644 --- a/assets/js/src/notices/notice.tsx +++ b/assets/js/src/notices/notice.tsx @@ -15,7 +15,7 @@ const propTypes = { PropTypes.string, PropTypes.element, PropTypes.arrayOf(PropTypes.element), - ]).isRequired, + ]).isRequired }; const Notice: FC> = ({ diff --git a/package-lock.json b/package-lock.json index f22c84837e..2e83c7facf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3939,6 +3939,12 @@ "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", "dev": true }, + "@types/eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==", + "dev": true + }, "@types/events": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", @@ -3956,6 +3962,12 @@ "@types/node": "*" } }, + "@types/json-schema": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz", + "integrity": "sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==", + "dev": true + }, "@types/minimatch": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", @@ -4027,6 +4039,137 @@ "vfile-message": "*" } }, + "@typescript-eslint/eslint-plugin": { + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.21.0.tgz", + "integrity": "sha512-b5jjjDMxzcjh/Sbjuo7WyhrQmVJg0WipTHQgXh5Xwx10uYm6nPWqN1WGOsaNq4HR3Zh4wUx4IRQdDkCHwyewyw==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "2.21.0", + "eslint-utils": "^1.4.3", + "functional-red-black-tree": "^1.0.1", + "regexpp": "^3.0.0", + "tsutils": "^3.17.1" + }, + "dependencies": { + "regexpp": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.0.0.tgz", + "integrity": "sha512-Z+hNr7RAVWxznLPuA7DIh8UNX1j9CDrUQxskw9IrBE1Dxue2lyXT+shqEIeLUjrokxIP8CMy1WkjgG3rTsd5/g==", + "dev": true + } + } + }, + "@typescript-eslint/experimental-utils": { + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.21.0.tgz", + "integrity": "sha512-olKw9JP/XUkav4lq0I7S1mhGgONJF9rHNhKFn9wJlpfRVjNo3PPjSvybxEldvCXnvD+WAshSzqH5cEjPp9CsBA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/typescript-estree": "2.21.0", + "eslint-scope": "^5.0.0" + }, + "dependencies": { + "eslint-scope": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", + "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + } + } + }, + "@typescript-eslint/parser": { + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.21.0.tgz", + "integrity": "sha512-VrmbdrrrvvI6cPPOG7uOgGUFXNYTiSbnRq8ZMyuGa4+qmXJXVLEEz78hKuqupvkpwJQNk1Ucz1TenrRP90gmBg==", + "dev": true, + "requires": { + "@types/eslint-visitor-keys": "^1.0.0", + "@typescript-eslint/experimental-utils": "2.21.0", + "@typescript-eslint/typescript-estree": "2.21.0", + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", + "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", + "dev": true + } + } + }, + "@typescript-eslint/typescript-estree": { + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.21.0.tgz", + "integrity": "sha512-NC/nogZNb9IK2MEFQqyDBAciOT8Lp8O3KgAfvHx2Skx6WBo+KmDqlU3R9KxHONaijfTIKtojRe3SZQyMjr3wBw==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "eslint-visitor-keys": "^1.1.0", + "glob": "^7.1.6", + "is-glob": "^4.0.1", + "lodash": "^4.17.15", + "semver": "^6.3.0", + "tsutils": "^3.17.1" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "eslint-visitor-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", + "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", + "dev": true + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, "@webassemblyjs/ast": { "version": "1.8.5", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz", @@ -17066,6 +17209,15 @@ "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", "dev": true }, + "tsutils": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", + "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, "tty-browserify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", diff --git a/package.json b/package.json index b487868d12..0580e3ae8f 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "scripts": { "lint": "npm run lint6 && npm run lint5 && npm run lint-tests", "lint6": "eslint -c .eslintrc.es6.json --max-warnings 0 'assets/js/src/**/*.jsx' 'tests/javascript/**/*.js'", + "lint-ts": "eslint -c .eslintrc.ts.json --max-warnings 0 'assets/js/src/**/*.tsx' 'assets/js/src/**/*.ts'", "lint5": "eslint -c .eslintrc.es5.json --ignore-pattern helpscout.js --max-warnings 0 'assets/js/src/**/*.js'", "lint-tests": "eslint -c .eslintrc.tests_newsletter_editor.json --max-warnings 0 'tests/javascript_newsletter_editor'", "autoprefixer": "postcss assets/dist/css/*.css --use autoprefixer --no-map --replace", @@ -85,6 +86,8 @@ "@babel/preset-typescript": "^7.8.3", "@babel/register": "^7.8.3", "@types/react": "^16.9.22", + "@typescript-eslint/eslint-plugin": "^2.21.0", + "@typescript-eslint/parser": "^2.21.0", "autoprefixer": "^9.7.4", "babel-eslint": "^10.0.1", "babel-loader": "^8.0.4",