Browse Source

meta: fix all ESLint warnings and turn them into errors (#4398)

Antoine du Hamel 2 years ago
parent
commit
0a7eceac21
40 changed files with 506 additions and 513 deletions
  1. 0 14
      .eslintignore
  2. 10 42
      .eslintrc.js
  3. 0 12
      .gitignore
  4. 3 5
      e2e/cypress/integration/dashboard-aws-multipart.spec.ts
  5. 3 5
      e2e/cypress/integration/dashboard-aws.spec.ts
  6. 28 37
      e2e/cypress/integration/dashboard-transloadit.spec.ts
  7. 6 8
      e2e/cypress/integration/dashboard-tus.spec.ts
  8. 1 1
      examples/angular-example/package.json
  9. 3 2
      examples/aws-nodejs/index.js
  10. 1 0
      examples/react-native-expo/App.js
  11. 4 4
      package.json
  12. 2 0
      packages/@uppy/companion/src/server/Uploader.js
  13. 1 0
      packages/@uppy/companion/src/server/controllers/index.js
  14. 6 3
      packages/@uppy/companion/src/server/controllers/oauth-redirect.js
  15. 36 30
      packages/@uppy/companion/src/server/controllers/s3.js
  16. 2 1
      packages/@uppy/companion/src/server/controllers/send-token.js
  17. 23 23
      packages/@uppy/companion/src/server/emitter/redis-emitter.js
  18. 10 10
      packages/@uppy/companion/src/server/helpers/oauth-state.js
  19. 2 3
      packages/@uppy/companion/src/server/helpers/utils.js
  20. 4 4
      packages/@uppy/companion/src/server/helpers/version.js
  21. 16 16
      packages/@uppy/companion/src/server/jobs.js
  22. 10 0
      packages/@uppy/companion/src/server/provider/index.js
  23. 1 0
      packages/@uppy/companion/test/__mocks__/tus-js-client.js
  24. 1 2
      packages/@uppy/companion/test/__tests__/callback.js
  25. 0 2
      packages/@uppy/companion/test/__tests__/cors.js
  26. 0 2
      packages/@uppy/companion/test/__tests__/header-blacklist.js
  27. 0 1
      packages/@uppy/companion/test/__tests__/logger.js
  28. 0 2
      packages/@uppy/companion/test/__tests__/preauth.js
  29. 1 2
      packages/@uppy/companion/test/__tests__/provider-manager.js
  30. 0 2
      packages/@uppy/companion/test/__tests__/url.js
  31. 1 0
      packages/@uppy/companion/test/mockserver.js
  32. 1 1
      packages/@uppy/locales/src/es_MX.js
  33. 1 1
      packages/@uppy/locales/src/hi_IN.js
  34. 1 1
      packages/@uppy/react/src/Dashboard.d.ts
  35. 1 1
      packages/@uppy/react/src/DashboardModal.d.ts
  36. 1 1
      packages/@uppy/react/src/DragDrop.d.ts
  37. 1 1
      packages/@uppy/react/src/FileInput.d.ts
  38. 1 1
      packages/@uppy/react/src/ProgressBar.d.ts
  39. 1 1
      packages/@uppy/react/src/StatusBar.d.ts
  40. 323 272
      yarn.lock

+ 0 - 14
.eslintignore

@@ -3,20 +3,6 @@ lib
 dist
 coverage
 test/lib/**
-website/public/**
-website/src/examples/**/*.html
-website/themes/uppy/source/js/smooth-scroll.min.js
-website/themes/uppy/source/js/uppy.js
-website/themes/uppy/source/uppy/**
 test/endtoend/*/build
 examples/svelte-example/public/build/
 bundle-legacy.js
-website/src/_posts/201*.md
-website/src/_posts/2020-*.md
-website/src/_posts/2021-01-*.md
-website/src/_posts/2021-02-*.md
-website/src/_posts/2021-03-*.md
-website/src/_posts/2021-04-*.md
-website/src/_posts/2021-05-*.md
-website/src/_posts/2021-06-*.md
-website/src/docs/react-dashboard.md

+ 10 - 42
.eslintrc.js

@@ -53,8 +53,15 @@ module.exports = {
       ignore: svgPresentationAttributes,
     }],
 
+    // Special rules for CI:
+    ...(process.env.CI && {
+      // Some imports are available only after a full build, which we don't do on CI.
+      'import/no-unresolved': 'off',
+    }),
+
     // rules we want to enforce
     'array-callback-return': 'error',
+    'func-names': 'error',
     'implicit-arrow-linebreak': 'error',
     'import/no-dynamic-require': 'error',
     'import/no-extraneous-dependencies': 'error',
