Forráskód Böngészése

Merge branch 'master' of https://github.com/transloadit/uppy

Artur Paikin 7 éve
szülő
commit
f1adcfa993

+ 1 - 0
CHANGELOG.md

@@ -125,6 +125,7 @@ To be released: 2018-03-29.
 - [x] s3: fix xhr response handlers (#625, @goto-bus-stop)
 - [ ] test: add typescript with JSDoc (@arturi)
 - [ ] dragdrop: allow customizing arrow icon https://github.com/transloadit/uppy/pull/374#issuecomment-334116208 (@arturi)
+- [x] core: ⚠️ **breaking** `onBeforeFileAdded()`, `onBeforeUpload()` and `addFile()` are now synchronous. You can no longer return a Promise from the `onBefore*()` functions. (#294, @goto-bus-stop)
 - [x] react: Allow overriding `<DashboardModal />` `target` prop (#740, @goto-bus-stop)
 
 ## 0.23.3

+ 4 - 0
examples/custom-provider/.gitignore

@@ -0,0 +1,4 @@
+uppy.min.css
+node_modules
+output/*
+!output/.empty

+ 108 - 0
examples/custom-provider/client/MyCustomProvider.js

@@ -0,0 +1,108 @@
+const Plugin = require('uppy/lib/core/Plugin')
+const { Provider } = require('uppy/lib/server')
+const { ProviderView } = require('uppy/lib/views')
+const { h } = require('preact')
+
+module.exports = class MyCustomProvider extends Plugin {
+  constructor (uppy, opts) {
+    super(uppy, opts)
+    this.type = 'acquirer'
+    this.id = this.opts.id || 'MyCustomProvider'
+    this.title = 'MyCustomProvider'
+    this.icon = () => (
+      <img src="https://uppy.io/images/logos/uppy-dog-head-arrow.svg" width="23" />
+    )
+
+    // writing out the key explicitly for readability the key used to store
+    // the provider instance must be equal to this.id.
+    this[this.id] = new Provider(uppy, {
+      host: this.opts.host,
+      provider: 'mycustomprovider'
+    })
+
+    this.files = []
+    this.onAuth = this.onAuth.bind(this)
+    this.render = this.render.bind(this)
+
+    // merge default options with the ones set by user
+    this.opts = Object.assign({}, opts)
+  }
+
+  install () {
+    this.view = new ProviderView(this)
+    // Set default state
+    this.setPluginState({
+      authenticated: false,
+      files: [],
+      folders: [],
+      directories: [],
+      activeRow: -1,
+      filterInput: '',
+      isSearchVisible: false
+    })
+
+    const target = this.opts.target
+    if (target) {
+      this.mount(target, this)
+    }
+  }
+
+  uninstall () {
+    this.view.tearDown()
+    this.unmount()
+  }
+
+  onAuth (authenticated) {
+    this.setPluginState({ authenticated })
+    if (authenticated) {
+      this.view.getFolder()
+    }
+  }
+
+  isFolder (item) {
+    return false
+  }
+
+  getItemData (item) {
+    return item
+  }
+
+  getItemIcon (item) {
+    return () => (
+      <img src="https://uppy.io/images/logos/uppy-dog-head-arrow.svg" />
+    )
+  }
+
+  getItemSubList (item) {
+    return item.entries
+  }
+
+  getItemName (item) {
+    return item.name
+  }
+
+  getMimeType (item) {
+    // mime types aren't supported.
+    return null
+  }
+
+  getItemId (item) {
+    return item.name
+  }
+
+  getItemRequestPath (item) {
+    return encodeURIComponent(item.name)
+  }
+
+  getItemModifiedDate (item) {
+    return Date.now()
+  }
+
+  getItemThumbnailUrl (item) {
+    return 'https://uppy.io/images/logos/uppy-dog-head-arrow.svg'
+  }
+
+  render (state) {
+    return this.view.render(state)
+  }
+}

+ 7 - 0
examples/custom-provider/client/aliasify.js

@@ -0,0 +1,7 @@
+const path = require('path')
+
+module.exports = {
+  replacements: {
+    '^uppy/lib/(.*?)$': path.join(__dirname, '../../../src/$1')
+  }
+}

+ 27 - 0
examples/custom-provider/client/main.js

@@ -0,0 +1,27 @@
+const Uppy = require('uppy/lib/core')
+const GoogleDrive = require('uppy/lib/plugins/GoogleDrive')
+const Tus = require('uppy/lib/plugins/Tus')
+const MyCustomProvider = require('./MyCustomProvider')
+const Dashboard = require('uppy/lib/plugins/Dashboard')
+
+const uppy = Uppy({
+  debug: true,
+  autoProceed: false
+})
+
+uppy.use(GoogleDrive, {
+  host: 'http://localhost:3020'
+})
+
+uppy.use(MyCustomProvider, {
+  host: 'http://localhost:3020'
+})
+
+uppy.use(Dashboard, {
+  inline: true,
+  target: 'body',
+  plugins: ['GoogleDrive', 'MyCustomProvider']
+})
+
+uppy.use(Tus, {endpoint: 'https://master.tus.io/files/'})
+uppy.run()

+ 12 - 0
examples/custom-provider/index.html

@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <title>Uppy Custom provider Example</title>
+    <link href="uppy.min.css" rel="stylesheet">
+  </head>
+  <body>
+    <script src="bundle.js"></script>
+  </body>
+</html>

+ 0 - 0
examples/custom-provider/output/.empty


+ 21 - 0
examples/custom-provider/package.json

@@ -0,0 +1,21 @@
+{
+  "private": true,
+  "name": "custom-provider-example",
+  "scripts": {
+    "copy": "cp ../../dist/uppy.min.css .",
+    "start:client": "budo client/main.js:bundle.js -- -t babelify -g aliasify",
+    "start:server": "node server/index.js",
+    "start": "npm-run-all --serial copy --parallel start:*"
+  },
+  "aliasify": "client/aliasify.js",
+  "dependencies": {
+    "aliasify": "^2.1.0",
+    "babelify": "^7.3.0",
+    "body-parser": "^1.18.2",
+    "budo": "^10.0.4",
+    "express": "^4.16.2",
+    "express-session": "^1.15.6",
+    "npm-run-all": "^4.1.2",
+    "uppy-server": "^0.11.0"
+  }
+}

+ 13 - 0
examples/custom-provider/readme.md

@@ -0,0 +1,13 @@
+# Uppy + Server + Custom Provider  Example
+
+This example uses uppy-server with a dummy custom provider.
+This serves as an illustration on how integrating custom providers would work
+
+## Run it
+
+Move into this directory, then:
+
+```bash
+npm install
+npm start
+```

+ 42 - 0
examples/custom-provider/server/customprovider.js

@@ -0,0 +1,42 @@
+const fs = require('fs')
+const path = require('path')
+const DUMM_FILE = path.join(__dirname, 'fixtures/image.jpg')
+
+/**
+ * an example of a custom provider module. It implements uppy-server's Provider interface
+ */
+class MyCustomProvider {
+  constructor (options) {
+    this.authProvider = MyCustomProvider.authProvider
+  }
+
+  static get authProvider () {
+    return 'mycustomprovider'
+  }
+
+  list (options, done) {
+    const response = {
+      body: {
+        entries: [
+          { name: 'file1.jpg' },
+          { name: 'file2.jpg' },
+          {name: 'file3.jpg'}
+        ]
+      }
+    }
+    return done(null, response, response.body)
+  }
+
+  download ({ id, token }, onData) {
+    return fs.readFile(DUMM_FILE, (err, data) => {
+      if (err) console.error(err)
+      onData(data)
+    })
+  }
+
+  size ({ id, token }, done) {
+    return done(fs.statSync(DUMM_FILE).size)
+  }
+}
+
+module.exports = MyCustomProvider

BIN
examples/custom-provider/server/fixtures/image.jpg


+ 89 - 0
examples/custom-provider/server/index.js

@@ -0,0 +1,89 @@
+const express = require('express')
+const uppy = require('uppy-server')
+const bodyParser = require('body-parser')
+const session = require('express-session')
+
+const app = express()
+
+app.use(bodyParser.json())
+app.use(session({
+  secret: 'some-secret',
+  resave: true,
+  saveUninitialized: true
+}))
+
+app.use((req, res, next) => {
+  res.setHeader('Access-Control-Allow-Origin', req.headers.origin || '*')
+  res.setHeader(
+    'Access-Control-Allow-Methods',
+    'GET, POST, OPTIONS, PUT, PATCH, DELETE'
+  )
+  res.setHeader(
+    'Access-Control-Allow-Headers',
+    'Authorization, Origin, Content-Type, Accept'
+  )
+  res.setHeader('Access-Control-Allow-Credentials', 'true')
+  next()
+})
+
+// Routes
+app.get('/', (req, res) => {
+  res.setHeader('Content-Type', 'text/plain')
+  res.send('Welcome to my uppy server')
+})
+
+// initialize uppy
+const uppyOptions = {
+  providerOptions: {
+    google: {
+      key: 'your google key',
+      secret: 'your google secret'
+    }
+  },
+  customProviders: {
+    mycustomprovider: {
+      config: {
+        // your oauth handlers
+        authorize_url: 'http://localhost:3020/oauth/authorize',
+        access_url: 'http://localhost:3020/oauth/token',
+        oauth: 2,
+        key: '***',
+        secret: '**',
+        scope: ['read', 'write']
+      },
+      // you provider module
+      module: require('./customprovider')
+    }
+  },
+  server: {
+    host: 'localhost:3020',
+    protocol: 'http'
+  },
+  filePath: './output',
+  secret: 'some-secret',
+  debug: true
+}
+
+app.get('/oauth/authorize', (req, res) => {
+  // skips the default oauth process.
+  // ideally this endpoint should handle the actual oauth process
+  res.redirect(`http://localhost:3020/mycustomprovider/callback?state=${req.query.state}&access_token=randombytes`)
+})
+
+app.use(uppy.app(uppyOptions))
+
+// handle 404
+app.use((req, res, next) => {
+  return res.status(404).json({ message: 'Not Found' })
+})
+
+// handle server errors
+app.use((err, req, res, next) => {
+  console.error('\x1b[31m', err.stack, '\x1b[0m')
+  res.status(err.status || 500).json({ message: err.message, error: err })
+})
+
+uppy.socket(app.listen(3020), uppyOptions)
+
+console.log('Welcome to Uppy Server!')
+console.log(`Listening on http://0.0.0.0:${3020}`)

+ 85 - 66
src/core/Core.js

@@ -49,8 +49,8 @@ class Uppy {
         allowedFileTypes: false
       },
       meta: {},
-      onBeforeFileAdded: (currentFile, files) => Promise.resolve(),
-      onBeforeUpload: (files) => Promise.resolve(),
+      onBeforeFileAdded: (currentFile, files) => currentFile,
+      onBeforeUpload: (files) => files,
       locale: defaultLocale,
       store: DefaultStore()
     }
@@ -339,73 +339,90 @@ class Uppy {
   * @param {object} file object to add
   */
   addFile (file) {
-    return Promise.resolve()
-      // Wrap this in a Promise `.then()` handler so errors will reject the Promise
-      // instead of throwing.
-      .then(() => this.opts.onBeforeFileAdded(file, this.getState().files))
-      .then(() => Utils.getFileType(file))
-      .then((fileType) => {
-        const updatedFiles = Object.assign({}, this.getState().files)
-        let fileName
-        if (file.name) {
-          fileName = file.name
-        } else if (fileType.split('/')[0] === 'image') {
-          fileName = fileType.split('/')[0] + '.' + fileType.split('/')[1]
-        } else {
-          fileName = 'noname'
-        }
-        const fileExtension = Utils.getFileNameAndExtension(fileName).extension
-        const isRemote = file.isRemote || false
-
-        const fileID = Utils.generateFileID(file)
-
-        const newFile = {
-          source: file.source || '',
-          id: fileID,
-          name: fileName,
-          extension: fileExtension || '',
-          meta: Object.assign({}, this.getState().meta, {
-            name: fileName,
-            type: fileType
-          }),
-          type: fileType,
-          data: file.data,
-          progress: {
-            percentage: 0,
-            bytesUploaded: 0,
-            bytesTotal: file.data.size || 0,
-            uploadComplete: false,
-            uploadStarted: false
-          },
-          size: file.data.size || 0,
-          isRemote: isRemote,
-          remote: file.remote || '',
-          preview: file.preview
-        }
+    const { files } = this.getState()
 
-        this._checkRestrictions(newFile)
+    const onError = (msg) => {
+      const err = typeof msg === 'object' ? msg : new Error(msg)
+      this.log(err.message)
+      this.info(err.message, 'error', 5000)
+      throw err
+    }
 
-        updatedFiles[fileID] = newFile
-        this.setState({files: updatedFiles})
+    let mappedFile
+    try {
+      mappedFile = this.opts.onBeforeFileAdded(file, files)
+    } catch (err) {
+      onError(err)
+    }
 
-        this.emit('file-added', newFile)
-        this.log(`Added file: ${fileName}, ${fileID}, mime type: ${fileType}`)
+    if (typeof mappedFile === 'object' && mappedFile) {
+      if (mappedFile.then) {
+        throw new TypeError('onBeforeFileAdded() returned a Promise, but this is no longer supported. It must be synchronous.')
+      }
+      file = mappedFile
+    }
 
-        if (this.opts.autoProceed && !this.scheduledAutoProceed) {
-          this.scheduledAutoProceed = setTimeout(() => {
-            this.scheduledAutoProceed = null
-            this.upload().catch((err) => {
-              console.error(err.stack || err.message || err)
-            })
-          }, 4)
-        }
-      })
-      .catch((err) => {
-        const message = typeof err === 'object' ? err.message : err
-        this.log(message)
-        this.info(message, 'error', 5000)
-        return Promise.reject(typeof err === 'object' ? err : new Error(err))
+    const fileType = Utils.getFileType(file)
+    let fileName
+    if (file.name) {
+      fileName = file.name
+    } else if (fileType.split('/')[0] === 'image') {
+      fileName = fileType.split('/')[0] + '.' + fileType.split('/')[1]
+    } else {
+      fileName = 'noname'
+    }
+    const fileExtension = Utils.getFileNameAndExtension(fileName).extension
+    const isRemote = file.isRemote || false
+
+    const fileID = Utils.generateFileID(file)
+
+    const newFile = {
+      source: file.source || '',
+      id: fileID,
+      name: fileName,
+      extension: fileExtension || '',
+      meta: Object.assign({}, this.getState().meta, {
+        name: fileName,
+        type: fileType
+      }),
+      type: fileType,
+      data: file.data,
+      progress: {
+        percentage: 0,
+        bytesUploaded: 0,
+        bytesTotal: file.data.size || 0,
+        uploadComplete: false,
+        uploadStarted: false
+      },
+      size: file.data.size || 0,
+      isRemote: isRemote,
+      remote: file.remote || '',
+      preview: file.preview
+    }
+
+    try {
+      this._checkRestrictions(newFile)
+    } catch (err) {
+      onError(err)
+    }
+
+    this.setState({
+      files: Object.assign({}, files, {
+        [fileID]: newFile
       })
+    })
+
+    this.emit('file-added', newFile)
+    this.log(`Added file: ${fileName}, ${fileID}, mime type: ${fileType}`)
+
+    if (this.opts.autoProceed && !this.scheduledAutoProceed) {
+      this.scheduledAutoProceed = setTimeout(() => {
+        this.scheduledAutoProceed = null
+        this.upload().catch((err) => {
+          console.error(err.stack || err.message || err)
+        })
+      }, 4)
+    }
   }
 
   removeFile (fileID) {
@@ -1109,7 +1126,7 @@ class Uppy {
     })
   }
 
-    /**
+  /**
    * Start an upload for all the files that are not currently being uploaded.
    *
    * @return {Promise}
@@ -1120,7 +1137,9 @@ class Uppy {
     }
 
     return Promise.resolve()
-      .then(() => this.opts.onBeforeUpload(this.getState().files))
+      .then(() => {
+        this.opts.onBeforeUpload(this.getState().files)
+      })
       .then(() => this._checkMinNumberOfFiles())
       .then(() => {
         const { currentUploads } = this.getState()

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 2 - 1
src/core/Core.test.js


+ 41 - 0
src/core/PromiseWaiter.js

@@ -0,0 +1,41 @@
+/**
+ * Wait for multiple Promises to resolve.
+ */
+module.exports = class PromiseWaiter {
+  constructor () {
+    this.promises = []
+  }
+
+  add (promise) {
+    this.promises.push(promise)
+
+    const remove = () => {
+      this.remove(promise)
+    }
+    promise.then(remove, remove)
+  }
+
+  remove (promise) {
+    const index = this.promises.indexOf(promise)
+    if (index !== -1) {
+      this.promises.splice(index, 1)
+    }
+  }
+
+  wait () {
+    const promises = this.promises
+    this.promises = []
+
+    function noop () {
+      // No result value
+    }
+
+    // Just wait for a Promise to conclude in some way, whether it's resolution
+    // or rejection. We don't care about the contents.
+    function concluded (promise) {
+      return promise.then(noop, noop)
+    }
+
+    return Promise.all(promises.map(concluded)).then(noop)
+  }
+}

+ 11 - 30
src/core/Utils.js

@@ -1,8 +1,4 @@
 const throttle = require('lodash.throttle')
-// we inline file-type module, as opposed to using the NPM version,
-// because of this https://github.com/sindresorhus/file-type/issues/78
-// and https://github.com/sindresorhus/copy-text-to-clipboard/issues/5
-const fileType = require('../vendor/file-type')
 
 /**
  * A collection of small utility functions that help with dom manipulation, adding listeners,
@@ -133,36 +129,21 @@ function getFileType (file) {
 
   if (file.isRemote) {
     // some remote providers do not support file types
-    const mime = file.type ? file.type : extensionsToMime[fileExtension]
-    return Promise.resolve(mime)
+    return file.type ? file.type : extensionsToMime[fileExtension]
   }
 
-  // 1. try to determine file type from magic bytes with file-type module
-  // this should be the most trustworthy way
-  const chunk = file.data.slice(0, 4100)
-  return getArrayBuffer(chunk)
-    .then((buffer) => {
-      const type = fileType(buffer)
-      if (type && type.mime) {
-        return type.mime
-      }
-
-      // 2. if that’s no good, check if mime type is set in the file object
-      if (file.type) {
-        return file.type
-      }
+  // 2. if that’s no good, check if mime type is set in the file object
+  if (file.type) {
+    return file.type
+  }
 
-      // 3. if that’s no good, see if we can map extension to a mime type
-      if (fileExtension && extensionsToMime[fileExtension]) {
-        return extensionsToMime[fileExtension]
-      }
+  // 3. if that’s no good, see if we can map extension to a mime type
+  if (fileExtension && extensionsToMime[fileExtension]) {
+    return extensionsToMime[fileExtension]
+  }
 
-      // if all fails, well, return empty
-      return null
-    })
-    .catch(() => {
-      return null
-    })
+  // if all fails, well, return empty
+  return null
 }
 
 // TODO Check which types are actually supported in browsers. Chrome likes webm

+ 4 - 32
src/core/Utils.test.js

@@ -149,35 +149,13 @@ describe('core/utils', () => {
   })
 
   describe('getFileType', () => {
-    beforeEach(() => {
-      global.FileReader = class FileReader {
-        addEventListener (e, cb) {
-          if (e === 'load') {
-            this.loadCb = cb
-          }
-          if (e === 'error') {
-            this.errorCb = cb
-          }
-        }
-        readAsArrayBuffer (chunk) {
-          this.loadCb({ target: { result: new ArrayBuffer(8) } })
-        }
-      }
-    })
-
-    afterEach(() => {
-      global.FileReader = undefined
-    })
-
     it('should trust the filetype if the file comes from a remote source', () => {
       const file = {
         isRemote: true,
         type: 'audio/webm',
         name: 'foo.webm'
       }
-      return utils.getFileType(file).then(r => {
-        expect(r).toEqual('audio/webm')
-      })
+      expect(utils.getFileType(file)).toEqual('audio/webm')
     })
 
     it('should determine the filetype from the mimetype', () => {
@@ -186,9 +164,7 @@ describe('core/utils', () => {
         name: 'foo.webm',
         data: 'sdfsdfhq9efbicw'
       }
-      return utils.getFileType(file).then(r => {
-        expect(r).toEqual('audio/webm')
-      })
+      expect(utils.getFileType(file)).toEqual('audio/webm')
     })
 
     it('should determine the filetype from the extension', () => {
@@ -196,9 +172,7 @@ describe('core/utils', () => {
         name: 'foo.mp3',
         data: 'sdfsfhfh329fhwihs'
       }
-      return utils.getFileType(file).then(r => {
-        expect(r).toEqual('audio/mp3')
-      })
+      expect(utils.getFileType(file)).toEqual('audio/mp3')
     })
 
     it('should fail gracefully if unable to detect', () => {
@@ -206,9 +180,7 @@ describe('core/utils', () => {
         name: 'foobar',
         data: 'sdfsfhfh329fhwihs'
       }
-      return utils.getFileType(file).then(r => {
-        expect(r).toEqual(null)
-      })
+      expect(utils.getFileType(file)).toEqual(null)
     })
   })
 

+ 4 - 4
src/core/__snapshots__/Core.test.js.snap

@@ -26,7 +26,7 @@ Object {
       "isRemote": false,
       "meta": Object {
         "name": "foo.jpg",
-        "type": null,
+        "type": "image/jpeg",
       },
       "name": "foo.jpg",
       "preview": undefined,
@@ -40,7 +40,7 @@ Object {
       "remote": "",
       "size": 0,
       "source": "jest",
-      "type": null,
+      "type": "image/jpeg",
     },
     Object {
       "data": Uint8Array [],
@@ -49,7 +49,7 @@ Object {
       "isRemote": false,
       "meta": Object {
         "name": "bar.jpg",
-        "type": null,
+        "type": "image/jpeg",
       },
       "name": "bar.jpg",
       "preview": undefined,
@@ -63,7 +63,7 @@ Object {
       "remote": "",
       "size": 0,
       "source": "jest",
-      "type": null,
+      "type": "image/jpeg",
     },
   ],
   "uploadID": "cjd09qwxb000dlql4tp4doz8h",

+ 0 - 6
src/plugins/Dashboard/index.js

@@ -289,8 +289,6 @@ module.exports = class Dashboard extends Plugin {
         name: file.name,
         type: file.type,
         data: blob
-      }).catch(() => {
-        // Ignore
       })
     })
   }
@@ -305,8 +303,6 @@ module.exports = class Dashboard extends Plugin {
         name: file.name,
         type: file.type,
         data: file
-      }).catch(() => {
-        // Ignore
       })
     })
   }
@@ -373,8 +369,6 @@ module.exports = class Dashboard extends Plugin {
         name: file.name,
         type: file.type,
         data: file
-      }).catch(() => {
-        // Ignore
       })
     })
   }

+ 0 - 4
src/plugins/DragDrop/index.js

@@ -83,8 +83,6 @@ module.exports = class DragDrop extends Plugin {
         name: file.name,
         type: file.type,
         data: file
-      }).catch(() => {
-        // Ignore
       })
     })
   }
@@ -100,8 +98,6 @@ module.exports = class DragDrop extends Plugin {
         name: file.name,
         type: file.type,
         data: file
-      }).catch(() => {
-        // Ignore
       })
     })
   }

+ 1 - 3
src/plugins/Dummy.js

@@ -35,9 +35,7 @@ module.exports = class Dummy extends Plugin {
       data: blob
     }
     this.props.log('Adding fake file blob')
-    this.props.addFile(file).catch(() => {
-      // Ignore
-    })
+    this.props.addFile(file)
   }
 
   render (state) {

+ 0 - 2
src/plugins/FileInput.js

@@ -51,8 +51,6 @@ module.exports = class FileInput extends Plugin {
         name: file.name,
         type: file.type,
         data: file
-      }).catch(() => {
-        // Ignore
       })
     })
   }

+ 24 - 29
src/plugins/Transloadit/index.test.js

@@ -45,15 +45,14 @@ describe('Transloadit', () => {
 
     const data = Buffer.alloc(4000)
     data.size = data.byteLength
-    return uppy.addFile({
+    uppy.addFile({
       name: 'testfile',
       data
-    }).then(() => {
-      return uppy.upload().then(() => {
-        throw new Error('should have rejected')
-      }, (err) => {
-        expect(err.message).toMatch(/The `params\.auth\.key` option is required/)
-      })
+    })
+    return uppy.upload().then(() => {
+      throw new Error('should have rejected')
+    }, (err) => {
+      expect(err.message).toMatch(/The `params\.auth\.key` option is required/)
     })
   })
 
@@ -84,17 +83,15 @@ describe('Transloadit', () => {
     const data = Buffer.alloc(10)
     data.size = data.byteLength
 
-    return Promise.all([
-      uppy.addFile({ name: 'a.png', data }),
-      uppy.addFile({ name: 'b.png', data }),
-      uppy.addFile({ name: 'c.png', data }),
-      uppy.addFile({ name: 'd.png', data })
-    ]).then(() => {
-      return uppy.upload().then(() => {
-        throw new Error('upload should have been rejected')
-      }, () => {
-        expect(i).toBe(4)
-      })
+    uppy.addFile({ name: 'a.png', data })
+    uppy.addFile({ name: 'b.png', data })
+    uppy.addFile({ name: 'c.png', data })
+    uppy.addFile({ name: 'd.png', data })
+
+    return uppy.upload().then(() => {
+      throw new Error('upload should have been rejected')
+    }, () => {
+      expect(i).toBe(4)
     })
   })
 
@@ -131,17 +128,15 @@ describe('Transloadit', () => {
     const data2 = Buffer.alloc(20)
     data2.size = data2.byteLength
 
-    return Promise.all([
-      uppy.addFile({ name: 'a.png', data }),
-      uppy.addFile({ name: 'b.png', data }),
-      uppy.addFile({ name: 'c.png', data }),
-      uppy.addFile({ name: 'd.png', data: data2 })
-    ]).then(() => {
-      return uppy.upload().then(() => {
-        throw new Error('Upload should have been rejected')
-      }, () => {
-        expect(i).toBe(2)
-      })
+    uppy.addFile({ name: 'a.png', data })
+    uppy.addFile({ name: 'b.png', data })
+    uppy.addFile({ name: 'c.png', data })
+    uppy.addFile({ name: 'd.png', data: data2 })
+
+    return uppy.upload().then(() => {
+      throw new Error('Upload should have been rejected')
+    }, () => {
+      expect(i).toBe(2)
     })
   })
 

+ 0 - 559
src/vendor/file-type/index.js

@@ -1,559 +0,0 @@
-'use strict';
-
-module.exports = input => {
-	const buf = new Uint8Array(input);
-
-	if (!(buf && buf.length > 1)) {
-		return null;
-	}
-
-	const check = (header, opts) => {
-		opts = Object.assign({
-			offset: 0
-		}, opts);
-
-		for (let i = 0; i < header.length; i++) {
-			if (header[i] !== buf[i + opts.offset]) {
-				return false;
-			}
-		}
-
-		return true;
-	};
-
-	if (check([0xFF, 0xD8, 0xFF])) {
-		return {
-			ext: 'jpg',
-			mime: 'image/jpeg'
-		};
-	}
-
-	if (check([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A])) {
-		return {
-			ext: 'png',
-			mime: 'image/png'
-		};
-	}
-
-	if (check([0x47, 0x49, 0x46])) {
-		return {
-			ext: 'gif',
-			mime: 'image/gif'
-		};
-	}
-
-	if (check([0x57, 0x45, 0x42, 0x50], {offset: 8})) {
-		return {
-			ext: 'webp',
-			mime: 'image/webp'
-		};
-	}
-
-	if (check([0x46, 0x4C, 0x49, 0x46])) {
-		return {
-			ext: 'flif',
-			mime: 'image/flif'
-		};
-	}
-
-	// Needs to be before `tif` check
-	if (
-		(check([0x49, 0x49, 0x2A, 0x0]) || check([0x4D, 0x4D, 0x0, 0x2A])) &&
-		check([0x43, 0x52], {offset: 8})
-	) {
-		return {
-			ext: 'cr2',
-			mime: 'image/x-canon-cr2'
-		};
-	}
-
-	if (
-		check([0x49, 0x49, 0x2A, 0x0]) ||
-		check([0x4D, 0x4D, 0x0, 0x2A])
-	) {
-		return {
-			ext: 'tif',
-			mime: 'image/tiff'
-		};
-	}
-
-	if (check([0x42, 0x4D])) {
-		return {
-			ext: 'bmp',
-			mime: 'image/bmp'
-		};
-	}
-
-	if (check([0x49, 0x49, 0xBC])) {
-		return {
-			ext: 'jxr',
-			mime: 'image/vnd.ms-photo'
-		};
-	}
-
-	if (check([0x38, 0x42, 0x50, 0x53])) {
-		return {
-			ext: 'psd',
-			mime: 'image/vnd.adobe.photoshop'
-		};
-	}
-
-	// Needs to be before the `zip` check
-	if (
-		check([0x50, 0x4B, 0x3, 0x4]) &&
-		check([0x6D, 0x69, 0x6D, 0x65, 0x74, 0x79, 0x70, 0x65, 0x61, 0x70, 0x70, 0x6C, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x2F, 0x65, 0x70, 0x75, 0x62, 0x2B, 0x7A, 0x69, 0x70], {offset: 30})
-	) {
-		return {
-			ext: 'epub',
-			mime: 'application/epub+zip'
-		};
-	}
-
-	// Needs to be before `zip` check
-	// Assumes signed `.xpi` from addons.mozilla.org
-	if (
-		check([0x50, 0x4B, 0x3, 0x4]) &&
-		check([0x4D, 0x45, 0x54, 0x41, 0x2D, 0x49, 0x4E, 0x46, 0x2F, 0x6D, 0x6F, 0x7A, 0x69, 0x6C, 0x6C, 0x61, 0x2E, 0x72, 0x73, 0x61], {offset: 30})
-	) {
-		return {
-			ext: 'xpi',
-			mime: 'application/x-xpinstall'
-		};
-	}
-
-	if (
-		check([0x50, 0x4B]) &&
-		(buf[2] === 0x3 || buf[2] === 0x5 || buf[2] === 0x7) &&
-		(buf[3] === 0x4 || buf[3] === 0x6 || buf[3] === 0x8)
-	) {
-		return {
-			ext: 'zip',
-			mime: 'application/zip'
-		};
-	}
-
-	if (check([0x75, 0x73, 0x74, 0x61, 0x72], {offset: 257})) {
-		return {
-			ext: 'tar',
-			mime: 'application/x-tar'
-		};
-	}
-
-	if (
-		check([0x52, 0x61, 0x72, 0x21, 0x1A, 0x7]) &&
-		(buf[6] === 0x0 || buf[6] === 0x1)
-	) {
-		return {
-			ext: 'rar',
-			mime: 'application/x-rar-compressed'
-		};
-	}
-
-	if (check([0x1F, 0x8B, 0x8])) {
-		return {
-			ext: 'gz',
-			mime: 'application/gzip'
-		};
-	}
-
-	if (check([0x42, 0x5A, 0x68])) {
-		return {
-			ext: 'bz2',
-			mime: 'application/x-bzip2'
-		};
-	}
-
-	if (check([0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C])) {
-		return {
-			ext: '7z',
-			mime: 'application/x-7z-compressed'
-		};
-	}
-
-	if (check([0x78, 0x01])) {
-		return {
-			ext: 'dmg',
-			mime: 'application/x-apple-diskimage'
-		};
-	}
-
-	if (
-		(
-			check([0x0, 0x0, 0x0]) &&
-			(buf[3] === 0x18 || buf[3] === 0x20) &&
-			check([0x66, 0x74, 0x79, 0x70], {offset: 4})
-		) ||
-		check([0x33, 0x67, 0x70, 0x35]) ||
-		(
-			check([0x0, 0x0, 0x0, 0x1C, 0x66, 0x74, 0x79, 0x70, 0x6D, 0x70, 0x34, 0x32]) &&
-			check([0x6D, 0x70, 0x34, 0x31, 0x6D, 0x70, 0x34, 0x32, 0x69, 0x73, 0x6F, 0x6D], {offset: 16})
-		) ||
-		check([0x0, 0x0, 0x0, 0x1C, 0x66, 0x74, 0x79, 0x70, 0x69, 0x73, 0x6F, 0x6D]) ||
-		check([0x0, 0x0, 0x0, 0x1C, 0x66, 0x74, 0x79, 0x70, 0x6D, 0x70, 0x34, 0x32, 0x0, 0x0, 0x0, 0x0])
-	) {
-		return {
-			ext: 'mp4',
-			mime: 'video/mp4'
-		};
-	}
-
-	if (check([0x0, 0x0, 0x0, 0x1C, 0x66, 0x74, 0x79, 0x70, 0x4D, 0x34, 0x56])) {
-		return {
-			ext: 'm4v',
-			mime: 'video/x-m4v'
-		};
-	}
-
-	if (check([0x4D, 0x54, 0x68, 0x64])) {
-		return {
-			ext: 'mid',
-			mime: 'audio/midi'
-		};
-	}
-
-	// https://github.com/threatstack/libmagic/blob/master/magic/Magdir/matroska
-	if (check([0x1A, 0x45, 0xDF, 0xA3])) {
-		const sliced = buf.subarray(4, 4 + 4096);
-		const idPos = sliced.findIndex((el, i, arr) => arr[i] === 0x42 && arr[i + 1] === 0x82);
-
-		if (idPos >= 0) {
-			const docTypePos = idPos + 3;
-			const findDocType = type => Array.from(type).every((c, i) => sliced[docTypePos + i] === c.charCodeAt(0));
-
-			if (findDocType('matroska')) {
-				return {
-					ext: 'mkv',
-					mime: 'video/x-matroska'
-				};
-			}
-
-			if (findDocType('webm')) {
-				return {
-					ext: 'webm',
-					mime: 'video/webm'
-				};
-			}
-		}
-	}
-
-	if (check([0x0, 0x0, 0x0, 0x14, 0x66, 0x74, 0x79, 0x70, 0x71, 0x74, 0x20, 0x20]) ||
-		check([0x66, 0x72, 0x65, 0x65], {offset: 4}) ||
-		check([0x66, 0x74, 0x79, 0x70, 0x71, 0x74, 0x20, 0x20], {offset: 4}) ||
-		check([0x6D, 0x64, 0x61, 0x74], {offset: 4}) || // MJPEG
-		check([0x77, 0x69, 0x64, 0x65], {offset: 4})) {
-		return {
-			ext: 'mov',
-			mime: 'video/quicktime'
-		};
-	}
-
-	if (
-		check([0x52, 0x49, 0x46, 0x46]) &&
-		check([0x41, 0x56, 0x49], {offset: 8})
-	) {
-		return {
-			ext: 'avi',
-			mime: 'video/x-msvideo'
-		};
-	}
-
-	if (check([0x30, 0x26, 0xB2, 0x75, 0x8E, 0x66, 0xCF, 0x11, 0xA6, 0xD9])) {
-		return {
-			ext: 'wmv',
-			mime: 'video/x-ms-wmv'
-		};
-	}
-
-	if (check([0x0, 0x0, 0x1, 0xBA])) {
-		return {
-			ext: 'mpg',
-			mime: 'video/mpeg'
-		};
-	}
-
-	if (
-		check([0x49, 0x44, 0x33]) ||
-		check([0xFF, 0xFB])
-	) {
-		return {
-			ext: 'mp3',
-			mime: 'audio/mpeg'
-		};
-	}
-
-	if (
-		check([0x66, 0x74, 0x79, 0x70, 0x4D, 0x34, 0x41], {offset: 4}) ||
-		check([0x4D, 0x34, 0x41, 0x20])
-	) {
-		return {
-			ext: 'm4a',
-			mime: 'audio/m4a'
-		};
-	}
-
-	// Needs to be before `ogg` check
-	if (check([0x4F, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64], {offset: 28})) {
-		return {
-			ext: 'opus',
-			mime: 'audio/opus'
-		};
-	}
-
-	if (check([0x4F, 0x67, 0x67, 0x53])) {
-		return {
-			ext: 'ogg',
-			mime: 'audio/ogg'
-		};
-	}
-
-	if (check([0x66, 0x4C, 0x61, 0x43])) {
-		return {
-			ext: 'flac',
-			mime: 'audio/x-flac'
-		};
-	}
-
-	if (
-		check([0x52, 0x49, 0x46, 0x46]) &&
-		check([0x57, 0x41, 0x56, 0x45], {offset: 8})
-	) {
-		return {
-			ext: 'wav',
-			mime: 'audio/x-wav'
-		};
-	}
-
-	if (check([0x23, 0x21, 0x41, 0x4D, 0x52, 0x0A])) {
-		return {
-			ext: 'amr',
-			mime: 'audio/amr'
-		};
-	}
-
-	if (check([0x25, 0x50, 0x44, 0x46])) {
-		return {
-			ext: 'pdf',
-			mime: 'application/pdf'
-		};
-	}
-
-	if (check([0x4D, 0x5A])) {
-		return {
-			ext: 'exe',
-			mime: 'application/x-msdownload'
-		};
-	}
-
-	if (
-		(buf[0] === 0x43 || buf[0] === 0x46) &&
-		check([0x57, 0x53], {offset: 1})
-	) {
-		return {
-			ext: 'swf',
-			mime: 'application/x-shockwave-flash'
-		};
-	}
-
-	if (check([0x7B, 0x5C, 0x72, 0x74, 0x66])) {
-		return {
-			ext: 'rtf',
-			mime: 'application/rtf'
-		};
-	}
-
-	if (check([0x00, 0x61, 0x73, 0x6D])) {
-		return {
-			ext: 'wasm',
-			mime: 'application/wasm'
-		};
-	}
-
-	if (
-		check([0x77, 0x4F, 0x46, 0x46]) &&
-		(
-			check([0x00, 0x01, 0x00, 0x00], {offset: 4}) ||
-			check([0x4F, 0x54, 0x54, 0x4F], {offset: 4})
-		)
-	) {
-		return {
-			ext: 'woff',
-			mime: 'font/woff'
-		};
-	}
-
-	if (
-		check([0x77, 0x4F, 0x46, 0x32]) &&
-		(
-			check([0x00, 0x01, 0x00, 0x00], {offset: 4}) ||
-			check([0x4F, 0x54, 0x54, 0x4F], {offset: 4})
-		)
-	) {
-		return {
-			ext: 'woff2',
-			mime: 'font/woff2'
-		};
-	}
-
-	if (
-		check([0x4C, 0x50], {offset: 34}) &&
-		(
-			check([0x00, 0x00, 0x01], {offset: 8}) ||
-			check([0x01, 0x00, 0x02], {offset: 8}) ||
-			check([0x02, 0x00, 0x02], {offset: 8})
-		)
-	) {
-		return {
-			ext: 'eot',
-			mime: 'application/octet-stream'
-		};
-	}
-
-	if (check([0x00, 0x01, 0x00, 0x00, 0x00])) {
-		return {
-			ext: 'ttf',
-			mime: 'font/ttf'
-		};
-	}
-
-	if (check([0x4F, 0x54, 0x54, 0x4F, 0x00])) {
-		return {
-			ext: 'otf',
-			mime: 'font/otf'
-		};
-	}
-
-	if (check([0x00, 0x00, 0x01, 0x00])) {
-		return {
-			ext: 'ico',
-			mime: 'image/x-icon'
-		};
-	}
-
-	if (check([0x46, 0x4C, 0x56, 0x01])) {
-		return {
-			ext: 'flv',
-			mime: 'video/x-flv'
-		};
-	}
-
-	if (check([0x25, 0x21])) {
-		return {
-			ext: 'ps',
-			mime: 'application/postscript'
-		};
-	}
-
-	if (check([0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00])) {
-		return {
-			ext: 'xz',
-			mime: 'application/x-xz'
-		};
-	}
-
-	if (check([0x53, 0x51, 0x4C, 0x69])) {
-		return {
-			ext: 'sqlite',
-			mime: 'application/x-sqlite3'
-		};
-	}
-
-	if (check([0x4E, 0x45, 0x53, 0x1A])) {
-		return {
-			ext: 'nes',
-			mime: 'application/x-nintendo-nes-rom'
-		};
-	}
-
-	if (check([0x43, 0x72, 0x32, 0x34])) {
-		return {
-			ext: 'crx',
-			mime: 'application/x-google-chrome-extension'
-		};
-	}
-
-	if (
-		check([0x4D, 0x53, 0x43, 0x46]) ||
-		check([0x49, 0x53, 0x63, 0x28])
-	) {
-		return {
-			ext: 'cab',
-			mime: 'application/vnd.ms-cab-compressed'
-		};
-	}
-
-	// Needs to be before `ar` check
-	if (check([0x21, 0x3C, 0x61, 0x72, 0x63, 0x68, 0x3E, 0x0A, 0x64, 0x65, 0x62, 0x69, 0x61, 0x6E, 0x2D, 0x62, 0x69, 0x6E, 0x61, 0x72, 0x79])) {
-		return {
-			ext: 'deb',
-			mime: 'application/x-deb'
-		};
-	}
-
-	if (check([0x21, 0x3C, 0x61, 0x72, 0x63, 0x68, 0x3E])) {
-		return {
-			ext: 'ar',
-			mime: 'application/x-unix-archive'
-		};
-	}
-
-	if (check([0xED, 0xAB, 0xEE, 0xDB])) {
-		return {
-			ext: 'rpm',
-			mime: 'application/x-rpm'
-		};
-	}
-
-	if (
-		check([0x1F, 0xA0]) ||
-		check([0x1F, 0x9D])
-	) {
-		return {
-			ext: 'Z',
-			mime: 'application/x-compress'
-		};
-	}
-
-	if (check([0x4C, 0x5A, 0x49, 0x50])) {
-		return {
-			ext: 'lz',
-			mime: 'application/x-lzip'
-		};
-	}
-
-	if (check([0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1])) {
-		return {
-			ext: 'msi',
-			mime: 'application/x-msi'
-		};
-	}
-
-	if (check([0x06, 0x0E, 0x2B, 0x34, 0x02, 0x05, 0x01, 0x01, 0x0D, 0x01, 0x02, 0x01, 0x01, 0x02])) {
-		return {
-			ext: 'mxf',
-			mime: 'application/mxf'
-		};
-	}
-
-	if (check([0x47], {offset: 4}) && (check([0x47], {offset: 192}) || check([0x47], {offset: 196}))) {
-		return {
-			ext: 'mts',
-			mime: 'video/mp2t'
-		};
-	}
-
-	if (check([0x42, 0x4C, 0x45, 0x4E, 0x44, 0x45, 0x52])) {
-		return {
-			ext: 'blend',
-			mime: 'application/x-blender'
-		};
-	}
-
-	if (check([0x42, 0x50, 0x47, 0xFB])) {
-		return {
-			ext: 'bpg',
-			mime: 'image/bpg'
-		};
-	}
-
-	return null;
-};

+ 0 - 21
src/vendor/file-type/license

@@ -1,21 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.

+ 0 - 109
src/vendor/file-type/package.json

@@ -1,109 +0,0 @@
-{
-  "name": "file-type",
-  "version": "5.2.0",
-  "description": "Detect the file type of a Buffer/Uint8Array",
-  "license": "MIT",
-  "repository": "sindresorhus/file-type",
-  "author": {
-    "name": "Sindre Sorhus",
-    "email": "sindresorhus@gmail.com",
-    "url": "sindresorhus.com"
-  },
-  "engines": {
-    "node": ">=4"
-  },
-  "scripts": {
-    "test": "xo && ava"
-  },
-  "files": [
-    "index.js"
-  ],
-  "keywords": [
-    "mime",
-    "file",
-    "type",
-    "archive",
-    "image",
-    "img",
-    "pic",
-    "picture",
-    "flash",
-    "photo",
-    "video",
-    "type",
-    "detect",
-    "check",
-    "is",
-    "exif",
-    "exe",
-    "binary",
-    "buffer",
-    "uint8array",
-    "jpg",
-    "png",
-    "gif",
-    "webp",
-    "flif",
-    "cr2",
-    "tif",
-    "bmp",
-    "jxr",
-    "psd",
-    "zip",
-    "tar",
-    "rar",
-    "gz",
-    "bz2",
-    "7z",
-    "dmg",
-    "mp4",
-    "m4v",
-    "mid",
-    "mkv",
-    "webm",
-    "mov",
-    "avi",
-    "mpg",
-    "mp3",
-    "m4a",
-    "ogg",
-    "opus",
-    "flac",
-    "wav",
-    "amr",
-    "pdf",
-    "epub",
-    "exe",
-    "swf",
-    "rtf",
-    "woff",
-    "woff2",
-    "eot",
-    "ttf",
-    "otf",
-    "ico",
-    "flv",
-    "ps",
-    "xz",
-    "sqlite",
-    "xpi",
-    "cab",
-    "deb",
-    "ar",
-    "rpm",
-    "Z",
-    "lz",
-    "msi",
-    "mxf",
-    "mts",
-    "wasm",
-    "webassembly",
-    "blend",
-    "bpg"
-  ],
-  "devDependencies": {
-    "ava": "*",
-    "read-chunk": "^2.0.0",
-    "xo": "*"
-  }
-}

+ 10 - 12
src/views/ProviderView/index.js

@@ -167,18 +167,16 @@ module.exports = class ProviderView {
       }
     }
 
-    Utils.getFileType(tagFile).then(fileType => {
-      if (fileType && Utils.isPreviewSupported(fileType)) {
-        tagFile.preview = this.plugin.getItemThumbnailUrl(file)
-      }
-      this.plugin.uppy.log('Adding remote file')
-      this.plugin.uppy.addFile(tagFile).catch(() => {
-        // Ignore
-      })
-      if (!isCheckbox) {
-        this.donePicking()
-      }
-    })
+    const fileType = Utils.getFileType(tagFile)
+    // TODO Should we just always use the thumbnail URL if it exists?
+    if (fileType && Utils.isPreviewSupported(fileType)) {
+      tagFile.preview = this.plugin.getItemThumbnailUrl(file)
+    }
+    this.plugin.uppy.log('Adding remote file')
+    this.plugin.uppy.addFile(tagFile)
+    if (!isCheckbox) {
+      this.donePicking()
+    }
   }
 
   /**

BIN
test/resources/image.jpg


+ 14 - 14
website/src/docs/uppy.md

@@ -21,8 +21,8 @@ const uppy = Uppy({
     allowedFileTypes: false
   },
   meta: {},
-  onBeforeFileAdded: (currentFile, files) => Promise.resolve(),
-  onBeforeUpload: (files) => Promise.resolve(),
+  onBeforeFileAdded: (currentFile, files) => currentFile,
+  onBeforeUpload: (files) => {},
   locale: defaultLocale,
   store: new DefaultStore()
 })
@@ -70,35 +70,35 @@ meta: {
 This global metadata is added to each file in Uppy. It can be modified with two methods:
 
 1. [`uppy.setMeta({ username: 'Peter' })`](/docs/uppy/#uppy-setmeta-data) — set or update meta for all files.
-2. [`uppy.setFileMeta('myfileID', { resize: 1500 })`](/docs/uppy/#uppy-setFileMeta-fileID-data) — set or update meta for specific file. 
+2. [`uppy.setFileMeta('myfileID', { resize: 1500 })`](/docs/uppy/#uppy-setFileMeta-fileID-data) — set or update meta for specific file.
 
 Metadata from each file is then attached to uploads in [Tus](/docs/tus/) and [XHRUpload](/docs/tus/) plugins.
 
 Metadata can also be added from a `<form>` element on your page via [Form](/docs/form/)plugin or via UI if you are using Dashboard with [`metaFields`](/docs/dashboard/#metaFields) option.
 
-### `onBeforeFileAdded: (currentFile, files) => Promise.resolve()`
+<a id="onBeforeFileAdded"></a>
+### `onBeforeFileAdded: (currentFile, files) => currentFile`
 
-A function run before a file is added to Uppy. Gets passed `(currentFile, files)` where `currentFile` is a file that is about to be added, and `files` is an object with all files that already are in Uppy. Return `Promise.resolve` to proceed with adding the file or `Promise.reject` to abort. Use this function to run any number of custom checks on the selected file, or manipulating it, like optimizing a file name, for example.
+A function run before a file is added to Uppy. Gets passed `(currentFile, files)` where `currentFile` is a file that is about to be added, and `files` is an object with all files that already are in Uppy. Return a file object or nothing to proceed with adding the file, or throw an error to abort. Use this function to run any number of custom checks on the selected file, or manipulating it, like optimizing a file name, for example.
 
 ```js
 onBeforeFileAdded: (currentFile, files) => {
   if (currentFile.name === 'forest-IMG_0616.jpg') {
-    return Promise.resolve()
+    return true
   }
-  return Promise.reject('This is not the file I was looking for')
+  throw new Error('This is not the file I was looking for')
 }
 ```
 
-### `onBeforeUpload: (files) => Promise.resolve()`
+### `onBeforeUpload: (files) => {}`
 
-A function run before an upload begins. Gets passed `files` object with all files that already are in Uppy. Return `Promise.resolve` to proceed with adding the file or `Promise.reject` to abort. Use this to check if all files or their total number match your requirements, or manipulate all the files at once before upload.
+A function run before an upload begins. Gets passed `files` object with all files that already are in Uppy. Return nothing to proceed with adding the file or throw an error to abort. Use this to check if all files or their total number match your requirements, or manipulate all the files at once before upload.
 
 ```js
 onBeforeUpload: (files) => {
   if (Object.keys(files).length < 2) {
-    return Promise.reject('too few files')
+    throw new Error('Not enough files.')
   }
-  return Promise.resolve()
 }
 ```
 
@@ -180,9 +180,9 @@ uppy.addFile({
 })
 ```
 
-`addFile` attempts to determine file type by [magic bytes](https://github.com/sindresorhus/file-type) + the provided `type` + extension; then checks if the file can be added, considering `uppy.opts.restrictions`, sets metadata and generates a preview, if it’s an image.
+`addFile` throws an error if the file cannot be added, either because `onBeforeFileAdded(file)` threw an error, or because `uppy.opts.restrictions` checks failed.
 
-If `uppy.opts.autoProceed === true`, Uppy will begin uploading after the first file is added.
+If `uppy.opts.autoProceed === true`, Uppy will begin uploading automatically when files are added.
 
 ### `uppy.getFile(fileID)`
 
@@ -375,7 +375,7 @@ Fired when upload starts.
 
 ```javascript
 uppy.on('upload', (data) => {
-  // data object consists of `id` with upload id and `fileIDs` array 
+  // data object consists of `id` with upload id and `fileIDs` array
   // with file ids in current upload
   // data: { id, fileIDs }
   console.log(`Starting upload ${id} for files ${fileIDs}`)

Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott