Skip to content

Commit

Permalink
Dev docs
Browse files Browse the repository at this point in the history
  • Loading branch information
fsoikin committed Oct 27, 2024
1 parent c0d1fdf commit 05b5da6
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 13 deletions.
47 changes: 47 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,50 @@ Learn by doing and get your hands dirty!
[f-f]: https://github.com/f-f
[discord]: https://purescript.org/chat
[spago-issues]: https://github.com/purescript/spago/issues

## Working with file paths

File paths are very important in Spago. A very big chunk of Spago does is
shuffling files around and manipulating their paths. Representing them as plain
strings is not enough.

Spago has three different kinds of paths, represented as distinct types:

- `RootPath` can generally be the root of anything, but in practice it usually
points to the root directory of the current workspace. It is constructed in
`Main.purs` close to the entry point and is available in all `ReaderT`
environments as `rootPath :: RootPath`
- `LocalPath` is path of a particular file or directory within the workspace. It
doesn't have to be literally _within_ the workspace directory - e.g. a custom
dependency that lives somewhere on the local file system, - but it's still
_relative_ to the workspace. A `LocalPath` is explicitly broken into two
parts: a `RootPath` and the "local" part relative to the root. This is useful
for printing out workspace-relative paths in user-facing output, while still
retaining the full path for actual file operations. A `LocalPath` can be
constructed by appending to a `RootPath`. Once so constructed, the `LocalPath`
always retains the same root, no matter what subsequent manipulations are done
to it. Therefore, if you have a `LocalPath` value, its root is probably
pointing to the current workspace directory.
- `GlobalPath` is for things that are not related to the current workspace.
Examples include paths to executables, such as `node` and `purs`, and global
directories, such as `registryPath` and `globalCachePath`.

Paths can be appended by using the `</>` operator. It is overloaded for all
three path types and allows to append string segments to them. When appending to
a `RootPath`, the result comes out as `LocalPath`. You cannot produce a new
`RootPath` by appending.

Most code that deals with the workspace operates in `LocalPath` values. Most
code that deals with external and global things operates in `GlobalPath` values.
Lower-level primitives, such as in the `Spago.FS` module, are polymorphic and
can take all three path types as parameters.

For example:

```haskell
rootPath <- Path.mkRootPath =<< Paths.cwd
config <- readConfig (rootPath </> "spago.yaml")
let srcDir = rootPath </> "src"
compileResult <- callCompiler [ srcDir </> "Main.purs", srcDir </> "Lib.purs" ]
FS.writeFile (rootPath </> "result.json") (serialize compipleResult)
```
26 changes: 13 additions & 13 deletions core/src/Path.purs
Original file line number Diff line number Diff line change
Expand Up @@ -69,28 +69,28 @@ class (Show path, Eq path, Ord path) <= IsPath path where
withForwardSlashes :: path -> path

instance IsPath LocalPath where
toGlobal (LocalPath { root: RootPath root, local }) =
toGlobal (LocalPath { root: RootPath root, local }) =
GlobalPath $ Path.concat [ root, local ]
relativeTo path root
| rootPart path == root = path
| otherwise = toGlobal path `relativeTo` root
quote (LocalPath path)
| path.local == "" = "\".\""
| path.local == "" = "\".\""
| otherwise = "\"" <> path.local <> "\""
replaceExtension p r (LocalPath path) =
replaceExtension p r (LocalPath path) =
LocalPath <<< path { local = _ } <$> replaceExtension_ p r path.local
withForwardSlashes (LocalPath path) =
withForwardSlashes (LocalPath path) =
LocalPath { root: withForwardSlashes path.root, local: withForwardSlashes' path.local }

instance IsPath GlobalPath where
toGlobal = identity
relativeTo (GlobalPath path) (RootPath root) =
relativeTo (GlobalPath path) (RootPath root) =
LocalPath { root: RootPath root, local: Path.relative root path }
quote (GlobalPath path) =
quote (GlobalPath path) =
"\"" <> path <> "\""
replaceExtension p r (GlobalPath path) =
replaceExtension p r (GlobalPath path) =
GlobalPath <$> replaceExtension_ p r path
withForwardSlashes (GlobalPath path) =
withForwardSlashes (GlobalPath path) =
GlobalPath $ withForwardSlashes' path

instance IsPath RootPath where
Expand All @@ -100,17 +100,17 @@ instance IsPath RootPath where
replaceExtension p r (RootPath path) = RootPath <$> replaceExtension_ p r path
withForwardSlashes (RootPath path) = RootPath $ withForwardSlashes' path

class AppendPath base path result | base path -> result where
appendPath :: base -> path -> result
instance AppendPath RootPath AdHocFilePath LocalPath where
class AppendPath base result | base -> result where
appendPath :: base -> AdHocFilePath -> result
instance AppendPath RootPath LocalPath where
appendPath root local
| Path.isAbsolute local = global local `relativeTo` root
| otherwise = LocalPath { root, local }
instance AppendPath LocalPath AdHocFilePath LocalPath where
instance AppendPath LocalPath LocalPath where
appendPath (LocalPath { root, local }) path
| Path.isAbsolute path = global path `relativeTo` root
| otherwise = LocalPath { root, local: Path.concat [ local, path ] }
instance AppendPath GlobalPath AdHocFilePath GlobalPath where
instance AppendPath GlobalPath GlobalPath where
appendPath (GlobalPath path) p
| Path.isAbsolute p = GlobalPath p
| otherwise = GlobalPath $ Path.concat [ path, p ]
Expand Down

0 comments on commit 05b5da6

Please sign in to comment.