From 70ad31d6826858751a90ded519f66a5c5d867529 Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Tue, 3 Mar 2026 17:29:43 +0100 Subject: [PATCH 1/2] Fix `quarto preview subdir/file.qmd` crashing with doubled path `quarto preview subdir/page.qmd` in a website project crashes with `readfile subdir\subdir\page.qmd`. The `projectPath` function was designed for output filenames (resolving relative to source directory via dirname+join), but was also called with the source path itself. When that source path was relative, dirname+join doubled the subdirectory. Split into two functions: `projectRelativeInput` for source paths (normalizes directly) and `projectOutputPath` for output filenames (preserves the dirname+join logic). --- src/command/render/render.ts | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/command/render/render.ts b/src/command/render/render.ts index 8cc9538bcfb..3da6c73f04d 100644 --- a/src/command/render/render.ts +++ b/src/command/render/render.ts @@ -385,8 +385,24 @@ export async function renderPandoc( )); } - // if there is a project context then return paths relative to the project - const projectPath = (path: string) => { + // Compute the project-relative path for the input source file. + // Uses normalizePath to handle both relative and absolute source paths. + const projectRelativeInput = (sourcePath: string) => { + if (context.project) { + return relative( + normalizePath(context.project.dir), + normalizePath(sourcePath), + ); + } + return sourcePath; + }; + + // Resolve an output file path to a project-relative path. + // Output paths (like "page.html") are relative to the source file's + // directory, so we join with dirname(target.source) before computing + // the project-relative result. Absolute output paths pass through + // normalizePath directly. + const projectOutputPath = (path: string) => { if (context.project) { if (isAbsolute(path)) { return relative( @@ -411,7 +427,7 @@ export async function renderPandoc( const result: RenderedFile = { isTransient: recipe.isOutputTransient, - input: projectPath(context.target.source), + input: projectRelativeInput(context.target.source), markdown: executeResult.markdown, format, supporting: supporting @@ -421,7 +437,7 @@ export async function renderPandoc( : undefined, file: recipe.isOutputTransient ? finalOutput! - : projectPath(finalOutput!), + : projectOutputPath(finalOutput!), resourceFiles: { globs: pandocResult.resources, files, From 2a8b9f83b76613121ebdf11a452df00cfb0dd1ec Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Tue, 3 Mar 2026 17:30:07 +0100 Subject: [PATCH 2/2] Add missing test for relative/absolute path cache convergence The FileInformationCacheMap (added in #13955) normalizes keys so that relative and absolute paths to the same file share one cache entry. This adds the test case that was missing from the original test suite. --- .../project/file-information-cache.test.ts | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/tests/unit/project/file-information-cache.test.ts b/tests/unit/project/file-information-cache.test.ts index 8ef119e22e1..9f552e9c96b 100644 --- a/tests/unit/project/file-information-cache.test.ts +++ b/tests/unit/project/file-information-cache.test.ts @@ -9,7 +9,7 @@ import { unitTest } from "../../test.ts"; import { assert } from "testing/asserts"; -import { join } from "../../../src/deno_ral/path.ts"; +import { join, relative } from "../../../src/deno_ral/path.ts"; import { ensureFileInformationCache, FileInformationCacheMap, @@ -107,3 +107,26 @@ unitTest( ); }, ); + +// deno-lint-ignore require-await +unitTest( + "fileInformationCache - relative and absolute paths share same entry", + async () => { + const project = createMockProjectContext(); + + const absolutePath = join(project.dir, "subdir", "page.qmd"); + const relativePath = relative(Deno.cwd(), absolutePath); + + const entry1 = ensureFileInformationCache(project, relativePath); + const entry2 = ensureFileInformationCache(project, absolutePath); + + assert( + entry1 === entry2, + "Relative and absolute paths to same file should share a cache entry", + ); + assert( + project.fileInformationCache.size === 1, + "Should have exactly one cache entry", + ); + }, +);