├── .gitattributes
├── test
├── assets
│ └── nyc.jpg
├── uppy.js
├── index.test.js
└── server.js
├── webpack.config.js
├── jest.config.js
├── .browserlistrc
├── jest-puppeteer.config.js
├── babel.config.js
├── .gitignore
├── LICENSE
├── README.md
├── package.json
└── src
└── index.js
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/test/assets/nyc.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arturi/uppy-plugin-image-compressor/HEAD/test/assets/nyc.jpg
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | entry: './test/uppy.js',
3 | output: {
4 | filename: './test/uppy.min.js',
5 | library: 'foo'
6 | },
7 | mode: 'production'
8 | }
9 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | preset: "jest-puppeteer",
3 | globals: {
4 | PATH: "http://localhost:4444"
5 | },
6 | testMatch: [
7 | "**/test/**/*.test.js"
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/.browserlistrc:
--------------------------------------------------------------------------------
1 | IE 10
2 | last 2 Safari versions
3 | last 2 Chrome versions
4 | last 2 ChromeAndroid versions
5 | last 2 Firefox versions
6 | last 2 FirefoxAndroid versions
7 | last 2 Edge versions
8 | iOS 11.2
9 |
--------------------------------------------------------------------------------
/jest-puppeteer.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | server: {
3 | command: 'npm run test:serve',
4 | port: 4444,
5 | launchTimeout: 10000
6 | }
7 | // launch: {
8 | // dumpio: true,
9 | // headless: false,
10 | // slowMo : 50
11 | // }
12 | }
13 |
--------------------------------------------------------------------------------
/test/uppy.js:
--------------------------------------------------------------------------------
1 | const Core = require('@uppy/core')
2 | const FileInput = require('@uppy/file-input')
3 | const ImageCompressor = require('../lib/index.js')
4 |
5 | const core = new Core({
6 | debug: true
7 | })
8 | core.use(FileInput, {
9 | target: 'body'
10 | })
11 | core.use(ImageCompressor, {
12 | quality: 0.6
13 | })
14 |
15 | window.uppy = core
16 |
17 | export default uppy
18 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = (api) => {
2 | const targets = {}
3 | if (api.env('test')) {
4 | targets.node = 'current'
5 | }
6 |
7 | return {
8 | presets: [
9 | ['@babel/preset-env', {
10 | modules: false,
11 | loose: true,
12 | targets
13 | }]
14 | ],
15 | plugins: [
16 | '@babel/plugin-transform-object-assign',
17 | ]
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | lib/
2 |
3 | # Logs
4 | logs
5 | *.log
6 | npm-debug.log*
7 | yarn-debug.log*
8 | yarn-error.log*
9 |
10 | # Runtime data
11 | pids
12 | *.pid
13 | *.seed
14 | *.pid.lock
15 |
16 | # Directory for instrumented libs generated by jscoverage/JSCover
17 | lib-cov
18 |
19 | # Coverage directory used by tools like istanbul
20 | coverage
21 |
22 | # nyc test coverage
23 | .nyc_output
24 |
25 | .DS_Store
26 |
27 |
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (https://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # TypeScript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # Yarn Integrity file
55 | .yarn-integrity
56 |
57 | # dotenv environment variables file
58 | .env
59 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Artur Paikin
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Uppy Image Compressor
2 |
3 |
4 |
5 | ImageCompressor is an [Uppy](https://uppy.io) file uploader plugin, that compresses images before upload, saving bandwidth.
6 |
7 | ImageCompressor uses [Compressor.js](https://github.com/fengyuanchen/compressorjs), and the compression is lossy. From Compressor.js readme:
8 |
9 | > JavaScript image compressor. Uses the Browser's native canvas.toBlob API to do the compression work, which means it is lossy compression. General use this to precompress a client image file before upload it.
10 |
11 | :warning: This is not an official Uppy plugin, so no support is offered for it. Please use at your own risk.
12 |
13 | Uppy is being developed by the folks at [Transloadit](https://transloadit.com), a versatile file encoding service.
14 |
15 | ## Example
16 |
17 | ```js
18 | const Uppy = require('@uppy/core')
19 | const ImageCompressor = require('uppy-plugin-image-compressor')
20 |
21 | const uppy = Uppy()
22 | uppy.use(ImageCompressor, {
23 | // Options from Compressor.js https://github.com/fengyuanchen/compressorjs#options, just don’t set `success` or `error`
24 | })
25 | ```
26 |
27 | ## Installation
28 |
29 | ```bash
30 | $ npm install uppy-plugin-image-compressor --save
31 | ```
32 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "uppy-plugin-image-compressor",
3 | "version": "1.1.0",
4 | "description": "Compresses images added to Uppy before upload, using Compressor.js package (lossy compression)",
5 | "repository": "https://github.com/arturi/uppy-plugin-image-compressor",
6 | "main": "lib/index.js",
7 | "scripts": {
8 | "build": "babel src/index.js -o lib/index.js",
9 | "test": "jest",
10 | "test:serve": "node test/server.js",
11 | "prepublish": "npm run build"
12 | },
13 | "keywords": [
14 | "uppy",
15 | "uppy-plugin",
16 | "file uploader"
17 | ],
18 | "author": "Artur Paikin",
19 | "license": "MIT",
20 | "jest": {
21 | "preset": "jest-puppeteer",
22 | "globals": {
23 | "PATH": "http://localhost:4444"
24 | },
25 | "testMatch": [
26 | "**/test/**/*.test.js"
27 | ]
28 | },
29 | "dependencies": {
30 | "@uppy/core": "^1.6.0",
31 | "@uppy/utils": "^2.1.0",
32 | "compressorjs": "^1.0.5"
33 | },
34 | "devDependencies": {
35 | "@babel/cli": "^7.6.4",
36 | "@babel/core": "^7.6.4",
37 | "@babel/plugin-transform-object-assign": "^7.2.0",
38 | "@babel/preset-env": "^7.6.3",
39 | "@uppy/dashboard": "^1.5.0",
40 | "@uppy/file-input": "^1.4.0",
41 | "express": "^4.17.1",
42 | "jest": "^24.9.0",
43 | "jest-puppeteer": "^4.3.0",
44 | "karmatic": "^1.4.0",
45 | "puppeteer": "^2.0.0",
46 | "webpack": "^4.41.2"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/test/index.test.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const nycJpgPath = path.join(__dirname, 'assets/nyc.jpg')
3 |
4 | beforeEach(async () => {
5 | jest.setTimeout(20 * 1000)
6 | await page.goto(PATH, { waitUntil: 'load' })
7 | })
8 |
9 | describe('Image Compressor', () => {
10 | it('should compress an image before upload, its size should become smaller', async () => {
11 | const [fileChooser] = await Promise.all([
12 | page.waitForFileChooser(),
13 | page.click('.uppy-FileInput-btn') // some button that triggers file selection
14 | ])
15 | await fileChooser.accept([nycJpgPath])
16 |
17 | const sizes = await page.evaluate(() => {
18 | return new Promise((resolve) => {
19 | const sizeBefore = window.uppy.getFiles()[0].data.size
20 | return window.uppy.upload().then(() => {
21 | const sizeAfter = window.uppy.getFiles()[0].data.size
22 | return resolve({
23 | before: sizeBefore,
24 | after: sizeAfter
25 | })
26 | })
27 | })
28 | })
29 |
30 | console.log(sizes)
31 |
32 | expect(sizes.after).toBeLessThan(sizes.before)
33 | expect(sizes.before).toBe(33981)
34 | expect(sizes.after).toBe(12174)
35 | })
36 | })
37 |
38 | // if we want to load an image as blob from a url
39 | // did this at first, went with `fileChooser` instead
40 |
41 | // window.loadImageAsBlob = (url, done) => {
42 | // const xhr = new XMLHttpRequest()
43 |
44 | // xhr.onload = () => {
45 | // const blob = xhr.response
46 |
47 | // blob.name = url.replace(/^.*?(\w+\.\w+)$/, '$1')
48 | // done(blob)
49 | // };
50 | // xhr.open('GET', url)
51 | // xhr.responseType = 'blob'
52 | // xhr.send()
53 | // }
54 |
--------------------------------------------------------------------------------
/test/server.js:
--------------------------------------------------------------------------------
1 | const express = require('express')
2 | const webpack = require('webpack')
3 | const middleware = require('webpack-dev-middleware')
4 | const path = require('path')
5 |
6 | const compiler = webpack(require('../webpack.config.js'))
7 |
8 | // Turns input into an array if not one already
9 | function normalizeArray (arr) {
10 | return Array.isArray(arr) ? arr : [arr]
11 | }
12 |
13 | // Gets all the Javascript paths that Webpack has compiled, across chunks
14 | function getAllJsPaths (webpackJson) {
15 | const { assetsByChunkName } = webpackJson
16 | return Object.values(assetsByChunkName).reduce((paths, assets) => {
17 | for (let asset of normalizeArray(assets)) {
18 | if (asset != null && asset.endsWith('.js')) {
19 | paths.push(asset)
20 | }
21 | }
22 | return paths
23 | }, [])
24 | }
25 |
26 | // Optionally, just get the Javascript paths from specific chunks
27 | function getJsPathsFromChunks (webpackJson, chunkNames) {
28 | const { assetsByChunkName } = webpackJson
29 | chunkNames = normalizeArray(chunkNames)
30 | return chunkNames.reduce((paths, name) => {
31 | if (assetsByChunkName[name] != null) {
32 | for (let asset of normalizeArray(assetsByChunkName[name])) {
33 | if (asset != null && asset.endsWith('.js')) {
34 | paths.push(asset)
35 | }
36 | }
37 | }
38 | return paths
39 | }, [])
40 | }
41 |
42 | let port = 4444
43 | const index = Math.max(process.argv.indexOf('--port'), process.argv.indexOf('-p'))
44 | if (index !== -1) {
45 | port = +process.argv[index + 1] || port
46 | }
47 |
48 | const app = express()
49 | app.use(middleware(compiler, { serverSideRender: true }))
50 | app.use(express.static(path.join(__dirname, 'assets')))
51 | app.use('/', (req, res) => {
52 | const webpackJson = res.locals.webpackStats.toJson()
53 | const paths = getAllJsPaths(webpackJson)
54 | res.send(
55 | `
56 |
57 |