Skip to content

Commit

Permalink
Add filter option to remove() and removeSync(), add filter tests
Browse files Browse the repository at this point in the history
  • Loading branch information
manidlou committed May 7, 2017
1 parent 44eb2c3 commit 952a369
Show file tree
Hide file tree
Showing 3 changed files with 282 additions and 15 deletions.
118 changes: 118 additions & 0 deletions lib/remove/__tests__/remove-filter.test.js
@@ -0,0 +1,118 @@
'use strict'

const assert = require('assert')
const fs = require('fs')
const os = require('os')
const path = require('path')
const fse = require(process.cwd())

/* global afterEach, beforeEach, describe, it */

let TEST_DIR

describe('+ remove() - filter option', () => {
beforeEach(done => {
TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'remove-filter')
fse.emptyDir(TEST_DIR, done)
})

afterEach(done => fse.remove(TEST_DIR, done))

describe('> when filter is used', () => {
it('should only remove files allowed by filter fn when path is a file', done => {
const f1 = path.join(TEST_DIR, '1.css')
fse.outputFileSync(f1, '')

const filter = p => path.extname(p) !== '.css'

fse.remove(f1, {filter: filter}, err => {
assert.ifError(err)
assert(fs.existsSync(f1))
done()
})
})

it('should only remove files allowed by filter fn when path is a directory', done => {
const src = path.join(TEST_DIR, 'src')
const f1 = path.join(src, '1.css')
const f2 = path.join(src, '2.js')
fse.outputFileSync(f1, '')
fse.outputFileSync(f2, '')

const filter = p => path.extname(p) !== '.css' && p !== src

fse.remove(src, {filter: filter}, err => {
assert.ifError(err)
assert(fs.existsSync(f1))
assert(!fs.existsSync(f2))
done()
})
})

it('should not remove and return when nothing matched', done => {
const src = path.join(TEST_DIR, 'src')
const f1 = path.join(src, '1.md')
const f2 = path.join(src, '2.js')
fse.outputFileSync(f1, '')
fse.outputFileSync(f2, '')

const filter = p => path.extname(p) === '.css'

fse.remove(src, {filter: filter}, err => {
assert.ifError(err)
assert(fs.existsSync(f1))
assert(fs.existsSync(f2))
done()
})
})

it('should apply filter recursively when applied on file', done => {
const src = path.join(TEST_DIR, 'src')
const srcFile1 = path.join(src, '1.js')
const srcFile2 = path.join(src, '2.css')
const srcFile3 = path.join(src, 'node_modules', '3.css')
fse.outputFileSync(srcFile1, 'file 1 stuff')
fse.outputFileSync(srcFile2, 'file 2 stuff')
fse.outputFileSync(srcFile3, 'file 3 stuff')

const filter = p => path.extname(p) === '.css'

fse.remove(src, {filter: filter}, err => {
assert.ifError(err)
assert(fs.existsSync(srcFile1))
assert(!fs.existsSync(srcFile2))
assert(!fs.existsSync(srcFile3))
done()
})
})

it('should apply filter recursively when applied on dir', done => {
const src = path.join(TEST_DIR, 'src')
const srcFile1 = path.join(src, '1.js')
const subdir1 = path.join(src, 'subdir1')
// one subdir in root (should be reomved)
const subdir2 = path.join(src, 'subdir2')
const srcFile2 = path.join(subdir2, '2.css')
// other subdir in some subdir (should be removed)
const subdir22 = path.join(subdir1, 'subdir2')
const srcFile22 = path.join(subdir22, '22.css')
const subdir3 = path.join(subdir1, 'subdir3')
fse.outputFileSync(srcFile1, 'file 1 stuff')
fse.outputFileSync(srcFile2, 'file 2 stuff')
fse.outputFileSync(srcFile22, 'file 22 stuff')
fse.ensureDir(subdir3)

const filter = p => p.indexOf('subdir2') > -1

fse.remove(src, {filter: filter}, err => {
assert.ifError(err)
assert(fs.existsSync(srcFile1))
assert(fs.existsSync(subdir1))
assert(!fs.existsSync(subdir2))
assert(!fs.existsSync(subdir22))
assert(fs.existsSync(subdir3))
done()
})
})
})
})
103 changes: 103 additions & 0 deletions lib/remove/__tests__/remove-sync-filter.test.js
@@ -0,0 +1,103 @@
'use strict'

const assert = require('assert')
const fs = require('fs')
const os = require('os')
const path = require('path')
const fse = require(process.cwd())

/* global afterEach, beforeEach, describe, it */

let TEST_DIR

