mirror of https://github.com/tootsuite/mastodon
368 lines
10 KiB
JavaScript
368 lines
10 KiB
JavaScript
// @ts-check
|
|
const { defineConfig } = require('eslint-define-config');
|
|
|
|
module.exports = defineConfig({
|
|
root: true,
|
|
|
|
extends: [
|
|
'eslint:recommended',
|
|
'plugin:react/recommended',
|
|
'plugin:react-hooks/recommended',
|
|
'plugin:jsx-a11y/recommended',
|
|
'plugin:import/recommended',
|
|
'plugin:promise/recommended',
|
|
'plugin:jsdoc/recommended',
|
|
],
|
|
|
|
env: {
|
|
browser: true,
|
|
node: true,
|
|
es6: true,
|
|
},
|
|
|
|
parser: '@typescript-eslint/parser',
|
|
|
|
plugins: [
|
|
'react',
|
|
'jsx-a11y',
|
|
'import',
|
|
'promise',
|
|
'@typescript-eslint',
|
|
'formatjs',
|
|
],
|
|
|
|
parserOptions: {
|
|
sourceType: 'module',
|
|
ecmaFeatures: {
|
|
jsx: true,
|
|
},
|
|
ecmaVersion: 2021,
|
|
requireConfigFile: false,
|
|
babelOptions: {
|
|
configFile: false,
|
|
presets: ['@babel/react', '@babel/env'],
|
|
},
|
|
},
|
|
|
|
settings: {
|
|
react: {
|
|
version: 'detect',
|
|
},
|
|
'import/ignore': [
|
|
'node_modules',
|
|
'\\.(css|scss|json)$',
|
|
],
|
|
'import/resolver': {
|
|
typescript: {},
|
|
},
|
|
},
|
|
|
|
rules: {
|
|
'consistent-return': 'error',
|
|
'dot-notation': 'error',
|
|
eqeqeq: ['error', 'always', { 'null': 'ignore' }],
|
|
'indent': ['error', 2],
|
|
'jsx-quotes': ['error', 'prefer-single'],
|
|
'semi': ['error', 'always'],
|
|
'no-catch-shadow': 'error',
|
|
'no-console': [
|
|
'warn',
|
|
{
|
|
allow: [
|
|
'error',
|
|
'warn',
|
|
],
|
|
},
|
|
],
|
|
'no-empty': ['error', { "allowEmptyCatch": true }],
|
|
'no-restricted-properties': [
|
|
'error',
|
|
{ property: 'substring', message: 'Use .slice instead of .substring.' },
|
|
{ property: 'substr', message: 'Use .slice instead of .substr.' },
|
|
],
|
|
'no-restricted-syntax': [
|
|
'error',
|
|
{
|
|
// eslint-disable-next-line no-restricted-syntax
|
|
selector: 'Literal[value=/•/], JSXText[value=/•/]',
|
|
// eslint-disable-next-line no-restricted-syntax
|
|
message: "Use '·' (middle dot) instead of '•' (bullet)",
|
|
},
|
|
],
|
|
'no-unused-expressions': 'error',
|
|
'no-unused-vars': 'off',
|
|
'@typescript-eslint/no-unused-vars': [
|
|
'error',
|
|
{
|
|
vars: 'all',
|
|
args: 'after-used',
|
|
destructuredArrayIgnorePattern: '^_',
|
|
ignoreRestSiblings: true,
|
|
},
|
|
],
|
|
'valid-typeof': 'error',
|
|
|
|
'react/jsx-filename-extension': ['error', { extensions: ['.jsx', 'tsx'] }],
|
|
'react/jsx-boolean-value': 'error',
|
|
'react/display-name': 'off',
|
|
'react/jsx-fragments': ['error', 'syntax'],
|
|
'react/jsx-equals-spacing': 'error',
|
|
'react/jsx-no-bind': 'error',
|
|
'react/jsx-no-useless-fragment': 'error',
|
|
'react/jsx-no-target-blank': 'off',
|
|
'react/jsx-tag-spacing': 'error',
|
|
'react/jsx-uses-react': 'off', // not needed with new JSX transform
|
|
'react/jsx-wrap-multilines': 'error',
|
|
'react/react-in-jsx-scope': 'off', // not needed with new JSX transform
|
|
'react/self-closing-comp': 'error',
|
|
|
|
// recommended values found in https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/v6.8.0/src/index.js#L46
|
|
'jsx-a11y/click-events-have-key-events': 'off',
|
|
'jsx-a11y/label-has-associated-control': 'off',
|
|
'jsx-a11y/media-has-caption': 'off',
|
|
'jsx-a11y/no-autofocus': 'off',
|
|
// recommended rule is:
|
|
// 'jsx-a11y/no-interactive-element-to-noninteractive-role': [
|
|
// 'error',
|
|
// {
|
|
// tr: ['none', 'presentation'],
|
|
// canvas: ['img'],
|
|
// },
|
|
// ],
|
|
'jsx-a11y/no-interactive-element-to-noninteractive-role': 'off',
|
|
// recommended rule is:
|
|
// 'jsx-a11y/no-noninteractive-tabindex': [
|
|
// 'error',
|
|
// {
|
|
// tags: [],
|
|
// roles: ['tabpanel'],
|
|
// allowExpressionValues: true,
|
|
// },
|
|
// ],
|
|
'jsx-a11y/no-noninteractive-tabindex': 'off',
|
|
// recommended is full 'error'
|
|
'jsx-a11y/no-static-element-interactions': [
|
|
'warn',
|
|
{
|
|
handlers: [
|
|
'onClick',
|
|
],
|
|
},
|
|
],
|
|
|
|
// See https://github.com/import-js/eslint-plugin-import/blob/v2.29.1/config/recommended.js
|
|
'import/extensions': [
|
|
'error',
|
|
'always',
|
|
{
|
|
js: 'never',
|
|
jsx: 'never',
|
|
mjs: 'never',
|
|
ts: 'never',
|
|
tsx: 'never',
|
|
},
|
|
],
|
|
'import/first': 'error',
|
|
'import/newline-after-import': 'error',
|
|
'import/no-anonymous-default-export': 'error',
|
|
'import/no-extraneous-dependencies': [
|
|
'error',
|
|
{
|
|
devDependencies: [
|
|
'.eslintrc.js',
|
|
'config/webpack/**',
|
|
'app/javascript/mastodon/performance.js',
|
|
'app/javascript/mastodon/test_setup.js',
|
|
'app/javascript/**/__tests__/**',
|
|
],
|
|
},
|
|
],
|
|
'import/no-amd': 'error',
|
|
'import/no-commonjs': 'error',
|
|
'import/no-import-module-exports': 'error',
|
|
'import/no-relative-packages': 'error',
|
|
'import/no-self-import': 'error',
|
|
'import/no-useless-path-segments': 'error',
|
|
'import/no-webpack-loader-syntax': 'error',
|
|
|
|
'import/order': [
|
|
'error',
|
|
{
|
|
alphabetize: { order: 'asc' },
|
|
'newlines-between': 'always',
|
|
groups: [
|
|
'builtin',
|
|
'external',
|
|
'internal',
|
|
'parent',
|
|
['index', 'sibling'],
|
|
'object',
|
|
],
|
|
pathGroups: [
|
|
// React core packages
|
|
{
|
|
pattern: '{react,react-dom,react-dom/client,prop-types}',
|
|
group: 'builtin',
|
|
position: 'after',
|
|
},
|
|
// I18n
|
|
{
|
|
pattern: '{react-intl,intl-messageformat}',
|
|
group: 'builtin',
|
|
position: 'after',
|
|
},
|
|
// Common React utilities
|
|
{
|
|
pattern: '{classnames,react-helmet,react-router,react-router-dom}',
|
|
group: 'external',
|
|
position: 'before',
|
|
},
|
|
// Immutable / Redux / data store
|
|
{
|
|
pattern: '{immutable,@reduxjs/toolkit,react-redux,react-immutable-proptypes,react-immutable-pure-component}',
|
|
group: 'external',
|
|
position: 'before',
|
|
},
|
|
// Internal packages
|
|
{
|
|
pattern: '{mastodon/**}',
|
|
group: 'internal',
|
|
position: 'after',
|
|
},
|
|
],
|
|
pathGroupsExcludedImportTypes: [],
|
|
},
|
|
],
|
|
|
|
'promise/always-return': 'off',
|
|
'promise/catch-or-return': [
|
|
'error',
|
|
{
|
|
allowFinally: true,
|
|
},
|
|
],
|
|
'promise/no-callback-in-promise': 'off',
|
|
'promise/no-nesting': 'off',
|
|
'promise/no-promise-in-callback': 'off',
|
|
|
|
'formatjs/blocklist-elements': 'error',
|
|
'formatjs/enforce-default-message': ['error', 'literal'],
|
|
'formatjs/enforce-description': 'off', // description values not currently used
|
|
'formatjs/enforce-id': 'off', // Explicit IDs are used in the project
|
|
'formatjs/enforce-placeholders': 'off', // Issues in short_number.jsx
|
|
'formatjs/enforce-plural-rules': 'error',
|
|
'formatjs/no-camel-case': 'off', // disabledAccount is only non-conforming
|
|
'formatjs/no-complex-selectors': 'error',
|
|
'formatjs/no-emoji': 'error',
|
|
'formatjs/no-id': 'off', // IDs are used for translation keys
|
|
'formatjs/no-invalid-icu': 'error',
|
|
'formatjs/no-literal-string-in-jsx': 'off', // Should be looked at, but mainly flagging punctuation outside of strings
|
|
'formatjs/no-multiple-whitespaces': 'error',
|
|
'formatjs/no-offset': 'error',
|
|
'formatjs/no-useless-message': 'error',
|
|
'formatjs/prefer-formatted-message': 'error',
|
|
'formatjs/prefer-pound-in-plural': 'error',
|
|
|
|
'jsdoc/check-types': 'off',
|
|
'jsdoc/no-undefined-types': 'off',
|
|
'jsdoc/require-jsdoc': 'off',
|
|
'jsdoc/require-param-description': 'off',
|
|
'jsdoc/require-property-description': 'off',
|
|
'jsdoc/require-returns-description': 'off',
|
|
'jsdoc/require-returns': 'off',
|
|
},
|
|
|
|
overrides: [
|
|
{
|
|
files: [
|
|
'.eslintrc.js',
|
|
'*.config.js',
|
|
'.*rc.js',
|
|
'ide-helper.js',
|
|
'config/webpack/**/*',
|
|
'config/formatjs-formatter.js',
|
|
],
|
|
|
|
env: {
|
|
commonjs: true,
|
|
},
|
|
|
|
parserOptions: {
|
|
sourceType: 'script',
|
|
},
|
|
|
|
rules: {
|
|
'import/no-commonjs': 'off',
|
|
},
|
|
},
|
|
{
|
|
files: [
|
|
'**/*.ts',
|
|
'**/*.tsx',
|
|
],
|
|
|
|
extends: [
|
|
'eslint:recommended',
|
|
'plugin:@typescript-eslint/strict-type-checked',
|
|
'plugin:@typescript-eslint/stylistic-type-checked',
|
|
'plugin:react/recommended',
|
|
'plugin:react-hooks/recommended',
|
|
'plugin:jsx-a11y/recommended',
|
|
'plugin:import/recommended',
|
|
'plugin:import/typescript',
|
|
'plugin:promise/recommended',
|
|
'plugin:jsdoc/recommended-typescript',
|
|
],
|
|
|
|
parserOptions: {
|
|
projectService: true,
|
|
tsconfigRootDir: __dirname,
|
|
},
|
|
|
|
rules: {
|
|
// Disable formatting rules that have been enabled in the base config
|
|
'indent': 'off',
|
|
|
|
// This is not needed as we use noImplicitReturns, which handles this in addition to understanding types
|
|
'consistent-return': 'off',
|
|
|
|
'import/consistent-type-specifier-style': ['error', 'prefer-top-level'],
|
|
|
|
'@typescript-eslint/consistent-type-definitions': ['warn', 'interface'],
|
|
'@typescript-eslint/consistent-type-exports': 'error',
|
|
'@typescript-eslint/consistent-type-imports': 'error',
|
|
"@typescript-eslint/prefer-nullish-coalescing": ['error', { ignorePrimitives: { boolean: true } }],
|
|
"@typescript-eslint/no-restricted-imports": [
|
|
"warn",
|
|
{
|
|
"name": "react-redux",
|
|
"importNames": ["useSelector", "useDispatch"],
|
|
"message": "Use typed hooks `useAppDispatch` and `useAppSelector` instead."
|
|
}
|
|
],
|
|
"@typescript-eslint/restrict-template-expressions": ['warn', { allowNumber: true }],
|
|
'jsdoc/require-jsdoc': 'off',
|
|
|
|
// Those rules set stricter rules for TS files
|
|
// to enforce better practices when converting from JS
|
|
'import/no-default-export': 'warn',
|
|
'react/prefer-stateless-function': 'warn',
|
|
'react/function-component-definition': ['error', { namedComponents: 'arrow-function' }],
|
|
'react/jsx-uses-react': 'off', // not needed with new JSX transform
|
|
'react/react-in-jsx-scope': 'off', // not needed with new JSX transform
|
|
'react/prop-types': 'off',
|
|
},
|
|
},
|
|
{
|
|
files: [
|
|
'**/__tests__/*.js',
|
|
'**/__tests__/*.jsx',
|
|
],
|
|
|
|
env: {
|
|
jest: true,
|
|
},
|
|
}
|
|
],
|
|
});
|