Path: blob/main/src/command/create/artifacts/artifact-shared.ts
3589 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 { ensureDirSync, walkSync } from "../../../deno_ral/fs.ts";15import { renderEjs } from "../../../core/ejs.ts";16import { safeExistsSync } from "../../../core/path.ts";17import { CreateDirective, CreateDirectiveData } from "../cmd-types.ts";1819// File paths that include this string will get fixed up20// and the value from the ejs data will be substituted21const keyRegExp = /(.*)qstart-(.*)-qend(.*)/;2223export function renderAndCopyArtifacts(24target: string,25artifactSrcDir: string,26createDirective: CreateDirective,27data: CreateDirectiveData,28quiet?: boolean,29) {30// Ensure that the target directory exists and31// copy the files32ensureDirSync(target);3334// Walk the artifact directory, copying to the target35// directoy and rendering as we go36const copiedFiles: string[] = [];37for (const artifact of walkSync(artifactSrcDir)) {38if (artifact.isFile) {39keyRegExp.lastIndex = 0;40let match = keyRegExp.exec(artifact.path);41let resolvedPath = artifact.path;42while (match) {43const prefix = match[1];44const key = match[2];45const suffix = match[3];4647if (data[key]) {48resolvedPath = `${prefix}${data[key]}${suffix}`;49} else {50resolvedPath = `${prefix}${key}${suffix}`;51}52match = keyRegExp.exec(resolvedPath);53}54keyRegExp.lastIndex = 0;55// Compute target paths56const targetRelativePath = relative(artifactSrcDir, resolvedPath);57const targetAbsolutePath = join(58createDirective.directory,59targetRelativePath,60);6162// Render the EJS file rather than copying this file63copiedFiles.push(renderArtifact(64artifact.path,65targetAbsolutePath,66data,67));68}69}7071// Provide status - wait until the end72// so that all files, renames, and so on will be completed73// (since some paths will be variables that are resolved at the very end)74if (!quiet) {75info(`Creating ${createDirective.displayType} at `, { newline: false });76info(`${createDirective.directory}`, { bold: true, newline: false });77info(":");7879for (const copiedFile of copiedFiles) {80const relPath = relative(createDirective.directory, copiedFile);81info(82` - Created ${relPath}`,83);84}85}8687return copiedFiles;88}8990// Render an ejs file to the output directory91const renderArtifact = (92src: string,93target: string,94data: CreateDirectiveData,95) => {96const srcFileName = basename(src);97if (srcFileName.includes(".ejs.")) {98// The target file name99const renderTarget = target.replace(/\.ejs\./, ".");100101if (safeExistsSync(renderTarget)) {102throw new Error(`The file ${renderTarget} already exists.`);103}104105// Render the EJS106const rendered = renderEjs(src, data, false);107108// Write the rendered EJS to the output file109ensureDirSync(dirname(renderTarget));110Deno.writeTextFileSync(renderTarget, rendered);111return renderTarget;112} else {113if (safeExistsSync(target)) {114throw new Error(`The file ${target} already exists.`);115}116ensureDirSync(dirname(target));117Deno.copyFileSync(src, target);118return target;119}120};121122export async function ejsData(123createDirective: CreateDirective,124): Promise<CreateDirectiveData> {125// Name variants126const title = capitalizeTitle(createDirective.name);127128const classname = title.replaceAll(/[^\w]/gm, "");129const filesafename = gfmAutoIdentifier(createDirective.name, true);130131// Other metadata132const version = "1.0.0";133const author = await gitAuthor() || "First Last";134135// Limit the quarto version to the major and minor version136const qVer = coerce(quartoConfig.version());137const quartoversion = `${qVer?.major}.${qVer?.minor}.0`;138139return {140name: createDirective.name,141filesafename,142title,143classname,144author: author.trim(),145version,146quartoversion,147};148}149150async function gitAuthor() {151const result = await execProcess({152cmd: "git",153args: ["config", "--global", "user.name"],154stdout: "piped",155stderr: "piped",156});157if (result.success) {158return result.stdout;159} else {160return undefined;161}162}163164165