#path

Manipulation of /-separated paths as text.

Nothing here touches the filesystem. A path is split, joined, and simplified by its text alone, so normalize cannot tell a directory from a symbolic link. fs is the module that reads the filesystem.

An extension includes its leading dot: extname "a.txt" is ".txt". A name that starts with a dot and has no other dot, such as .bashrc, has no extension.

#Components

#dirname

dirname : String -> String
dirname path

The directory part of a path: everything before the last /.

"." when the path has no /. A trailing / is itself the last /, so it is stripped without also dropping the segment before it: dirname "a/" is "a", not ".". Apply normalize first to collapse a trailing slash when that segment should be dropped as well.

> dirname "a/b/c.txt"
"a/b"
> dirname "c.txt"
"."

#basename

basename : String -> String
basename path

The last component of a path: everything after the last /.

The whole path when it has no /, and "" when it ends in /. Apply normalize first to ignore a trailing slash.

> basename "a/b/c.txt"
"c.txt"
> basename "a/b/"
""

#extname

extname : String -> String
extname path

The extension of the last component, with its dot.

"" when there is none, including for a name that starts with its only dot.

> extname "a/b/c.txt"
".txt"
> extname ".bashrc"
""

#stem

stem : String -> String
stem path

The last component without its extension.

> stem "a/b/c.txt"
"c"
> stem ".bashrc"
".bashrc"

#hasExtension

hasExtension : String -> String -> Bool
hasExtension ext path

Whether the last component has the extension ext.

The leading dot on ext is optional.

> hasExtension "txt" "a/b.txt"
True
> hasExtension ".md" "a/b.txt"
False

#withExtension

withExtension : String -> String -> String
withExtension ext path

The path with the last component's extension replaced by ext.

The leading dot on ext is optional. When the path has no extension, ext is appended.

> withExtension ".md" "a/b.txt"
"a/b.md"
> withExtension "md" "a/b"
"a/b.md"

#Joining and splitting

#joinPath

joinPath : String -> String -> String
joinPath a b

Two path segments joined with a single /.

Slashes already at the boundary are collapsed, and an empty segment is skipped.

> joinPath "a/b" "c.txt"
"a/b/c.txt"
> joinPath "a/b/" "/c.txt"
"a/b/c.txt"

#joinAll

joinAll : List String -> String

The segments joined in order with joinPath.

"" for the empty list.

> joinAll ["a", "b", "c.txt"]
"a/b/c.txt"

#segments

segments : String -> List String
segments path

The non-empty components of a path, split on /.

. and .. are kept; apply normalize first to resolve them.

> segments "a/b/c.txt"
["a", "b", "c.txt"]
> segments "/a//b/"
["a", "b"]

#Predicates

#isAbsolute

isAbsolute : String -> Bool
isAbsolute path

Whether the path starts with /.

> isAbsolute "/a/b"
True
> isAbsolute "a/b"
False

#stripPrefix

stripPrefix : String -> String -> Option String
stripPrefix prefix path

The path with prefix removed from its front, or None when the path does not start with prefix at a component boundary.

The empty prefix always matches.

> stripPrefix "a/b" "a/b/c.txt"
Some "c.txt"
> stripPrefix "a/x" "a/b/c.txt"
None

#Normalization

#normalize

normalize : String -> String
normalize path

The path simplified by its text alone.

Repeated / are collapsed, . segments are dropped, a .. cancels the segment before it, and a trailing / is dropped. A leading .. on a relative path is kept, and a .. above the root of an absolute path is dropped. The empty path becomes ".".

> normalize "a//b/./c/../d"
"a/b/d"
> normalize "../a/../../b"
"../../b"