diff --git a/package-lock.json b/package-lock.json index 5ef898e..2b46491 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1857,7 +1857,7 @@ }, "node_modules/@quartz-community/utils": { "version": "0.1.0", - "resolved": "git+ssh://git@github.com/quartz-community/utils.git#83f824b0ad495ae24b7f3524dd2049da76d24c2c", + "resolved": "git+ssh://git@github.com/quartz-community/utils.git#7a43501ac1c728e8a7bfa75db35e8791bfa1f41c", "dev": true, "license": "MIT", "dependencies": { diff --git a/quartz/components/pages/404.tsx b/quartz/components/pages/404.tsx index dafabad..622a18d 100644 --- a/quartz/components/pages/404.tsx +++ b/quartz/components/pages/404.tsx @@ -16,13 +16,13 @@ const NotFound: QuartzComponent = ({ cfg }: QuartzComponentProps) => { __html: ` if (typeof fetchData !== "undefined") { fetchData.then(function(index) { - var base = document.querySelector("base"); - var basePath = (base && base.getAttribute("href")) || "/"; + var basePath = document.body.dataset.basepath || ""; if (basePath.length > 1 && basePath.endsWith("/")) { basePath = basePath.slice(0, -1); } var pathname = window.location.pathname; - if (basePath.length > 1 && pathname.startsWith(basePath)) { + var hasBasePrefix = basePath.length > 1 && pathname.startsWith(basePath); + if (hasBasePrefix) { pathname = pathname.slice(basePath.length); } if (pathname.startsWith("/")) { @@ -31,17 +31,16 @@ const NotFound: QuartzComponent = ({ cfg }: QuartzComponentProps) => { if (pathname.endsWith("/")) { pathname = pathname.slice(0, -1); } - // Strip .html extension if present if (pathname.endsWith(".html")) { pathname = pathname.slice(0, -5); } - // Convert trailing /index to folder slug if (pathname.endsWith("/index")) { pathname = pathname.slice(0, -6); } var lowered = pathname.toLowerCase(); if (lowered !== pathname && index[lowered] != null) { - var target = basePath + (basePath.endsWith("/") ? "" : "/") + lowered; + var prefix = hasBasePrefix ? basePath : ""; + var target = prefix + (prefix.endsWith("/") ? "" : "/") + lowered; window.location.replace(target); } }); diff --git a/quartz/plugins/pageTypes/dispatcher.ts b/quartz/plugins/pageTypes/dispatcher.ts index aa853af..543cb07 100644 --- a/quartz/plugins/pageTypes/dispatcher.ts +++ b/quartz/plugins/pageTypes/dispatcher.ts @@ -78,10 +78,15 @@ async function emitPage( ) { const cfg = ctx.cfg.configuration // For the 404 page, use an absolute base path so assets resolve correctly - // when the hosting provider serves 404.html from any URL depth + // when the hosting provider serves 404.html from any URL depth. + // During local dev (--serve), the dev server strips baseDir itself and + // serves files from root, so the 404 page must use "/" to avoid requesting + // assets under a path prefix that the dev server doesn't serve. const baseDir = slug === "404" - ? (new URL(`https://${cfg.baseUrl ?? "example.com"}`).pathname as FullSlug) + ? ((ctx.argv.serve + ? "/" + : new URL(`https://${cfg.baseUrl ?? "example.com"}`).pathname) as FullSlug) : pathToRoot(slug) const externalResources = pageResources(baseDir, resources) const componentData: QuartzComponentProps = { diff --git a/quartz/util/path.test.ts b/quartz/util/path.test.ts index b6fb9fb..c143414 100644 --- a/quartz/util/path.test.ts +++ b/quartz/util/path.test.ts @@ -170,6 +170,15 @@ describe("transforms", () => { ["content/with spaces", "./content/with-spaces"], ["content/with spaces/index", "./content/with-spaces/"], ["content/with spaces#and Anchor!", "./content/with-spaces#and-anchor"], + // Folder note convention: same-name parent triggers /index rewrite → folder path + ["characters/characters", "./characters/"], + ["My Folder/My Folder", "./my-folder/"], + ["a/b/c/d/d", "./a/b/c/d/"], + ["My Folder/My Folder#heading", "./my-folder/#heading"], + // Non-matching last segments: no folder rewrite + ["characters/alice", "./characters/alice"], + // Percent-encoded spaces + ["My%20Folder/My%20Note", "./my-folder/my-note"], ], path.transformInternalLink, (_x: string): _x is string => true,