Skip to content

Commit

Permalink
Update all the stuff
Browse files Browse the repository at this point in the history
* switch to ESM, require Node.js 12
* switch to GitHub Actions
* add coverage with c8
* update all packages to the latest version
  • Loading branch information
XhmikosR committed Jan 13, 2022
1 parent 84a8c10 commit 5691b35
Show file tree
Hide file tree
Showing 7 changed files with 171 additions and 142 deletions.
35 changes: 35 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: CI

on:
push:
pull_request:
workflow_dispatch:

env:
FORCE_COLOR: 2

jobs:
test:
name: Node ${{ matrix.node }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}

strategy:
fail-fast: false
matrix:
node: [12, 14, 16]
os: [ubuntu-latest, windows-latest]

steps:
- name: Clone repository
uses: actions/checkout@v2

- name: Set up Node.js
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node }}

- name: Install npm dependencies
run: npm install

- name: Run tests
run: npm run test-ci
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules
yarn.lock
/coverage
5 changes: 0 additions & 5 deletions .travis.yml

This file was deleted.

142 changes: 70 additions & 72 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,72 +1,68 @@
'use strict';
const path = require('path');
const fs = require('graceful-fs');
const decompressTar = require('decompress-tar');
const decompressTarbz2 = require('decompress-tarbz2');
const decompressTargz = require('decompress-targz');
const decompressUnzip = require('decompress-unzip');
const makeDir = require('make-dir');
const pify = require('pify');
const stripDirs = require('strip-dirs');
import {Buffer} from 'node:buffer';
import path from 'node:path';
import process from 'node:process';
import decompressTar from 'decompress-tar';
import decompressTarbz2 from 'decompress-tarbz2';
import decompressTargz from 'decompress-targz';
import decompressUnzip from 'decompress-unzip';
import fs from 'graceful-fs';
import makeDir from 'make-dir';
import pify from 'pify';
import stripDirs from 'strip-dirs';

const fsP = pify(fs);

