// Copyright 2018-2025 the Deno authors. MIT license.
// Copyright the Browserify authors. MIT License.
// Ported from https://github.com/browserify/path-browserify/
// This module is browser compatible.
import { CHAR_DOT, CHAR_FORWARD_SLASH } from "./constants.ts";
// Resolves . and .. elements in a path with directory names
export function normalizeString(
path: string,
allowAboveRoot: boolean,
separator: string,
isPathSeparator: (code: number) => boolean,
): string {
let res = "";
let lastSegmentLength = 0;
let lastSlash = -1;
let dots = 0;
let code: number | undefined;
for (let i = 0; i <= path.length; ++i) {
if (i < path.length) code = path.charCodeAt(i);
else if (isPathSeparator(code!)) break;
else code = CHAR_FORWARD_SLASH;
if (isPathSeparator(code!)) {
if (lastSlash === i - 1 || dots === 1) {
// NOOP
} else if (lastSlash !== i - 1 && dots === 2) {
if (
res.length < 2 ||
lastSegmentLength !== 2 ||
res.charCodeAt(res.length - 1) !== CHAR_DOT ||
res.charCodeAt(res.length - 2) !== CHAR_DOT
) {
if (res.length > 2) {
const lastSlashIndex = res.lastIndexOf(separator);
if (lastSlashIndex === -1) {
res = "";
lastSegmentLength = 0;
} else {
res = res.slice(0, lastSlashIndex);
lastSegmentLength = res.length - 1 - res.lastIndexOf(separator);
}
lastSlash = i;
dots = 0;
continue;
} else if (res.length === 2 || res.length === 1) {
res = "";
lastSegmentLength = 0;
lastSlash = i;
dots = 0;
continue;
}
}
if (allowAboveRoot) {
if (res.length > 0) res += `${separator}..`;
else res = "..";
lastSegmentLength = 2;
}
} else {
if (res.length > 0) res += separator + path.slice(lastSlash + 1, i);
else res = path.slice(lastSlash + 1, i);
lastSegmentLength = i - lastSlash - 1;
}
lastSlash = i;
dots = 0;
} else if (code === CHAR_DOT && dots !== -1) {
++dots;
} else {
dots = -1;
}
}
return res;
}
|