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