diff --git a/README.md b/README.md index c0eacab..7238386 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,8 @@ Usage - custom `fs`, useful when mocking `fs` object. - `filter` `` - function that gets one argument `fn({path: '', stats: {}})` and returns true to include or false to exclude the item. - + - `traverseAll` `` + - traverse all subdirectories, regardless of `filter` option. (When set to `true`, `traverseAll` produces similar behavior to the default behavior prior to v4.0.0. The current default of `traverseAll: false` is equivalent to the old `noRecurseOnFailedFilter: true`). - **Return:** `>` `[{path: '', stats: {}}]` Examples @@ -93,7 +94,7 @@ const paths = klawSync('/some/dir', { filter: filterFn}) _**filter based on stats**_ -Again here `noRecurseOnFailedFilter` option is not required since we still want to read all directories even though they don't pass the `filter` function, to see if their contents pass the `filter` function. +Here `traverseAll` option is required since we still want to read all directories even if they don't pass the `filter` function, to see if their contents do pass the `filter` function. ```js const klawSync = require('klaw-sync') diff --git a/klaw-sync.js b/klaw-sync.js index 100f0ce..0810fc4 100644 --- a/klaw-sync.js +++ b/klaw-sync.js @@ -16,12 +16,16 @@ function klawSync (dir, opts, ls) { const st = opts.fs.statSync(pi) const item = {path: pi, stats: st} const isUnderDepthLimit = (!opts.rootDepth || pi.split(path.sep).length - opts.rootDepth < opts.depthLimit) + const filterResult = opts.filter ? opts.filter(item) : true + const isDirectory = st.isDirectory() + const shouldAdd = filterResult && (isDirectory ? !opts.nodir : !opts.nofile) + const shouldTraverse = isDirectory && isUnderDepthLimit && (opts.traverseAll || filterResult) - if (st.isDirectory()) { - if (!opts.nodir) { if ((opts.filter && opts.filter(item)) || !opts.filter) ls.push(item) } - if (isUnderDepthLimit && ((opts.filter && opts.filter(item)) || !opts.filter)) ls = klawSync(pi, opts, ls) - } else if (!st.isDirectory()) { - if (!opts.nofile) { if ((opts.filter && opts.filter(item)) || !opts.filter) ls.push(item) } + if (shouldAdd) { + ls.push(item) + } + if (shouldTraverse) { + ls = klawSync(pi, opts, ls) } } return ls diff --git a/test/klaw-sync.test.js b/test/klaw-sync.test.js index 01e9be8..d0b1890 100644 --- a/test/klaw-sync.test.js +++ b/test/klaw-sync.test.js @@ -335,4 +335,53 @@ describe('klaw-sync', () => { assert.deepStrictEqual(items, expected) } }) + describe('traverse all', function () { + beforeEach(() => { + fs.emptyDirSync(TEST_DIR) + }) + it('should honor traverseAll option with no filter & nodir: false', () => { + const expected = ['a', 'a/b', 'a/b/c', 'a/b/c/d.txt', 'a/e.jpg', 'h', 'h/i', + 'h/i/j', 'h/i/j/k.txt', 'h/i/l.txt', 'h/i/m.jpg', 't.txt'] + testTraverseAll({ nodir: false }, expected) + }) + it('should honor traverseAll option with no filter & nodir: true', () => { + const expected = ['a/b/c/d.txt', 'a/e.jpg', 'h/i/j/k.txt', 'h/i/l.txt', 'h/i/m.jpg', 't.txt'] + testTraverseAll({ nodir: true }, expected) + }) + it('should honor traverseAll option with filter & nodir: true', () => { + const expected = ['a/b/c/d.txt', 'h/i/j/k.txt', 'h/i/l.txt', 't.txt'] + const filter = function (item) { + return path.extname(item.path) === '.txt' // no need to greenlight dirs in filter + } + testTraverseAll({ nodir: true, filter }, expected) + }) + it('should honor traverseAll option with filter & nodir: false', () => { + const expected = ['a', 'a/b', 'a/b/c', 'a/e.jpg', 'h', + 'h/i/j', 'h/i/m.jpg'] + const filter = function (item) { + return path.basename(item.path) !== 'i' && path.extname(item.path) !== '.txt' + } + testTraverseAll({ nodir: false, filter }, expected) + }) + + function testTraverseAll ({ filter, nodir = false } = {}, expected) { + const fixtures = [ + 'a/b/c/d.txt', + 'a/e.jpg', + 'h/i/j/k.txt', + 'h/i/l.txt', + 'h/i/m.jpg', + 't.txt' + ] + fixtures.forEach(f => { + f = path.join(TEST_DIR, f) + fs.outputFileSync(f, path.basename(f, path.extname(f))) + }) + + const items = klawSync(TEST_DIR, { traverseAll: true, nodir, filter }).map(i => i.path) + items.sort() + expected = expected.map(item => path.join(path.join(TEST_DIR, item))) + assert.deepStrictEqual(items, expected) + } + }) })