-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
dirname.ts
141 lines (118 loc) · 3.45 KB
/
dirname.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/**
* @file dirname
* @module pathe/lib/dirname
*/
import ensurePosix from '#src/internal/ensure-posix'
import isDrivePath from '#src/internal/is-drive-path'
import isSep from '#src/internal/is-sep'
import validateString from '#src/internal/validate-string'
import { DOT, at, isEmptyString } from '@flex-development/tutils'
import isAbsolute from './is-absolute'
import sep from './sep'
/**
* Returns the directory name of a `path`, similar to the Unix `dirname`
* command.
*
* Trailing [directory separators][1] are ignored.
*
* [1]: https://nodejs.org/api/path.html#pathsep
*
* @param {string} path - Path to evaluate
* @return {string} Directory name of `path`
* @throws {TypeError} If `path` is not a string
*/
const dirname = (path: string): string => {
validateString(path, 'path')
// exit early if path is empty string
if (isEmptyString(path)) return DOT
// ensure path meets posix standards
path = ensurePosix(path)
// exit early if path length equals 1
if (path.length === 1) return isSep(path) ? path : DOT
/**
* Index to begin searching for directory name.
*
* @var {number} offset
*/
let offset: number = 0
/**
* End index of {@linkcode path} root.
*
* @var {number} root_end
*/
let root_end: number = -1
// adjust offset if path is drive path
if (isDrivePath(path)) {
offset = root_end = path.length > 2 && isAbsolute(path) ? 3 : 2
}
// adjust offset if path is absolute
if (isSep(at(path, 0))) {
root_end = offset = 1
// try adjusting offset if path is possible unc path
if (isSep(at(path, 1))) {
/**
* Current position in {@linkcode path}.
*
* @var {number} j
*/
let j: number = 2
/**
* Last visited position in {@linkcode path}.
*
* @var {number} last
*/
let last: number = j
// match 1 or more non-directory separators
while (j < path.length && !isSep(at(path, j))) j++
if (j < path.length && j !== last) {
// set last visited position to index of directory separator
last = j
// match 1 or more directory separators
while (j < path.length && isSep(at(path, j))) j++
if (j < path.length && j !== last) {
// set last visited position to index of non-directory separator
last = j
// match 1 or more non-directory separators
while (j < path.length && !isSep(at(path, j))) j++
// matched unc root
if (j === path.length) return path
// matched unc root with leftovers
// offset by 1 to include the separator after the root so that it is
// treated as a "normal root" on top of a unc root
if (j !== last) root_end = offset = j + 1
}
}
}
}
/**
* End index of directory name.
*
* @var {number} end
*/
let end: number = -1
/**
* Directory separator match check.
*
* @var {boolean} sep_match
*/
let sep_match: boolean = true
// get end index of directory name
for (let i = path.length - 1; i >= offset; --i) {
if (isSep(at(path, i))) {
// set end index of directory name
if (!sep_match) {
end = i
break
}
} else {
// non-directory separator was encountered
sep_match = false
}
}
return end === -1 && root_end === -1
? DOT
: isSep(at(path, 0)) && end === 1
? sep.repeat(2)
: path.slice(0, end === -1 ? root_end : end)
}
export default dirname