import { ensureDirSync } from "../deno_ral/fs.ts";
import { dirname, extname, join, relative } from "../deno_ral/path.ts";
import * as ld from "../core/lodash.ts";
import { asArray } from "../core/array.ts";
import {
normalizePath,
resolvePathGlobs,
safeExistsSync,
} from "../core/path.ts";
import { kCssImportRegex, kCssUrlRegex } from "../core/css.ts";
import {
kProject404File,
kProjectOutputDir,
kProjectResources,
ProjectConfig,
} from "./types.ts";
import { kQuartoIgnore } from "./project-gitignore.ts";
import { copyFileIfNewer } from "../core/copy.ts";
import { existsSync1 } from "../core/file.ts";
export function projectResourceFiles(
dir: string,
config: ProjectConfig,
): string[] {
let resourceGlobs = asArray(config.project[kProjectResources]);
const resourceFiles: string[] = [];
const outputDir = config.project[kProjectOutputDir];
if (outputDir) {
resourceGlobs = (resourceGlobs || [])
.concat(kQuartoIgnore.map((entry) => `!${entry}`));
const exclude = outputDir ? [outputDir] : [];
const projectResourceFiles = resolvePathGlobs(
dir,
resourceGlobs,
exclude,
);
resourceFiles.push(
...ld.difference(
projectResourceFiles.include,
projectResourceFiles.exclude,
),
);
resourceFiles.push(
...["robots.txt", ".nojekyll", "CNAME", "_redirects", kProject404File]
.map((file) => join(dir, file))
.filter(existsSync1),
);
}
return ld.uniq(resourceFiles);
}
export function copyResourceFile(
rootDir: string,
srcFile: string,
destFile: string,
) {
if (!normalizePath(srcFile).startsWith(normalizePath(rootDir))) {
return;
}
ensureDirSync(dirname(destFile));
copyFileIfNewer(srcFile, destFile);
if (extname(srcFile).toLowerCase() === ".css") {
handleCssReferences(rootDir, srcFile, destFile);
}
}
export function fixupCssReferences(
css: string,
offset: string,
onRef: (ref: string) => string,
) {
let destCss = css.replaceAll(
kCssUrlRegex,
(_match, p1: string, p2: string) => {
const ref = p2.startsWith("/") ? `${offset}${p2.slice(1)}` : p2;
return `url(${p1 || ""}${onRef(ref)}${p1 || ""})`;
},
);
destCss = destCss.replaceAll(
kCssImportRegex,
(_match, p1: string, p2: string) => {
const ref = p2.startsWith("/") ? `${offset}${p2.slice(1)}` : p2;
return `@import ${p1 || ""}${onRef(ref)}${p1 || ""}`;
},
);
return destCss;
}
function handleCssReferences(
rootDir: string,
srcFile: string,
destFile: string,
) {
const css = Deno.readTextFileSync(destFile);
const offset = relative(dirname(srcFile), rootDir);
const copyRef = (ref: string) => {
const refPath = join(dirname(srcFile), ref);
if (safeExistsSync(refPath)) {
const refDestPath = join(dirname(destFile), ref);
copyResourceFile(rootDir, refPath, refDestPath);
}
return ref;
};
const destCss = fixupCssReferences(css, offset, copyRef);
if (destCss !== css) {
Deno.writeTextFileSync(destFile, destCss);
}
}