Path: blob/main/src/command/render/render-shared.ts
3584 views
/*1* render-shared.ts2*3* Copyright (C) 2020-2022 Posit Software, PBC4*/56import { dirname } from "../../deno_ral/path.ts";78import { info } from "../../deno_ral/log.ts";9import * as colors from "fmt/colors";1011import {12projectContext,13projectContextForDirectory,14} from "../../project/project-context.ts";1516import { renderProject } from "./project.ts";17import { renderFiles } from "./render-files.ts";18import { resourceFilesFromRenderedFile } from "./resources.ts";19import { RenderFlags, RenderOptions, RenderResult } from "./types.ts";2021import {22isProjectInputFile,23projectExcludeDirs,24} from "../../project/project-shared.ts";2526import {27initState,28setInitializer,29} from "../../core/lib/yaml-validation/state.ts";30import { initYamlIntelligenceResourcesFromFilesystem } from "../../core/schema/utils.ts";31import { kTextPlain } from "../../core/mime.ts";32import { normalizePath } from "../../core/path.ts";33import { notebookContext } from "../../render/notebook/notebook-context.ts";34import { singleFileProjectContext } from "../../project/types/single-file/single-file.ts";35import { assert } from "testing/asserts";3637export async function render(38path: string,39options: RenderOptions,40): Promise<RenderResult> {41// one time initialization of yaml validators42setInitializer(initYamlIntelligenceResourcesFromFilesystem);43await initState();4445const nbContext = notebookContext();4647// determine target context/files48let context = await projectContext(path, nbContext, options);4950// if there is no project parent and an output-dir was passed, then force a project51if (!context && options.flags?.outputDir) {52// recompute context53context = await projectContextForDirectory(path, nbContext, options);5455// force clean as --output-dir implies fully overwrite the target56options.forceClean = options.flags.clean !== false;57}5859// set env var if requested60if (context && options.setProjectDir) {61Deno.env.set("QUARTO_PROJECT_DIR", context.dir);62}6364if (Deno.statSync(path).isDirectory) {65// if the path is a sub-directory of the project, then create66// a files list that is only those files in the subdirectory67let files: string[] | undefined;68if (context) {69const renderDir = normalizePath(path);70const projectDir = normalizePath(context.dir);71if (renderDir !== projectDir) {72files = context.files.input.filter((file) =>73file.startsWith(renderDir)74);75}76return renderProject(77context,78options,79files,80);81} else {82throw new Error(83"The specified directory ('" + path +84"') 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.)",85);86}87} else if (context?.config) {88// if there is a project file then treat this as a project render89// if the passed file is in the render list90if (isProjectInputFile(path, context)) {91return renderProject(context, options, [path]);92}93}9495// validate that we didn't get any project-only options96validateDocumentRenderFlags(options.flags);9798assert(!context, "Expected no context here");99// NB: singleFileProjectContext is currently not fully-featured100context = await singleFileProjectContext(path, nbContext, options.flags);101102// otherwise it's just a file render103const result = await renderFiles(104[{ path }],105options,106nbContext,107undefined,108undefined,109context,110);111112// get partitioned markdown if we had result files113const { engine } = await context.fileExecutionEngineAndTarget(114path,115);116const partitioned = (engine && result.files.length > 0)117? await engine.partitionedMarkdown(path)118: undefined;119120const excludeDirs = context ? projectExcludeDirs(context) : [];121122// compute render result123const renderResult = {124context,125files: await Promise.all(result.files.map(async (file) => {126const resourceFiles = await resourceFilesFromRenderedFile(127dirname(path),128excludeDirs,129file,130partitioned,131);132return {133input: file.input,134markdown: file.markdown,135format: file.format,136file: file.file,137supporting: file.supporting,138resourceFiles,139};140})),141error: result.error,142baseDir: normalizePath(dirname(path)),143};144145if (!renderResult.error && engine?.postRender) {146for (const file of renderResult.files) {147await engine.postRender(file, renderResult.context);148}149}150151// return152return renderResult;153}154155export function printWatchingForChangesMessage() {156info("Watching files for changes", { format: colors.green });157}158159export function previewUnableToRenderResponse() {160return new Response("not found", {161status: 404,162headers: {163"Content-Type": kTextPlain,164},165});166}167168// QUARTO_RENDER_TOKEN169let quartoRenderToken: string | null | undefined;170export function renderToken(): string | null {171const kQuartoRenderToken = "QUARTO_RENDER_TOKEN";172if (quartoRenderToken === undefined) {173quartoRenderToken = Deno.env.get(kQuartoRenderToken) || null;174Deno.env.delete(kQuartoRenderToken);175}176return quartoRenderToken;177}178179function validateDocumentRenderFlags(flags?: RenderFlags) {180if (flags) {181const projectOnly: { [key: string]: string | undefined } = {182["--output-dir"]: flags.outputDir,183["--site-url"]: flags.siteUrl,184};185for (const arg of Object.keys(projectOnly)) {186if (projectOnly[arg]) {187throw new Error(188`The ${arg} flag can only be used when rendering projects.`,189);190}191}192}193}194195196