Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: findFarthestFile #32

Merged
merged 4 commits into from
Sep 6, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@
],
"scripts": {
"prepack": "unbuild",
"release": "standard-version && pnpm publish && git push --follow-tags",
"dev": "vitest",
"release": "pnpm test && standard-version && pnpm publish && git push --follow-tags",
"lint": "eslint --ext .ts,.js,.mjs,.cjs .",
"test": "vitest run",
"test": "vitest run --coverage",
"test:types": "tsc --noEmit --module esnext --skipLibCheck --moduleResolution node ./test/*.test.ts"
},
"dependencies": {
Expand Down
4 changes: 2 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { promises as fsp } from 'fs'
import { ResolveOptions as _ResolveOptions, resolvePath } from 'mlly'
import { isAbsolute } from 'pathe'
import { findNearestFile, FindNearestFileOptions } from './utils'
import { FindFileOptions, findNearestFile } from './utils'
import type { PackageJson, TSConfig } from './types'

export * from './types'
export * from './utils'

export type ResolveOptions = _ResolveOptions & FindNearestFileOptions
export type ResolveOptions = _ResolveOptions & FindFileOptions

export function definePackageJSON (pkg: PackageJson): PackageJson {
return pkg
Expand Down
35 changes: 29 additions & 6 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { statSync } from 'fs'
import { join, resolve } from 'pathe'

export interface FindNearestFileOptions {
export interface FindFileOptions {
/**
* The starting directory for the search.
* @default . (same as `process.cwd()`)
Expand All @@ -12,6 +12,10 @@ export interface FindNearestFileOptions {
* @default /^node_modules$/
*/
rootPattern?: RegExp
/**
* If true, search starts from root level descending into subdirectories
*/
reverse?: boolean
/**
* A matcher that can evaluate whether the given path is a valid file (for example,
* by testing whether the file path exists.
Expand All @@ -21,9 +25,13 @@ export interface FindNearestFileOptions {
test?: (filePath: string) => boolean | null | Promise<boolean | null>
}

const defaultFindOptions: Required<FindNearestFileOptions> = {
/** @deprecated */
export type FindNearestFileOptions = FindFileOptions

const defaultFindOptions: Required<FindFileOptions> = {
startingFrom: '.',
rootPattern: /^node_modules$/,
reverse: false,
test: (filePath: string) => {
try {
if (statSync(filePath).isFile()) { return true }
Expand All @@ -32,7 +40,7 @@ const defaultFindOptions: Required<FindNearestFileOptions> = {
}
}

export async function findNearestFile (filename: string, _options: FindNearestFileOptions = {}): Promise<string> {
export async function findFile (filename: string, _options: FindFileOptions = {}): Promise<string> {
const options = { ...defaultFindOptions, ..._options }
const basePath = resolve(options.startingFrom)
const leadingSlash = basePath[0] === '/'
Expand All @@ -47,10 +55,25 @@ export async function findNearestFile (filename: string, _options: FindNearestFi
let root = segments.findIndex(r => r.match(options.rootPattern))
if (root === -1) { root = 0 }

for (let i = segments.length; i > root; i--) {
const filePath = join(...segments.slice(0, i), filename)
if (await options.test(filePath)) { return filePath }
if (!options.reverse) {
for (let i = segments.length; i > root; i--) {
const filePath = join(...segments.slice(0, i), filename)
if (await options.test(filePath)) { return filePath }
}
} else {
for (let i = root + 1; i < segments.length; i++) {
const filePath = join(...segments.slice(0, i), filename)
if (await options.test(filePath)) { return filePath }
}
}

throw new Error(`Cannot find matching ${filename} in ${options.startingFrom} or parent directories`)
}

export function findNearestFile (filename: string, _options: FindFileOptions = {}): Promise<string> {
return findFile(filename, _options)
}

export function findFarthestFile (filename: string, _options: FindFileOptions = {}): Promise<string> {
return findFile(filename, { ..._options, reverse: true })
}
4 changes: 4 additions & 0 deletions test/fixture/sub/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"name": "foo-sub",
"version": "1.0.0"
}
9 changes: 7 additions & 2 deletions test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import {
resolvePackageJSON,
writePackageJSON,
writeTSConfig,
TSConfig
TSConfig,
ResolveOptions
} from '../src'

const fixtureDir = resolve(dirname(fileURLToPath(import.meta.url)), 'fixture')
Expand All @@ -20,11 +21,15 @@ async function expectToReject (p: Promise<any>) {
return expect(await p.then(() => null).catch((err: Error) => err.toString()))
}

function testResolve (filename: string, resolveFn: (id?: string) => Promise<string | null>) {
function testResolve (filename: string, resolveFn: (id?: string, opts?: ResolveOptions) => Promise<string | null>) {
it('finds a package.json in root directory', async () => {
const pkgPath = await resolveFn(rFixture('.'))
expect(pkgPath).to.equal(rFixture(filename))
})
it('finds package.json from root', async () => {
const pkgPath = await resolveFn(rFixture('.'), { reverse: true })
expect(pkgPath).to.equal(rFixture('../..', filename)) // Top level in pkg-types repo
})
it('handles non-existent paths', async () => {
const pkgPath = await resolveFn(rFixture('further', 'dir', 'file.json'))
expect(pkgPath).to.equal(rFixture(filename))
Expand Down