Skip to content

Commit

Permalink
feat!: improve resolving (#4)
Browse files Browse the repository at this point in the history
Co-authored-by: Pooya Parsa <pyapar@gmail.com>
  • Loading branch information
danielroe and pi0 committed Oct 27, 2021
1 parent 9a8dbe0 commit da4567f
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 117 deletions.
38 changes: 19 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,22 @@ import { writePackageJSON } from 'pkg-types'
await writePackageJSON('path/to/package.json', pkg)
```

### `findNearestPackageJSON`
### `resolvePackageJSON`

```js
import { findNearestPackageJSON } from 'pkg-types'
const filename = await findNearestPackageJSON()
import { resolvePackageJSON } from 'pkg-types'
const filename = await resolvePackageJSON()
// or
const packageJson = await findNearestPackageJSON('/fully/resolved/path/to/folder')
const packageJson = await resolvePackageJSON('/fully/resolved/path/to/folder')
```

### `readNearestPackageJSON`
### `readPackageJSON`

```js
import { readNearestPackageJSON } from 'pkg-types'
const filename = await readNearestPackageJSON()
import { readPackageJSON } from 'pkg-types'
const filename = await readPackageJSON()
// or
const packageJson = await readNearestPackageJSON('/fully/resolved/path/to/folder')
const packageJson = await readPackageJSON('/fully/resolved/path/to/folder')
```

### `readTSConfig`
Expand All @@ -68,29 +68,29 @@ import { writeTSConfig } from 'pkg-types'
await writeTSConfig('path/to/tsconfig.json', tsconfig)
```

### `findNearestTSConfig`
### `resolveTSConfig`

```js
import { findNearestTSConfig } from 'pkg-types'
const filename = await findNearestTSConfig()
import { resolveTSConfig } from 'pkg-types'
const filename = await resolveTSConfig()
// or
const tsconfig = await findNearestTSConfig('/fully/resolved/path/to/folder')
const tsconfig = await resolveTSConfig('/fully/resolved/path/to/folder')
```

### `readNearestTSConfig`
### `readTSConfig`

```js
import { readNearestTSConfig } from 'pkg-types'
const filename = await readNearestTSConfig()
import { readTSConfig } from 'pkg-types'
const filename = await readTSConfig()
// or
const tsconfig = await readNearestTSConfig('/fully/resolved/path/to/folder')
const tsconfig = await readTSConfig('/fully/resolved/path/to/folder')
```

### `findNearestFile`
### `resolveFile`

```js
import { findNearestFile } from 'pkg-types'
const filename = await findNearestFile('README.md', {
import { resolveFile } from 'pkg-types'
const filename = await resolveFile('README.md', {
startingFrom: id,
rootPattern: /^node_modules$/,
matcher: filename => filename.endsWith('.md'),
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
},
"dependencies": {
"jsonc-parser": "^3.0.0",
"mlly": "^0.3.6",
"pathe": "^0.2.0"
},
"devDependencies": {
Expand Down
25 changes: 20 additions & 5 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import * as jsonc from 'jsonc-parser'
import { promises as fsp } from 'fs'
import { findNearestFile } from './utils'
import { ResolveOptions, resolvePath } from 'mlly'
import type { PackageJson, TSConfig } from './types'
import { isAbsolute } from 'pathe'

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

export function definePackageJSON(pkg: PackageJson): PackageJson {
return pkg
Expand All @@ -13,20 +16,32 @@ export function defineTSConfig(tsconfig: TSConfig): TSConfig {
return tsconfig
}

export async function readPackageJSON(path: string): Promise<PackageJson> {
const blob = await fsp.readFile(path, 'utf-8')
export async function readPackageJSON(id: string, opts: ResolveOptions = {}): Promise<PackageJson> {
const resolvedPath = await resolvePackageJSON(id, opts)
const blob = await fsp.readFile(resolvedPath, 'utf-8')
return JSON.parse(blob) as PackageJson
}

export async function writePackageJSON(path: string, pkg: PackageJson): Promise<void> {
await fsp.writeFile(path, JSON.stringify(pkg, null, 2))
}

export async function readTSConfig(path: string): Promise<TSConfig> {
const blob = await fsp.readFile(path, 'utf-8')
export async function readTSConfig(id: string, opts: ResolveOptions = {}): Promise<TSConfig> {
const resolvedPath = await resolveTSConfig(id, opts)
const blob = await fsp.readFile(resolvedPath, 'utf-8')
return jsonc.parse(blob) as TSConfig
}

export async function writeTSConfig(path: string, tsconfig: TSConfig): Promise<void> {
await fsp.writeFile(path, JSON.stringify(tsconfig, null, 2))
}

export async function resolvePackageJSON (id: string = process.cwd(), opts: ResolveOptions = {}): Promise<string> {
const resolvedPath = isAbsolute(id) ? id : await resolvePath(id, opts)
return findNearestFile('package.json', { startingFrom: resolvedPath })
}

export async function resolveTSConfig (id: string = process.cwd(), opts: ResolveOptions = {}): Promise<string> {
const resolvedPath = isAbsolute(id) ? id : await resolvePath(id, opts)
return findNearestFile('tsconfig.json', { startingFrom: resolvedPath })
}
29 changes: 2 additions & 27 deletions src/fs.ts → src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { statSync } from 'fs'
import { join, resolve } from 'pathe'
import { PackageJson, readPackageJSON, readTSConfig, TSConfig } from '.'

export interface FindNearestFileOptions {
/**
Expand Down Expand Up @@ -33,7 +32,7 @@ const defaultFindOptions: Required<FindNearestFileOptions> = {
}
}

export async function findNearestFile (filename: string, _options: FindNearestFileOptions = {}) {
export async function findNearestFile (filename: string, _options: FindNearestFileOptions = {}): Promise<string> {
const options = { ...defaultFindOptions, ..._options }
const basePath = resolve(options.startingFrom)
const leadingSlash = basePath[0] === '/'
Expand All @@ -53,30 +52,6 @@ export async function findNearestFile (filename: string, _options: FindNearestFi
if (await options.test(filePath)) { return filePath }
}

return null
}

export async function findNearestPackageJSON (id: string = process.cwd()): Promise<string | null> {
return findNearestFile('package.json', { startingFrom: id })
}

export async function findNearestTSConfig (id: string = process.cwd()): Promise<string | null> {
return findNearestFile('tsconfig.json', { startingFrom: id })
}

export async function readNearestPackageJSON (id?: string): Promise<PackageJson | null> {
const filePath = await findNearestPackageJSON(id)

if (!filePath) { return null }

return readPackageJSON(filePath)
}

export async function readNearestTSConfig (id?: string): Promise<TSConfig | null> {
const filePath = await findNearestTSConfig(id)

if (!filePath) { return null }

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

3 changes: 2 additions & 1 deletion test/fixture/package.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"name": "foo"
"name": "foo",
"version": "1.0.0"
}
57 changes: 0 additions & 57 deletions test/fs.test.ts

This file was deleted.

52 changes: 44 additions & 8 deletions test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,77 @@ import { expect } from 'chai'
import { expectTypeOf } from 'expect-type'
import {
readPackageJSON,
writePackageJSON,
readTSConfig,
resolveTSConfig,
resolvePackageJSON,
writePackageJSON,
writeTSConfig,
TSConfig
TSConfig,
} from '../src'


const fixtureDir = resolve(dirname(fileURLToPath(import.meta.url)), 'fixture')

const rFixture = (...p: string[]) => resolve(fixtureDir, ...p)

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>) {
it('finds a package.json in root directory', async () => {
const pkgPath = await resolveFn(rFixture('.'))
expect(pkgPath).to.equal(rFixture(filename))
})
it('handles non-existent paths', async () => {
const pkgPath = await resolveFn(rFixture('further', 'dir', 'file.json'))
expect(pkgPath).to.equal(rFixture(filename))
})
it('works all the way up the tree', async () => {
(await expectToReject(resolveFn('/a/full/nonexistent/path'))).to.contain('Cannot find matching')
})
it('stops at `node_modules`', async () => {
(await expectToReject(resolveFn(rFixture('further', 'node_modules', 'file.json')))).to.contain('Cannot find matching')
})
it(`finds the working directory`, async () => {
const pkgPath = await resolveFn()
expect(pkgPath).to.equal(rFixture('../..', filename))
})
}

describe('package.json', () => {
testResolve('package.json', resolvePackageJSON)

it('read package.json', async () => {
const pkg = await readPackageJSON(rFixture('package.json'))
expect(pkg.name).to.equal('foo')
})

it('write package.json', async () => {
const pkg = await writePackageJSON(rFixture('package.json.tmp'), { version: '1.0.0' })
expect((await readPackageJSON(rFixture('package.json.tmp'))).version).to.equal('1.0.0')
})

it('correctly reads a version from absolute path', async () => {
expect(await readPackageJSON(rFixture('.')).then(p => p?.version)).to.equal('1.0.0')
})

it('correctly reads a version from package', async () => {
expect(await readPackageJSON('pathe').then(p => p?.version)).to.be.a('string')
})
})

describe('tsconfig.json', () => {
testResolve('tsconfig.json', resolveTSConfig)

it('read tsconfig.json', async () => {
const tsConfig = await readTSConfig(rFixture('tsconfig.json'))
expect(tsConfig.compilerOptions?.target).to.equal('ESNext')
})
it('write tsconfig.json', async () => {
const tsConfig = await writeTSConfig(rFixture('tsconfig.json.tmp'), { include: ['foo'] })
expect((await readTSConfig(rFixture('tsconfig.json.tmp'))).include).to.deep.equal(['foo'])
const tsConfig = await writeTSConfig(rFixture('tsconfig.json.tmp'), { include: ['src'] })
expect((await readTSConfig(rFixture('tsconfig.json.tmp'))).include).to.deep.equal(['src'])
})
})


describe.skip('tsconfig types', () => {
it('strips enums', () => {
const options: TSConfig['compilerOptions'] = {}
expectTypeOf(options.moduleResolution).toEqualTypeOf<any>()
Expand Down
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1910,6 +1910,11 @@ mlly@^0.2.6:
dependencies:
import-meta-resolve "^1.1.1"

mlly@^0.3.6:
version "0.3.6"
resolved "https://registry.yarnpkg.com/mlly/-/mlly-0.3.6.tgz#639867d616569d12a4aa1dff99f18a5e63126941"
integrity sha512-mSEVMwiNEjppuRmhfzWQgzZtC/N2eWiDAhIfTM1U4fO0zJjacJRDnaT//qNlmNbeWQn3fzDSA5UC6jsvAfL4AA==

mocha@^9.1.2:
version "9.1.2"
resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.1.2.tgz#93f53175b0f0dc4014bd2d612218fccfcf3534d3"
Expand Down

0 comments on commit da4567f

Please sign in to comment.