const runPlugins = (input, opts) => {
if (opts.plugins.length === 0) {
const runPlugins = (input, options) => {
if (options.plugins.length === 0) {
return Promise.resolve([]);
}

return Promise.all(opts.plugins.map(x => x(input, opts))).then(files => files.reduce((a, b) => a.concat(b)));
// eslint-disable-next-line unicorn/no-array-reduce, unicorn/prefer-spread
return Promise.all(options.plugins.map(x => x(input, options))).then(files => files.reduce((a, b) => a.concat(b)));
};

const safeMakeDir = (dir, realOutputPath) => {
return fsP.realpath(dir)
.catch(_ => {
const parent = path.dirname(dir);
return safeMakeDir(parent, realOutputPath);
})
.then(realParentPath => {
if (realParentPath.indexOf(realOutputPath) !== 0) {
throw (new Error('Refusing to create a directory outside the output path.'));
}

return makeDir(dir).then(fsP.realpath);
});
};
const safeMakeDir = (dir, realOutputPath) => fsP.realpath(dir)
.catch(_ => {
const parent = path.dirname(dir);
return safeMakeDir(parent, realOutputPath);
})
.then(realParentPath => {
if (realParentPath.indexOf(realOutputPath) !== 0) {
throw new Error('Refusing to create a directory outside the output path.');
}

const preventWritingThroughSymlink = (destination, realOutputPath) => {
return fsP.readlink(destination)
.catch(_ => {
// Either no file exists, or it's not a symlink. In either case, this is
// not an escape we need to worry about in this phase.
return null;
})
.then(symlinkPointsTo => {
if (symlinkPointsTo) {
throw new Error('Refusing to write into a symlink');
}

// No symlink exists at `destination`, so we can continue
return realOutputPath;
});
};
return makeDir(dir).then(fsP.realpath);
});

const preventWritingThroughSymlink = (destination, realOutputPath) => fsP.readlink(destination)
// Either no file exists, or it's not a symlink. In either case, this is
// not an escape we need to worry about in this phase.
.catch(_ => null)
.then(symlinkPointsTo => {
if (symlinkPointsTo) {
throw new Error('Refusing to write into a symlink');
}

// No symlink exists at `destination`, so we can continue
return realOutputPath;
});

const extractFile = (input, output, opts) => runPlugins(input, opts).then(files => {
if (opts.strip > 0) {
const extractFile = (input, output, options) => runPlugins(input, options).then(files => {
if (options.strip > 0) {
files = files
.map(x => {
x.path = stripDirs(x.path, opts.strip);
x.path = stripDirs(x.path, options.strip);
return x;
})
.filter(x => x.path !== '.');
}

if (typeof opts.filter === 'function') {
files = files.filter(opts.filter);
if (typeof options.filter === 'function') {
files = files.filter(options.filter); // eslint-disable-line unicorn/no-array-callback-reference
}

if (typeof opts.map === 'function') {
files = files.map(opts.map);
if (typeof options.map === 'function') {
files = files.map(options.map); // eslint-disable-line unicorn/no-array-callback-reference
}

if (!output) {
Expand All @@ -75,7 +71,7 @@ const extractFile = (input, output, opts) => runPlugins(input, opts).then(files

return Promise.all(files.map(x => {
const dest = path.join(output, x.path);
const mode = x.mode & ~process.umask();
const mode = x.mode & ~process.umask(); // eslint-disable-line no-bitwise
const now = new Date();

if (x.type === 'directory') {
Expand All @@ -88,26 +84,23 @@ const extractFile = (input, output, opts) => runPlugins(input, opts).then(files

return makeDir(output)
.then(outputPath => fsP.realpath(outputPath))
.then(realOutputPath => {
.then(realOutputPath =>
// Attempt to ensure parent directory exists (failing if it's outside the output dir)
return safeMakeDir(path.dirname(dest), realOutputPath)
.then(() => realOutputPath);
})
safeMakeDir(path.dirname(dest), realOutputPath).then(() => realOutputPath),
)
.then(realOutputPath => {
if (x.type === 'file') {
return preventWritingThroughSymlink(dest, realOutputPath);
}

return realOutputPath;
})
.then(realOutputPath => {
return fsP.realpath(path.dirname(dest))
.then(realDestinationDir => {
if (realDestinationDir.indexOf(realOutputPath) !== 0) {
throw (new Error('Refusing to write outside output directory: ' + realDestinationDir));
}
});
})
.then(realOutputPath => fsP.realpath(path.dirname(dest))
.then(realDestinationDir => {
if (realDestinationDir.indexOf(realOutputPath) !== 0) {
throw new Error(`Refusing to write outside output directory: ${realDestinationDir}`);
}
}))
.then(() => {
if (x.type === 'link') {
return fsP.link(x.linkname, dest);
Expand All @@ -128,24 +121,29 @@ const extractFile = (input, output, opts) => runPlugins(input, opts).then(files
}));
});

module.exports = (input, output, opts) => {
const decompress = (input, output, options) => {
if (typeof input !== 'string' && !Buffer.isBuffer(input)) {
return Promise.reject(new TypeError('Input file required'));
}

if (typeof output === 'object') {
opts = output;
options = output;
output = null;
}

opts = Object.assign({plugins: [
decompressTar(),
decompressTarbz2(),
decompressTargz(),
decompressUnzip()
]}, opts);
options = {
plugins: [
decompressTar(),
decompressTarbz2(),
decompressTargz(),
decompressUnzip(),
],
...options,
};

const read = typeof input === 'string' ? fsP.readFile(input) : Promise.resolve(input);

return read.then(buf => extractFile(buf, output, opts));
return read.then(buf => extractFile(buf, output, options));
};

export default decompress;
47 changes: 22 additions & 25 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,18 @@
"url": "github.com/kevva"
},
"engines": {
"node": ">=4"
"node": "^12.20.0 || ^14.14.0 || >=16.0.0"
},
"scripts": {
"test": "xo && ava"
"ava": "ava",
"xo": "xo",
"test": "npm run xo && npm run ava",
"test-ci": "npm run xo && c8 ava"
},
"main": "index.js",
"type": "module",
"exports": {
".": "./index.js"
},
"files": [
"index.js"
Expand All @@ -30,32 +38,21 @@
"unzip"
],
"dependencies": {
"decompress-tar": "^4.0.0",
"decompress-tarbz2": "^4.0.0",
"decompress-targz": "^4.0.0",
"decompress-tar": "^4.1.1",
"decompress-tarbz2": "^4.1.1",
"decompress-targz": "^4.1.1",
"decompress-unzip": "^4.0.1",
"graceful-fs": "^4.1.10",
"make-dir": "^1.0.0",
"pify": "^2.3.0",
"strip-dirs": "^2.0.0"
"graceful-fs": "^4.2.9",
"make-dir": "^3.1.0",
"pify": "^5.0.0",
"strip-dirs": "^3.0.0"
},
"devDependencies": {
"ava": "*",
"esm": "^3.2.25",
"is-jpg": "^1.0.0",
"path-exists": "^3.0.0",
"pify": "^2.3.0",
"ava": "^4.0.1",
"c8": "^7.11.0",
"is-jpg": "^3.0.0",
"path-exists": "^5.0.0",
"rimraf": "^3.0.2",
"xo": "*"
},
"ava": {
"require": [
"esm"
]
},
"xo": {
"rules": {
"promise/prefer-await-to-then": "off"
}
"xo": "^0.47.0"
}
}
16 changes: 8 additions & 8 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
# decompress [![Build Status](https://travis-ci.org/kevva/decompress.svg?branch=master)](https://travis-ci.org/kevva/decompress)
# decompress [![CI](https://github.com/kevva/decompress/actions/workflows/ci.yml/badge.svg)](https://github.com/kevva/decompress/actions/workflows/ci.yml)

> Extracting archives made easy
*See [decompress-cli](https://github.com/kevva/decompress-cli) for the command-line version.*

## Install

```
$ npm install decompress
```sh
npm install decompress
```


## Usage

```js
const decompress = require('decompress');
import decompress from 'decompress';

decompress('unicorn.zip', 'dist').then(files => {
console.log('done!');
Expand Down Expand Up @@ -87,15 +87,15 @@ decompress('unicorn.zip', 'dist', {

##### plugins

Type: `Array`<br>
Default: `[decompressTar(), decompressTarbz2(), decompressTargz(), decompressUnzip()]`
* Type: `Array`
* Default: `[decompressTar(), decompressTarbz2(), decompressTargz(), decompressUnzip()]`

Array of [plugins](https://www.npmjs.com/browse/keyword/decompressplugin) to use.

##### strip

Type: `number`<br>
Default: `0`
* Type: `number`
* Default: `0`

Remove leading directory components from extracted files.

Expand Down
Loading

0 comments on commit 5691b35

Please sign in to comment.