mirror of
https://github.com/denoland/std.git
synced 2024-11-22 04:59:05 +00:00
118 lines
3.2 KiB
TypeScript
118 lines
3.2 KiB
TypeScript
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
|
// This module is browser compatible.
|
|
|
|
import { assertArg } from "../_common/dirname.ts";
|
|
import { CHAR_COLON } from "../_common/constants.ts";
|
|
import { stripTrailingSeparators } from "../_common/strip_trailing_separators.ts";
|
|
import {
|
|
isPathSeparator,
|
|
isPosixPathSeparator,
|
|
isWindowsDeviceRoot,
|
|
} from "./_util.ts";
|
|
|
|
/**
|
|
* Return the directory path of a `path`.
|
|
*
|
|
* @example Usage
|
|
* ```ts
|
|
* import { dirname } from "@std/path/windows/dirname";
|
|
* import { assertEquals } from "@std/assert";
|
|
*
|
|
* const dir = dirname("C:\\foo\\bar\\baz.ext");
|
|
* assertEquals(dir, "C:\\foo\\bar");
|
|
* ```
|
|
*
|
|
* Note: If you are working with file URLs,
|
|
* use the new version of `dirname` from `@std/path/windows/unstable-dirname`.
|
|
*
|
|
* @param path The path to get the directory from.
|
|
* @returns The directory path.
|
|
*/
|
|
export function dirname(path: string): string {
|
|
assertArg(path);
|
|
|
|
const len = path.length;
|
|
let rootEnd = -1;
|
|
let end = -1;
|
|
let matchedSlash = true;
|
|
let offset = 0;
|
|
const code = path.charCodeAt(0);
|
|
|
|
// Try to match a root
|
|
if (len > 1) {
|
|
if (isPathSeparator(code)) {
|
|
// Possible UNC root
|
|
|
|
rootEnd = offset = 1;
|
|
|
|
if (isPathSeparator(path.charCodeAt(1))) {
|
|
// Matched double path separator at beginning
|
|
let j = 2;
|
|
let last = j;
|
|
// Match 1 or more non-path separators
|
|
for (; j < len; ++j) {
|
|
if (isPathSeparator(path.charCodeAt(j))) break;
|
|
}
|
|
if (j < len && j !== last) {
|
|
// Matched!
|
|
last = j;
|
|
// Match 1 or more path separators
|
|
for (; j < len; ++j) {
|
|
if (!isPathSeparator(path.charCodeAt(j))) break;
|
|
}
|
|
if (j < len && j !== last) {
|
|
// Matched!
|
|
last = j;
|
|
// Match 1 or more non-path separators
|
|
for (; j < len; ++j) {
|
|
if (isPathSeparator(path.charCodeAt(j))) break;
|
|
}
|
|
if (j === len) {
|
|
// We matched a UNC root only
|
|
return path;
|
|
}
|
|
if (j !== last) {
|
|
// We matched a UNC root with leftovers
|
|
|
|
// Offset by 1 to include the separator after the UNC root to
|
|
// treat it as a "normal root" on top of a (UNC) root
|
|
rootEnd = offset = j + 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (isWindowsDeviceRoot(code)) {
|
|
// Possible device root
|
|
|
|
if (path.charCodeAt(1) === CHAR_COLON) {
|
|
rootEnd = offset = 2;
|
|
if (len > 2) {
|
|
if (isPathSeparator(path.charCodeAt(2))) rootEnd = offset = 3;
|
|
}
|
|
}
|
|
}
|
|
} else if (isPathSeparator(code)) {
|
|
// `path` contains just a path separator, exit early to avoid
|
|
// unnecessary work
|
|
return path;
|
|
}
|
|
|
|
for (let i = len - 1; i >= offset; --i) {
|
|
if (isPathSeparator(path.charCodeAt(i))) {
|
|
if (!matchedSlash) {
|
|
end = i;
|
|
break;
|
|
}
|
|
} else {
|
|
// We saw the first non-path separator
|
|
matchedSlash = false;
|
|
}
|
|
}
|
|
|
|
if (end === -1) {
|
|
if (rootEnd === -1) return ".";
|
|
else end = rootEnd;
|
|
}
|
|
return stripTrailingSeparators(path.slice(0, end), isPosixPathSeparator);
|
|
}
|