| | |
| | | |
| | | Paths are pretty complex to reason about because, especially for a static site generator, they can come from so many places. |
| | | |
| | | The current browser URL? Technically a path. A full file path to a piece of content? Also a path. What about a slug for a piece of content? Yet another path. |
| | | A full file path to a piece of content? Also a path. What about a slug for a piece of content? Yet another path. |
| | | |
| | | It would be silly to type these all as `string` and call it a day as it's pretty common to accidentally mistake one type of path for another. Unfortunately, TypeScript does not have [nominal types](https://en.wikipedia.org/wiki/Nominal_type_system) for type aliases meaning even if you made custom types of a server-side slug or a client-slug slug, you can still accidentally assign one to another and TypeScript wouldn't catch it. |
| | | |
| | |
| | | |
| | | ```typescript |
| | | // instead of |
| | | type ClientSlug = string |
| | | type FullSlug = string |
| | | |
| | | // we do |
| | | type ClientSlug = string & { __brand: "client" } |
| | | type FullSlug = string & { __brand: "full" } |
| | | |
| | | // that way, the following will fail typechecking |
| | | const slug: ClientSlug = "some random slug" |
| | | const slug: FullSlug = "some random string" |
| | | ``` |
| | | |
| | | While this prevents most typing mistakes _within_ our nominal typing system (e.g. mistaking a server slug for a client slug), it doesn't prevent us from _accidentally_ mistaking a string for a client slug when we forcibly cast it. |
| | |
| | | |
| | | ```mermaid |
| | | graph LR |
| | | Browser{{Browser}} --> Window{{Window}} & LinkElement{{Link Element}} |
| | | Window --"getCanonicalSlug()"--> Canonical[Canonical Slug] |
| | | Window --"getClientSlug()"--> Client[Client Slug] |
| | | Browser{{Browser}} --> Window{{Body}} & LinkElement{{Link Element}} |
| | | Window --"getFullSlug()"--> FullSlug[Full Slug] |
| | | LinkElement --".href"--> Relative[Relative URL] |
| | | Client --"canonicalizeClient()"--> Canonical |
| | | Canonical --"pathToRoot()"--> Relative |
| | | Canonical --"resolveRelative()" --> Relative |
| | | FullSlug --"simplifySlug()" --> SimpleSlug[Simple Slug] |
| | | SimpleSlug --"pathToRoot()"--> Relative |
| | | SimpleSlug --"resolveRelative()" --> Relative |
| | | MD{{Markdown File}} --> FilePath{{File Path}} & Links[Markdown links] |
| | | Links --"transformLink()"--> Relative |
| | | FilePath --"slugifyFilePath()"--> Server[Server Slug] |
| | | Server --> HTML["HTML File"] |
| | | Server --"canonicalizeServer()"--> Canonical |
| | | style Canonical stroke-width:4px |
| | | FilePath --"slugifyFilePath()"--> FullSlug[Full Slug] |
| | | style FullSlug stroke-width:4px |
| | | ``` |
| | | |
| | | Here are the main types of slugs with a rough description of each type of path: |
| | | |
| | | - `ClientSlug`: client-side slug, usually obtained through `window.location`. Contains the protocol (i.e. starts with `https://`) |
| | | - `CanonicalSlug`: should be used whenever you need to refer to the location of a file/note. Shouldn't be a relative path and shouldn't have leading or trailing slashes `/` either. Also shouldn't have `/index` as an ending or a file extension. |
| | | - `RelativeURL`: must start with `.` or `..` to indicate it's a relative URL. Shouldn't have `/index` as an ending or a file extension. |
| | | - `ServerSlug`: cannot be relative and may not have leading or trailing slashes. |
| | | - `FilePath`: a real file path to a file on disk. Cannot be relative and must have a file extension. |
| | | - `FullSlug`: cannot be relative and may not have leading or trailing slashes. It can have `index` as it's last segment. Use this wherever possible is it's the most 'general' interpretation of a slug. |
| | | - `SimpleSlug`: cannot be relative and shouldn't have `/index` as an ending or a file extension. It _can_ however have a trailing slash to indicate a folder path. |
| | | - `RelativeURL`: must start with `.` or `..` to indicate it's a relative URL. Shouldn't have `/index` as an ending or a file extension but can contain a trailing slash. |
| | | |
| | | To get a clearer picture of how these relate to each other, take a look at the path tests in `quartz/path.test.ts`. |