@oazmi/kitchensink - v0.9.13
    Preparing search index...

    Function relativePath

    • find the path to_path, relative to from_path.

      TODO: the claim below is wrong, because joinSlash cannot do "./" traversal correctly. for instance joinSlash("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 with joinSlash, but it will work with resolveAsUrl or URL.parse (when you provide from_path as the base path, and from_path is NOT a relative path, otherwise the URL constructor will fail).

      if we call the result rel_path, then joining the from_path with rel_path and normalizing it should give you back to_path.

      note that both from_path and to_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, while to_path begins with either "./" or "D:/" or http:// 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").

      Parameters

      • from_path: string
      • to_path: string

      Returns string

      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",
      ))