Skip to content

Commit ae6cdcc

Browse files
authored
Merge pull request #8821 from quarto-dev/improve/single-extension-context
perf - create a single extension context
2 parents c1a99ee + 36fc51e commit ae6cdcc

File tree

9 files changed

+77
-31
lines changed

9 files changed

+77
-31
lines changed

src/command/preview/cmd.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,11 @@ export const previewCommand = new Command()
391391
// see if we are serving a project or a file
392392
if (Deno.statSync(file).isDirectory) {
393393
// project preview
394-
await serveProject(projectTarget, flags, args, {
394+
const renderOptions = {
395+
services: renderServices(notebookContext()),
396+
flags,
397+
};
398+
await serveProject(projectTarget, renderOptions, args, {
395399
port: options.port,
396400
host: options.host,
397401
browser: (options.browser === false || options.browse === false)

src/command/render/project.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ export async function renderProject(
294294
context = await projectContextForDirectory(
295295
context.dir,
296296
context.notebookContext,
297-
projectRenderConfig.options.flags,
297+
projectRenderConfig.options,
298298
);
299299

300300
// Validate that certain project properties haven't been mutated

src/command/render/render-shared.ts

+2-6
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,6 @@ import { renderProject } from "./project.ts";
1818
import { renderFiles } from "./render-files.ts";
1919
import { resourceFilesFromRenderedFile } from "./resources.ts";
2020
import { RenderFlags, RenderOptions, RenderResult } from "./types.ts";
21-
import {
22-
fileExecutionEngine,
23-
fileExecutionEngineAndTarget,
24-
} from "../../execute/engine.ts";
2521

2622
import {
2723
isProjectInputFile,
@@ -49,12 +45,12 @@ export async function render(
4945
const nbContext = notebookContext();
5046

5147
// determine target context/files
52-
let context = await projectContext(path, nbContext, options.flags);
48+
let context = await projectContext(path, nbContext, options);
5349

5450
// if there is no project parent and an output-dir was passed, then force a project
5551
if (!context && options.flags?.outputDir) {
5652
// recompute context
57-
context = await projectContextForDirectory(path, nbContext, options.flags);
53+
context = await projectContextForDirectory(path, nbContext, options);
5854

5955
// force clean as --output-dir implies fully overwrite the target
6056
options.forceClean = options.flags.clean !== false;

src/core/main.ts

+12
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ import { initializeLogger, logError, logOptions } from "../../src/core/log.ts";
1010
import { Args } from "flags/mod.ts";
1111
import { parse } from "flags/mod.ts";
1212
import { exitWithCleanup } from "./cleanup.ts";
13+
import {
14+
captureFileReads,
15+
reportPeformanceMetrics,
16+
} from "./performance/metrics.ts";
1317

1418
type Runner = (args: Args) => Promise<unknown>;
1519
export async function mainRunner(runner: Runner) {
@@ -24,6 +28,10 @@ export async function mainRunner(runner: Runner) {
2428
Deno.addSignalListener("SIGTERM", abend);
2529
}
2630

31+
if (Deno.env.get("QUARTO_REPORT_PERFORMANCE_METRICS") !== undefined) {
32+
captureFileReads();
33+
}
34+
2735
await runner(args);
2836

2937
// if profiling, wait for 10 seconds before quitting
@@ -33,6 +41,10 @@ export async function mainRunner(runner: Runner) {
3341
await new Promise((resolve) => setTimeout(resolve, 10000));
3442
}
3543

44+
if (Deno.env.get("QUARTO_REPORT_PERFORMANCE_METRICS") !== undefined) {
45+
reportPeformanceMetrics();
46+
}
47+
3648
exitWithCleanup(0);
3749
} catch (e) {
3850
if (e) {

src/core/performance/metrics.ts

+24
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,33 @@
66

77
import { inputTargetIndexCacheMetrics } from "../../project/project-index.ts";
88

9+
type FileReadRecord = {
10+
path: string;
11+
stack: string;
12+
};
13+
14+
let fileReads: FileReadRecord[] | undefined = undefined;
15+
16+
export function captureFileReads() {
17+
fileReads = [];
18+
19+
const originalReadTextFileSync = Deno.readTextFileSync;
20+
21+
Deno.readTextFileSync = function (path: string | URL) {
22+
try {
23+
throw new Error("File read");
24+
} catch (e) {
25+
const stack = e.stack!.split("\n").slice(2);
26+
fileReads!.push({ path: String(path), stack });
27+
}
28+
return originalReadTextFileSync(path);
29+
};
30+
}
31+
932
export function quartoPerformanceMetrics() {
1033
return {
1134
inputTargetIndexCache: inputTargetIndexCacheMetrics,
35+
fileReads,
1236
};
1337
}
1438

src/project/project-context.ts

+12-6
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,11 @@ import {
6969
projectResolveFullMarkdownForFile,
7070
projectVarsFile,
7171
} from "./project-shared.ts";
72-
import { RenderFlags, RenderServices } from "../command/render/types.ts";
72+
import {
73+
RenderFlags,
74+
RenderOptions,
75+
RenderServices,
76+
} from "../command/render/types.ts";
7377
import { kWebsite } from "./types/website/website-constants.ts";
7478

7579
import { readAndValidateYamlFromFile } from "../core/schema/validated-yaml.ts";
@@ -95,16 +99,18 @@ import { MappedString } from "../core/mapped-text.ts";
9599
export async function projectContext(
96100
path: string,
97101
notebookContext: NotebookContext,
98-
flags?: RenderFlags,
102+
renderOptions?: RenderOptions,
99103
force = false,
100104
): Promise<ProjectContext | undefined> {
105+
const flags = renderOptions?.flags;
101106
let dir = normalizePath(
102107
Deno.statSync(path).isDirectory ? path : dirname(path),
103108
);
104109
const originalDir = dir;
105110

106-
// create a shared extension context
107-
const extensionContext = createExtensionContext();
111+
// create an extension context if one doesn't exist
112+
const extensionContext = renderOptions?.services.extension ||
113+
createExtensionContext();
108114

109115
// first pass uses the config file resolve
110116
const configSchema = await getProjectConfigSchema();
@@ -636,9 +642,9 @@ async function resolveLanguageTranslations(
636642
export function projectContextForDirectory(
637643
path: string,
638644
notebookContext: NotebookContext,
639-
flags?: RenderFlags,
645+
renderOptions?: RenderOptions,
640646
): Promise<ProjectContext> {
641-
return projectContext(path, notebookContext, flags, true) as Promise<
647+
return projectContext(path, notebookContext, renderOptions, true) as Promise<
642648
ProjectContext
643649
>;
644650
}

src/project/serve/serve.ts

+14-5
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,11 @@ import { htmlResourceResolverPostprocessor } from "../../project/types/website/w
8282
import { inputFilesDir } from "../../core/render.ts";
8383
import { kResources, kTargetFormat } from "../../config/constants.ts";
8484
import { resourcesFromMetadata } from "../../command/render/resources.ts";
85-
import { RenderFlags, RenderResult } from "../../command/render/types.ts";
85+
import {
86+
RenderFlags,
87+
RenderOptions,
88+
RenderResult,
89+
} from "../../command/render/types.ts";
8690
import {
8791
kPdfJsInitialPath,
8892
pdfJsBaseDir,
@@ -124,18 +128,23 @@ export const kRenderDefault = "default";
124128

125129
export async function serveProject(
126130
target: string | ProjectContext,
127-
flags: RenderFlags,
131+
renderOptions: RenderOptions,
128132
pandocArgs: string[],
129133
options: ServeOptions,
130134
noServe: boolean,
131135
) {
132136
let project: ProjectContext | undefined;
133-
const nbContext = notebookContext();
137+
let flags = renderOptions.flags;
138+
const nbContext = renderOptions.services.notebook;
134139
if (typeof target === "string") {
135140
if (target === ".") {
136141
target = Deno.cwd();
137142
}
138-
project = await projectContext(target, nbContext, flags);
143+
project = await projectContext(
144+
target,
145+
nbContext,
146+
renderOptions,
147+
);
139148
if (!project || !project?.config) {
140149
throw new Error(`${target} is not a project`);
141150
}
@@ -301,7 +310,7 @@ export async function serveProject(
301310
project,
302311
extensionDirs,
303312
resourceFiles,
304-
flags,
313+
{ ...renderOptions, flags },
305314
pandocArgs,
306315
options,
307316
!pdfOutput, // we don't render on reload for pdf output

src/project/serve/watch.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import { projectContext } from "../../project/project-context.ts";
2525

2626
import { ProjectWatcher, ServeOptions } from "./types.ts";
2727
import { httpDevServer } from "../../core/http-devserver.ts";
28-
import { RenderFlags } from "../../command/render/types.ts";
28+
import { RenderOptions } from "../../command/render/types.ts";
2929
import { renderProject } from "../../command/render/project.ts";
3030
import { render } from "../../command/render/render-shared.ts";
3131
import { renderServices } from "../../command/render/render-services.ts";
@@ -53,17 +53,19 @@ export function watchProject(
5353
project: ProjectContext,
5454
extensionDirs: string[],
5555
resourceFiles: string[],
56-
flags: RenderFlags,
56+
renderOptions: RenderOptions,
5757
pandocArgs: string[],
5858
options: ServeOptions,
5959
renderingOnReload: boolean,
6060
renderManager: ServeRenderManager,
6161
stopServer: VoidFunction,
6262
): Promise<ProjectWatcher> {
6363
const nbContext = notebookContext();
64+
const flags = renderOptions.flags;
6465
// helper to refresh project config
6566
const refreshProjectConfig = async () => {
66-
project = (await projectContext(project.dir, nbContext, flags, false))!;
67+
project =
68+
(await projectContext(project.dir, nbContext, renderOptions, false))!;
6769
};
6870

6971
// See if we're in draft mode

src/quarto.ts

+2-9
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,7 @@ import {
1313
} from "cliffy/command/mod.ts";
1414

1515
import { commands } from "./command/command.ts";
16-
import {
17-
appendLogOptions,
18-
cleanupLogger,
19-
initializeLogger,
20-
logError,
21-
logOptions,
22-
} from "./core/log.ts";
16+
import { appendLogOptions } from "./core/log.ts";
2317
import { debug } from "log/mod.ts";
2418

2519
import { cleanupSessionTempDir, initSessionTempDir } from "./core/temp.ts";
@@ -36,9 +30,8 @@ import {
3630
reconfigureQuarto,
3731
} from "./core/devconfig.ts";
3832
import { typstBinaryPath } from "./core/typst.ts";
39-
import { exitWithCleanup, onCleanup } from "./core/cleanup.ts";
33+
import { onCleanup } from "./core/cleanup.ts";
4034

41-
import { parse } from "flags/mod.ts";
4235
import { runScript } from "./command/run/run.ts";
4336

4437
// ensures run handlers are registered

0 commit comments

Comments
 (0)