From b3b3b00a2f9518b023c6287d3b01b4bf0b4fb7b5 Mon Sep 17 00:00:00 2001 From: Vasily Polovnyov Date: Sun, 26 Sep 2021 13:57:29 +0300 Subject: [PATCH] Add `stopAt` option (#54) --- index.d.ts | 13 ++++++++++--- index.js | 6 ++++-- index.test-d.ts | 11 +++++++++++ readme.md | 7 +++++++ test.js | 37 +++++++++++++++++++++++++++++++++++++ 5 files changed, 69 insertions(+), 5 deletions(-) diff --git a/index.d.ts b/index.d.ts index 62596f6..d3c13e1 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/unified-signatures */ -import {Options} from 'locate-path'; +import {Options as LocatePathOptions} from 'locate-path'; /** Return this in a `matcher` function to stop the search and force `findUp` to immediately return `undefined`. @@ -8,6 +8,15 @@ export const findUpStop: unique symbol; export type Match = string | typeof findUpStop | undefined; +export interface Options extends LocatePathOptions { + /** + The path to the directory to stop the search before reaching root if there were no matches before the `stopAt` directory. + + @default path.parse(cwd).root + */ + readonly stopAt?: string; +} + /** Find a file or directory by walking up parent directories. @@ -137,5 +146,3 @@ console.log(pathExistsSync('/Users/sindresorhus/unicorn.png')); ``` */ export function pathExistsSync(path: string): boolean; - -export {Options} from 'locate-path'; diff --git a/index.js b/index.js index 99006d8..94f00bb 100644 --- a/index.js +++ b/index.js @@ -6,6 +6,7 @@ export const findUpStop = Symbol('findUpStop'); export async function findUp(name, options = {}) { let directory = path.resolve(options.cwd || ''); const {root} = path.parse(directory); + const stopAt = path.resolve(directory, options.stopAt || root); const paths = [name].flat(); const runMatcher = async locateOptions => { @@ -34,7 +35,7 @@ export async function findUp(name, options = {}) { return path.resolve(directory, foundPath); } - if (directory === root) { + if (directory === stopAt) { return; } @@ -45,6 +46,7 @@ export async function findUp(name, options = {}) { export function findUpSync(name, options = {}) { let directory = path.resolve(options.cwd || ''); const {root} = path.parse(directory); + const stopAt = options.stopAt || root; const paths = [name].flat(); const runMatcher = locateOptions => { @@ -72,7 +74,7 @@ export function findUpSync(name, options = {}) { return path.resolve(directory, foundPath); } - if (directory === root) { + if (directory === stopAt) { return; } diff --git a/index.test-d.ts b/index.test-d.ts index 8c68226..539323c 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -9,6 +9,7 @@ expectType>(findUp(['rainbow.png', 'unicorn.png'], { 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(['rainbow.png', 'unicorn.png'], {stopAt: 'foo'})); expectError(findUp(['rainbow.png', 'unicorn.png'], {concurrency: 1})); expectType>(findUp(() => 'unicorn.png')); @@ -17,26 +18,31 @@ expectType>(findUp(() => 'unicorn.png', {allowSymlin expectType>(findUp(() => 'unicorn.png', {allowSymlinks: false})); expectType>(findUp(() => 'unicorn.png', {type: 'file'})); expectType>(findUp(() => 'unicorn.png', {type: 'directory'})); +expectType>(findUp(() => 'unicorn.png', {stopAt: 'foo'})); expectType>(findUp(() => undefined)); expectType>(findUp(() => undefined, {cwd: ''})); expectType>(findUp(() => undefined, {allowSymlinks: true})); expectType>(findUp(() => undefined, {allowSymlinks: false})); expectType>(findUp(() => undefined, {type: 'file'})); expectType>(findUp(() => undefined, {type: 'directory'})); +expectType>(findUp(() => undefined, {stopAt: 'foo'})); expectType>(findUp((): typeof findUpStop => findUpStop)); expectType>(findUp((): typeof findUpStop => findUpStop, {cwd: ''})); +expectType>(findUp((): typeof findUpStop => findUpStop, {stopAt: 'foo'})); expectType>(findUp(async () => 'unicorn.png')); expectType>(findUp(async () => 'unicorn.png', {cwd: ''})); expectType>(findUp(async () => 'unicorn.png', {allowSymlinks: true})); expectType>(findUp(async () => 'unicorn.png', {allowSymlinks: false})); expectType>(findUp(async () => 'unicorn.png', {type: 'file'})); expectType>(findUp(async () => 'unicorn.png', {type: 'directory'})); +expectType>(findUp(async () => 'unicorn.png', {stopAt: 'foo'})); expectType>(findUp(async () => undefined)); expectType>(findUp(async () => undefined, {cwd: ''})); expectType>(findUp(async () => undefined, {allowSymlinks: true})); expectType>(findUp(async () => undefined, {allowSymlinks: false})); expectType>(findUp(async () => undefined, {type: 'file'})); expectType>(findUp(async () => undefined, {type: 'directory'})); +expectType>(findUp(async () => undefined, {stopAt: 'foo'})); expectType>(findUp(async (): Promise => findUpStop)); expectType>(findUp(async (): Promise => findUpStop, {cwd: ''})); @@ -44,6 +50,7 @@ expectType>(findUp(async (): Promise>(findUp(async (): Promise => findUpStop, {allowSymlinks: false})); expectType>(findUp(async (): Promise => findUpStop, {type: 'file'})); expectType>(findUp(async (): Promise => findUpStop, {type: 'directory'})); +expectType>(findUp(async (): Promise => findUpStop, {stopAt: 'foo'})); expectType(findUpSync('unicorn.png')); expectType(findUpSync('unicorn.png', {cwd: ''})); @@ -53,6 +60,7 @@ expectType(findUpSync(['rainbow.png', 'unicorn.png'], {allow expectType(findUpSync(['rainbow.png', 'unicorn.png'], {allowSymlinks: false})); expectType(findUpSync(['rainbow.png', 'unicorn.png'], {type: 'file'})); expectType(findUpSync(['rainbow.png', 'unicorn.png'], {type: 'directory'})); +expectType(findUpSync(['rainbow.png', 'unicorn.png'], {stopAt: 'foo'})); expectType(findUpSync(() => 'unicorn.png')); expectType(findUpSync(() => 'unicorn.png', {cwd: ''})); @@ -60,16 +68,19 @@ expectType(findUpSync(() => 'unicorn.png', {allowSymlinks: t expectType(findUpSync(() => 'unicorn.png', {allowSymlinks: false})); expectType(findUpSync(() => 'unicorn.png', {type: 'file'})); expectType(findUpSync(() => 'unicorn.png', {type: 'directory'})); +expectType(findUpSync(() => 'unicorn.png', {stopAt: 'foo'})); expectType(findUpSync(() => undefined)); expectType(findUpSync(() => undefined, {cwd: ''})); expectType(findUpSync(() => undefined, {allowSymlinks: true})); expectType(findUpSync(() => undefined, {allowSymlinks: false})); expectType(findUpSync(() => undefined, {type: 'file'})); expectType(findUpSync(() => undefined, {type: 'directory'})); +expectType(findUpSync(() => undefined, {stopAt: 'foo'})); expectType(findUpSync((): typeof findUpStop => findUpStop)); expectType(findUpSync((): typeof findUpStop => findUpStop, {cwd: ''})); expectType(findUpSync((): typeof findUpStop => findUpStop, {type: 'file'})); expectType(findUpSync((): typeof findUpStop => findUpStop, {type: 'directory'})); +expectType(findUpSync((): typeof findUpStop => findUpStop, {stopAt: 'foo'})); expectType>(pathExists('unicorn.png')); expectType(pathExistsSync('unicorn.png')); diff --git a/readme.md b/readme.md index 6c766ea..797f806 100644 --- a/readme.md +++ b/readme.md @@ -100,6 +100,13 @@ Default: `true` Allow symbolic links to match if they point to the chosen path type. +##### stopAt + +Type: `string`\ +Default: `path.parse(cwd).root` + +The path to the directory to stop the search before reaching root if there were no matches before the `stopAt` directory. + ### pathExists(path) Returns a `Promise` of whether the path exists. diff --git a/test.js b/test.js index 75be95d..f9d3245 100644 --- a/test.js +++ b/test.js @@ -40,6 +40,7 @@ absolute.fixtureDirectory = path.join( ); absolute.baz = path.join(absolute.fixtureDirectory, name.baz); absolute.qux = path.join(absolute.fixtureDirectory, name.qux); +absolute.fooDir = path.join(absolute.fixtureDirectory, 'foo'); absolute.barDir = path.join(absolute.fixtureDirectory, 'foo', 'bar'); absolute.fileLink = path.join(absolute.fixtureDirectory, name.fileLink); absolute.directoryLink = path.join(absolute.fixtureDirectory, name.directoryLink); @@ -210,6 +211,42 @@ test('sync (cousin file, custom cwd)', t => { t.is(foundPath, absolute.baz); }); +test('async (cousin file, custom cwd with stopAt)', async t => { + const foundPath = await findUp(name.baz, { + cwd: relative.barDir, + stopAt: absolute.fooDir, + }); + + t.is(foundPath, undefined); +}); + +test('sync (cousin file, custom cwd with stopAt)', t => { + const foundPath = findUpSync(name.baz, { + cwd: relative.barDir, + stopAt: absolute.fooDir, + }); + + t.is(foundPath, undefined); +}); + +test('async (cousin file, custom cwd, stopAt equal to foundPath)', async t => { + const foundPath = await findUp(name.baz, { + cwd: relative.barDir, + stopAt: absolute.baz, + }); + + t.is(foundPath, absolute.baz); +}); + +test('sync (cousin file, custom cwd, stopAt equal to foundPath)', t => { + const foundPath = findUpSync(name.baz, { + cwd: relative.barDir, + stopAt: absolute.baz, + }); + + t.is(foundPath, absolute.baz); +}); + test('async (nested descendant file)', async t => { const foundPath = await findUp(relative.baz);