From c874e7e9378a5ba895870e9680484fb4af5c6e93 Mon Sep 17 00:00:00 2001 From: Jacky Zhao Date: Sat, 19 Aug 2023 15:52:25 -0700 Subject: [PATCH] base path refactor to better support subpath hosting --- content/advanced/paths.md | 32 ++-- content/hosting.md | 2 +- index.d.ts | 2 +- package.json | 2 +- quartz/components/Backlinks.tsx | 6 +- quartz/components/Head.tsx | 5 +- quartz/components/PageList.tsx | 8 +- quartz/components/PageTitle.tsx | 5 +- quartz/components/TagList.tsx | 5 +- quartz/components/pages/FolderContent.tsx | 6 +- quartz/components/pages/TagContent.tsx | 4 +- quartz/components/renderPage.tsx | 19 +- quartz/components/scripts/graph.inline.ts | 34 ++-- quartz/components/scripts/search.inline.ts | 10 +- quartz/components/scripts/spa.inline.ts | 8 +- quartz/plugins/emitters/aliases.ts | 17 +- quartz/plugins/emitters/componentResources.ts | 8 +- quartz/plugins/emitters/contentIndex.ts | 30 ++- quartz/plugins/emitters/contentPage.tsx | 6 +- quartz/plugins/emitters/folderPage.tsx | 20 +- quartz/plugins/emitters/tagPage.tsx | 12 +- quartz/plugins/index.ts | 4 +- quartz/plugins/transformers/links.ts | 26 ++- quartz/plugins/transformers/ofm.ts | 5 +- quartz/plugins/types.ts | 4 +- quartz/util/ctx.ts | 4 +- quartz/util/path.test.ts | 179 +++++++----------- quartz/util/path.ts | 179 ++++++------------ quartz/worker.ts | 4 +- 29 files changed, 257 insertions(+), 389 deletions(-) diff --git a/content/advanced/paths.md b/content/advanced/paths.md index 68fc181b4..9455b9819 100644 --- a/content/advanced/paths.md +++ b/content/advanced/paths.md @@ -4,7 +4,7 @@ title: Paths in Quartz 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. @@ -12,13 +12,13 @@ Luckily, we can mimic nominal typing using [brands](https://www.typescriptlang.o ```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. @@ -29,27 +29,23 @@ The following diagram draws the relationships between all the path sources, nomi ```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`. diff --git a/content/hosting.md b/content/hosting.md index b24762700..d6ccd0bd0 100644 --- a/content/hosting.md +++ b/content/hosting.md @@ -17,7 +17,7 @@ However, if you'd like to publish your site to the world, you need a way to host | Configuration option | Value | | ---------------------- | ------------------ | -| Production branch | `v4` | +| Production branch | `v4` | | Framework preset | `None` | | Build command | `npx quartz build` | | Build output directory | `public` | diff --git a/index.d.ts b/index.d.ts index 4a93f1648..aec536d25 100644 --- a/index.d.ts +++ b/index.d.ts @@ -5,7 +5,7 @@ declare module "*.scss" { // dom custom event interface CustomEventMap { - nav: CustomEvent<{ url: CanonicalSlug }> + nav: CustomEvent<{ url: FullSlug }> } declare const fetchData: Promise diff --git a/package.json b/package.json index 17e46b06f..08ab2d606 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "@jackyzha0/quartz", "description": "🌱 publish your digital garden and notes as a website", "private": true, - "version": "4.0.7", + "version": "4.0.8", "type": "module", "author": "jackyzha0 ", "license": "MIT", diff --git a/quartz/components/Backlinks.tsx b/quartz/components/Backlinks.tsx index 8cf3afa2f..e88966b1c 100644 --- a/quartz/components/Backlinks.tsx +++ b/quartz/components/Backlinks.tsx @@ -1,9 +1,9 @@ import { QuartzComponentConstructor, QuartzComponentProps } from "./types" import style from "./styles/backlinks.scss" -import { canonicalizeServer, resolveRelative } from "../util/path" +import { resolveRelative, simplifySlug } from "../util/path" function Backlinks({ fileData, allFiles }: QuartzComponentProps) { - const slug = canonicalizeServer(fileData.slug!) + const slug = simplifySlug(fileData.slug!) const backlinkFiles = allFiles.filter((file) => file.links?.includes(slug)) return (