-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Lexus Drumgold <[email protected]>
- Loading branch information
1 parent
3ece912
commit 18d449a
Showing
4 changed files
with
254 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
/** | ||
* @file Unit Tests - dirname | ||
* @module pathe/lib/tests/unit/dirname | ||
* @see https://github.com/nodejs/node/blob/main/test/parallel/test-path-dirname.js | ||
*/ | ||
|
||
import sep from '#src/lib/sep' | ||
import { posix, win32 } from 'node:path' | ||
import testSubject from '../dirname' | ||
|
||
describe('unit:lib/dirname', () => { | ||
/** | ||
* Converts Windows-style path separators (`\`) to POSIX (`/`). | ||
* | ||
* @param {string} path - Path to normalize | ||
* @return {string} `path` normalized | ||
*/ | ||
const ensurePosix = (path: string): string => path.replace(/\\/g, sep) | ||
|
||
it('should return directory name of path', () => { | ||
// Arrange | ||
const cases: Parameters<typeof testSubject>[] = [ | ||
[''], | ||
['//a'], | ||
['/a'], | ||
['/a/b'], | ||
['/a/b/'], | ||
['a'], | ||
['foo'], | ||
[posix.sep], | ||
[posix.sep.repeat(4)] | ||
] | ||
|
||
// Act + Expect | ||
cases.forEach(([path]) => { | ||
expect(testSubject(path)).to.equal(posix.dirname(path)) | ||
}) | ||
}) | ||
|
||
describe('windows', () => { | ||
it('should return directory name of path', () => { | ||
// Arrange | ||
const cases: Parameters<typeof testSubject>[] = [ | ||
[''], | ||
['/a'], | ||
['/a/b'], | ||
['/a/b/'], | ||
['\\\\unc\\share'], | ||
['\\\\unc\\share\\foo'], | ||
['\\\\unc\\share\\foo\\'], | ||
['\\\\unc\\share\\foo\\bar'], | ||
['\\\\unc\\share\\foo\\bar\\'], | ||
['\\\\unc\\share\\foo\\bar\\baz'], | ||
['\\foo bar\\baz'], | ||
['\\foo'], | ||
['\\foo\\'], | ||
['\\foo\\bar'], | ||
['\\foo\\bar\\'], | ||
['\\foo\\bar\\baz'], | ||
['a'], | ||
['c:'], | ||
['c:\\'], | ||
['c:\\foo bar\\baz'], | ||
['c:\\foo'], | ||
['c:\\foo\\'], | ||
['c:\\foo\\bar'], | ||
['c:\\foo\\bar\\'], | ||
['c:\\foo\\bar\\baz'], | ||
['c:foo bar\\baz'], | ||
['c:foo'], | ||
['c:foo\\'], | ||
['c:foo\\bar'], | ||
['c:foo\\bar\\'], | ||
['c:foo\\bar\\baz'], | ||
['dir\\file:stream'], | ||
['file:stream'], | ||
['foo'], | ||
[posix.sep], | ||
[posix.sep.repeat(4)], | ||
[win32.sep], | ||
[win32.sep.repeat(2)] | ||
] | ||
|
||
// Act + Expect | ||
cases.forEach(([path]) => { | ||
expect(testSubject(path)).to.equal(ensurePosix(win32.dirname(path))) | ||
}) | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
/** | ||
* @file dirname | ||
* @module pathe/lib/dirname | ||
*/ | ||
|
||
import { DOT } from '#src/internal/constants' | ||
import ensurePosix from '#src/internal/ensure-posix' | ||
import isDrivePath from '#src/internal/is-drive-path' | ||
import isSep from '#src/internal/is-sep' | ||
import isUncPath from '#src/internal/is-unc-path' | ||
import validateString from '#src/internal/validate-string' | ||
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 (path.length === 0) 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 | ||
|
||
/** | ||
* UNC path check. | ||
* | ||
* @const {boolean} unc | ||
*/ | ||
const unc: boolean = isUncPath(path) | ||
|
||
/** | ||
* Leading path separator check. | ||
* | ||
* @const {boolean} root | ||
*/ | ||
const root: boolean = isSep(path[0]) | ||
|
||
/** | ||
* Start index of directory name. | ||
* | ||
* @var {number} start | ||
*/ | ||
let start: number = root ? 1 : 0 | ||
|
||
/** | ||
* 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 | ||
|
||
// adjust start index if path starts with drive letter | ||
if (isDrivePath(path)) { | ||
start = path.length > 2 && isSep(path.charAt(2)) ? 3 : 2 | ||
} | ||
|
||
// adjust start index if path is unc path | ||
if (unc) { | ||
// reset start index of directory name | ||
start = 1 | ||
|
||
// match unc roots | ||
if (isSep(path.charAt(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(path.charAt(j))) j++ | ||
|
||
if (j < path.length && j !== last) { | ||
// set index of non-directory separator match | ||
last = j | ||
|
||
// match 1 or more directory separators | ||
while (j < path.length && isSep(path.charAt(j))) j++ | ||
|
||
if (j < path.length && j !== last) { | ||
// set index of separator match | ||
last = j | ||
|
||
// match 1 or more non-directory separators | ||
while (j < path.length && !isSep(path.charAt(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) end = start = j + 1 | ||
} | ||
} | ||
} | ||
} | ||
|
||
// get end index of directory name | ||
for (let i = path.length - 1; i >= start; --i) { | ||
if (isSep(path.charAt(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 && !unc | ||
? sep | ||
: isDrivePath(path) | ||
? path.length <= 3 | ||
? path | ||
: path.slice(0, start) | ||
: DOT | ||
: root && end === 1 | ||
? sep + sep | ||
: path.slice(0, end) | ||
} | ||
|
||
export default dirname |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters