.eslintrc.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. /* eslint-disable quote-props */
  2. 'use strict'
  3. const svgPresentationAttributes = [
  4. 'alignment-baseline', 'baseline-shift', 'class', 'clip', 'clip-path', 'clip-rule', 'color', 'color-interpolatio', 'color-interpolatio-filters', 'color-profile', 'color-rendering', 'cursor', 'direction', 'display', 'dominant-baseline', 'enable-background', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'glyph-orientation-horizontal', 'glyph-orientation-vertical', 'image-rendering', 'kerning', 'letter-spacing', 'lighting-color', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'overflow', 'pointer-events', 'shape-rendering', 'stop-color', 'stop-opacity', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'text-anchor', 'text-decoration', 'text-rendering', 'transform', 'transform-origin', 'unicode-bidi', 'vector-effect', 'visibility', 'word-spacing', 'writing-mod',
  5. ]
  6. module.exports = {
  7. root: true,
  8. extends: ['transloadit', 'prettier'],
  9. env: {
  10. es6: true,
  11. jest: true,
  12. node: true,
  13. // extra:
  14. browser: true,
  15. },
  16. globals: {
  17. globalThis: true,
  18. hexo: true,
  19. window: true,
  20. },
  21. plugins: [
  22. '@babel/eslint-plugin',
  23. 'jest',
  24. 'markdown',
  25. 'node',
  26. 'prefer-import',
  27. 'promise',
  28. 'react',
  29. // extra:
  30. 'compat',
  31. 'jsdoc',
  32. 'no-only-tests',
  33. 'unicorn',
  34. ],
  35. parser: '@babel/eslint-parser',
  36. parserOptions: {
  37. sourceType: 'script',
  38. ecmaVersion: 2022,
  39. ecmaFeatures: {
  40. jsx: true,
  41. },
  42. },
  43. rules: {
  44. // transloadit rules we are actually ok with in the uppy repo
  45. 'import/extensions': 'off',
  46. 'object-shorthand': ['error', 'always'],
  47. 'strict': 'off',
  48. 'key-spacing': 'off',
  49. 'max-classes-per-file': ['error', 2],
  50. 'react/no-unknown-property': ['error', {
  51. ignore: svgPresentationAttributes,
  52. }],
  53. 'import/no-unresolved': 'off',
  54. // rules we want to enforce
  55. 'array-callback-return': 'error',
  56. 'func-names': 'error',
  57. 'import/no-dynamic-require': 'error',
  58. 'import/no-extraneous-dependencies': 'error',
  59. 'max-len': 'error',
  60. 'no-empty': 'error',
  61. 'no-bitwise': 'error',
  62. 'no-continue': 'error',
  63. 'no-lonely-if': 'error',
  64. 'no-nested-ternary': 'error',
  65. 'no-restricted-properties': 'error',
  66. 'no-return-assign': 'error',
  67. 'no-underscore-dangle': 'error',
  68. 'no-unused-expressions': 'error',
  69. 'no-unused-vars': 'error',
  70. 'no-useless-concat': 'error',
  71. 'no-var': 'error',
  72. 'node/handle-callback-err': 'error',
  73. 'prefer-destructuring': 'error',
  74. 'prefer-spread': 'error',
  75. 'unicorn/prefer-node-protocol': 'error',
  76. 'react/button-has-type': 'error',
  77. 'react/forbid-prop-types': 'error',
  78. 'react/no-access-state-in-setstate': 'error',
  79. 'react/no-array-index-key': 'error',
  80. 'react/no-deprecated': 'error',
  81. 'react/no-this-in-sfc': 'error',
  82. 'react/no-will-update-set-state': 'error',
  83. 'react/prefer-stateless-function': 'error',
  84. 'react/require-default-props': ['error', {
  85. forbidDefaultForRequired: true,
  86. functions: 'ignore',
  87. }],
  88. 'react/sort-comp': 'error',
  89. 'react/static-property-placement': 'off',
  90. 'react/style-prop-object': 'error',
  91. // accessibility
  92. 'jsx-a11y/alt-text': 'error',
  93. 'jsx-a11y/anchor-has-content': 'error',
  94. 'jsx-a11y/click-events-have-key-events': 'error',
  95. 'jsx-a11y/control-has-associated-label': 'error',
  96. 'jsx-a11y/label-has-associated-control': 'error',
  97. 'jsx-a11y/media-has-caption': 'error',
  98. 'jsx-a11y/mouse-events-have-key-events': 'error',
  99. 'jsx-a11y/no-interactive-element-to-noninteractive-role': 'error',
  100. 'jsx-a11y/no-noninteractive-element-interactions': 'error',
  101. 'jsx-a11y/no-static-element-interactions': 'error',
  102. // compat
  103. 'compat/compat': ['error'],
  104. // jsdoc
  105. 'jsdoc/check-alignment': 'error',
  106. 'jsdoc/check-examples': 'off', // cannot yet be supported for ESLint 8, see https://github.com/eslint/eslint/issues/14745
  107. 'jsdoc/check-param-names': 'error',
  108. 'jsdoc/check-syntax': 'error',
  109. 'jsdoc/check-tag-names': ['error', { jsxTags: true }],
  110. 'jsdoc/check-types': 'error',
  111. 'jsdoc/valid-types': 'error',
  112. 'jsdoc/check-indentation': ['off'],
  113. },
  114. settings: {
  115. 'import/core-modules': ['tsd'],
  116. react: {
  117. pragma: 'h',
  118. },
  119. jsdoc: {
  120. mode: 'typescript',
  121. },
  122. polyfills: [
  123. 'Promise',
  124. 'fetch',
  125. 'Object.assign',
  126. 'document.querySelector',
  127. ],
  128. },
  129. overrides: [
  130. {
  131. files: [
  132. '*.jsx',
  133. '*.tsx',
  134. 'packages/@uppy/react-native/**/*.js',
  135. ],
  136. parser: 'espree',
  137. parserOptions: {
  138. sourceType: 'module',
  139. ecmaFeatures: {
  140. jsx: true,
  141. },
  142. },
  143. rules: {
  144. 'no-restricted-globals': [
  145. 'error',
  146. {
  147. name: '__filename',
  148. message: 'Use import.meta.url instead',
  149. },
  150. {
  151. name: '__dirname',
  152. message: 'Not available in ESM',
  153. },
  154. {
  155. name: 'exports',
  156. message: 'Not available in ESM',
  157. },
  158. {
  159. name: 'module',
  160. message: 'Not available in ESM',
  161. },
  162. {
  163. name: 'require',
  164. message: 'Use import instead',
  165. },
  166. {
  167. name: 'JSX',
  168. message: 'Use h.JSX.Element, ComponentChild, or ComponentChildren from Preact',
  169. },
  170. {
  171. name: 'React',
  172. message: 'Import the value instead of relying on global.React.',
  173. },
  174. ],
  175. 'import/extensions': ['error', 'ignorePackages'],
  176. },
  177. },
  178. {
  179. files: [
  180. '*.mjs',
  181. 'e2e/clients/**/*.js',
  182. 'examples/aws-companion/*.js',
  183. 'examples/aws-php/*.js',
  184. 'examples/bundled/*.js',
  185. 'examples/custom-provider/client/*.js',
  186. 'examples/digitalocean-spaces/*.js',
  187. 'examples/multiple-instances/*.js',
  188. 'examples/node-xhr/*.js',
  189. 'examples/php-xhr/*.js',
  190. 'examples/python-xhr/*.js',
  191. 'examples/react-example/*.js',
  192. 'examples/redux/*.js',
  193. 'examples/transloadit/*.js',
  194. 'examples/transloadit-markdown-bin/*.js',
  195. 'examples/xhr-bundle/*.js',
  196. 'private/dev/*.js',
  197. 'private/release/*.js',
  198. 'private/remark-lint-uppy/*.js',
  199. 'packages/@uppy/!(companion|react-native)/**/*.js',
  200. ],
  201. parser: 'espree',
  202. parserOptions: {
  203. sourceType: 'module',
  204. ecmaFeatures: {
  205. jsx: false,
  206. },
  207. },
  208. rules: {
  209. 'import/named': 'off', // Disabled because that rule tries and fails to parse JSX dependencies.
  210. 'import/no-named-as-default': 'off', // Disabled because that rule tries and fails to parse JSX dependencies.
  211. 'import/no-named-as-default-member': 'off', // Disabled because that rule tries and fails to parse JSX dependencies.
  212. 'no-restricted-globals': [
  213. 'error',
  214. {
  215. name: '__filename',
  216. message: 'Use import.meta.url instead',
  217. },
  218. {
  219. name: '__dirname',
  220. message: 'Not available in ESM',
  221. },
  222. {
  223. name: 'exports',
  224. message: 'Not available in ESM',
  225. },
  226. {
  227. name: 'module',
  228. message: 'Not available in ESM',
  229. },
  230. {
  231. name: 'require',
  232. message: 'Use import instead',
  233. },
  234. {
  235. name: 'JSX',
  236. message: 'Use h.JSX.Element, ComponentChild, or ComponentChildren from Preact',
  237. },
  238. {
  239. name: 'React',
  240. message: 'Import the value instead of relying on global.React.',
  241. },
  242. ],
  243. 'import/extensions': ['error', 'ignorePackages'],
  244. },
  245. },
  246. {
  247. files: ['packages/uppy/*.mjs'],
  248. rules: {
  249. 'import/first': 'off',
  250. 'import/newline-after-import': 'off',
  251. 'import/no-extraneous-dependencies': ['error', {
  252. devDependencies: true,
  253. }],
  254. },
  255. },
  256. {
  257. files: [
  258. 'packages/@uppy/*/types/*.d.ts',
  259. ],
  260. rules : {
  261. 'import/no-unresolved': 'off',
  262. 'max-classes-per-file': 'off',
  263. 'no-use-before-define': 'off',
  264. },
  265. },
  266. {
  267. files: [
  268. 'packages/@uppy/dashboard/src/components/**/*.jsx',
  269. ],
  270. rules: {
  271. 'react/destructuring-assignment': 'off',
  272. },
  273. },
  274. {
  275. files: [
  276. // Those need looser rules, and cannot be made part of the stricter rules above.
  277. // TODO: update those to more modern code when switch to ESM is complete
  278. 'examples/react-native-expo/*.js',
  279. 'examples/svelte-example/**/*.js',
  280. 'examples/vue/**/*.js',
  281. 'examples/vue3/**/*.js',
  282. ],
  283. rules: {
  284. 'no-unused-vars': [
  285. 'error',
  286. {
  287. 'varsIgnorePattern': 'React',
  288. },
  289. ],
  290. },
  291. parserOptions: {
  292. sourceType: 'module',
  293. },
  294. },
  295. {
  296. files: ['./packages/@uppy/companion/**/*.js'],
  297. rules: {
  298. 'no-underscore-dangle': 'off',
  299. },
  300. },
  301. {
  302. files: [
  303. '*.test.js',
  304. '*.test.ts',
  305. 'test/endtoend/*.js',
  306. 'bin/**.js',
  307. ],
  308. rules: {
  309. 'compat/compat': ['off'],
  310. },
  311. },
  312. {
  313. files: [
  314. 'bin/**.js',
  315. 'bin/**.mjs',
  316. 'examples/**/*.cjs',
  317. 'examples/**/*.js',
  318. 'packages/@uppy/companion/test/**/*.js',
  319. 'test/**/*.js',
  320. 'test/**/*.ts',
  321. '*.test.js',
  322. '*.test.ts',
  323. '*.test-d.ts',
  324. '*.test-d.tsx',
  325. 'postcss.config.js',
  326. '.eslintrc.js',
  327. 'private/**/*.js',
  328. 'private/**/*.mjs',
  329. ],
  330. rules: {
  331. 'no-console': 'off',
  332. 'import/no-extraneous-dependencies': ['error', {
  333. devDependencies: true,
  334. }],
  335. },
  336. },
  337. {
  338. files: [
  339. 'packages/@uppy/locales/src/*.js',
  340. 'packages/@uppy/locales/template.js',
  341. ],
  342. rules: {
  343. camelcase: ['off'],
  344. 'quote-props': ['error', 'as-needed', { 'numbers': true }],
  345. },
  346. },
  347. {
  348. files: ['test/endtoend/*/*.mjs', 'test/endtoend/*/*.ts'],
  349. rules: {
  350. // we mostly import @uppy stuff in these files.
  351. 'import/no-extraneous-dependencies': ['off'],
  352. },
  353. },
  354. {
  355. files: ['test/endtoend/*/*.js'],
  356. env: {
  357. mocha: true,
  358. },
  359. },
  360. {
  361. files: ['packages/@uppy/react/src/**/*.js'],
  362. rules: {
  363. 'import/no-extraneous-dependencies': ['error', {
  364. peerDependencies: true,
  365. }],
  366. },
  367. },
  368. {
  369. files: ['**/*.md', '*.md'],
  370. processor: 'markdown/markdown',
  371. },
  372. {
  373. files: ['**/*.md/*.js', '**/*.md/*.javascript'],
  374. parserOptions: {
  375. sourceType: 'module',
  376. },
  377. rules: {
  378. 'react/destructuring-assignment': 'off',
  379. 'no-restricted-globals': [
  380. 'error',
  381. {
  382. name: '__filename',
  383. message: 'Use import.meta.url instead',
  384. },
  385. {
  386. name: '__dirname',
  387. message: 'Not available in ESM',
  388. },
  389. {
  390. name: 'exports',
  391. message: 'Not available in ESM',
  392. },
  393. {
  394. name: 'module',
  395. message: 'Not available in ESM',
  396. },
  397. {
  398. name: 'require',
  399. message: 'Use import instead',
  400. },
  401. ],
  402. },
  403. },
  404. {
  405. files: ['**/*.ts', '**/*.md/*.ts', '**/*.md/*.typescript', '**/*.tsx', '**/*.md/*.tsx'],
  406. excludedFiles: ['examples/angular-example/**/*.ts', 'packages/@uppy/angular/**/*.ts'],
  407. parser: '@typescript-eslint/parser',
  408. settings: {
  409. 'import/resolver': {
  410. node: {
  411. extensions: ['.js', '.jsx', '.ts', '.tsx'],
  412. },
  413. },
  414. },
  415. plugins: ['@typescript-eslint'],
  416. extends: [
  417. 'eslint:recommended',
  418. 'plugin:@typescript-eslint/eslint-recommended',
  419. 'plugin:@typescript-eslint/recommended',
  420. ],
  421. rules: {
  422. 'no-extra-semi': 'off',
  423. 'no-restricted-syntax': ['error', {
  424. selector: 'ImportDeclaration[source.value=/^@uppy\\x2F[a-z-0-9]+\\x2F/]:not([source.value=/^@uppy\\x2Futils\\x2F/]):not([source.value=/\\.(js|css)$/])',
  425. message: 'Use ".js" file extension for import type declarations from a different package',
  426. }, {
  427. selector: 'ImportDeclaration[importKind="type"][source.value=/^\\.\\.?\\x2F.+\\.js$/]',
  428. message: 'Do not use ".js" file extension for relative import type declarations',
  429. }, {
  430. selector: 'ImportDeclaration[source.value=/^@uppy\\x2Futils\\x2Flib\\x2F.+\\.[mc]?[jt]sx?$/]',
  431. message: 'Do not use file extension when importing from @uppy/utils',
  432. }],
  433. 'import/extensions': ['error', 'ignorePackages'],
  434. 'import/prefer-default-export': 'off',
  435. '@typescript-eslint/no-empty-function': 'off',
  436. '@typescript-eslint/no-explicit-any': 'off',
  437. '@typescript-eslint/no-extra-semi': 'off',
  438. '@typescript-eslint/no-namespace': 'off',
  439. '@typescript-eslint/no-non-null-assertion': 'off',
  440. },
  441. },
  442. {
  443. files: ['packages/@uppy/*/src/**/*.ts'],
  444. excludedFiles: ['packages/@uppy/**/*.test.ts', 'packages/@uppy/core/src/mocks/*.ts'],
  445. rules: {
  446. '@typescript-eslint/explicit-module-boundary-types': 'error',
  447. },
  448. },
  449. {
  450. files: ['**/*.md/*.*'],
  451. rules: {
  452. 'import/no-extraneous-dependencies': 'off',
  453. 'import/no-unresolved': 'off',
  454. 'no-console': 'off',
  455. 'no-undef': 'off',
  456. 'no-unused-vars': 'off',
  457. },
  458. },
  459. {
  460. files: ['**/react/*.md/*.js', '**/react.md/*.js', '**/react-*.md/*.js', '**/react/**/*.test-d.tsx'],
  461. settings: {
  462. react: { pragma: 'React' },
  463. },
  464. },
  465. {
  466. files: ['**/react/**/*.test-d.tsx'],
  467. rules: {
  468. 'import/extensions': 'off',
  469. 'import/no-useless-path-segments': 'off',
  470. 'no-alert': 'off',
  471. 'no-inner-declarations': 'off',
  472. 'no-lone-blocks': 'off',
  473. 'no-unused-expressions': 'off',
  474. 'no-unused-vars': 'off',
  475. },
  476. },
  477. {
  478. files: [
  479. 'packages/@uppy/svelte/**',
  480. ],
  481. parserOptions: {
  482. sourceType: 'module',
  483. },
  484. },
  485. {
  486. files: ['e2e/**/*.ts'],
  487. extends: ['plugin:cypress/recommended'],
  488. },
  489. {
  490. files: ['e2e/**/*.ts', 'e2e/**/*.js', 'e2e/**/*.jsx', 'e2e/**/*.mjs'],
  491. rules: {
  492. 'import/no-extraneous-dependencies': 'off',
  493. 'no-console': 'off',
  494. 'no-only-tests/no-only-tests': 'error',
  495. 'no-unused-expressions': 'off',
  496. },
  497. },
  498. {
  499. files: ["packages/@uppy/vue/**"],
  500. rules: {
  501. 'react-hooks/rules-of-hooks': 'off',
  502. },
  503. },
  504. ],
  505. }