normalize a path by reducing and removing redundant dot-slash ("./" and "../") path navigators from a path.

if you provide the optional config with the keepRelative set to false, then in the output, there will no be leading dot-slashes ("./"). read more about the option here: NormalizePathConfig.keepRelative. but note that irrespective of what you set this option to be, leading leading dotdot-slashes ("../") and leading slashes ("/") will not be trimmed.

even though config should be of NormalizePathConfig type, it also accepts number so that the function's signature becomes compatible with the Array.prototype.map method, however, unless you pass the correct config object type, only the default action will be taken.

[!warning] you MUST provide a posix path (i.e. use "/" for dir-separator). there will not be any implicit conversion of windows "\" dir-separator.

import { assertEquals } from "jsr:@std/assert"

// aliasing our functions for brevity
const eq = assertEquals, fn = normalizePosixPath
// aliasing the config for disabling the preservation of leading "./"
const remove_rel: NormalizePathConfig = { keepRelative: false }

eq(fn("../a/./b/../././/c.txt"), "../a//c.txt")
eq(fn("./././a/b/.././././//c.txt"), "./a///c.txt")
eq(fn("./././a/b/.././././//c.txt", remove_rel), "a///c.txt")
eq(fn("/a/b/.././././//c.txt"), "/a///c.txt")
eq(fn("///a/b/.././././//c.txt"), "///a///c.txt")
eq(fn("///a/b/.././.././//c.txt"), "/////c.txt")
eq(fn("file:///./././a/b/.././././c.txt"), "file:///a/c.txt")
eq(fn(""), "")
eq(fn("./"), "./")
eq(fn("../"), "../")
eq(fn("./././././"), "./")
eq(fn(".././././"), "../")
eq(fn("./././.././././"), "../")
eq(fn("./././.././.././"), "../../")
eq(fn("./", remove_rel), "")
eq(fn("./././././", remove_rel), "")
eq(fn("./././.././././", remove_rel), "../")
eq(fn("./././.././.././", remove_rel), "../../")