├── .editorconfig ├── .gitattributes ├── .gitignore ├── .travis.yml ├── fixtures ├── file.zip ├── multiple.zip └── symlink.zip ├── index.js ├── license ├── package.json ├── readme.md └── test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.{json,yml}] 11 | indent_size = 2 12 | indent_style = space 13 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.js text eol=lf 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - '6' 5 | - '4' 6 | -------------------------------------------------------------------------------- /fixtures/file.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevva/decompress-unzip/0c33871268e2eee3cb5370bec8ae8a82d887f5d7/fixtures/file.zip -------------------------------------------------------------------------------- /fixtures/multiple.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevva/decompress-unzip/0c33871268e2eee3cb5370bec8ae8a82d887f5d7/fixtures/multiple.zip -------------------------------------------------------------------------------- /fixtures/symlink.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevva/decompress-unzip/0c33871268e2eee3cb5370bec8ae8a82d887f5d7/fixtures/symlink.zip -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const fileType = require('file-type'); 3 | const getStream = require('get-stream'); 4 | const pify = require('pify'); 5 | const yauzl = require('yauzl'); 6 | 7 | const getType = (entry, mode) => { 8 | const IFMT = 61440; 9 | const IFDIR = 16384; 10 | const IFLNK = 40960; 11 | const madeBy = entry.versionMadeBy >> 8; 12 | 13 | if ((mode & IFMT) === IFLNK) { 14 | return 'symlink'; 15 | } 16 | 17 | if ((mode & IFMT) === IFDIR || (madeBy === 0 && entry.externalFileAttributes === 16)) { 18 | return 'directory'; 19 | } 20 | 21 | return 'file'; 22 | }; 23 | 24 | const extractEntry = (entry, zip) => { 25 | const file = { 26 | mode: (entry.externalFileAttributes >> 16) & 0xFFFF, 27 | mtime: entry.getLastModDate(), 28 | path: entry.fileName 29 | }; 30 | 31 | file.type = getType(entry, file.mode); 32 | 33 | if (file.mode === 0 && file.type === 'directory') { 34 | file.mode = 493; 35 | } 36 | 37 | if (file.mode === 0) { 38 | file.mode = 420; 39 | } 40 | 41 | return pify(zip.openReadStream.bind(zip))(entry) 42 | .then(getStream.buffer) 43 | .then(buf => { 44 | file.data = buf; 45 | 46 | if (file.type === 'symlink') { 47 | file.linkname = buf.toString(); 48 | } 49 | 50 | return file; 51 | }) 52 | .catch(err => { 53 | zip.close(); 54 | throw err; 55 | }); 56 | }; 57 | 58 | const extractFile = zip => new Promise((resolve, reject) => { 59 | const files = []; 60 | 61 | zip.readEntry(); 62 | 63 | zip.on('entry', entry => { 64 | extractEntry(entry, zip) 65 | .catch(reject) 66 | .then(file => { 67 | files.push(file); 68 | zip.readEntry(); 69 | }); 70 | }); 71 | 72 | zip.on('error', reject); 73 | zip.on('end', () => resolve(files)); 74 | }); 75 | 76 | module.exports = () => buf => { 77 | if (!Buffer.isBuffer(buf)) { 78 | return Promise.reject(new TypeError(`Expected a Buffer, got ${typeof buf}`)); 79 | } 80 | 81 | if (!fileType(buf) || fileType(buf).ext !== 'zip') { 82 | return Promise.resolve([]); 83 | } 84 | 85 | return pify(yauzl.fromBuffer)(buf, {lazyEntries: true}).then(extractFile); 86 | }; 87 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Kevin Mårtensson 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "decompress-unzip", 3 | "version": "4.0.1", 4 | "description": "decompress zip plugin", 5 | "license": "MIT", 6 | "repository": "kevva/decompress-unzip", 7 | "author": { 8 | "name": "Kevin Mårtensson", 9 | "email": "kevinmartensson@gmail.com", 10 | "url": "https://github.com/kevva" 11 | }, 12 | "engines": { 13 | "node": ">=4" 14 | }, 15 | "scripts": { 16 | "test": "xo && ava" 17 | }, 18 | "files": [ 19 | "index.js" 20 | ], 21 | "keywords": [ 22 | "decompress", 23 | "decompressplugin", 24 | "extract", 25 | "zip" 26 | ], 27 | "dependencies": { 28 | "file-type": "^3.8.0", 29 | "get-stream": "^3.0.0", 30 | "pify": "^2.3.0", 31 | "yauzl": "^2.4.2" 32 | }, 33 | "devDependencies": { 34 | "ava": "*", 35 | "is-jpg": "^1.0.0", 36 | "xo": "*" 37 | }, 38 | "xo": { 39 | "esnext": true 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # decompress-unzip [![Build Status](https://travis-ci.org/kevva/decompress-unzip.svg?branch=master)](https://travis-ci.org/kevva/decompress-unzip) 2 | 3 | > zip decompress plugin 4 | 5 | 6 | ## Install 7 | 8 | ``` 9 | $ npm install --save decompress-unzip 10 | ``` 11 | 12 | 13 | ## Usage 14 | 15 | ```js 16 | const decompress = require('decompress'); 17 | const decompressUnzip = require('decompress-unzip'); 18 | 19 | decompress('unicorn.zip', 'dist', { 20 | plugins: [ 21 | decompressUnzip() 22 | ] 23 | }).then(() => { 24 | console.log('Files decompressed'); 25 | }); 26 | ``` 27 | 28 | 29 | ## API 30 | 31 | ### decompressUnzip()(buf) 32 | 33 | #### buf 34 | 35 | Type: `Buffer` 36 | 37 | Buffer to decompress. 38 | 39 | 40 | ## License 41 | 42 | MIT © [Kevin Mårtensson](https://github.com/kevva) 43 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | import isJpg from 'is-jpg'; 4 | import pify from 'pify'; 5 | import test from 'ava'; 6 | import m from './'; 7 | 8 | const fsP = pify(fs); 9 | 10 | test('extract file', async t => { 11 | const buf = await fsP.readFile(path.join(__dirname, 'fixtures', 'file.zip')); 12 | const files = await m()(buf); 13 | 14 | t.is(files[0].path, 'test.jpg'); 15 | t.true(isJpg(files[0].data)); 16 | }); 17 | 18 | test('extract multiple files', async t => { 19 | const buf = await fsP.readFile(path.join(__dirname, 'fixtures', 'multiple.zip')); 20 | const files = await m()(buf); 21 | 22 | t.is(files.length, 4); 23 | t.is(files[0].path, '0.txt'); 24 | t.is(files[0].type, 'file'); 25 | t.is(files[0].data.toString(), '1'); 26 | t.is(files[3].path, '3/4/'); 27 | t.is(files[3].type, 'directory'); 28 | }); 29 | 30 | test('extract symlinks', async t => { 31 | const buf = await fsP.readFile(path.join(__dirname, 'fixtures', 'symlink.zip')); 32 | const files = await m()(buf); 33 | 34 | t.is(files[0].path, 'ReactiveCocoa'); 35 | t.is(files[0].type, 'symlink'); 36 | t.is(files[0].linkname, 'Versions/Current/ReactiveCocoa'); 37 | }); 38 | 39 | test('return empty array if non-valid file is supplied', async t => { 40 | const buf = await fsP.readFile(__filename); 41 | const files = await m()(buf); 42 | 43 | t.is(files.length, 0); 44 | }); 45 | 46 | test('throw on wrong input', async t => { 47 | await t.throws(m()('foo'), 'Expected a Buffer, got string'); 48 | }); 49 | --------------------------------------------------------------------------------