rvx
Version:
A signal based rendering library
82 lines (78 loc) • 2.26 kB
text/typescript
/**
* Normalize a path:
* + Empty paths are represented as an empty string.
* + Non-empty paths start with a slash.
*
* @param path The path to normalize.
* @param preserveDir True to keep trailing slashes in non-empty paths. Default is `true`.
* @returns The normalized path.
*/
export function normalize(path: string, preserveDir = true): string {
if (path === "/" || path === "") {
return "";
}
if (!preserveDir && path.endsWith("/")) {
path = path.slice(0, path.length - 1);
}
if (path.startsWith("/")) {
return path;
}
return "/" + path;
}
/**
* Join two paths.
*
* Note, that this dosn't handle empty, ".." or "." parts.
*
* @param parent The parent path.
* @param child The child path.
* @param preserveDir True to keep trailing slashes. Default is `true`.
* @returns A {@link normalize normalized} path.
*/
export function join(parent: string, child: string, preserveDir = true): string {
child = normalize(child, preserveDir);
parent = normalize(parent, child === "" ? preserveDir : false);
return parent + child;
}
/**
* Get a normalized relative path.
*
* Note, that this dosn't handle empty, ".." or "." parts, but inserts ".." parts if necessary.
*
* @param from The path from which the relative path starts.
* @param to The path to which the relative path points.
* @param preserveDir True to keep trailing slashes from the `to` path. Trailing slashes from the `from` path are always discarded. Default is `true`.
* @returns A {@link normalize normalized} path.
*/
export function relative(from: string, to: string, preserveDir = true): string {
const base = normalize(from, false);
to = normalize(to, preserveDir);
if (base.length === 0) {
return to;
}
let basePos = 0;
for (;;) {
const sep = base.indexOf("/", basePos + 1);
const end = sep < 0 ? base.length : sep;
const part = base.slice(basePos, end);
if (to === part || (to.startsWith(part, basePos) && to[basePos + part.length] === "/")) {
basePos = end;
} else {
break;
}
if (sep < 0) {
break;
}
}
let back = 0;
for (let i = basePos; i < base.length; i++) {
if (base[i] === "/") {
back++;
}
}
to = to.slice(basePos);
if (back === 0 && to === "/") {
return "";
}
return "/..".repeat(back) + to;
}