Error
an error will be thrown if there isn't any common ancestral directory between the two provided paths.
import { assertEquals, assertThrows } from "jsr:@std/assert"
// aliasing our functions for brevity
const eq = assertEquals, err = assertThrows, fn = relativePath
eq(fn(
"././hello/world/a/b/c/d/g/../e.txt",
"././hello/world/a/b/x/y/w/../z/",
), "../../x/y/z/")
eq(fn(
"././hello/world/a/b/c/d/g/../e.txt",
"././hello/world/a/b/x/y/w/../z/e.md",
), "../../x/y/z/e.md")
eq(fn(
".\\./hello\\world\\a/b\\c/d/g/../",
"././hello/world/a/b/x/y/w/../z/e.md",
), "../../x/y/z/e.md")
eq(fn(
"././hello/world/a/b/c/d/",
"././hello/world/a/b/x/y/w/../z/e.md",
), "../../x/y/z/e.md")
eq(fn(
"././hello/world/a/b/c/d/g/../",
"././hello/world/a/b/x/y/w/../z/e.md",
), "../../x/y/z/e.md")
eq(fn(
"././hello/world/a/b/c/d/",
"././hello/world/a/b/x/y/w/../z/",
), "../../x/y/z/")
eq(fn(
"./././e.txt",
"./e.md",
), "./e.md")
eq(fn(
"/e.txt",
"/e.md",
), "./e.md")
eq(fn(
"C:/e.txt",
"C:/e.md",
), "./e.md")
eq(fn(
"././hello/world/a/b/c/d/g/../e.txt",
"././hello/world/a/k/../b/q/../c/d/e.md",
), "./e.md")
eq(fn(
"./",
"./",
), "./")
eq(fn(
"/",
"/",
), "./")
// there is no common ancestral root between the two paths (since one is absolute, while the other is relative)
err(() => fn(
"/e.txt",
"./e.md",
))
// there is no common ancestral root between the two paths
err(() => fn(
"C:/e.txt",
"D:/e.md",
))
// there is no common ancestral root between the two paths
err(() => fn(
"http://e.txt",
"./e.md",
))
// there is no common ancestral root between the two paths
err(() => fn(
"file:///C:/e.txt",
"C:/e.md",
))
find the path
to_path
, relative tofrom_path
.TODO: the claim below is wrong, because
joinSlash
cannot do "./" traversal correctly. for instancejoinSlash("a/b/c.txt", "./") === "a/b/c.txt/"
, but you'd expect it to be"a/b/"
if we had correctly resolved the path. which is why the output from this function will not work withjoinSlash
, but it will work with resolveAsUrl orURL.parse
(when you providefrom_path
as the base path, andfrom_path
is NOT a relative path, otherwise theURL
constructor will fail).if we call the resultrel_path
, then joining thefrom_path
withrel_path
and normalizing it should give you backto_path
.note that both
from_path
andto_path
must have a common root folder in their path strings. for instance, if both paths begin with a relative segment"./"
, then it will be assumed that both paths are referring to the same common root ancestral directory. however, if for instance,from_path
begins with a"C:/"
segment, whileto_path
begins with either"./"
or"D:/"
orhttp://
segment, then this function will fail, as it will not be possible for it to navigate/transcend from one point of reference to a completely different point of reference.so, to be safe, wherever you are certain that both paths are of a certain common type: before passing them here, you should either apply the ensureStartDotSlash function for relative paths, or apply the ensureStartSlash for absolute local paths, or write a custom "ensure" function for your situation. (for example, you could write an "ensureHttp" function that ensures that your path begins with
"http"
).