From abacc4099b6748322f293bd26927f0262baf55ce Mon Sep 17 00:00:00 2001 From: isaacs Date: Wed, 21 Oct 2020 15:39:43 -0700 Subject: [PATCH] Maintain order in package.json files array globs Fix: npm/cli#2009 --- index.js | 54 ++++++++++++++---------- test/ignore-file-included-by-globstar.js | 22 ++++++++++ 2 files changed, 54 insertions(+), 22 deletions(-) create mode 100644 test/ignore-file-included-by-globstar.js diff --git a/index.js b/index.js index 3bda083..0ab0387 100644 --- a/index.js +++ b/index.js @@ -220,35 +220,45 @@ const npmWalker = Class => class Walker extends Class { let n = patterns.length const set = new Set() const negates = new Set() - const then = (pattern, negate, er, fileList) => { + const results = [] + const then = (pattern, negate, er, fileList, i) => { if (er) return this.emit('error', er) - if (negate) { - fileList.forEach(f => { - set.delete(f) - negates.add(f) - }) - } else { - fileList.forEach(f => { - set.add(f) - negates.delete(f) - }) - } - + results[i] = { negate, fileList } if (--n === 0) { - const list = Array.from(set) - // replace the files array with our computed explicit set - pkg.files = list.concat(Array.from(negates).map(f => '!' + f)) - const rdResult = Array.from(new Set( - list.map(f => f.replace(/^\/+/, '')) - )) - super.onReaddir(rdResult) + processResults(results) } } + const processResults = results => { + for (const {negate, fileList} of results) { + if (negate) { + fileList.forEach(f => { + set.delete(f) + negates.add(f) + }) + } else { + fileList.forEach(f => { + set.add(f) + negates.delete(f) + }) + } + } + + const list = Array.from(set) + // replace the files array with our computed explicit set + pkg.files = list.concat(Array.from(negates).map(f => '!' + f)) + const rdResult = Array.from(new Set( + list.map(f => f.replace(/^\/+/, '')) + )) + super.onReaddir(rdResult) + } - patterns.forEach(({pattern, negate}) => - this.globFiles(pattern, (er, res) => then(pattern, negate, er, res))) + // maintain the index so that we process them in-order only once all + // are completed, otherwise the parallelism messes things up, since a + // glob like **/*.js will always be slower than a subsequent !foo.js + patterns.forEach(({pattern, negate}, i) => + this.globFiles(pattern, (er, res) => then(pattern, negate, er, res, i))) } filterEntry (entry, partial) { diff --git a/test/ignore-file-included-by-globstar.js b/test/ignore-file-included-by-globstar.js new file mode 100644 index 0000000..84b9b72 --- /dev/null +++ b/test/ignore-file-included-by-globstar.js @@ -0,0 +1,22 @@ +const t = require('tap') +const packlist = require('../') +t.test('include a globstar, then exclude one of them', t => { + const path = t.testdir({ + 'foo.js': '', + 'bar.js': '', + bar: { + 'bar.js': '' + }, + 'glorp.txt': '', + 'package.json': JSON.stringify({ + name: 'cli-issue-2009', + version: '1.0.0', + files: [ + '**/*.js', + '!foo.js' + ] + }) + }) + const expect = ['bar.js', 'bar/bar.js', 'package.json'] + return packlist({path}).then(actual => t.strictSame(actual, expect)) +})