Skip to content

Commit b6e1c2e

Browse files
authored
Fix quarto inspect emitting project for standalone files in RStudio
Since v1.9.35, `quarto inspect` on a standalone .qmd file emits a `project` field because `singleFileProjectContext` now always sets `config`. RStudio's publishing wizard reads `project.dir`, assumes `_quarto.yml` exists there, and crashes on the missing file. Gate `project` emission on `!(isSingleFile && isRStudio())` so RStudio gets the pre-1.9.35 output while other consumers still see the project metadata. Add `_setIsRStudioForTest()` to avoid `Deno.env.set()` race conditions in parallel tests. The RStudio-side fix (rstudio/rstudio#17336) is merged but won't be backported — this protects users on older RStudio who upgrade Quarto. Ref: rstudio/rstudio#17333
1 parent 61a7614 commit b6e1c2e

5 files changed

Lines changed: 97 additions & 1 deletion

File tree

news/changelog-1.10.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ All changes included in 1.10:
55
- ([#14267](https://github.com/quarto-dev/quarto-cli/issues/14267)): Fix Windows paths with accented characters (e.g., `C:\Users\Sébastien\`) breaking dart-sass compilation.
66
- ([#14281](https://github.com/quarto-dev/quarto-cli/issues/14281)): Fix transient `.quarto_ipynb` files accumulating during `quarto preview` with Jupyter engine.
77
- ([#14298](https://github.com/quarto-dev/quarto-cli/issues/14298)): Fix `quarto preview` browse URL including output filename (e.g., `hello.html`) for single-file documents, breaking Posit Workbench proxied server access.
8+
- ([rstudio/rstudio#17333](https://github.com/rstudio/rstudio/issues/17333)): Fix `quarto inspect` on standalone files emitting project metadata that breaks RStudio's publishing wizard.
89

910
## Formats
1011

src/core/platform.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,15 @@ export function isWSL() {
1414
return !!Deno.env.get("WSL_DISTRO_NAME");
1515
}
1616

17+
// Test override for isRStudio — avoids Deno.env.set() race conditions
18+
// in parallel tests. See quarto-dev/quarto-cli#14218 and PR #12621.
19+
let _isRStudioOverride: boolean | undefined;
20+
export function _setIsRStudioForTest(value: boolean | undefined) {
21+
_isRStudioOverride = value;
22+
}
23+
1724
export function isRStudio() {
25+
if (_isRStudioOverride !== undefined) return _isRStudioOverride;
1826
return !!Deno.env.get("RSTUDIO");
1927
}
2028

src/inspect/inspect.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ import {
4949
import { validateDocumentFromSource } from "../core/schema/validate-document.ts";
5050
import { error } from "../deno_ral/log.ts";
5151
import { ProjectContext } from "../project/types.ts";
52+
import { isRStudio } from "../core/platform.ts";
5253

5354
export function isProjectConfig(
5455
config: InspectedConfig,
@@ -238,7 +239,9 @@ const inspectDocumentConfig = async (path: string) => {
238239
};
239240

240241
// if there is a project then add it
241-
if (context?.config) {
242+
// Suppress project for standalone files in RStudio: current releases
243+
// assume project.dir implies _quarto.yml exists (rstudio/rstudio#17333)
244+
if (context?.config && !(context.isSingleFile && isRStudio())) {
242245
config.project = await inspectProjectConfig(context);
243246
}
244247
return config;
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
title: Hello
3+
format: html
4+
---
5+
6+
Hello world.
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* inspect-standalone-rstudio.test.ts
3+
*
4+
* Copyright (C) 2020-2026 Posit Software, PBC
5+
*
6+
*/
7+
8+
import { existsSync } from "../../../src/deno_ral/fs.ts";
9+
import { _setIsRStudioForTest } from "../../../src/core/platform.ts";
10+
import {
11+
ExecuteOutput,
12+
testQuartoCmd,
13+
} from "../../test.ts";
14+
import { assert, assertEquals } from "testing/asserts";
15+
16+
// Test: standalone file inspect with RStudio override should NOT emit project.
17+
// Uses _setIsRStudioForTest to avoid Deno.env.set() race conditions in
18+
// parallel tests (see #14218, PR #12621).
19+
(() => {
20+
const input = "docs/inspect/standalone-hello.qmd";
21+
const output = "docs/inspect/standalone-hello.json";
22+
testQuartoCmd(
23+
"inspect",
24+
[input, output],
25+
[
26+
{
27+
name: "inspect-standalone-no-project-in-rstudio",
28+
verify: async (_outputs: ExecuteOutput[]) => {
29+
assert(existsSync(output));
30+
const json = JSON.parse(Deno.readTextFileSync(output));
31+
assertEquals(json.project, undefined,
32+
"Standalone file inspect should not emit 'project' when in RStudio");
33+
}
34+
}
35+
],
36+
{
37+
setup: async () => {
38+
_setIsRStudioForTest(true);
39+
},
40+
teardown: async () => {
41+
_setIsRStudioForTest(undefined);
42+
if (existsSync(output)) {
43+
Deno.removeSync(output);
44+
}
45+
}
46+
},
47+
);
48+
})();
49+
50+
// Test: standalone file inspect WITHOUT RStudio should still emit project
51+
(() => {
52+
const input = "docs/inspect/standalone-hello.qmd";
53+
const output = "docs/inspect/standalone-hello-nors.json";
54+
testQuartoCmd(
55+
"inspect",
56+
[input, output],
57+
[
58+
{
59+
name: "inspect-standalone-has-project-outside-rstudio",
60+
verify: async (_outputs: ExecuteOutput[]) => {
61+
assert(existsSync(output));
62+
const json = JSON.parse(Deno.readTextFileSync(output));
63+
assert(json.project !== undefined,
64+
"Standalone file inspect should emit 'project' when not in RStudio");
65+
assert(json.project.dir !== undefined,
66+
"project.dir should be set");
67+
}
68+
}
69+
],
70+
{
71+
teardown: async () => {
72+
if (existsSync(output)) {
73+
Deno.removeSync(output);
74+
}
75+
}
76+
},
77+
);
78+
})();

0 commit comments

Comments
 (0)