From 8b53c0802cdd119beebc563d5fc791248391ccb7 Mon Sep 17 00:00:00 2001 From: pooya parsa Date: Tue, 6 Sep 2022 14:30:21 +0200 Subject: [PATCH] feat: `findWorkspaceDir` (#34) --- README.md | 15 +++++++++++++++ src/index.ts | 28 +++++++++++++++++++++++++++- src/utils.ts | 2 +- test/index.test.ts | 12 +++++++++++- 4 files changed, 54 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5b890ac..831175a 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,21 @@ import { resolveLockFile } from 'pkg-types' const lockfile = await resolveLockFile('.') ``` +### `findWorkspaceDir` + +Try to detect workspace dir by in order: + +1. Nearest `.git` directory +2. Farthest lockfile +3. Farthest `package.json` file + +If fails, throws an error. + +```js +import { findWorkspaceDir } from 'pkg-types' +const workspaceDir = await findWorkspaceDir('.') +``` + ## Types **Note:** In order to make types working, you need to install `typescript` as a devDependency. diff --git a/src/index.ts b/src/index.ts index 12e37d9..23c6832 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,8 @@ import { promises as fsp } from 'fs' +import { dirname, resolve } from 'path' import { ResolveOptions as _ResolveOptions, resolvePath } from 'mlly' import { isAbsolute } from 'pathe' -import { FindFileOptions, findNearestFile } from './utils' +import { findFile, FindFileOptions, findNearestFile } from './utils' import type { PackageJson, TSConfig } from './types' export * from './types' @@ -60,3 +61,28 @@ export async function resolveLockfile (id: string = process.cwd(), opts: Resolve } throw new Error('No lockfile found from ' + id) } + +export async function findWorkspaceDir (id: string = process.cwd(), opts: ResolveOptions = {}): Promise { + const resolvedPath = isAbsolute(id) ? id : await resolvePath(id, opts) + const _opts = { startingFrom: resolvedPath, ...opts } + + // Lookup for .git/config + try { + const r = await findNearestFile('.git/config', _opts) + return resolve(r, '../..') + } catch { } + + // Lookdown for lockfile + try { + const r = await resolveLockfile(resolvedPath, { ..._opts, reverse: true }) + return dirname(r) + } catch { } + + // Lookdown for package.json + try { + const r = await findFile(resolvedPath, _opts) + return dirname(r) + } catch { } + + throw new Error('Cannot detect workspace root from ' + id) +} diff --git a/src/utils.ts b/src/utils.ts index 70efce2..468dd3a 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -61,7 +61,7 @@ export async function findFile (filename: string, _options: FindFileOptions = {} if (await options.test(filePath)) { return filePath } } } else { - for (let i = root + 1; i < segments.length; i++) { + for (let i = root + 1; i <= segments.length; i++) { const filePath = join(...segments.slice(0, i), filename) if (await options.test(filePath)) { return filePath } } diff --git a/test/index.test.ts b/test/index.test.ts index b2e2670..7771a8e 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -11,7 +11,8 @@ import { writeTSConfig, TSConfig, ResolveOptions, - resolveLockfile + resolveLockfile, + findWorkspaceDir } from '../src' const fixtureDir = resolve(dirname(fileURLToPath(import.meta.url)), 'fixture') @@ -97,3 +98,12 @@ describe('resolveLockfile', () => { expect(await resolveLockfile(rFixture('.'))).to.equal(rFixture('../..', 'pnpm-lock.yaml')) }) }) + +describe('findWorkspaceDir', () => { + it('works', async () => { + expect(await findWorkspaceDir(rFixture('./sub'))).to.equal(rFixture('../..')) + expect(await findWorkspaceDir(rFixture('.'))).to.equal(rFixture('../..')) + expect(await findWorkspaceDir(rFixture('..'))).to.equal(rFixture('../..')) + expect(await findWorkspaceDir(rFixture('../..'))).to.equal(rFixture('../..')) + }) +})