Skip to content

Commit

Permalink
feat: require Node 10.12 (#89)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: drop support for Node < 10.12

* build: replace Travis CI with GitHub Actions
* refactor: remove use of temp module
* test: don't check for the exact tmp dir
* test: do not run some symlink tests on Windows
* feat: drop use of mkdirp
* docs: update badges
* build(deps): upgrade debug to ^4.1.1
* build(deps-dev): upgrade rimraf to ^3.0.2
* build(deps-dev): upgrade standard to ^14.3.3
* refactor: remove use of deprecated fs.exists
* build(deps): upgrade concat-stream to ^2.0.0
* test: add nyc for code coverage
* test: add test for onEntry option
* refactor: use const/let instead of var
* refactor: use octal literals
* refactor: use String.prototype.startsWith
* test: test the symlink's link
* docs: note Node version requirement in the readme
* docs: fix dir description
  • Loading branch information
malept authored Mar 25, 2020
1 parent 79e0910 commit 7993cb8
Show file tree
Hide file tree
Showing 8 changed files with 188 additions and 139 deletions.
41 changes: 41 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: CI

on:
push:
branches:
- master
tags:
- v[0-9]+.[0-9]+.[0-9]+*
pull_request:

jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [windows-latest, macOS-latest, ubuntu-latest]
node-version: [10.x, 12.x]

steps:
- name: Fix git checkout line endings
run: git config --global core.autocrlf input
- uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: Get yarn cache
id: yarn-cache
run: echo "::set-output name=dir::$(yarn cache dir)"
- uses: actions/cache@v1
with:
path: ${{ steps.yarn-cache.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/package.json') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install
run: yarn
- name: Lint
run: yarn lint
- name: Test
run: yarn coverage
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.DS_Store
.nyc_output
node_modules
package-lock.json
yarn.lock
9 changes: 0 additions & 9 deletions .travis.yml

This file was deleted.

2 changes: 1 addition & 1 deletion cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ if (!source) {
process.exit(1)
}

extract(source, {dir: dest}, function (err, results) {
extract(source, { dir: dest }, function (err, results) {
if (err) {
console.error('error!', err)
process.exit(1)
Expand Down
78 changes: 41 additions & 37 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
var fs = require('fs')
var path = require('path')
var yauzl = require('yauzl')
var mkdirp = require('mkdirp')
var concat = require('concat-stream')
var debug = require('debug')('extract-zip')
const fs = require('fs')
const path = require('path')
const yauzl = require('yauzl')
const concat = require('concat-stream')
const debug = require('debug')('extract-zip')

module.exports = function (zipPath, opts, cb) {
debug('creating target directory', opts.dir)
Expand All @@ -12,7 +11,7 @@ module.exports = function (zipPath, opts, cb) {
return cb(new Error('Target directory is expected to be absolute'))
}

mkdirp(opts.dir, function (err) {
fs.mkdir(opts.dir, { recursive: true }, function (err) {
if (err) return cb(err)

fs.realpath(opts.dir, function (err, canonicalDir) {
Expand All @@ -27,10 +26,10 @@ module.exports = function (zipPath, opts, cb) {
function openZip () {
debug('opening', zipPath, 'with opts', opts)

yauzl.open(zipPath, {lazyEntries: true}, function (err, zipfile) {
yauzl.open(zipPath, { lazyEntries: true }, function (err, zipfile) {
if (err) return cb(err)

var cancelled = false
let cancelled = false

zipfile.on('error', function (err) {
if (err) {
Expand All @@ -48,36 +47,38 @@ module.exports = function (zipPath, opts, cb) {
})

zipfile.on('entry', function (entry) {
/* istanbul ignore if */
if (cancelled) {
debug('skipping entry', entry.fileName, {cancelled: cancelled})
debug('skipping entry', entry.fileName, { cancelled: cancelled })
return
}

debug('zipfile entry', entry.fileName)

if (/^__MACOSX\//.test(entry.fileName)) {
// dir name starts with __MACOSX/
if (entry.fileName.startsWith('__MACOSX/')) {
zipfile.readEntry()
return
}

var destDir = path.dirname(path.join(opts.dir, entry.fileName))
const destDir = path.dirname(path.join(opts.dir, entry.fileName))

mkdirp(destDir, function (err) {
fs.mkdir(destDir, { recursive: true }, function (err) {
/* istanbul ignore if */
if (err) {
cancelled = true
zipfile.close()
return cb(err)
}

fs.realpath(destDir, function (err, canonicalDestDir) {
/* istanbul ignore if */
if (err) {
cancelled = true
zipfile.close()
return cb(err)
}

var relativeDestDir = path.relative(opts.dir, canonicalDestDir)
const relativeDestDir = path.relative(opts.dir, canonicalDestDir)

if (relativeDestDir.split(path.sep).indexOf('..') !== -1) {
cancelled = true
Expand All @@ -100,25 +101,26 @@ module.exports = function (zipPath, opts, cb) {
})

function extractEntry (entry, done) {
/* istanbul ignore if */
if (cancelled) {
debug('skipping entry extraction', entry.fileName, {cancelled: cancelled})
debug('skipping entry extraction', entry.fileName, { cancelled: cancelled })
return setImmediate(done)
}

if (opts.onEntry) {
opts.onEntry(entry, zipfile)
}

var dest = path.join(opts.dir, entry.fileName)
const dest = path.join(opts.dir, entry.fileName)

// convert external file attr int into a fs stat mode int
var mode = (entry.externalFileAttributes >> 16) & 0xFFFF
let mode = (entry.externalFileAttributes >> 16) & 0xFFFF
// check if it's a symlink or dir (using stat mode constants)
var IFMT = 61440
var IFDIR = 16384
var IFLNK = 40960
var symlink = (mode & IFMT) === IFLNK
var isDir = (mode & IFMT) === IFDIR
const IFMT = 61440
const IFDIR = 16384
const IFLNK = 40960
const symlink = (mode & IFMT) === IFLNK
let isDir = (mode & IFMT) === IFDIR

// Failsafe, borrowed from jsZip
if (!isDir && entry.fileName.slice(-1) === '/') {
Expand All @@ -127,35 +129,35 @@ module.exports = function (zipPath, opts, cb) {

// check for windows weird way of specifying a directory
// https://github.com/maxogden/extract-zip/issues/13#issuecomment-154494566
var madeBy = entry.versionMadeBy >> 8
const madeBy = entry.versionMadeBy >> 8
if (!isDir) isDir = (madeBy === 0 && entry.externalFileAttributes === 16)

// if no mode then default to default modes
if (mode === 0) {
if (isDir) {
if (opts.defaultDirMode) mode = parseInt(opts.defaultDirMode, 10)
if (!mode) mode = 493 // Default to 0755
if (!mode) mode = 0o755
} else {
if (opts.defaultFileMode) mode = parseInt(opts.defaultFileMode, 10)
if (!mode) mode = 420 // Default to 0644
if (!mode) mode = 0o644
}
}

debug('extracting entry', { filename: entry.fileName, isDir: isDir, isSymlink: symlink })

// reverse umask first (~)
var umask = ~process.umask()
const umask = ~process.umask()
// & with processes umask to override invalid perms
var procMode = mode & umask
const procMode = mode & umask

// always ensure folders are created
var destDir = dest
if (!isDir) destDir = path.dirname(dest)
const destDir = isDir ? dest : path.dirname(dest)

debug('mkdirp', {dir: destDir})
mkdirp(destDir, function (err) {
debug('mkdirp', { dir: destDir })
fs.mkdir(destDir, { recursive: true }, function (err) {
/* istanbul ignore if */
if (err) {
debug('mkdirp error', destDir, {error: err})
debug('mkdirp error', destDir, { error: err })
cancelled = true
return done(err)
}
Expand All @@ -164,29 +166,31 @@ module.exports = function (zipPath, opts, cb) {

debug('opening read stream', dest)
zipfile.openReadStream(entry, function (err, readStream) {
/* istanbul ignore if */
if (err) {
debug('openReadStream error', err)
cancelled = true
return done(err)
}

readStream.on('error', function (err) {
/* istanbul ignore next */
console.log('read err', err)
})

if (symlink) writeSymlink()
else writeStream()

function writeStream () {
var writeStream = fs.createWriteStream(dest, {mode: procMode})
const writeStream = fs.createWriteStream(dest, { mode: procMode })
readStream.pipe(writeStream)

writeStream.on('finish', function () {
done()
})

writeStream.on('error', function (err) {
debug('write error', {error: err})
writeStream.on('error', /* istanbul ignore next */ function (err) {
debug('write error', { error: err })
cancelled = true
return done(err)
})
Expand All @@ -195,7 +199,7 @@ module.exports = function (zipPath, opts, cb) {
// AFAICT the content of the symlink file itself is the symlink target filename string
function writeSymlink () {
readStream.pipe(concat(function (data) {
var link = data.toString()
const link = data.toString()
debug('creating symlink', link, dest)
fs.symlink(link, dest, function (err) {
if (err) cancelled = true
Expand Down
20 changes: 12 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
"extract-zip": "cli.js"
},
"scripts": {
"test": "standard && node test/test.js"
"coverage": "nyc node test/test.js",
"lint": "standard",
"test": "node test/test.js"
},
"files": [
"*.js"
Expand All @@ -20,17 +22,19 @@
"zip",
"extract"
],
"engines": {
"node": ">= 10.12.0"
},
"dependencies": {
"concat-stream": "^1.6.2",
"debug": "^2.6.9",
"mkdirp": "^0.5.4",
"concat-stream": "^2.0.0",
"debug": "^4.1.1",
"yauzl": "^2.10.0"
},
"devDependencies": {
"rimraf": "^2.2.8",
"standard": "^5.2.2",
"tape": "^4.2.0",
"temp": "^0.8.3"
"nyc": "^15.0.0",
"rimraf": "^3.0.2",
"standard": "^14.3.3",
"tape": "^4.2.0"
},
"directories": {
"test": "test"
Expand Down
12 changes: 7 additions & 5 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ Unzip written in pure JavaScript. Extracts a zip into a directory. Available as

Uses the [`yauzl`](http://npmjs.org/yauzl) ZIP parser.

[![NPM](https://nodei.co/npm/extract-zip.png?global=true)](https://nodei.co/npm/extract-zip/)
[![js-standard-style](https://cdn.rawgit.com/feross/standard/master/badge.svg)](https://github.com/feross/standard)
[![Build Status](https://travis-ci.org/maxogden/extract-zip.svg?branch=master)](https://travis-ci.org/maxogden/extract-zip)
[![NPM](https://nodei.co/npm/extract-zip.png?global=true)](https://npm.im/extract-zip)
[![Uses JS Standard Style](https://cdn.jsdelivr.net/gh/standard/standard/badge.svg)](https://github.com/standard/standard)
[![Build Status](https://github.com/maxogden/extract-zip/workflows/CI/badge.svg)](https://github.com/maxogden/extract-zip/actions?query=workflow%3ACI)

## Installation

Make sure you have Node 10 or greater installed.

Get the library:

```
Expand All @@ -26,14 +28,14 @@ npm install extract-zip -g

```js
var extract = require('extract-zip')
extract(sourcePath, {dir: targetPath}, function (err) {
extract(source, {dir: target}, function (err) {
// extraction is complete. make sure to handle the err
})
```

### Options

- `dir` - defaults to `process.cwd()`
- `dir` (required) - the path to the directory where the extracted files are written
- `defaultDirMode` - integer - Directory Mode (permissions) will default to `493` (octal `0755` in integer)
- `defaultFileMode` - integer - File Mode (permissions) will default to `420` (octal `0644` in integer)
- `onEntry` - function - if present, will be called with `(entry, zipfile)`, entry is every entry from the zip file forwarded from the `entry` event from yauzl. `zipfile` is the `yauzl` instance
Expand Down
Loading

0 comments on commit 7993cb8

Please sign in to comment.