From da17a221ef1151afa34fc4f7da148ea4f3b931ff Mon Sep 17 00:00:00 2001 From: Corey Farrell Date: Tue, 7 May 2019 00:12:12 -0400 Subject: [PATCH] Add ability to specify if looking for file or directory, only match files by default (#40) --- fixture/directory-link | 1 + fixture/file-link | 1 + index.d.ts | 6 ++-- index.js | 4 +-- index.test-d.ts | 10 ++++++ package.json | 2 +- readme.md | 17 ++++++++- test.js | 80 +++++++++++++++++++++++++++++++++--------- 8 files changed, 98 insertions(+), 23 deletions(-) create mode 120000 fixture/directory-link create mode 120000 fixture/file-link diff --git a/fixture/directory-link b/fixture/directory-link new file mode 120000 index 0000000..945c9b4 --- /dev/null +++ b/fixture/directory-link @@ -0,0 +1 @@ +. \ No newline at end of file diff --git a/fixture/file-link b/fixture/file-link new file mode 120000 index 0000000..250a17a --- /dev/null +++ b/fixture/file-link @@ -0,0 +1 @@ +baz.js \ No newline at end of file diff --git a/index.d.ts b/index.d.ts index 425e324..9d8f281 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,3 +1,5 @@ +import * as locatePath from 'locate-path'; + declare namespace findUp { interface Options { /** @@ -41,7 +43,7 @@ declare const findUp: { })(); ``` */ - (name: string | string[], options?: findUp.Options): Promise; + (name: string | string[], options?: locatePath.AsyncOptions): Promise; /** Find a file or directory by walking up parent directories. @@ -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. diff --git a/index.js b/index.js index 35c261d..0bd0b19 100644 --- a/index.js +++ b/index.js @@ -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; @@ -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; diff --git a/index.test-d.ts b/index.test-d.ts index 42b1397..bea7a80 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -5,6 +5,11 @@ expectType>(findUp('unicorn.png')); expectType>(findUp('unicorn.png', {cwd: ''})); expectType>(findUp(['rainbow.png', 'unicorn.png'])); expectType>(findUp(['rainbow.png', 'unicorn.png'], {cwd: ''})); +expectType>(findUp(['rainbow.png', 'unicorn.png'], {allowSymlinks: true})); +expectType>(findUp(['rainbow.png', 'unicorn.png'], {allowSymlinks: false})); +expectType>(findUp(['rainbow.png', 'unicorn.png'], {type: 'file'})); +expectType>(findUp(['rainbow.png', 'unicorn.png'], {type: 'directory'})); + expectType>(findUp(() => 'unicorn.png')); expectType>(findUp(() => 'unicorn.png', {cwd: ''})); expectType>(findUp(() => undefined)); @@ -22,6 +27,11 @@ expectType(findUp.sync('unicorn.png')); expectType(findUp.sync('unicorn.png', {cwd: ''})); expectType(findUp.sync(['rainbow.png', 'unicorn.png'])); expectType(findUp.sync(['rainbow.png', 'unicorn.png'], {cwd: ''})); +expectType(findUp.sync(['rainbow.png', 'unicorn.png'], {allowSymlinks: true})); +expectType(findUp.sync(['rainbow.png', 'unicorn.png'], {allowSymlinks: false})); +expectType(findUp.sync(['rainbow.png', 'unicorn.png'], {type: 'file'})); +expectType(findUp.sync(['rainbow.png', 'unicorn.png'], {type: 'directory'})); + expectType(findUp.sync(() => 'unicorn.png')); expectType(findUp.sync(() => 'unicorn.png', {cwd: ''})); expectType(findUp.sync(() => undefined)); diff --git a/package.json b/package.json index cb92c5a..9b35999 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "path" ], "dependencies": { - "locate-path": "^4.0.0" + "locate-path": "^5.0.0" }, "devDependencies": { "ava": "^1.4.1", diff --git a/readme.md b/readme.md index c0fbbf5..bcb845f 100644 --- a/readme.md +++ b/readme.md @@ -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 @@ -106,6 +106,21 @@ Default: `process.cwd()` Directory to start from. +##### type + +Type: `string`
+Default: `file`
+Values: `file` `directory` + +The type of paths that can match. + +##### allowSymlinks + +Type: `boolean`
+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. diff --git a/test.js b/test.js index 8fbb5d6..a1a9564 100644 --- a/test.js +++ b/test.js @@ -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 @@ -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 => { @@ -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 @@ -136,7 +172,8 @@ 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); @@ -144,7 +181,8 @@ test('async (cwd)', async t => { test('sync (cwd)', t => { const foundPath = findUp.sync(name.packageDirectory, { - cwd: absolute.packageDirectory + cwd: absolute.packageDirectory, + type: 'directory' }); t.is(foundPath, absolute.packageDirectory); @@ -179,20 +217,21 @@ 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); @@ -200,7 +239,8 @@ test('async (nested descendant directory, custom cwd)', async t => { 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); @@ -208,7 +248,8 @@ test('sync (nested descendant directory, custom cwd)', t => { 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); @@ -216,7 +257,8 @@ test('async (nested cousin directory, custom cwd)', async t => { 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); @@ -224,7 +266,8 @@ test('sync (nested cousin directory, custom cwd)', t => { 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); @@ -232,20 +275,21 @@ test('async (ancestor directory, custom cwd)', async t => { 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); }); @@ -264,7 +308,8 @@ 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); @@ -272,7 +317,8 @@ test('async (absolute directory, disjoint cwd)', async t => { 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);