├── .npmignore ├── index.js ├── .eslintrc ├── .eslintignore ├── .gitignore ├── .travis.yml ├── .editorconfig ├── bin └── gzipme ├── LICENSE ├── package.json ├── lib └── gzipme.js ├── CHANGELOG.md ├── README.md └── test └── gzipme.test.js /.npmignore: -------------------------------------------------------------------------------- 1 | test 2 | coverage 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/gzipme'); 2 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb-base", 3 | "rules" : { 4 | "no-console": 0, 5 | "no-param-reassign": 0 6 | } 7 | } -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | .gitignore 2 | .DS_Store 3 | npm-debug.log 4 | node_modules 5 | .grunt 6 | .git 7 | www 8 | yarn.lock 9 | build 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /coverage 3 | /build 4 | /test/compress 5 | .DS_Store 6 | npm-debug.log 7 | yarn-debug.log 8 | yarn-error.log 9 | .DS_Store -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "11.0" 4 | - "12.0" 5 | - "13.0" 6 | - "14.0" 7 | cache: 8 | directories: 9 | - node_modules 10 | script: 11 | - npm run coverage 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | max_line_length = 100 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | 15 | -------------------------------------------------------------------------------- /bin/gzipme: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | const fs = require('fs'); 3 | const program = require('commander'); 4 | const gzipme = require('../index'); 5 | const pkg = require('../package.json'); 6 | 7 | program 8 | .version(pkg.version) 9 | .usage('[options] ') 10 | .option('-O, --output [name]', 'Specify output filename (default .gz).') 11 | .option('-o, --overwrite', 'Overwrite file (default false).') 12 | .option('-c, --compress [compressMode]', 'The compress mode is "best" or "fast". (default best).') 13 | .parse(process.argv); 14 | 15 | const { overwrite, output, compress } = program; 16 | const file = program.args.toString(); 17 | const params = { overwrite: !!overwrite, output, compress }; 18 | 19 | gzipme(file, params) 20 | .then(() => { 21 | const gzFile = overwrite ? file : `${file}.gz`; 22 | console.info(`File: ${file}`); 23 | console.info(`Size: ${fs.statSync(file).size}`); 24 | console.info(`Gzip: ${gzFile}`); 25 | console.info(`Size: ${fs.statSync(gzFile).size}`); 26 | console.info('Gzipme has finished the compression!'); 27 | }) 28 | .catch(console.error); 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Caio Ribeiro Pereira 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. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gzipme", 3 | "description": "Gzipme is a module and CLI tool to compress files to gzip", 4 | "version": "1.0.0", 5 | "author": "Caio Ribeiro Pereira ", 6 | "repository": "git://github.com/caio-ribeiro-pereira/gzipme", 7 | "keywords": [ 8 | "gzip", 9 | "zlib", 10 | "compression", 11 | "compress", 12 | "gzip-files" 13 | ], 14 | "main": "index.js", 15 | "bin": { 16 | "gzipme": "./bin/gzipme" 17 | }, 18 | "scripts": { 19 | "test": "jest test/**", 20 | "coverage": "jest test/** --coverage && cat ./coverage/lcov.info | coveralls" 21 | }, 22 | "dependencies": { 23 | "commander": "6.1.0" 24 | }, 25 | "devDependencies": { 26 | "coveralls": "3.1.0", 27 | "eslint": "7.7.0", 28 | "eslint-config-airbnb-base": "14.2.0", 29 | "eslint-plugin-import": "2.22.0", 30 | "jest": "26.4.2" 31 | }, 32 | "engines": { 33 | "node": ">= 11.0.0" 34 | }, 35 | "jest": { 36 | "verbose": true, 37 | "collectCoverage": true, 38 | "coverageDirectory": "coverage", 39 | "collectCoverageFrom": [ 40 | "**/*.{js,jsx}", 41 | "!**/node_modules/**", 42 | "!**/vendor/**", 43 | "!**/coverage/**" 44 | ] 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/gzipme.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const zlib = require('zlib'); 4 | 5 | const OPTS = { 6 | best: { 7 | level: zlib.Z_BEST_COMPRESSION, 8 | memLevel: zlib.Z_BEST_COMPRESSION, 9 | }, 10 | fast: { 11 | level: zlib.Z_BEST_SPEED, 12 | memLevel: zlib.Z_BEST_SPEED, 13 | }, 14 | }; 15 | 16 | const ALLOWED_OPTS = Object.keys(OPTS); 17 | 18 | const isValidObject = (object) => object && (typeof object === 'object') && !Array.isArray(object); 19 | 20 | module.exports = (file = '', params = {}) => ( 21 | new Promise((resolve, reject) => { 22 | if (!file || typeof file !== 'string') { 23 | return reject(new Error('File must be a string.')); 24 | } 25 | 26 | if (!isValidObject(params)) { 27 | return reject(new Error('Params must be an object.')); 28 | } 29 | 30 | if (!fs.existsSync(file)) { 31 | return reject(new Error(`File ${file} doesn't exist.`)); 32 | } 33 | 34 | const overwrite = params.overwrite || false; 35 | const mode = params.mode || 'best'; 36 | const output = params.output || ''; 37 | if (!ALLOWED_OPTS.includes(mode)) { 38 | return reject(new Error('Mode not allowed.')); 39 | } 40 | 41 | const gzFile = output || `${file}.gz`; 42 | const filePath = path.resolve(file); 43 | const gzFilePath = path.resolve(gzFile); 44 | 45 | try { 46 | const gzip = zlib.createGzip(OPTS[mode]); 47 | const inputStream = fs.createReadStream(filePath); 48 | const outStream = fs.createWriteStream(gzFilePath); 49 | 50 | inputStream.pipe(gzip).pipe(outStream); 51 | if (outStream) { 52 | outStream.on('finish', () => { 53 | if (overwrite) { 54 | fs.unlinkSync(filePath); 55 | fs.renameSync(gzFilePath, filePath); 56 | } 57 | return resolve(); 58 | }); 59 | return true; 60 | } 61 | return resolve(); 62 | } catch (e) { 63 | if (!overwrite && fs.existsSync(gzFilePath)) { 64 | fs.unlinkSync(gzFilePath); 65 | } 66 | return reject(e); 67 | } 68 | }) 69 | ); 70 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.0.0 2 | + Improvements: Update lib to support nodejs v11+ 3 | + Improvements: Update doc, licenses, changelog 4 | + Improvements: Update dependencies and tests 5 | + Improvements: Added Promise support 6 | + Improvements: Added params validation to avoid invalid data bugs 7 | + Feature: Added coveralls for test coverage 8 | + Feature: Added codacy for code quality 9 | + Feature: Added eslint for code quality 10 | 11 | ## 0.1.2 12 | + Bugfix: Fix error when stream is finished but zip is not finished thanks by [lushibi](https://github.com/caio-ribeiro-pereira/gzipme/pull/7) 13 | 14 | ## 0.1.1 15 | + Improvements: update mocha, commander and should modules 16 | + Bugfix: Fix error when there is no callback function thanks by [nbonamy](https://github.com/caio-ribeiro-pereira/gzipme/pull/4) 17 | 18 | ## 0.1.0 19 | + Improvements: Added ES6 support 20 | + Improvements: Remove jscoverage and coverage test 21 | + Improvements: Update mocha, should and commander modules 22 | + Improvements: Removed old node versions for travis-ci 23 | + Improvements: Added node 6.0 support for travis-ci 24 | + Improvements: Move makefile tests to npm test alias command 25 | + Improvements: Fix outputStream finish callback thanks by [frjanibo](https://github.com/caio-ribeiro-pereira/gzipme/issues/3#issue-180284790) 26 | 27 | ## 0.0.9 28 | + Improvements: Drop node 0.5, 0.6, 0.7, 0.9, 11.0 support 29 | + Improvements: Update commander.js to 2.0.0 30 | 31 | ## 0.0.8 32 | + Improvements: Update commander.js 33 | + Improvements: Normalize race conditions tests 34 | + Improvements: Sintax sugar JS 35 | + Improvements: CLI prints file and gzip file sizes 36 | 37 | ## 0.0.7 38 | + Improvements: Avoid overwrite a file when throw an error 39 | + Improvements: Log a detailed error when throw it 40 | + Improvements: Update commander.js 41 | 42 | ## 0.0.6 43 | + Bugfix: Using path.resolve to avoid path problems 44 | 45 | ## 0.0.5 46 | + Feature: Compress mode (best and fast compression) 47 | + Feature: Compress mode for CLI 48 | + Improvements: Log filename on invalid file error 49 | 50 | ## 0.0.4 51 | + Bugfix: Running on Node 0.6.x 52 | 53 | ## 0.0.3 54 | + Feature: Generate custom gzip file by [jylauril](https://github.com/jylauril) 55 | 56 | ## 0.0.2 57 | + Bugfix: Grunt compatibility by [jylauril](https://github.com/jylauril) 58 | 59 | ## 0.0.1 60 | + Feature: Compress gzip file 61 | + Feature: Compress gzip file overwrite mode 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Gzipme 2 | 3 | [![Donate via Paypal](https://img.shields.io/badge/donate-paypal-blue)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=L8MUNAKECUULY&source=url) [![Build Status](https://travis-ci.org/caio-ribeiro-pereira/gzipme.svg?branch=master)](https://travis-ci.org/caio-ribeiro-pereira/gzipme) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/0e2b271a992343e8804ad868a9b17354)](https://www.codacy.com/manual/caio-ribeiro-pereira/gzipme?utm_source=github.com&utm_medium=referral&utm_content=caio-ribeiro-pereira/gzipme&utm_campaign=Badge_Grade) [![Coverage Status](https://coveralls.io/repos/github/caio-ribeiro-pereira/gzipme/badge.svg?branch=master)](https://coveralls.io/github/caio-ribeiro-pereira/gzipme?branch=master) ![npm](https://img.shields.io/npm/dt/gzipme) ![GitHub](https://img.shields.io/github/license/caio-ribeiro-pereira/gzipme) ![npm](https://img.shields.io/npm/v/gzipme) ![GitHub stars](https://img.shields.io/github/stars/caio-ribeiro-pereira/gzipme) ![GitHub forks](https://img.shields.io/github/forks/caio-ribeiro-pereira/gzipme) 4 | 5 | 6 | ## About 7 | 8 | A simple and tiny lib/cli for gzip file compression. It's very simple to use it, take a look: 9 | 10 | ### Instalation 11 | 12 | ``` bash 13 | npm install gzipme 14 | ``` 15 | 16 | ## Module version 17 | ### How to use 18 | 19 | ``` javascript 20 | // Load gzipme module 21 | const gzipme = require('gzipme'); 22 | 23 | // Compress 'file.txt' to 'file.txt.gz' in the same dir. 24 | gzipme('file.txt'); 25 | 26 | // Compress 'file.txt' into the same file. 27 | gzipme('file.txt', { overwrite: true }); 28 | 29 | // Compress 'file.txt' to generate a file named as 'compressed.txt' in the same dir. 30 | gzipme('file.txt', { output: 'compressed.txt' }); 31 | 32 | // Compress 'file.txt' using best compress mode (few bytes, but slow compression). 33 | gzipme('file.txt', { mode: 'best' }); 34 | 35 | // Compress 'file.txt' using fast compress mode (fast compression, but more bytes). 36 | gzipme('file.txt', { mode: 'fast' }); 37 | ``` 38 | 39 | [Click here](https://nodejs.org/api/zlib.html#zlib_zlib_constants) to understand the Node.js Zlib compreension mode. 40 | 41 | 42 | ## CLI version 43 | ### Instalation 44 | 45 | ``` bash 46 | npm install -g gzipme 47 | ``` 48 | 49 | ### All commands 50 | 51 | ``` bash 52 | # It's the same as function 'gzipme('file.txt')'. 53 | gzipme file.txt 54 | # It's the same as function 'gzipme('file.txt', { overwrite: true });'. 55 | gzipme -o file.txt 56 | # It's the same as function 'gzipme('file.txt', { output: 'compressed.txt' });'. 57 | gzipme -O compressed.txt file.txt 58 | # It's the same as function 'gzipme('file.txt', { mode: 'fast' });'. 59 | gzipme -c best file.txt 60 | # It's the same as function 'gzipme('file.txt', { mode: 'fast' });'. 61 | gzipme -c fast file.txt 62 | ``` 63 | 64 | ## Running tests 65 | 66 | Just clone this repository, and follow the commands below: 67 | ``` bash 68 | git clone git@github.com:caio-ribeiro-pereira/gzipme.git 69 | cd gzipme 70 | npm install 71 | npm test 72 | ``` 73 | 74 | ## Author 75 | 76 | Caio Ribeiro Pereira 77 | Twitter: 78 | About me: 79 | -------------------------------------------------------------------------------- /test/gzipme.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, it, expect, beforeEach, afterEach */ 2 | const fs = require('fs'); 3 | const { exec } = require('child_process'); 4 | const gzipme = require('../index.js'); 5 | 6 | const testPath = `${__dirname}/compress`; 7 | const testFile = `${testPath}/test.json`; 8 | const testOutputFile = `${testPath}/output.json`; 9 | const testGzipFile = `${testFile}.gz`; 10 | const testFileContent = JSON.stringify({ test: 'this is a test.json' }); 11 | 12 | describe('gzipme', () => { 13 | beforeEach((done) => { 14 | exec(`mkdir -p ${testPath}`, () => { 15 | fs.writeFileSync(testFile, testFileContent); 16 | done(); 17 | }); 18 | }); 19 | 20 | afterEach((done) => exec(`rm -rf ${testPath}`, done)); 21 | 22 | it('compress test.json to test.json.gz using default params', async () => { 23 | try { 24 | await gzipme(testFile); 25 | const existGzip = fs.existsSync(testGzipFile); 26 | expect(existGzip).toBe(true); 27 | } catch (err) { 28 | expect(err).toBeUndefined(); 29 | } 30 | }); 31 | 32 | it('compress test.json using best compress mode', async () => { 33 | try { 34 | await gzipme(testFile, { compress: 'best' }); 35 | const existGzip = fs.existsSync(testGzipFile); 36 | expect(existGzip).toBe(true); 37 | } catch (err) { 38 | expect(err).toBeUndefined(); 39 | } 40 | }); 41 | 42 | it('compress test.json using fast compress mode', async () => { 43 | try { 44 | await gzipme(testFile, { compress: 'fast' }); 45 | const existGzip = fs.existsSync(testGzipFile); 46 | expect(existGzip).toBe(true); 47 | } catch (err) { 48 | expect(err).toBeUndefined(); 49 | } 50 | }); 51 | 52 | it('compress test.json overwrite to test.json', async () => { 53 | try { 54 | await gzipme(testFile, { overwrite: true }); 55 | const existGzip = fs.existsSync(testFile); 56 | expect(existGzip).toBe(true); 57 | } catch (err) { 58 | expect(err).toBeUndefined(); 59 | } 60 | }); 61 | 62 | it('compress test.json to new name file when set output params', async () => { 63 | try { 64 | await gzipme(testFile, { output: testOutputFile }); 65 | const existDefault = fs.existsSync(testGzipFile); 66 | const existGzip = fs.existsSync(testOutputFile); 67 | expect(existDefault).toBe(false); 68 | expect(existGzip).toBe(true); 69 | } catch (err) { 70 | expect(err).toBeUndefined(); 71 | } 72 | }); 73 | 74 | it('fails when use invalid compress mode', async () => { 75 | try { 76 | await gzipme(testFile, { compress: 'best' }); 77 | } catch (err) { 78 | expect(err).toMatch('Mode not allowed.'); 79 | } 80 | }); 81 | 82 | it('fails when file not exists', async () => { 83 | const invalidFile = 'unknown.json'; 84 | try { 85 | await gzipme(invalidFile); 86 | } catch (err) { 87 | expect(err).toStrictEqual(new Error(`File ${invalidFile} doesn't exist.`)); 88 | } 89 | }); 90 | 91 | it('fails when file is not a string', async () => { 92 | const invalidFile = {}; 93 | try { 94 | await gzipme(invalidFile); 95 | } catch (err) { 96 | expect(err).toStrictEqual(new Error('File must be a string.')); 97 | } 98 | }); 99 | 100 | it('fails when params is an array', async () => { 101 | try { 102 | await gzipme(testFile, []); 103 | } catch (err) { 104 | expect(err).toStrictEqual(new Error('Params must be an object.')); 105 | } 106 | }); 107 | 108 | it('fails when params is a string', async () => { 109 | try { 110 | await gzipme(testFile, 'invalid'); 111 | } catch (err) { 112 | expect(err).toStrictEqual(new Error('Params must be an object.')); 113 | } 114 | }); 115 | 116 | it('fails when params is a date', async () => { 117 | try { 118 | await gzipme(testFile, new Date()); 119 | } catch (err) { 120 | expect(err).toStrictEqual(new Error('Params must be an object.')); 121 | } 122 | }); 123 | 124 | it('fails when params is a number', async () => { 125 | try { 126 | await gzipme(testFile, 1111); 127 | } catch (err) { 128 | expect(err).toStrictEqual(new Error('Params must be an object.')); 129 | } 130 | }); 131 | 132 | it('fails when params is a boolean', async () => { 133 | try { 134 | await gzipme(testFile, true); 135 | } catch (err) { 136 | expect(err).toStrictEqual(new Error('Params must be an object.')); 137 | } 138 | }); 139 | 140 | it('fails when file is empty', async () => { 141 | try { 142 | await gzipme(); 143 | } catch (err) { 144 | expect(err).toStrictEqual(new Error('File must be a string.')); 145 | } 146 | }); 147 | }); 148 | --------------------------------------------------------------------------------