Skip to content

Commit bace4e8

Browse files
committed
feat(lib): createGetCanonicalFileName
Signed-off-by: Lexus Drumgold <[email protected]>
1 parent f7052e6 commit bace4e8

15 files changed

+332
-5
lines changed

README.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ Utilities for working with [`tsconfig`][tsconfig] files
2020
- [Use](#use)
2121
- [API](#api)
2222
- [Types](#types)
23-
- [Interfaces](#interfaces)
2423
- [`tsconfig-types`](#tsconfig-types)
24+
- [Interfaces](#interfaces)
25+
- [Types](#types)
2526
- [Related](#related)
2627
- [Contribute](#contribute)
2728

@@ -81,6 +82,7 @@ import {
8182

8283
This package exports the following identifiers:
8384

85+
- [`createGetCanonicalFileName`](./src/lib/create-get-canonical-file-name.mts)
8486
- [`createModuleResolutionHost`](./src/lib/create-module-resolution-host.mts)
8587
- [`createParseConfigHost`](./src/lib/create-parse-config-host.mts)
8688
- [`isResolvedTsconfig`](./src/lib/is-resolved-tsconfig.mts)
@@ -96,6 +98,11 @@ There is no default export.
9698

9799
This package is fully typed with [TypeScript][].
98100

101+
### `tsconfig-types`
102+
103+
This package re-exports TypeScript definitions from [`tsconfig-types`][tsconfig-types]. This is primarily for the
104+
convenience of TypeScript users who do not hoist packages, but may need to `import` definitions used in this package.
105+
99106
### Interfaces
100107

101108
- [`FileSystem`](./src/interfaces/file-system.mts)
@@ -109,10 +116,9 @@ This package is fully typed with [TypeScript][].
109116
- [`ResolvedTsconfig`](./src/interfaces/options-resolve-path.mts)
110117
- [`TsconfigHost`](./src/interfaces/tsconfig-host.mts)
111118

112-
### `tsconfig-types`
119+
### Types
113120

114-
This package re-exports TypeScript definitions from [`tsconfig-types`][tsconfig-types]. This is primarily for the
115-
convenience of TypeScript users who do not hoist packages, but may need to `import` definitions used in this package.
121+
- [`GetCanonicalFileName`](./src/types/get-canonical-file-name.mts)
116122

117123
## Related
118124

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,11 @@
8080
"tsconfig-utils": "./src/lib/*.mts",
8181
"default": "./dist/lib/*.mjs"
8282
},
83-
"#tests/*": "./__tests__/*.mts"
83+
"#tests/*": "./__tests__/*.mts",
84+
"#types/*": {
85+
"tsconfig-utils": "./src/types/*.mts",
86+
"default": "./dist/types/*.d.mts"
87+
}
8488
},
8589
"module": "./dist/index.mjs",
8690
"types": "./dist/index.d.mts",

src/__snapshots__/index.e2e.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
exports[`e2e:tsconfig-utils > should expose public api 1`] = `
44
[
5+
"createGetCanonicalFileName",
56
"createModuleResolutionHost",
67
"createParseConfigHost",
78
"isResolvedTsconfig",

src/index.mts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@
55

66
export type * from '#interfaces/index'
77
export * from '#lib/index'
8+
export type * from '#types/index'
89
export type * from '@flex-development/tsconfig-types'
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2+
3+
exports[`unit:internal/toPath > should return \`input\` as path ("./tsconfig.json") 1`] = `"./tsconfig.json"`;
4+
5+
exports[`unit:internal/toPath > should return \`input\` as path ("file:///tsconfig.json") 1`] = `"/tsconfig.json"`;
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/**
2+
* @file Unit Tests - toPath
3+
* @module tsconfig-utils/internal/tests/unit/toPath
4+
*/
5+
6+
import testSubject from '#internal/to-path'
7+
import { codes, isNodeError, type NodeError } from '@flex-development/errnode'
8+
import pathe from '@flex-development/pathe'
9+
10+
describe('unit:internal/toPath', () => {
11+
it.each<[URL | string]>([
12+
[new URL('file:///tsconfig.json')],
13+
[pathe.dot + pathe.sep + 'tsconfig.json']
14+
])('should return `input` as path (%j)', input => {
15+
// Act
16+
const result = testSubject(input)
17+
18+
// Expect
19+
expect(result).toMatchSnapshot()
20+
})
21+
22+
it.each<[URL | string]>([
23+
['node:test'],
24+
[new URL('node:test/reporters')]
25+
])('should throw if `input` is not a path or `file:` URL (%j)', input => {
26+
// Arrange
27+
let error!: NodeError
28+
29+
// Act
30+
try {
31+
testSubject(input)
32+
} catch (e: unknown) {
33+
error = e as typeof error
34+
}
35+
36+
// Expect
37+
expect(error).to.satisfy(isNodeError)
38+
expect(error).to.have.property('code', codes.ERR_INVALID_URL_SCHEME)
39+
expect(error).to.have.property('message').match(/of scheme file/)
40+
})
41+
})
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* @file Unit Tests - validateURLString
3+
* @module tsconfig-utils/internal/tests/unit/validateURLString
4+
*/
5+
6+
import testSubject from '#internal/validate-url-string'
7+
import { codes, isNodeError, type NodeError } from '@flex-development/errnode'
8+
9+
describe('unit:internal/validateURLString', () => {
10+
let name: string
11+
12+
beforeAll(() => {
13+
name = 'value'
14+
})
15+
16+
it('should return `true` if `value` is a `URL`', () => {
17+
expect(testSubject(new URL(import.meta.url), name)).to.be.true
18+
})
19+
20+
it('should return `true` if `value` is a string', () => {
21+
expect(testSubject(import.meta.url, name)).to.be.true
22+
})
23+
24+
it('should throw if `value` is not a `URL` or string', () => {
25+
// Arrange
26+
let error!: NodeError
27+
28+
// Act
29+
try {
30+
testSubject(null, name)
31+
} catch (e: unknown) {
32+
error = e as typeof error
33+
}
34+
35+
// Expect
36+
expect(error).to.satisfy(isNodeError)
37+
expect(error).to.have.property('code', codes.ERR_INVALID_ARG_TYPE)
38+
})
39+
})

src/internal/to-path.mts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/**
2+
* @file Internal - toPath
3+
* @module tsconfig-utils/internal/toPath
4+
*/
5+
6+
import validateURLString from '#internal/validate-url-string'
7+
import {
8+
ERR_INVALID_URL_SCHEME,
9+
type ErrInvalidUrlScheme
10+
} from '@flex-development/errnode'
11+
import pathe from '@flex-development/pathe'
12+
13+
export default toPath
14+
15+
/**
16+
* Convert `input` to a path.
17+
*
18+
* > 👉 **Note**: `input` is assumed to be a path if it a string and cannot be
19+
* > parsed to an URL (checked using {@linkcode pathe.isURL}).
20+
*
21+
* @see {@linkcode ErrInvalidUrlScheme}
22+
*
23+
* @internal
24+
*
25+
* @this {void}
26+
*
27+
* @param {URL | string} input
28+
* The {@linkcode URL}, URL string, or path to convert
29+
* @return {string}
30+
* `input` as path
31+
* @throws {ErrInvalidUrlScheme}
32+
* If `input` is not a path or `file:` URL
33+
*/
34+
function toPath(
35+
this: void,
36+
input: URL | string
37+
): string {
38+
validateURLString(input, 'input')
39+
40+
if (typeof input === 'string') {
41+
if (!pathe.isURL(input)) return pathe.toPosix(input)
42+
input = new URL(input)
43+
}
44+
45+
if (input.protocol === 'file:') return pathe.fileURLToPath(input)
46+
throw new ERR_INVALID_URL_SCHEME('file')
47+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* @file Internal - validateURLString
3+
* @module tsconfig-utils/internal/validateURLString
4+
*/
5+
6+
import {
7+
ERR_INVALID_ARG_TYPE,
8+
type ErrInvalidArgType
9+
} from '@flex-development/errnode'
10+
import pathe from '@flex-development/pathe'
11+
12+
/**
13+
* Check if `value` is a {@linkcode URL} object or string.
14+
*
15+
* @see {@linkcode ErrInvalidArgType}
16+
*
17+
* @internal
18+
*
19+
* @param {unknown} value
20+
* Value to check
21+
* @param {string} name
22+
* Name of invalid argument or property
23+
* @return {value is URL | string}
24+
* `true` if `value` is `URL` object or string
25+
* @throws {ErrInvalidArgType}
26+
* If `value` is not `URL` object or string
27+
*/
28+
function validateURLString(
29+
value: unknown,
30+
name: string
31+
): value is URL | string {
32+
if (typeof value === 'string' || pathe.isURL(value)) return true
33+
throw new ERR_INVALID_ARG_TYPE(name, ['URL', 'string'], value)
34+
}
35+
36+
export default validateURLString
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/**
2+
* @file Unit Tests - createGetCanonicalFileName
3+
* @module tsconfig-utils/lib/tests/unit/createGetCanonicalFileName
4+
*/
5+
6+
import toPath from '#internal/to-path'
7+
import testSubject from '#lib/create-get-canonical-file-name'
8+
import pathe from '@flex-development/pathe'
9+
import { lowercase } from '@flex-development/tutils'
10+
11+
describe('unit:lib/createGetCanonicalFileName', () => {
12+
it('should return canonical filename of `id`', () => {
13+
// Arrange
14+
const id: URL = new URL('file:///creatE-geT-canonicaL-filE-namE.mts')
15+
16+
// Act
17+
const result = testSubject()(id)
18+
19+
// Expect
20+
expect(result).to.be.a('string').and.not.satisfy(pathe.isURL)
21+
expect(lowercase(result)).to.eql(lowercase(toPath(id)))
22+
})
23+
})

0 commit comments

Comments
 (0)