@@ -76,25 +83,8 @@ module.exports = {
     'prefer-spread': 'error',
     'unicorn/prefer-node-protocol': 'error',
 
-    // transloadit rules we would like to enforce in the future
-    // but will require separate PRs to gradually get there
-    // and so the meantime: just warn
-    'class-methods-use-this': ['warn'],
-    'consistent-return': ['warn'],
-    'default-case': ['warn'],
-    'global-require': ['warn'],
-    'import/no-unresolved': ['warn'],
-    'import/order': ['warn'],
-    'no-mixed-operators': ['warn'],
-    'no-param-reassign': ['warn'],
-    'no-redeclare': ['warn'],
-    'no-shadow': ['warn'],
-    'no-use-before-define': ['warn', { 'functions': false }],
-    'radix': ['warn'],
     'react/button-has-type': 'error',
-    'react/destructuring-assignment': ['warn'],
     'react/forbid-prop-types': 'error',
-    'react/jsx-props-no-spreading': ['warn'],
     'react/no-access-state-in-setstate': 'error',
     'react/no-array-index-key': 'error',
     'react/no-deprecated': 'error',
@@ -122,8 +112,8 @@ module.exports = {
     // jsdoc
     'jsdoc/check-alignment': 'error',
     'jsdoc/check-examples': 'off', // cannot yet be supported for ESLint 8, see https://github.com/eslint/eslint/issues/14745
-    'jsdoc/check-param-names': ['warn'],
-    'jsdoc/check-syntax': ['warn'],
+    'jsdoc/check-param-names': 'error',
+    'jsdoc/check-syntax': 'error',
     'jsdoc/check-tag-names': ['error', { jsxTags: true }],
     'jsdoc/check-types': 'error',
     'jsdoc/newline-after-description': 'error',
@@ -253,7 +243,6 @@ module.exports = {
         'packages/@uppy/webcam/src/**/*.js',
         'packages/@uppy/xhr-upload/src/**/*.js',
         'packages/@uppy/zoom/src/**/*.js',
-        'website/src/examples/*/*.es6',
       ],
       parser: 'espree',
       parserOptions: {
@@ -359,20 +348,10 @@ module.exports = {
         'no-use-before-define': 'warn',
       },
     },
-    {
-      files: [
-        'website/src/examples/*/*.es6',
-      ],
-      rules: {
-        'import/no-extraneous-dependencies': 'off',
-        'no-console': 'off',
-      },
-    },
     {
       files: [
         '*.test.js',
         'test/endtoend/*.js',
-        'website/*.js',
         'bin/**.js',
       ],
       rules: {
@@ -383,8 +362,8 @@ module.exports = {
       files: [
         'bin/**.js',
         'bin/**.mjs',
-        'examples/**/*.config.js',
         'examples/**/*.cjs',
+        'examples/**/*.js',
         'packages/@uppy/companion/test/**/*.js',
         'test/**/*.js',
         'test/**/*.ts',
@@ -392,8 +371,6 @@ module.exports = {
         '*.test-d.ts',
         'postcss.config.js',
         '.eslintrc.js',
-        'website/*.js',
-        'website/**/*.js',
         'private/**/*.js',
       ],
       rules: {
@@ -415,15 +392,6 @@ module.exports = {
       },
     },
 
-    {
-      files: [
-        'website/themes/uppy/source/js/*.js',
-      ],
-      rules: {
-        'prefer-const': ['off'],
-      },
-    },
-
     {
       files: ['test/endtoend/*/*.mjs', 'test/endtoend/*/*.ts'],
       rules: {

+ 0 - 12
.gitignore

@@ -25,18 +25,6 @@ test/endtoend/create-react-app/coverage/
 uppy-*.tgz
 generatedLocale.d.ts
 
-website/db.json
-website/*.log
-website/node_modules/
-website/public/
-website/.deploy*/
-website/src/_drafts
-website/themes/uppy/source/uppy/
-website/themes/uppy/_config.yml
-website/src/_template/list_of_locale_packs.md
-website/src/examples/locale_list.json
-website/themes/uppy/layout/partials/generated_stargazers.ejs
-
 **/output/*
 !output/.keep
 examples/dev/file.txt

+ 3 - 5
e2e/cypress/integration/dashboard-aws-multipart.spec.ts

@@ -10,10 +10,8 @@ describe('Dashboard with @uppy/aws-s3-multipart', () => {
   it('should upload cat image successfully', () => {
     cy.get('@file-input').selectFile('cypress/fixtures/images/cat.jpg', { force:true })
 
-    cy.get('.uppy-StatusBar-actionBtn--upload').click().then(() => {
-      cy.wait(['@post', '@get', '@put']).then(() => {
-        cy.get('.uppy-StatusBar-statusPrimary').should('contain', 'Complete')
-      })
-    })
+    cy.get('.uppy-StatusBar-actionBtn--upload').click()
+    cy.wait(['@post', '@get', '@put'])
+    cy.get('.uppy-StatusBar-statusPrimary').should('contain', 'Complete')
   })
 })

+ 3 - 5
e2e/cypress/integration/dashboard-aws.spec.ts

@@ -9,10 +9,8 @@ describe('Dashboard with @uppy/aws-s3', () => {
   it('should upload cat image successfully', () => {
     cy.get('@file-input').selectFile('cypress/fixtures/images/cat.jpg', { force:true })
 
-    cy.get('.uppy-StatusBar-actionBtn--upload').click().then(() => {
-      cy.wait(['@post', '@get']).then(() => {
-        cy.get('.uppy-StatusBar-statusPrimary').should('contain', 'Complete')
-      })
-    })
+    cy.get('.uppy-StatusBar-actionBtn--upload').click()
+    cy.wait(['@post', '@get'])
+    cy.get('.uppy-StatusBar-statusPrimary').should('contain', 'Complete')
   })
 })

+ 28 - 37
e2e/cypress/integration/dashboard-transloadit.spec.ts

@@ -10,10 +10,9 @@ describe('Dashboard with Transloadit', () => {
   it('should upload cat image successfully', () => {
     cy.get('@file-input').selectFile('cypress/fixtures/images/cat.jpg', { force:true })
 
-    cy.get('.uppy-StatusBar-actionBtn--upload').click().then(() => {
-      cy.wait(['@assemblies', '@resumable']).then(() => {
-        cy.get('.uppy-StatusBar-statusPrimary').should('contain', 'Complete')
-      })
+    cy.get('.uppy-StatusBar-actionBtn--upload').click()
+    cy.wait(['@assemblies', '@resumable']).then(() => {
+      cy.get('.uppy-StatusBar-statusPrimary').should('contain', 'Complete')
     })
   })
 
@@ -31,17 +30,16 @@ describe('Dashboard with Transloadit', () => {
       cy.get('@file-input').selectFile(
         ['cypress/fixtures/images/cat.jpg', 'cypress/fixtures/images/traffic.jpg', 'cypress/fixtures/images/car.jpg'],
         { force:true },
-      ).then(() => {
-        cy.get('.uppy-StatusBar-actionBtn--upload').click().then(() => {
-          cy.wait(['@createAssemblies']).then(() => {
-            expect(Object.values(uppy.getPlugin('Transloadit').activeAssemblies).every((a: any) => a.pollInterval)).to.equal(true)
+      )
+      cy.get('.uppy-StatusBar-actionBtn--upload').click()
+
+      cy.wait(['@createAssemblies']).then(() => {
+        expect(Object.values(uppy.getPlugin('Transloadit').activeAssemblies).every((a: any) => a.pollInterval)).to.equal(true)
 
-            uppy.cancelAll()
+        uppy.cancelAll()
 
-            cy.wait(['@delete']).then(() => {
-              expect(Object.values(uppy.getPlugin('Transloadit').activeAssemblies).some((a: any) => a.pollInterval)).to.equal(false)
-            })
-          })
+        cy.wait(['@delete']).then(() => {
+          expect(Object.values(uppy.getPlugin('Transloadit').activeAssemblies).some((a: any) => a.pollInterval)).to.equal(false)
         })
       })
     })
@@ -75,20 +73,15 @@ describe('Dashboard with Transloadit', () => {
         { method: 'DELETE', pathname: '/assemblies/*', times: 1 },
       ).as('assemblyDeletion')
 
-      cy.get('.uppy-StatusBar-actionBtn--upload').click().then(() => {
-        cy.wait('@assemblyPolling').then(() => {
-          cy.get('button[data-cy=cancel]').click().then(() => {
-            cy.wait('@assemblyDeletion').then(() => {
-              // Unfortunately, waiting on a network request somehow often results in a race condition.
-              // We just want to know wether this is called or not, so waiting for 2 sec to be sure.
-              // eslint-disable-next-line cypress/no-unnecessary-waiting
-              cy.wait(2000).then(() => {
-                expect(spy).to.be.calledOnce
-              })
-            })
-          })
-        })
-      })
+      cy.get('.uppy-StatusBar-actionBtn--upload').click()
+      cy.wait('@assemblyPolling')
+      cy.get('button[data-cy=cancel]').click()
+      cy.wait('@assemblyDeletion')
+      // Unfortunately, waiting on a network request somehow often results in a race condition.
+      // We just want to know wether this is called or not, so waiting for 2 sec to be sure.
+      // eslint-disable-next-line cypress/no-unnecessary-waiting
+      cy.wait(2000)
+      expect(spy).to.be.calledOnce
     })
   })
 
@@ -116,18 +109,16 @@ describe('Dashboard with Transloadit', () => {
         { method: 'DELETE', pathname: '/assemblies/*', times: 1 },
       ).as('assemblyDeletion')
 
-      cy.get('.uppy-StatusBar-actionBtn--upload').click().then(() => {
-        cy.wait('@assemblyPolling').then(() => {
-          expect(Object.values(uppy.getPlugin('Transloadit').activeAssemblies).every((a: any) => a.pollInterval)).to.equal(true)
+      cy.get('.uppy-StatusBar-actionBtn--upload').click()
+      cy.wait('@assemblyPolling')
+      expect(Object.values(uppy.getPlugin('Transloadit').activeAssemblies).every((a: any) => a.pollInterval)).to.equal(true)
 
-          const { files } = uppy.getState()
-          uppy.removeFiles(Object.keys(files))
+      const { files } = uppy.getState()
+      uppy.removeFiles(Object.keys(files))
 
-          cy.wait('@assemblyDeletion').then(() => {
-            expect(Object.values(uppy.getPlugin('Transloadit').activeAssemblies).some((a: any) => a.pollInterval)).to.equal(false)
-            expect(spy).to.be.calledOnce
-          })
-        })
+      cy.wait('@assemblyDeletion').then(() => {
+        expect(Object.values(uppy.getPlugin('Transloadit').activeAssemblies).some((a: any) => a.pollInterval)).to.equal(false)
+        expect(spy).to.be.calledOnce
       })
     })
   })

+ 6 - 8
e2e/cypress/integration/dashboard-tus.spec.ts

@@ -22,10 +22,9 @@ describe('Dashboard with Tus', () => {
   it('should upload cat image successfully', () => {
     cy.get('@file-input').selectFile('cypress/fixtures/images/cat.jpg', { force:true })
 
-    cy.get('.uppy-StatusBar-actionBtn--upload').click().then(() => {
-      cy.wait(['@post', '@patch']).then(() => {
-        cy.get('.uppy-StatusBar-statusPrimary').should('contain', 'Complete')
-      })
+    cy.get('.uppy-StatusBar-actionBtn--upload').click()
+    cy.wait(['@post', '@patch']).then(() => {
+      cy.get('.uppy-StatusBar-statusPrimary').should('contain', 'Complete')
     })
   })
 
@@ -37,10 +36,9 @@ describe('Dashboard with Tus', () => {
       { statusCode: 429, body: {} },
     ).as('patch')
 
-    cy.get('.uppy-StatusBar-actionBtn--upload').click().then(() => {
-      cy.wait('@tus').then(() => {
-        cy.get('.uppy-StatusBar-statusPrimary').should('contain', 'Complete')
-      })
+    cy.get('.uppy-StatusBar-actionBtn--upload').click()
+    cy.wait('@tus').then(() => {
+      cy.get('.uppy-StatusBar-statusPrimary').should('contain', 'Complete')
     })
   })
 

+ 1 - 1
examples/angular-example/package.json

@@ -41,7 +41,7 @@
     "@types/jasmine": "~4.0.0",
     "eslint": "^8.0.0",
     "eslint-plugin-import": "^2.22.1",
-    "eslint-plugin-jsdoc": "^39.0.0",
+    "eslint-plugin-jsdoc": "^40.0.0",
     "eslint-plugin-prefer-arrow": "^1.2.3",
     "jasmine-core": "~4.2.0",
     "karma": "~6.4.0",

+ 3 - 2
examples/aws-nodejs/index.js

@@ -137,11 +137,11 @@ app.get('/s3/multipart/:uploadId', (req, res, next) => {
   const { key } = req.query
 
   if (typeof key !== 'string') {
-    return res.status(400).json({ error: 's3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"' })
+    res.status(400).json({ error: 's3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"' })
+    return
   }
 
   const parts = []
-  listPartsPage(0)
 
   function listPartsPage (startAt) {
     client.listParts({
@@ -165,6 +165,7 @@ app.get('/s3/multipart/:uploadId', (req, res, next) => {
       }
     })
   }
+  listPartsPage(0)
 })
 
 function isValidPart (part) {

+ 1 - 0
examples/react-native-expo/App.js

@@ -112,6 +112,7 @@ export default function App () {
       <View style={{ alignItems: 'center' }}>
         <Image
           style={styles.logo}
+          // eslint-disable-next-line global-require
           source={require('./assets/uppy-logo.png')}
         />
       </View>

+ 4 - 4
package.json

@@ -4,7 +4,7 @@
   "version": "0.0.0",
   "description": "Extensible JavaScript file upload widget with support for drag&drop, resumable uploads, previews, restrictions, file processing/encoding, remote providers like Instagram, Dropbox, Google Drive, S3 and more :dog:",
   "lint-staged": {
-    "*.js": "eslint",
+    "*.js": "eslint --fix",
     "*.jsx": "eslint --fix",
     "*.md": [
       "remark --silently-ignore -i .remarkignore -foq",
@@ -68,8 +68,8 @@
     "eslint-plugin-compat": "^4.0.0",
     "eslint-plugin-cypress": "^2.12.1",
     "eslint-plugin-import": "^2.25.2",
-    "eslint-plugin-jest": "^26.0.0",
-    "eslint-plugin-jsdoc": "^39.0.0",
+    "eslint-plugin-jest": "^27.0.0",
+    "eslint-plugin-jsdoc": "^40.0.0",
     "eslint-plugin-jsx-a11y": "^6.4.1",
     "eslint-plugin-markdown": "^3.0.0",
     "eslint-plugin-node": "^11.1.0",
@@ -77,7 +77,7 @@
     "eslint-plugin-promise": "^6.0.0",
     "eslint-plugin-react": "^7.22.0",
     "eslint-plugin-react-hooks": "^4.2.0",
-    "eslint-plugin-unicorn": "^43.0.0",
+    "eslint-plugin-unicorn": "^46.0.0",
     "github-contributors-list": "^1.2.4",
     "glob": "^8.0.0",
     "jest": "^29.0.0",

+ 2 - 0
packages/@uppy/companion/src/server/Uploader.js

@@ -532,8 +532,10 @@ class Uploader {
           // previously made to providers. Deleting the field would prevent it from getting leaked
           // to the frontend etc.
           // @ts-ignore
+          // eslint-disable-next-line no-param-reassign
           delete error.originalRequest
           // @ts-ignore
+          // eslint-disable-next-line no-param-reassign
           delete error.originalResponse
           reject(error)
         },

+ 1 - 0
packages/@uppy/companion/src/server/controllers/index.js

@@ -1,3 +1,4 @@
+/* eslint-disable global-require */
 module.exports = {
   callback: require('./callback'),
   deauthorizationCallback: require('./deauth-callback'),

+ 6 - 3
packages/@uppy/companion/src/server/controllers/oauth-redirect.js

@@ -12,19 +12,22 @@ module.exports = function oauthRedirect (req, res) {
   const params = qs.stringify(req.query)
   const { authProvider } = req.companion.provider
   if (!req.companion.options.server.oauthDomain) {
-    return res.redirect(req.companion.buildURL(`/connect/${authProvider}/callback?${params}`, true))
+    res.redirect(req.companion.buildURL(`/connect/${authProvider}/callback?${params}`, true))
+    return
   }
 
   const state = oAuthState.getDynamicStateFromRequest(req)
   if (!state) {
-    return res.status(400).send('Cannot find state in session')
+    res.status(400).send('Cannot find state in session')
+    return
   }
   const handler = oAuthState.getFromState(state, 'companionInstance', req.companion.options.secret)
   const handlerHostName = (new URL(handler)).host
 
   if (hasMatch(handlerHostName, req.companion.options.server.validHosts)) {
     const url = `${handler}/connect/${authProvider}/callback?${params}`
-    return res.redirect(url)
+    res.redirect(url)
+    return
   }
 
   res.status(400).send('Invalid Host in state')

+ 36 - 30
packages/@uppy/companion/src/server/controllers/s3.js

@@ -28,13 +28,15 @@ module.exports = function s3 (config) {
     const client = req.companion.s3Client
 
     if (!client || typeof config.bucket !== 'string') {
-      return res.status(400).json({ error: 'This Companion server does not support uploading to S3' })
+      res.status(400).json({ error: 'This Companion server does not support uploading to S3' })
+      return
     }
 
     const metadata = req.query.metadata || {}
     const key = config.getKey(req, req.query.filename, metadata)
     if (typeof key !== 'string') {
-      return res.status(500).json({ error: 'S3 uploads are misconfigured: filename returned from `getKey` must be a string' })
+      res.status(500).json({ error: 'S3 uploads are misconfigured: filename returned from `getKey` must be a string' })
+      return
     }
 
     const fields = {
@@ -45,8 +47,8 @@ module.exports = function s3 (config) {
 
     if (config.acl != null) fields.acl = config.acl
 
-    Object.keys(metadata).forEach((key) => {
-      fields[`x-amz-meta-${key}`] = metadata[key]
+    Object.keys(metadata).forEach((metadataKey) => {
+      fields[`x-amz-meta-${metadataKey}`] = metadata[metadataKey]
     })
 
     client.createPresignedPost({
@@ -88,10 +90,12 @@ module.exports = function s3 (config) {
     const key = config.getKey(req, req.body.filename, req.body.metadata || {})
     const { type, metadata } = req.body
     if (typeof key !== 'string') {
-      return res.status(500).json({ error: 's3: filename returned from `getKey` must be a string' })
+      res.status(500).json({ error: 's3: filename returned from `getKey` must be a string' })
+      return
     }
     if (typeof type !== 'string') {
-      return res.status(400).json({ error: 's3: content type must be a string' })
+      res.status(400).json({ error: 's3: content type must be a string' })
+      return
     }
 
     const params = {
@@ -135,11 +139,11 @@ module.exports = function s3 (config) {
     const { key } = req.query
 
     if (typeof key !== 'string') {
-      return res.status(400).json({ error: 's3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"' })
+      res.status(400).json({ error: 's3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"' })
+      return
     }
 
     let parts = []
-    listPartsPage(0)
 
     function listPartsPage (startAt) {
       client.listParts({
@@ -159,14 +163,11 @@ module.exports = function s3 (config) {
           // Get the next page.
           listPartsPage(data.NextPartNumberMarker)
         } else {
-          done()
+          res.json(parts)
         }
       })
     }
-
-    function done () {
-      res.json(parts)
-    }
+    listPartsPage(0)
   }
 
   /**
@@ -187,10 +188,12 @@ module.exports = function s3 (config) {
     const { key } = req.query
 
     if (typeof key !== 'string') {
-      return res.status(400).json({ error: 's3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"' })
+      res.status(400).json({ error: 's3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"' })
+      return
     }
     if (!parseInt(partNumber, 10)) {
-      return res.status(400).json({ error: 's3: the part number must be a number between 1 and 10000.' })
+      res.status(400).json({ error: 's3: the part number must be a number between 1 and 10000.' })
+      return
     }
 
     client.getSignedUrl('uploadPart', {
@@ -229,19 +232,20 @@ module.exports = function s3 (config) {
     const { key, partNumbers } = req.query
 
     if (typeof key !== 'string') {
-      return res.status(400).json({ error: 's3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"' })
+      res.status(400).json({ error: 's3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"' })
+      return
     }
 
     if (typeof partNumbers !== 'string') {
-      return res.status(400).json({ error: 's3: the part numbers must be passed as a comma separated query parameter. For example: "?partNumbers=4,6,7,21"' })
+      res.status(400).json({ error: 's3: the part numbers must be passed as a comma separated query parameter. For example: "?partNumbers=4,6,7,21"' })
+      return
     }
 
     const partNumbersArray = partNumbers.split(',')
-    partNumbersArray.forEach((partNumber) => {
-      if (!parseInt(partNumber, 10)) {
-        return res.status(400).json({ error: 's3: the part numbers must be a number between 1 and 10000.' })
-      }
-    })
+    if (!partNumbersArray.every((partNumber) => parseInt(partNumber, 10))) {
+      res.status(400).json({ error: 's3: the part numbers must be a number between 1 and 10000.' })
+      return
+    }
 
     Promise.all(
       partNumbersArray.map((partNumber) => {
@@ -282,7 +286,8 @@ module.exports = function s3 (config) {
     const { key } = req.query
 
     if (typeof key !== 'string') {
-      return res.status(400).json({ error: 's3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"' })
+      res.status(400).json({ error: 's3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"' })
+      return
     }
 
     client.abortMultipartUpload({
@@ -318,10 +323,15 @@ module.exports = function s3 (config) {
     const { parts } = req.body
 
     if (typeof key !== 'string') {
-      return res.status(400).json({ error: 's3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"' })
+      res.status(400).json({ error: 's3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"' })
+      return
     }
-    if (!Array.isArray(parts) || !parts.every(isValidPart)) {
-      return res.status(400).json({ error: 's3: `parts` must be an array of {ETag, PartNumber} objects.' })
+    if (
+      !Array.isArray(parts)
+      || !parts.every(part => typeof part === 'object' && typeof part?.PartNumber === 'number' && typeof part.ETag === 'string')
+    ) {
+      res.status(400).json({ error: 's3: `parts` must be an array of {ETag, PartNumber} objects.' })
+      return
     }
 
     client.completeMultipartUpload({
@@ -352,7 +362,3 @@ module.exports = function s3 (config) {
     .post('/multipart/:uploadId/complete', express.json({ limit: '1mb' }), completeMultipartUpload)
     .delete('/multipart/:uploadId', abortMultipartUpload)
 }
-
-function isValidPart (part) {
-  return part && typeof part === 'object' && typeof part.PartNumber === 'number' && typeof part.ETag === 'string'
-}

+ 2 - 1
packages/@uppy/companion/src/server/controllers/send-token.js

@@ -44,7 +44,8 @@ module.exports = function sendToken (req, res, next) {
     const allowedClients = req.companion.options.clients
     // if no preset clients then allow any client
     if (!allowedClients || hasMatch(origin, allowedClients) || hasMatch((new URL(origin)).host, allowedClients)) {
-      return res.send(htmlContent(uppyAuthToken, origin))
+      res.send(htmlContent(uppyAuthToken, origin))
+      return
     }
   }
   next()

+ 23 - 23
packages/@uppy/companion/src/server/emitter/redis-emitter.js

@@ -37,6 +37,29 @@ module.exports = (redisUrl, redisPubSubScope) => {
     }
   }
 
+  /**
+   * Remove an event listener
+   *
+   * @param {string} eventName name of the event
+   * @param {any} handler the handler of the event to remove
+   */
+  function removeListener (eventName, handler) {
+    if (eventName === 'error') return errorEmitter.removeListener('error', handler)
+
+    return runWhenConnected(() => {
+      const handlersByThisEventName = handlersByEvent.get(eventName)
+      if (handlersByThisEventName == null) return undefined
+
+      const actualHandler = handlersByThisEventName.get(handler)
+      if (actualHandler == null) return undefined
+
+      handlersByThisEventName.delete(handler)
+      if (handlersByThisEventName.size === 0) handlersByEvent.delete(eventName)
+
+      return subscriber.pUnsubscribe(getPrefixedEventName(eventName), actualHandler)
+    })
+  }
+
   function addListener (eventName, handler, _once = false) {
     function actualHandler (message) {
       if (_once) removeListener(eventName, handler)
@@ -92,29 +115,6 @@ module.exports = (redisUrl, redisPubSubScope) => {
     runWhenConnected(() => publisher.publish(getPrefixedEventName(eventName), JSON.stringify(args)))
   }
 
-  /**
-   * Remove an event listener
-   *
-   * @param {string} eventName name of the event
-   * @param {any} handler the handler of the event to remove
-   */
-  function removeListener (eventName, handler) {
-    if (eventName === 'error') return errorEmitter.removeListener('error', handler)
-
-    return runWhenConnected(() => {
-      const handlersByThisEventName = handlersByEvent.get(eventName)
-      if (handlersByThisEventName == null) return undefined
-
-      const actualHandler = handlersByThisEventName.get(handler)
-      if (actualHandler == null) return undefined
-
-      handlersByThisEventName.delete(handler)
-      if (handlersByThisEventName.size === 0) handlersByEvent.delete(eventName)
-
-      return subscriber.pUnsubscribe(getPrefixedEventName(eventName), actualHandler)
-    })
-  }
-
   /**
    * Remove all listeners of an event
    *

+ 10 - 10
packages/@uppy/companion/src/server/helpers/oauth-state.js

@@ -2,6 +2,16 @@ const crypto = require('node:crypto')
 const atob = require('atob')
 const { encrypt, decrypt } = require('./utils')
 
+const setState = (state, secret) => {
+  const encodedState = Buffer.from(JSON.stringify(state)).toString('base64')
+  return encrypt(encodedState, secret)
+}
+
+const getState = (state, secret) => {
+  const encodedState = decrypt(state, secret)
+  return JSON.parse(atob(encodedState))
+}
+
 module.exports.generateState = (secret) => {
   const state = {}
   state.id = crypto.randomBytes(10).toString('hex')
@@ -17,16 +27,6 @@ module.exports.getFromState = (state, name, secret) => {
   return getState(state, secret)[name]
 }
 
-const setState = (state, secret) => {
-  const encodedState = Buffer.from(JSON.stringify(state)).toString('base64')
-  return encrypt(encodedState, secret)
-}
-
-const getState = (state, secret) => {
-  const encodedState = decrypt(state, secret)
-  return JSON.parse(atob(encodedState))
-}
-
 module.exports.getDynamicStateFromRequest = (req) => {
   const dynamic = (req.session.grant || {}).dynamic || {}
   const { state } = dynamic

+ 2 - 3
packages/@uppy/companion/src/server/helpers/utils.js

@@ -23,7 +23,7 @@ exports.jsonStringify = (data) => {
     if (typeof value === 'object' && value !== null) {
       if (cache.indexOf(value) !== -1) {
         // Circular reference found, discard key
-        return
+        return undefined
       }
       cache.push(value)
     }
@@ -90,8 +90,7 @@ function urlEncode (unencoded) {
 }
 
 function urlDecode (encoded) {
-  encoded = encoded.replace(/-/g, '+').replace(/_/g, '/').replace(/~/g, '=')
-  return encoded
+  return encoded.replace(/-/g, '+').replace(/_/g, '/').replace(/~/g, '=')
 }
 
 /**

+ 4 - 4
packages/@uppy/companion/src/server/helpers/version.js

@@ -8,8 +8,8 @@ const semver = require('semver')
  * @returns {boolean}
  */
 exports.gte = (v1, v2) => {
-  v1 = semver.coerce(v1).version
-  v2 = semver.coerce(v2).version
-
-  return semver.gte(v1, v2)
+  return semver.gte(
+    semver.coerce(v1).version,
+    semver.coerce(v2).version,
+  )
 }

+ 16 - 16
packages/@uppy/companion/src/server/jobs.js

@@ -10,17 +10,6 @@ const logger = require('./logger')
 // TODO rewrite to use require('timers/promises').setTimeout when we support newer node versions
 const sleep = promisify(setTimeout)
 
-/**
- * Runs a function every 24 hours, to clean up stale, upload related files.
- *
- * @param {string} dirPath path to the directory which you want to clean
- */
-exports.startCleanUpJob = (dirPath) => {
-  logger.info('starting clean up job', 'jobs.cleanup.start')
-  // run once a day
-  schedule.scheduleJob('0 23 * * *', () => cleanUpFinishedUploads(dirPath))
-}
-
 const cleanUpFinishedUploads = (dirPath) => {
   logger.info(`running clean up job for path: ${dirPath}`, 'jobs.cleanup.progress.read')
   fs.readdir(dirPath, (err, files) => {
@@ -40,12 +29,12 @@ const cleanUpFinishedUploads = (dirPath) => {
       }
       const fullPath = path.join(dirPath, file)
 
-      fs.stat(fullPath, (err, stats) => {
+      fs.stat(fullPath, (err2, stats) => {
         const twelveHoursAgo = 12 * 60 * 60 * 1000
-        if (err) {
+        if (err2) {
           // we still delete the file if we can't get the stats
           // but we also log the error
-          logger.error(err, 'jobs.cleanup.stat.error')
+          logger.error(err2, 'jobs.cleanup.stat.error')
         // @ts-ignore
         } else if (((new Date()) - stats.mtime) < twelveHoursAgo) {
           logger.info(`skipping file ${file}`, 'jobs.cleanup.skip')
@@ -53,14 +42,25 @@ const cleanUpFinishedUploads = (dirPath) => {
         }
 
         logger.info(`deleting file ${file}`, 'jobs.cleanup.progress.delete')
-        fs.unlink(fullPath, (err) => {
-          if (err) logger.error(err, 'jobs.cleanup.delete.error')
+        fs.unlink(fullPath, (err3) => {
+          if (err3) logger.error(err3, 'jobs.cleanup.delete.error')
         })
       })
     })
   })
 }
 
+/**
+ * Runs a function every 24 hours, to clean up stale, upload related files.
+ *
+ * @param {string} dirPath path to the directory which you want to clean
+ */
+exports.startCleanUpJob = (dirPath) => {
+  logger.info('starting clean up job', 'jobs.cleanup.start')
+  // run once a day
+  schedule.scheduleJob('0 23 * * *', () => cleanUpFinishedUploads(dirPath))
+}
+
 async function runPeriodicPing ({ urls, payload, requestTimeout }) {
   // Run requests in parallel
   await Promise.all(urls.map(async (url) => {

+ 10 - 0
packages/@uppy/companion/src/server/provider/index.js

@@ -89,9 +89,11 @@ module.exports.addCustomProviders = (customProviders, providers, grantConfig) =>
   Object.keys(customProviders).forEach((providerName) => {
     const customProvider = customProviders[providerName]
 
+    // eslint-disable-next-line no-param-reassign
     providers[providerName] = customProvider.module
 
     if (isOAuthProvider(customProvider.module.authProvider)) {
+      // eslint-disable-next-line no-param-reassign
       grantConfig[providerName] = {
         ...customProvider.config,
         // todo: consider setting these options from a universal point also used
@@ -116,6 +118,7 @@ module.exports.addProviderOptions = (companionOptions, grantConfig) => {
     return
   }
 
+  // eslint-disable-next-line no-param-reassign
   grantConfig.defaults = {
     host: server.host,
     protocol: server.protocol,
@@ -128,9 +131,12 @@ module.exports.addProviderOptions = (companionOptions, grantConfig) => {
     const authProvider = providerNameToAuthName(providerName, companionOptions)
     if (isOAuthProvider(authProvider) && grantConfig[authProvider]) {
       // explicitly add providerOptions so users don't override other providerOptions.
+      // eslint-disable-next-line no-param-reassign
       grantConfig[authProvider].key = providerOptions[providerName].key
+      // eslint-disable-next-line no-param-reassign
       grantConfig[authProvider].secret = providerOptions[providerName].secret
       if (providerOptions[providerName].credentialsURL) {
+        // eslint-disable-next-line no-param-reassign
         grantConfig[authProvider].dynamic = ['key', 'secret', 'redirect_uri']
       }
 
@@ -140,16 +146,20 @@ module.exports.addProviderOptions = (companionOptions, grantConfig) => {
       // override grant.js redirect uri with companion's custom redirect url
       const isExternal = !!server.implicitPath
       const redirectPath = `/${providerName}/redirect`
+      // eslint-disable-next-line no-param-reassign
       grantConfig[authProvider].redirect_uri = getURLBuilder(companionOptions)(redirectPath, isExternal)
       if (oauthDomain) {
         const fullRedirectPath = getURLBuilder(companionOptions)(redirectPath, isExternal, true)
+        // eslint-disable-next-line no-param-reassign
         grantConfig[authProvider].redirect_uri = `${server.protocol}://${oauthDomain}${fullRedirectPath}`
       }
 
       if (server.implicitPath) {
         // no url builder is used for this because grant internally adds the path
+        // eslint-disable-next-line no-param-reassign
         grantConfig[authProvider].callback = `${server.implicitPath}${grantConfig[authProvider].callback}`
       } else if (server.path) {
+        // eslint-disable-next-line no-param-reassign
         grantConfig[authProvider].callback = `${server.path}${grantConfig[authProvider].callback}`
       }
     }

+ 1 - 0
packages/@uppy/companion/test/__mocks__/tus-js-client.js

@@ -13,6 +13,7 @@ class Upload {
     setTimeout(this._triggerProgressThenSuccess.bind(this), 100)
   }
 
+  // eslint-disable-next-line class-methods-use-this
   abort () {
     // noop
   }

+ 1 - 2
packages/@uppy/companion/test/__tests__/callback.js

@@ -1,7 +1,6 @@
-/* global jest:false, test:false, expect:false, describe:false */
-
 const mockOauthState = require('../mockoauthstate')()
 
+// eslint-disable-next-line import/order
 const request = require('supertest')
 const tokenService = require('../../src/server/helpers/jwt')
 const { getServer } = require('../mockserver')

+ 0 - 2
packages/@uppy/companion/test/__tests__/cors.js

@@ -1,5 +1,3 @@
-/* global jest:false, test:false, describe:false, expect:false */
-
 const { cors } = require('../../src/server/middlewares')
 
 function testWithMock ({ corsOptions, get = () => {}, origin = 'https://localhost:1234' } = {}) {

+ 0 - 2
packages/@uppy/companion/test/__tests__/header-blacklist.js

@@ -1,5 +1,3 @@
-/* global test:false, expect:false, describe:false, */
-
 const headerSanitize = require('../../src/server/header-blacklist')
 
 describe('Header black-list testing', () => {

+ 0 - 1
packages/@uppy/companion/test/__tests__/logger.js

@@ -1,4 +1,3 @@
-/* global test:false, expect:false, describe:false, beforeAll:false, */
 const chalk = require('chalk')
 const logger = require('../../src/server/logger')
 

+ 0 - 2
packages/@uppy/companion/test/__tests__/preauth.js

@@ -1,5 +1,3 @@
-/* global jest:false, test:false, expect:false, describe:false */
-
 jest.mock('../../src/server/helpers/jwt', () => {
   return {
     generateToken: () => {},

+ 1 - 2
packages/@uppy/companion/test/__tests__/provider-manager.js

@@ -1,5 +1,3 @@
-/* global test:false, expect:false, describe:false, beforeEach:false */
-
 const providerManager = require('../../src/server/provider')
 const { getCompanionOptions } = require('../../src/standalone/helper')
 const { setDefaultEnv } = require('../mockserver')
@@ -10,6 +8,7 @@ let companionOptions
 describe('Test Provider options', () => {
   beforeEach(() => {
     setDefaultEnv()
+    // eslint-disable-next-line global-require
     grantConfig = require('../../src/config/grant')()
     companionOptions = getCompanionOptions()
   })

+ 0 - 2
packages/@uppy/companion/test/__tests__/url.js

@@ -1,5 +1,3 @@
-/* global jest:false, test:false, expect:false, describe:false */
-
 const nock = require('nock')
 const request = require('supertest')
 

+ 1 - 0
packages/@uppy/companion/test/mockserver.js

@@ -62,6 +62,7 @@ module.exports.getServer = (extraEnv) => {
   // todo rewrite companion to not use global state
   // https://github.com/transloadit/uppy/issues/3284
   jest.resetModules()
+  // eslint-disable-next-line global-require
   const standalone = require('../src/standalone')
   const authServer = express()
 

+ 1 - 1
packages/@uppy/locales/src/es_MX.js

@@ -172,7 +172,7 @@ es_MX.strings = {
   zoomOut: 'Alejar',
 }
 
-es_MX.pluralize = function (count) {
+es_MX.pluralize = (count) => {
   if (count === 1) {
     return 0
   }

+ 1 - 1
packages/@uppy/locales/src/hi_IN.js

@@ -185,7 +185,7 @@ hi_IN.strings = {
   zoomOut: 'ज़ूम आउट',
 }
 
-hi_IN.pluralize = function (count) {
+hi_IN.pluralize = (count) => {
   if (count === 1) {
     return 0
   }

+ 1 - 1
packages/@uppy/react/src/Dashboard.d.ts

@@ -1,6 +1,6 @@
 import * as React from 'react'
 import type { DashboardOptions } from '@uppy/dashboard'
-import { Omit, WithBaseUppyProps } from './CommonTypes'
+import type { Omit, WithBaseUppyProps } from './CommonTypes'
 
 // This type is mapped into `DashboardProps` below so IntelliSense doesn't display this big mess of nested types
 type DashboardPropsInner = Omit<

+ 1 - 1
packages/@uppy/react/src/DashboardModal.d.ts

@@ -1,5 +1,5 @@
 import type { DashboardOptions } from '@uppy/dashboard'
-import { Omit, ToUppyProps } from './CommonTypes'
+import type { Omit, ToUppyProps } from './CommonTypes'
 
 // This type is mapped into `DashboardModalProps` below so IntelliSense doesn't display this big mess of nested types
 type DashboardModalPropsInner = {

+ 1 - 1
packages/@uppy/react/src/DragDrop.d.ts

@@ -1,5 +1,5 @@
 import type { DragDropOptions } from '@uppy/drag-drop'
-import { ToUppyProps } from './CommonTypes'
+import type { ToUppyProps } from './CommonTypes'
 
 export type DragDropProps = ToUppyProps<DragDropOptions>  & React.BaseHTMLAttributes<HTMLDivElement>
 

+ 1 - 1
packages/@uppy/react/src/FileInput.d.ts

@@ -1,5 +1,5 @@
 import type { FileInputOptions } from '@uppy/file-input'
-import { ToUppyProps } from './CommonTypes'
+import type { ToUppyProps } from './CommonTypes'
 
 export type FileInputProps = ToUppyProps<FileInputOptions>
 

+ 1 - 1
packages/@uppy/react/src/ProgressBar.d.ts

@@ -1,5 +1,5 @@
 import type { ProgressBarOptions } from '@uppy/progress-bar'
-import { ToUppyProps } from './CommonTypes'
+import type { ToUppyProps } from './CommonTypes'
 
 export type ProgressBarProps = ToUppyProps<ProgressBarOptions> & React.BaseHTMLAttributes<HTMLDivElement>
 

+ 1 - 1
packages/@uppy/react/src/StatusBar.d.ts

@@ -1,5 +1,5 @@
 import type { StatusBarOptions } from '@uppy/status-bar'
-import { ToUppyProps } from './CommonTypes'
+import type { ToUppyProps } from './CommonTypes'
 
 export type StatusBarProps = ToUppyProps<StatusBarOptions>  & React.BaseHTMLAttributes<HTMLDivElement>
 

File diff suppressed because it is too large
+ 323 - 272
yarn.lock


Some files were not shown because too many files changed in this diff