Skip to content

Commit

Permalink
Add ability to specify if looking for file or directory, only match f…
Browse files Browse the repository at this point in the history
…iles by default (#40)
  • Loading branch information
coreyfarrell authored and sindresorhus committed May 7, 2019
1 parent 985b911 commit da17a22
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 23 deletions.
1 change: 1 addition & 0 deletions fixture/directory-link
1 change: 1 addition & 0 deletions fixture/file-link
6 changes: 4 additions & 2 deletions index.d.ts
@@ -1,3 +1,5 @@
import * as locatePath from 'locate-path';

declare namespace findUp {
interface Options {
/**
Expand Down Expand Up @@ -41,7 +43,7 @@ declare const findUp: {
})();
```
*/
(name: string | string[], options?: findUp.Options): Promise<string | undefined>;
(name: string | string[], options?: locatePath.AsyncOptions): Promise<string | undefined>;

/**
Find a file or directory by walking up parent directories.
Expand All @@ -57,7 +59,7 @@ declare const findUp: {
@param name - Name of the file or directory to find. Can be multiple.
@returns The first path found (by respecting the order of `name`s) or `undefined` if none could be found.
*/
sync(name: string | string[], options?: findUp.Options): string | undefined;
sync(name: string | string[], options?: locatePath.Options): string | undefined;

/**
Synchronously find a file or directory by walking up parent directories.
Expand Down
4 changes: 2 additions & 2 deletions index.js
Expand Up @@ -11,7 +11,7 @@ module.exports = async (name, options = {}) => {
// eslint-disable-next-line no-constant-condition
while (true) {
// eslint-disable-next-line no-await-in-loop
const foundPath = await (typeof name === 'function' ? name(directory) : locatePath(paths, {cwd: directory}));
const foundPath = await (typeof name === 'function' ? name(directory) : locatePath(paths, {...options, cwd: directory}));

if (foundPath === stop) {
return;
Expand All @@ -36,7 +36,7 @@ module.exports.sync = (name, options = {}) => {

// eslint-disable-next-line no-constant-condition
while (true) {
const foundPath = typeof name === 'function' ? name(directory) : locatePath.sync(paths, {cwd: directory});
const foundPath = typeof name === 'function' ? name(directory) : locatePath.sync(paths, {...options, cwd: directory});

if (foundPath === stop) {
return;
Expand Down
10 changes: 10 additions & 0 deletions index.test-d.ts
Expand Up @@ -5,6 +5,11 @@ expectType<Promise<string | undefined>>(findUp('unicorn.png'));
expectType<Promise<string | undefined>>(findUp('unicorn.png', {cwd: ''}));
expectType<Promise<string | undefined>>(findUp(['rainbow.png', 'unicorn.png']));
expectType<Promise<string | undefined>>(findUp(['rainbow.png', 'unicorn.png'], {cwd: ''}));
expectType<Promise<string | undefined>>(findUp(['rainbow.png', 'unicorn.png'], {allowSymlinks: true}));
expectType<Promise<string | undefined>>(findUp(['rainbow.png', 'unicorn.png'], {allowSymlinks: false}));
expectType<Promise<string | undefined>>(findUp(['rainbow.png', 'unicorn.png'], {type: 'file'}));
expectType<Promise<string | undefined>>(findUp(['rainbow.png', 'unicorn.png'], {type: 'directory'}));

expectType<Promise<string | undefined>>(findUp(() => 'unicorn.png'));
expectType<Promise<string | undefined>>(findUp(() => 'unicorn.png', {cwd: ''}));
expectType<Promise<string | undefined>>(findUp(() => undefined));
Expand All @@ -22,6 +27,11 @@ expectType<string | undefined>(findUp.sync('unicorn.png'));
expectType<string | undefined>(findUp.sync('unicorn.png', {cwd: ''}));
expectType<string | undefined>(findUp.sync(['rainbow.png', 'unicorn.png']));
expectType<string | undefined>(findUp.sync(['rainbow.png', 'unicorn.png'], {cwd: ''}));
expectType<string | undefined>(findUp.sync(['rainbow.png', 'unicorn.png'], {allowSymlinks: true}));
expectType<string | undefined>(findUp.sync(['rainbow.png', 'unicorn.png'], {allowSymlinks: false}));
expectType<string | undefined>(findUp.sync(['rainbow.png', 'unicorn.png'], {type: 'file'}));
expectType<string | undefined>(findUp.sync(['rainbow.png', 'unicorn.png'], {type: 'directory'}));

expectType<string | undefined>(findUp.sync(() => 'unicorn.png'));
expectType<string | undefined>(findUp.sync(() => 'unicorn.png', {cwd: ''}));
expectType<string | undefined>(findUp.sync(() => undefined));
Expand Down
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -40,7 +40,7 @@
"path"
],
"dependencies": {
"locate-path": "^4.0.0"
"locate-path": "^5.0.0"
},
"devDependencies": {
"ava": "^1.4.1",
Expand Down
17 changes: 16 additions & 1 deletion readme.md
Expand Up @@ -93,7 +93,7 @@ Type: `Function`
A function that will be called with each directory until it returns a `string` with the path, which stops the search, or the root directory has been reached and nothing was found. Useful if you want to match files with certain patterns, set of permissions, or other advanced use cases.
When using async mode, the `matcher` may optionally be an async or promise-returning function that returns the path.
When using async mode, the `matcher` may optionally be an async or promise-returning function that returns the path. When a `matcher` function is used, only the `cwd` option is supported.
#### options
Expand All @@ -106,6 +106,21 @@ Default: `process.cwd()`
Directory to start from.
##### type
Type: `string`<br>
Default: `file`<br>
Values: `file` `directory`
The type of paths that can match.
##### allowSymlinks
Type: `boolean`<br>
Default: `true`
Allow symbolic links to match if they point to the chosen path type.
### findUp.stop
A [Symbol](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol) that can be returned by a `matcher` function to stop the search and cause `findUp` to immediately return `undefined`. Useful as a performance optimization in case the current working directory is deeply nested in the filesystem.
Expand Down
80 changes: 63 additions & 17 deletions test.js
Expand Up @@ -12,7 +12,9 @@ const name = {
fixtureDirectory: 'fixture',
modulesDirectory: 'node_modules',
baz: 'baz.js',
qux: 'qux.js'
qux: 'qux.js',
fileLink: 'file-link',
directoryLink: 'directory-link'
};

// These paths are relative to the project root
Expand All @@ -35,6 +37,8 @@ absolute.fixtureDirectory = path.join(
absolute.baz = path.join(absolute.fixtureDirectory, name.baz);
absolute.qux = path.join(absolute.fixtureDirectory, name.qux);
absolute.barDir = path.join(absolute.fixtureDirectory, 'foo', 'bar');
absolute.fileLink = path.join(absolute.fixtureDirectory, name.fileLink);
absolute.directoryLink = path.join(absolute.fixtureDirectory, name.directoryLink);

// Create a disjoint directory, used for the not-found tests
test.beforeEach(t => {
Expand All @@ -59,17 +63,49 @@ test('sync (child file)', t => {
});

test('async (child directory)', async t => {
const foundPath = await findUp(name.fixtureDirectory);
const foundPath = await findUp(name.fixtureDirectory, {type: 'directory'});

t.is(foundPath, absolute.fixtureDirectory);
});

test('sync (child directory)', t => {
const foundPath = findUp.sync(name.fixtureDirectory);
const foundPath = findUp.sync(name.fixtureDirectory, {type: 'directory'});

t.is(foundPath, absolute.fixtureDirectory);
});

test('async (explicit type file)', async t => {
t.is(await findUp(name.packageJson, {type: 'file'}), absolute.packageJson);
t.is(await findUp(name.packageJson, {type: 'directory'}), undefined);
});

test('sync (explicit type file)', t => {
t.is(findUp.sync(name.packageJson, {type: 'file'}), absolute.packageJson);
t.is(findUp.sync(name.packageJson, {type: 'directory'}), undefined);
});

if (process.platform !== 'win32') {
test('async (symbolic links)', async t => {
const cwd = absolute.fixtureDirectory;

t.is(await findUp(name.fileLink, {cwd}), absolute.fileLink);
t.is(await findUp(name.fileLink, {cwd, allowSymlinks: false}), undefined);

t.is(await findUp(name.directoryLink, {cwd, type: 'directory'}), absolute.directoryLink);
t.is(await findUp(name.directoryLink, {cwd, type: 'directory', allowSymlinks: false}), undefined);
});

test('sync (symbolic links)', t => {
const cwd = absolute.fixtureDirectory;

t.is(findUp.sync(name.fileLink, {cwd}), absolute.fileLink);
t.is(findUp.sync(name.fileLink, {cwd, allowSymlinks: false}), undefined);

t.is(findUp.sync(name.directoryLink, {cwd, type: 'directory'}), absolute.directoryLink);
t.is(findUp.sync(name.directoryLink, {cwd, type: 'directory', allowSymlinks: false}), undefined);
});
}

test('async (child file, custom cwd)', async t => {
const foundPath = await findUp(name.baz, {
cwd: relative.fixtureDirectory
Expand Down Expand Up @@ -136,15 +172,17 @@ test('sync (second child file, array, custom cwd)', t => {

test('async (cwd)', async t => {
const foundPath = await findUp(name.packageDirectory, {
cwd: absolute.packageDirectory
cwd: absolute.packageDirectory,
type: 'directory'
});

t.is(foundPath, absolute.packageDirectory);
});

test('sync (cwd)', t => {
const foundPath = findUp.sync(name.packageDirectory, {
cwd: absolute.packageDirectory
cwd: absolute.packageDirectory,
type: 'directory'
});

t.is(foundPath, absolute.packageDirectory);
Expand Down Expand Up @@ -179,73 +217,79 @@ test('sync (nested descendant file)', t => {
});

test('async (nested descendant directory)', async t => {
const foundPath = await findUp(relative.barDir);
const foundPath = await findUp(relative.barDir, {type: 'directory'});

t.is(foundPath, absolute.barDir);
});

test('sync (nested descendant directory)', t => {
const foundPath = findUp.sync(relative.barDir);
const foundPath = findUp.sync(relative.barDir, {type: 'directory'});

t.is(foundPath, absolute.barDir);
});

test('async (nested descendant directory, custom cwd)', async t => {
const filePath = await findUp(relative.barDir, {
cwd: relative.modulesDirectory
cwd: relative.modulesDirectory,
type: 'directory'
});

t.is(filePath, absolute.barDir);
});

test('sync (nested descendant directory, custom cwd)', t => {
const filePath = findUp.sync(relative.barDir, {
cwd: relative.modulesDirectory
cwd: relative.modulesDirectory,
type: 'directory'
});

t.is(filePath, absolute.barDir);
});

test('async (nested cousin directory, custom cwd)', async t => {
const foundPath = await findUp(relative.barDir, {
cwd: relative.fixtureDirectory
cwd: relative.fixtureDirectory,
type: 'directory'
});

t.is(foundPath, absolute.barDir);
});

test('sync (nested cousin directory, custom cwd)', t => {
const foundPath = findUp.sync(relative.barDir, {
cwd: relative.fixtureDirectory
cwd: relative.fixtureDirectory,
type: 'directory'
});

t.is(foundPath, absolute.barDir);
});

test('async (ancestor directory, custom cwd)', async t => {
const foundPath = await findUp(name.fixtureDirectory, {
cwd: relative.barDir
cwd: relative.barDir,
type: 'directory'
});

t.is(foundPath, absolute.fixtureDirectory);
});

test('sync (ancestor directory, custom cwd)', t => {
const foundPath = findUp.sync(name.fixtureDirectory, {
cwd: relative.barDir
cwd: relative.barDir,
type: 'directory'
});

t.is(foundPath, absolute.fixtureDirectory);
});

test('async (absolute directory)', async t => {
const filePath = await findUp(absolute.barDir);
const filePath = await findUp(absolute.barDir, {type: 'directory'});

t.is(filePath, absolute.barDir);
});

test('sync (absolute directory)', t => {
const filePath = findUp.sync(absolute.barDir);
const filePath = findUp.sync(absolute.barDir, {type: 'directory'});

t.is(filePath, absolute.barDir);
});
Expand All @@ -264,15 +308,17 @@ test('sync (not found, absolute file)', t => {

test('async (absolute directory, disjoint cwd)', async t => {
const filePath = await findUp(absolute.barDir, {
cwd: t.context.disjoint
cwd: t.context.disjoint,
type: 'directory'
});

t.is(filePath, absolute.barDir);
});

test('sync (absolute directory, disjoint cwd)', t => {
const filePath = findUp.sync(absolute.barDir, {
cwd: t.context.disjoint
cwd: t.context.disjoint,
type: 'directory'
});

t.is(filePath, absolute.barDir);
Expand Down

0 comments on commit da17a22

Please sign in to comment.