Path: blob/main/src/command/create/artifacts/artifact-shared.ts
12926 views
/*1* artifact-shared.ts2*3* Copyright (C) 2020-2022 Posit Software, PBC4*/56import { capitalizeTitle } from "../../../core/text.ts";7import { quartoConfig } from "../../../core/quarto.ts";8import { execProcess } from "../../../core/process.ts";9import { gfmAutoIdentifier } from "../../../core/pandoc/pandoc-id.ts";1011import { coerce } from "semver/mod.ts";12import { info } from "../../../deno_ral/log.ts";13import { basename, dirname, join, relative } from "../../../deno_ral/path.ts";14import {15ensureDirSync,16ensureUserWritable,17walkSync,18} from "../../../deno_ral/fs.ts";19import { renderEjs } from "../../../core/ejs.ts";20import { safeExistsSync } from "../../../core/path.ts";21import { CreateDirective, CreateDirectiveData } from "../cmd-types.ts";2223// File paths that include this string will get fixed up24// and the value from the ejs data will be substituted25const keyRegExp = /(.*)qstart-(.*)-qend(.*)/;2627export function renderAndCopyArtifacts(28target: string,29artifactSrcDir: string,30createDirective: CreateDirective,31data: CreateDirectiveData,32quiet?: boolean,33) {34// Ensure that the target directory exists and35// copy the files36ensureDirSync(target);3738// Walk the artifact directory, copying to the target39// directoy and rendering as we go40const copiedFiles: string[] = [];41for (const artifact of walkSync(artifactSrcDir)) {42if (artifact.isFile) {43keyRegExp.lastIndex = 0;44let match = keyRegExp.exec(artifact.path);45let resolvedPath = artifact.path;46while (match) {47const prefix = match[1];48const key = match[2];49const suffix = match[3];5051if (data[key]) {52resolvedPath = `${prefix}${data[key]}${suffix}`;53} else {54resolvedPath = `${prefix}${key}${suffix}`;55}56match = keyRegExp.exec(resolvedPath);57}58keyRegExp.lastIndex = 0;59// Compute target paths60const targetRelativePath = relative(artifactSrcDir, resolvedPath);61const targetAbsolutePath = join(62createDirective.directory,63targetRelativePath,64);6566// Render the EJS file rather than copying this file67copiedFiles.push(renderArtifact(68artifact.path,69targetAbsolutePath,70data,71));72}73}7475// Provide status - wait until the end76// so that all files, renames, and so on will be completed77// (since some paths will be variables that are resolved at the very end)78if (!quiet) {79info(`Creating ${createDirective.displayType} at `, { newline: false });80info(`${createDirective.directory}`, { bold: true, newline: false });81info(":");8283for (const copiedFile of copiedFiles) {84const relPath = relative(createDirective.directory, copiedFile);85info(86` - Created ${relPath}`,87);88}89}9091return copiedFiles;92}9394// Render an ejs file to the output directory95const renderArtifact = (96src: string,97target: string,98data: CreateDirectiveData,99) => {100const srcFileName = basename(src);101if (srcFileName.includes(".ejs.")) {102// The target file name103const renderTarget = target.replace(/\.ejs\./, ".");104105if (safeExistsSync(renderTarget)) {106throw new Error(`The file ${renderTarget} already exists.`);107}108109// Render the EJS110const rendered = renderEjs(src, data, false);111112// Write the rendered EJS to the output file113ensureDirSync(dirname(renderTarget));114Deno.writeTextFileSync(renderTarget, rendered);115return renderTarget;116} else {117if (safeExistsSync(target)) {118throw new Error(`The file ${target} already exists.`);119}120ensureDirSync(dirname(target));121Deno.copyFileSync(src, target);122ensureUserWritable(target);123return target;124}125};126127export async function ejsData(128createDirective: CreateDirective,129): Promise<CreateDirectiveData> {130// Name variants131const title = capitalizeTitle(createDirective.name);132133const classname = title.replaceAll(/[^\w]/gm, "");134const filesafename = gfmAutoIdentifier(createDirective.name, true);135136// Other metadata137const version = "1.0.0";138const author = await gitAuthor() || "First Last";139140// Limit the quarto version to the major and minor version141const qVer = coerce(quartoConfig.version());142const quartoversion = `${qVer?.major}.${qVer?.minor}.0`;143144return {145name: createDirective.name,146filesafename,147title,148classname,149author: author.trim(),150version,151quartoversion,152cellLanguage: (createDirective.options?.cellLanguage as string) ||153filesafename,154};155}156157async function gitAuthor() {158const result = await execProcess({159cmd: "git",160args: ["config", "--global", "user.name"],161stdout: "piped",162stderr: "piped",163});164if (result.success) {165return result.stdout;166} else {167return undefined;168}169}170171172