describe('+ removeSync() - filter option', () => {
beforeEach(done => {
TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'remove-filter')
fse.emptyDir(TEST_DIR, done)
})

afterEach(done => fse.remove(TEST_DIR, done))

describe('> when filter is used', () => {
it('should only remove files allowed by filter fn when path is a file', () => {
const f1 = path.join(TEST_DIR, '1.css')
fse.outputFileSync(f1, '')

const filter = p => path.extname(p) !== '.css'

fse.removeSync(f1, {filter: filter})
assert(fs.existsSync(f1))
})

it('should only remove files allowed by filter fn when path is a directory', () => {
const src = path.join(TEST_DIR, 'src')
const f1 = path.join(src, '1.css')
const f2 = path.join(src, '2.js')
fse.outputFileSync(f1, '')
fse.outputFileSync(f2, '')

const filter = p => path.extname(p) !== '.css' && p !== src

fse.removeSync(src, {filter: filter})
assert(fs.existsSync(f1))
assert(!fs.existsSync(f2))
})

it('should not remove and return when nothing matched', () => {
const src = path.join(TEST_DIR, 'src')
const f1 = path.join(src, '1.md')
const f2 = path.join(src, '2.js')
fse.outputFileSync(f1, '')
fse.outputFileSync(f2, '')

const filter = p => path.extname(p) === '.css'

fse.removeSync(src, {filter: filter})
assert(fs.existsSync(f1))
assert(fs.existsSync(f2))
})

it('should apply filter recursively when applied on file', () => {
const src = path.join(TEST_DIR, 'src')
const srcFile1 = path.join(src, '1.js')
const srcFile2 = path.join(src, '2.css')
const srcFile3 = path.join(src, 'node_modules', '3.css')
fse.outputFileSync(srcFile1, 'file 1 stuff')
fse.outputFileSync(srcFile2, 'file 2 stuff')
fse.outputFileSync(srcFile3, 'file 3 stuff')

const filter = p => path.extname(p) === '.css'

fse.removeSync(src, {filter: filter})
assert(fs.existsSync(srcFile1))
assert(!fs.existsSync(srcFile2))
assert(!fs.existsSync(srcFile3))
})

it('should apply filter recursively when applied on dir', () => {
const src = path.join(TEST_DIR, 'src')
const srcFile1 = path.join(src, '1.js')
const subdir1 = path.join(src, 'subdir1')
// one subdir in root (should be reomved)
const subdir2 = path.join(src, 'subdir2')
const srcFile2 = path.join(subdir2, '2.css')
// other subdir in some subdir (should be removed)
const subdir22 = path.join(subdir1, 'subdir2')
const srcFile22 = path.join(subdir22, '22.css')
const subdir3 = path.join(subdir1, 'subdir3')
fse.outputFileSync(srcFile1, 'file 1 stuff')
fse.outputFileSync(srcFile2, 'file 2 stuff')
fse.outputFileSync(srcFile22, 'file 22 stuff')
fse.ensureDir(subdir3)

const filter = p => p.indexOf('subdir2') > -1

fse.removeSync(src, {filter: filter})
assert(fs.existsSync(srcFile1))
assert(fs.existsSync(subdir1))
assert(!fs.existsSync(subdir2))
assert(!fs.existsSync(subdir22))
assert(fs.existsSync(subdir3))
})
})
})
76 changes: 61 additions & 15 deletions lib/remove/rimraf.js
Expand Up @@ -87,28 +87,58 @@ function rimraf_ (p, options, cb) {
}

if (st && st.isDirectory()) {
if (options.filter) return filterDir(p, options, er, cb)
return rmdir(p, options, er, cb)
}

options.unlink(p, er => {
if (er) {
if (er.code === 'ENOENT') {
return cb(null)
}
if (er.code === 'EPERM') {
return (isWindows)
? fixWinEPERM(p, options, er, cb)
: rmdir(p, options, er, cb)
}
if (er.code === 'EISDIR') {
return rmdir(p, options, er, cb)
}
if (options.filter) return filterFile(p, options, cb)
return rmFile(p, options, cb)
})
}

function rmFile (p, options, cb) {
options.unlink(p, er => {
if (er) {
if (er.code === 'ENOENT') {
return cb(null)
}
return cb(er)
})
if (er.code === 'EPERM') {
return (isWindows)
? fixWinEPERM(p, options, er, cb)
: rmdir(p, options, er, cb)
}
if (er.code === 'EISDIR') {
return rmdir(p, options, er, cb)
}
}
return cb(er)
})
}

function filterDir (p, options, originalEr, cb) {
if (!options.filter(p)) return readFailedDir()
return rmdir(p, options, originalEr, cb)

function readFailedDir () {
options.readdir(p, (err, items) => {
if (err) return cb(err)
Promise.all(items.map(item => {
return new Promise((resolve, reject) => {
rimraf(path.join(p, item), options, err => {
if (err) reject(err)
else resolve()
})
})
})).then(() => cb()).catch(cb)
})
}
}

function filterFile (p, options, cb) {
if (!options.filter(p)) return cb()
return rmFile(p, options, cb)
}

function fixWinEPERM (p, options, er, cb) {
assert(p)
assert(options)
Expand All @@ -125,8 +155,10 @@ function fixWinEPERM (p, options, er, cb) {
if (er3) {
cb(er3.code === 'ENOENT' ? null : er)
} else if (stats.isDirectory()) {
if (options.filter) return filterDir(p, options, er, cb)
rmdir(p, options, er, cb)
} else {
if (options.filter) return filterFile(p, options, cb)
options.unlink(p, cb)
}
})
Expand Down Expand Up @@ -164,8 +196,10 @@ function fixWinEPERMSync (p, options, er) {
}

if (stats.isDirectory()) {
if (options.filter) return filterDirSync(p, options, er)
rmdirSync(p, options, er)
} else {
if (options.filter) return filterFileSync(p, options)
options.unlinkSync(p)
}
}
Expand Down Expand Up @@ -249,8 +283,10 @@ function rimrafSync (p, options) {
try {
// sunos lets the root user unlink directories, which is... weird.
if (st && st.isDirectory()) {
if (options.filter) return filterDirSync(p, options, null)
rmdirSync(p, options, null)
} else {
if (options.filter) return filterFileSync(p, options)
options.unlinkSync(p)
}
} catch (er) {
Expand Down Expand Up @@ -292,5 +328,15 @@ function rmkidsSync (p, options) {
options.rmdirSync(p, options)
}

function filterDirSync (p, options, originalEr) {
if (!options.filter(p)) return options.readdirSync(p).forEach(f => rimrafSync(path.join(p, f), options))
return rmdirSync(p, options, originalEr)
}

function filterFileSync (p, options) {
if (!options.filter(p)) return
options.unlinkSync(p)
}

module.exports = rimraf
rimraf.sync = rimrafSync

0 comments on commit 952a369

Please sign in to comment.