Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

fix: Only traverse directories that can match the glob base #131

Merged
merged 5 commits into from
Apr 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ jobs:

- name: Run tests
run: npm test
env:
TEMP: ${{ runner.temp }}

- name: Coveralls
uses: coverallsapp/github-action@v1.1.2
Expand Down
57 changes: 46 additions & 11 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ function walkdir() {

var ee = new EventEmitter();

var queue = fastq(readdir, 1);
var queue = fastq(process, 1);
queue.drain = function () {
ee.emit('end');
};
Expand All @@ -52,20 +52,39 @@ function walkdir() {
ee.end = function () {
queue.kill();
};
ee.walk = function (filepath) {
queue.push(filepath);
};
ee.walk = walk;
ee.exists = exists;

function isDefined(value) {
return typeof value !== 'undefined';
}

function queuePush(value) {
queue.push(value);
function walk(path) {
queue.push({ action: 'walk', path: path });
}

function exists(path) {
queue.push({ action: 'exists', path: path });
}

function readdir(filepath, cb) {
fs.readdir(filepath, readdirOpts, onReaddir);
function process(data, cb) {
if (data.action === 'walk') {
fs.readdir(data.path, readdirOpts, onReaddir);
} else {
fs.stat(data.path, onStat);
}

function onStat(err, stat) {
if (err) {
// Ignore errors but also don't emit the path
return cb();
}

// `stat` has `isDirectory()` which is what we use from Dirent
ee.emit('path', data.path, stat);

cb();
}

function onReaddir(err, dirents) {
if (err) {
Expand All @@ -77,14 +96,14 @@ function walkdir() {
return cb(err);
}

dirs.filter(isDefined).forEach(queuePush);
dirs.filter(isDefined).forEach(walk);

cb();
});
}

function processDirents(dirent, key, cb) {
var nextpath = path.join(filepath, dirent.name);
var nextpath = path.join(data.path, dirent.name);
ee.emit('path', nextpath, dirent);

if (dirent.isDirectory()) {
Expand Down Expand Up @@ -138,6 +157,10 @@ function validateGlobs(globs) {
}
}

function isPositiveGlob(glob) {
return !isNegatedGlob(glob).negated;
}

function validateOptions(opts) {
if (typeof opts.cwd !== 'string') {
throw new Error('The `cwd` option must be a string');
Expand Down Expand Up @@ -251,7 +274,19 @@ function globStream(globs, opt) {
walker.on('path', onPath);
walker.once('end', onEnd);
walker.once('error', onError);
walker.walk(ourOpt.cwd);
ourGlobs.forEach(function (glob) {
if (isGlob(glob)) {
// We only want to walk the glob-parent directories of any positive glob
// to reduce the amount of files have to check.
if (isPositiveGlob(glob)) {
var base = globParent(glob);
walker.walk(base);
}
} else {
// If the strig is not a glob, we just check for the existence of it.
walker.exists(glob);
}
});

function read(cb) {
walker.resume();
Expand Down
57 changes: 56 additions & 1 deletion test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ var sinon = require('sinon');

// Need to wrap this to cause walker to emit an error
var fs = require('fs');
var os = require('os');

var globStream = require('../');

Expand Down Expand Up @@ -370,6 +371,11 @@ function suite(moduleName) {
});

it('emits all objects (unordered) when given multiple absolute paths and no cwd', function (done) {
var testFile = path.join(os.tmpdir(), "glob-stream-test.txt");
fs.writeFileSync(testFile, "test");

var tmp = deWindows(os.tmpdir());

var expected = [
{
cwd: cwd,
Expand All @@ -387,24 +393,73 @@ function suite(moduleName) {
base: dir + '/fixtures/whatsgoingon',
path: dir + '/fixtures/whatsgoingon/test.js',
},
{
cwd: cwd,
base: tmp,
path: tmp + '/glob-stream-test.txt',
}
];

var paths = [
dir + '/fixtures/whatsgoingon/hey/isaidhey/whatsgoingon/test.txt',
dir + '/fixtures/test.coffee',
dir + '/fixtures/whatsgoingon/test.js',
tmp + '/glob-stream-*.txt',
];

console.log(paths);

function assert(pathObjs) {
expect(pathObjs.length).toEqual(3);
fs.unlinkSync(testFile, "test");
expect(pathObjs.length).toEqual(4);
expect(pathObjs).toContainEqual(expected[0]);
expect(pathObjs).toContainEqual(expected[1]);
expect(pathObjs).toContainEqual(expected[2]);
expect(pathObjs).toContainEqual(expected[3]);
}

stream.pipeline([globStream(paths), concat(assert)], done);
});

it('resolves globs when process.chdir() changes the cwd', function (done) {
var prevCwd = process.cwd();
process.chdir('test/fixtures/stuff');

var expected = {
cwd: dir + '/fixtures/stuff',
base: dir + '/fixtures',
path: dir + '/fixtures/test.coffee',
};

function assert(pathObjs) {
process.chdir(prevCwd);

expect(pathObjs.length).toEqual(1);
expect(pathObjs[0]).toEqual(expected);
}

stream.pipeline([globStream(['../*.coffee']), concat(assert)], done);
});

// https://github.com/gulpjs/glob-stream/issues/129
it('does not take a long time when when checking a singular glob in the project root', function (done) {
// Extremely short timeout to ensure we aren't traversing node_modules
this.timeout(20);

var expected = {
cwd: cwd,
base: cwd,
path: cwd + '/package.json',
};

function assert(pathObjs) {
expect(pathObjs.length).toEqual(1);
expect(pathObjs[0]).toEqual(expected);
}

stream.pipeline([globStream(['./package.json']), concat(assert)], done);
});

it('removes duplicate objects from the stream using default (path) filter', function (done) {
var expected = {
cwd: dir,
Expand Down
Loading