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
3584 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 { assert } from "testing/asserts";
37
38
export async function render(
39
path: string,
40
options: RenderOptions,
41
): Promise<RenderResult> {
42
// one time initialization of yaml validators
43
setInitializer(initYamlIntelligenceResourcesFromFilesystem);
44
await initState();
45
46
const nbContext = notebookContext();
47
48
// determine target context/files
49
let context = await projectContext(path, nbContext, options);
50
51
// if there is no project parent and an output-dir was passed, then force a project
52
if (!context && options.flags?.outputDir) {
53
// recompute context
54
context = await projectContextForDirectory(path, nbContext, options);
55
56
// force clean as --output-dir implies fully overwrite the target
57
options.forceClean = options.flags.clean !== false;
58
}
59
60
// set env var if requested
61
if (context && options.setProjectDir) {
62
Deno.env.set("QUARTO_PROJECT_DIR", context.dir);
63
}
64
65
if (Deno.statSync(path).isDirectory) {
66
// if the path is a sub-directory of the project, then create
67
// a files list that is only those files in the subdirectory
68
let files: string[] | undefined;
69
if (context) {
70
const renderDir = normalizePath(path);
71
const projectDir = normalizePath(context.dir);
72
if (renderDir !== projectDir) {
73
files = context.files.input.filter((file) =>
74
file.startsWith(renderDir)
75
);
76
}
77
return renderProject(
78
context,
79
options,
80
files,
81
);
82
} else {
83
throw new Error(
84
"The specified directory ('" + path +
85
"') 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.)",
86
);
87
}
88
} else if (context?.config) {
89
// if there is a project file then treat this as a project render
90
// if the passed file is in the render list
91
if (isProjectInputFile(path, context)) {
92
return renderProject(context, options, [path]);
93
}
94
}
95
96
// validate that we didn't get any project-only options
97
validateDocumentRenderFlags(options.flags);
98
99
assert(!context, "Expected no context here");
100
// NB: singleFileProjectContext is currently not fully-featured
101
context = await singleFileProjectContext(path, nbContext, options.flags);
102
103
// otherwise it's just a file render
104
const result = await renderFiles(
105
[{ path }],
106
options,
107
nbContext,
108
undefined,
109
undefined,
110
context,
111
);
112
113
// get partitioned markdown if we had result files
114
const { engine } = await context.fileExecutionEngineAndTarget(
115
path,
116
);
117
const partitioned = (engine && result.files.length > 0)
118
? await engine.partitionedMarkdown(path)
119
: undefined;
120
121
const excludeDirs = context ? projectExcludeDirs(context) : [];
122
123
// compute render result
124
const renderResult = {
125
context,
126
files: await Promise.all(result.files.map(async (file) => {
127
const resourceFiles = await resourceFilesFromRenderedFile(
128
dirname(path),
129
excludeDirs,
130
file,
131
partitioned,
132
);
133
return {
134
input: file.input,
135
markdown: file.markdown,
136
format: file.format,
137
file: file.file,
138
supporting: file.supporting,
139
resourceFiles,
140
};
141
})),
142
error: result.error,
143
baseDir: normalizePath(dirname(path)),
144
};
145
146
if (!renderResult.error && engine?.postRender) {
147
for (const file of renderResult.files) {
148
await engine.postRender(file, renderResult.context);
149
}
150
}
151
152
// return
153
return renderResult;
154
}
155
156
export function printWatchingForChangesMessage() {
157
info("Watching files for changes", { format: colors.green });
158
}
159
160
export function previewUnableToRenderResponse() {
161
return new Response("not found", {
162
status: 404,
163
headers: {
164
"Content-Type": kTextPlain,
165
},
166
});
167
}
168
169
// QUARTO_RENDER_TOKEN
170
let quartoRenderToken: string | null | undefined;
171
export function renderToken(): string | null {
172
const kQuartoRenderToken = "QUARTO_RENDER_TOKEN";
173
if (quartoRenderToken === undefined) {
174
quartoRenderToken = Deno.env.get(kQuartoRenderToken) || null;
175
Deno.env.delete(kQuartoRenderToken);
176
}
177
return quartoRenderToken;
178
}
179
180
function validateDocumentRenderFlags(flags?: RenderFlags) {
181
if (flags) {
182
const projectOnly: { [key: string]: string | undefined } = {
183
["--output-dir"]: flags.outputDir,
184
["--site-url"]: flags.siteUrl,
185
};
186
for (const arg of Object.keys(projectOnly)) {
187
if (projectOnly[arg]) {
188
throw new Error(
189
`The ${arg} flag can only be used when rendering projects.`,
190
);
191
}
192
}
193
}
194
}
195
196