diff --git a/index.js b/index.js index 3a6ac08..253846c 100644 --- a/index.js +++ b/index.js @@ -35,6 +35,24 @@ const checkCwdOption = options => { }; const getPathString = fastGlobResult => fastGlobResult.path || fastGlobResult; +const unionFastGlobResults = (results, filter) => { + const seen = new Set(); + + return results.flat().filter(fastGlobResult => { + if (filter(fastGlobResult)) { + return false; + } + + const value = getPathString(fastGlobResult); + if (seen.has(value)) { + return false; + } + + seen.add(value); + + return true; + }); +}; export const generateGlobTasks = (patterns, taskOptions) => { patterns = arrayUnion([patterns].flat()); @@ -150,9 +168,9 @@ export const globby = async (patterns, options = {}) => { }; const [filter, tasks] = await Promise.all([getFilter(options), getTasks()]); - const paths = await Promise.all(tasks.map(task => fastGlob(task.pattern, task.options))); + const results = await Promise.all(tasks.map(task => fastGlob(task.pattern, task.options))); - return arrayUnion(...paths).filter(path_ => !filter(path_)); + return unionFastGlobResults(results, filter); }; export const globbySync = (patterns, options = {}) => { @@ -165,13 +183,9 @@ export const globbySync = (patterns, options = {}) => { } const filter = getFilterSync(options); + const results = tasks.map(task => fastGlob.sync(task.pattern, task.options)); - let matches = []; - for (const task of tasks) { - matches = arrayUnion(matches, fastGlob.sync(task.pattern, task.options)); - } - - return matches.filter(path_ => !filter(path_)); + return unionFastGlobResults(results, filter); }; export const globbyStream = (patterns, options = {}) => { @@ -184,8 +198,8 @@ export const globbyStream = (patterns, options = {}) => { } const filter = getFilterSync(options); - const filterStream = new FilterStream(p => !filter(p)); - const uniqueStream = new UniqueStream(); + const filterStream = new FilterStream(fastGlobResult => !filter(fastGlobResult)); + const uniqueStream = new UniqueStream(fastGlobResult => getPathString(fastGlobResult)); return merge2(tasks.map(task => fastGlob.stream(task.pattern, task.options))) .pipe(filterStream) diff --git a/stream-utils.js b/stream-utils.js index 91a99bb..2f64be2 100644 --- a/stream-utils.js +++ b/stream-utils.js @@ -24,15 +24,19 @@ export class FilterStream extends ObjectTransform { } export class UniqueStream extends ObjectTransform { - constructor() { + constructor(comparator) { super(); + this._comparator = comparator; this._pushed = new Set(); } _transform(data, encoding, callback) { - if (!this._pushed.has(data)) { + const {_comparator: comparator, _pushed: pushed} = this; + const value = comparator(data); + + if (!pushed.has(value)) { this.push(data); - this._pushed.add(data); + pushed.add(value); } callback(); diff --git a/test.js b/test.js index f403623..f52d377 100644 --- a/test.js +++ b/test.js @@ -425,3 +425,23 @@ test('don\'t throw when specifying a non-existing cwd directory - sync', t => { t.is(actual.length, 0); } }); + +test('unique when using objectMode option', async t => { + const patterns = ['a.tmp', '*.tmp']; + const options = {cwd, objectMode: true}; + const isUnique = result => [...new Set(result)].length === result.length; + + const syncResult = globbySync(patterns, options).map(({path}) => path); + t.true(isUnique(syncResult)); + + const result = await globby(patterns, options); + t.deepEqual(result.map(({path}) => path), syncResult); + + // TODO: Use `Array.fromAsync` when Node.js supports it + const streamResult = []; + for await (const {path} of globbyStream(patterns, options)) { + streamResult.push(path); + } + + t.deepEqual(streamResult, syncResult); +});