Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
quarto-dev
GitHub Repository: quarto-dev/quarto-cli
Path: blob/main/src/command/render/render-shared.ts
6428 views
1
/*
2
* render-shared.ts
3
*
4
* Copyright (C) 2020-2022 Posit Software, PBC
5
*/
6
7
import { dirname } from "../../deno_ral/path.ts";
8
9
import { info } from "../../deno_ral/log.ts";
10
import * as colors from "fmt/colors";
11
12
import {
13
projectContext,
14
projectContextForDirectory,
15
} from "../../project/project-context.ts";
16
17
import { renderProject } from "./project.ts";
18
import { renderFiles } from "./render-files.ts";
19
import { resourceFilesFromRenderedFile } from "./resources.ts";
20
import { RenderFlags, RenderOptions, RenderResult } from "./types.ts";
21
22
import {
23
isProjectInputFile,
24
projectExcludeDirs,
25
} from "../../project/project-shared.ts";
26
27
import {
28
initState,
29
setInitializer,
30
} from "../../core/lib/yaml-validation/state.ts";
31
import { initYamlIntelligenceResourcesFromFilesystem } from "../../core/schema/utils.ts";
32
import { kTextPlain } from "../../core/mime.ts";
33
import { normalizePath } from "../../core/path.ts";
34
import { notebookContext } from "../../render/notebook/notebook-context.ts";
35
import { singleFileProjectContext } from "../../project/types/single-file/single-file.ts";
36
import { ProjectContext } from "../../project/types.ts";
37
38
export async function render(
39
path: string,
40
options: RenderOptions,
41
pContext?: ProjectContext,
42
): Promise<RenderResult> {
43
// one time initialization of yaml validators
44
setInitializer(initYamlIntelligenceResourcesFromFilesystem);
45
await initState();
46
47
const nbContext = pContext?.notebookContext || notebookContext();
48
49
// determine target context/files
50
let context = pContext || (await projectContext(path, nbContext, options));
51
52
// Create a synthetic project when --output-dir is used without a project file
53
// This creates a temporary .quarto directory to manage the render, which must
54
// be fully cleaned up afterward to avoid leaving debris (see #9745)
55
if (!context && options.flags?.outputDir) {
56
context = await projectContextForDirectory(path, nbContext, options);
57
58
// forceClean signals this is a synthetic project that needs full cleanup
59
// including removing the .quarto scratch directory after rendering (#13625)
60
options.forceClean = options.flags.clean !== false;
61
}
62
63
// Fall back to single file context if we still don't have a context
64
if (!context) {
65
context = await singleFileProjectContext(path, nbContext, options);
66
}
67
68
// set env var if requested
69
if (context && options.setProjectDir) {
70
// FIXME we can't set environment variables like this with asyncs flying around
71
Deno.env.set("QUARTO_PROJECT_DIR", context.dir);
72
}
73
74
if (Deno.statSync(path).isDirectory) {
75
// if the path is a sub-directory of the project, then create
76
// a files list that is only those files in the subdirectory
77
let files: string[] | undefined;
78
if (context) {
79
const renderDir = normalizePath(path);
80
const projectDir = normalizePath(context.dir);
81
if (renderDir !== projectDir) {
82
files = context.files.input.filter((file) =>
83
file.startsWith(renderDir)
84
);
85
}
86
return renderProject(
87
context,
88
options,
89
files,
90
);
91
} else {
92
throw new Error(
93
"The specified directory ('" + path +
94
"') is not a Quarto project.\n(If you have not specified a path, quarto will attempt to render the entire current directory as a project.)",
95
);
96
}
97
} else if (context?.config) {
98
// if there is a project file then treat this as a project render
99
// if the passed file is in the render list
100
if (isProjectInputFile(path, context)) {
101
return renderProject(context, options, [path]);
102
}
103
}
104
105
// validate that we didn't get any project-only options
106
validateDocumentRenderFlags(options.flags);
107
108
// otherwise it's just a file render
109
const result = await renderFiles(
110
[{ path }],
111
options,
112
nbContext,
113
undefined,
114
undefined,
115
context,
116
);
117
118
// get partitioned markdown if we had result files
119
const { engine } = await context.fileExecutionEngineAndTarget(
120
path,
121
);
122
const partitioned = (engine && result.files.length > 0)
123
? await engine.partitionedMarkdown(path)
124
: undefined;
125
126
const excludeDirs = context ? projectExcludeDirs(context) : [];
127
128
// compute render result
129
const renderResult = {
130
context,
131
files: await Promise.all(result.files.map(async (file) => {
132
const resourceFiles = await resourceFilesFromRenderedFile(
133
dirname(path),
134
excludeDirs,
135
file,
136
partitioned,
137
);
138
return {
139
input: file.input,
140
markdown: file.markdown,
141
format: file.format,
142
file: file.file,
143
supporting: file.supporting,
144
resourceFiles,
145
};
146
})),
147
error: result.error,
148
baseDir: normalizePath(dirname(path)),
149
};
150
151
if (!renderResult.error && engine?.postRender) {
152
for (const file of renderResult.files) {
153
await engine.postRender(file);
154
}
155
}
156
157
// return
158
return renderResult;
159
}
160
161
export function printWatchingForChangesMessage() {
162
info("Watching files for changes", { format: colors.green });
163
}
164
165
export function previewUnableToRenderResponse() {
166
return new Response("not found", {
167
status: 404,
168
headers: {
169
"Content-Type": kTextPlain,
170
},
171
});
172
}
173
174
// QUARTO_RENDER_TOKEN
175
let quartoRenderToken: string | null | undefined;
176
export function renderToken(): string | null {
177
const kQuartoRenderToken = "QUARTO_RENDER_TOKEN";
178
if (quartoRenderToken === undefined) {
179
quartoRenderToken = Deno.env.get(kQuartoRenderToken) || null;
180
Deno.env.delete(kQuartoRenderToken);
181
}
182
return quartoRenderToken;
183
}
184
185
function validateDocumentRenderFlags(flags?: RenderFlags) {
186
if (flags) {
187
const projectOnly: { [key: string]: string | undefined } = {
188
["--output-dir"]: flags.outputDir,
189
["--site-url"]: flags.siteUrl,
190
};
191
for (const arg of Object.keys(projectOnly)) {
192
if (projectOnly[arg]) {
193
throw new Error(
194
`The ${arg} flag can only be used when rendering projects.`,
195
);
196
}
197
}
198
}
199
}
200
201