Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
quarto-dev
GitHub Repository: quarto-dev/quarto-cli
Path: blob/main/src/project/project-resources.ts
6449 views
1
/*
2
* project-resources.ts
3
*
4
* Copyright (C) 2020-2022 Posit Software, PBC
5
*/
6
7
import { ensureDirSync } from "../deno_ral/fs.ts";
8
import { dirname, extname, join, relative } from "../deno_ral/path.ts";
9
import * as ld from "../core/lodash.ts";
10
import { asArray } from "../core/array.ts";
11
12
import {
13
normalizePath,
14
resolvePathGlobs,
15
safeExistsSync,
16
} from "../core/path.ts";
17
import { kCssImportRegex, kCssUrlRegex } from "../core/css.ts";
18
19
import {
20
kProject404File,
21
kProjectOutputDir,
22
kProjectResources,
23
ProjectConfig,
24
} from "./types.ts";
25
26
import { kQuartoIgnore } from "./project-gitignore.ts";
27
import { copyFileIfNewer } from "../core/copy.ts";
28
import { existsSync1 } from "../core/file.ts";
29
30
export function projectResourceFiles(
31
dir: string,
32
config: ProjectConfig,
33
): string[] {
34
let resourceGlobs = asArray(config.project[kProjectResources]);
35
const resourceFiles: string[] = [];
36
// This usage of reading the output directory is necessary since we haven't
37
// yet formed the project context (this function is used in creating the context)
38
const outputDir = config.project[kProjectOutputDir];
39
if (outputDir) {
40
resourceGlobs = (resourceGlobs || [])
41
// ignore standard quarto ignores (e.g. /.quarto/)
42
.concat(kQuartoIgnore.map((entry) => `!${entry}`));
43
44
const exclude = outputDir ? [outputDir] : [];
45
const projectResourceFiles = resolvePathGlobs(
46
dir,
47
resourceGlobs,
48
exclude,
49
);
50
resourceFiles.push(
51
...ld.difference(
52
projectResourceFiles.include,
53
projectResourceFiles.exclude,
54
),
55
);
56
// literals
57
resourceFiles.push(
58
...["robots.txt", ".nojekyll", "CNAME", "_redirects", kProject404File]
59
.map((file) => join(dir, file))
60
.filter(existsSync1),
61
);
62
}
63
return ld.uniq(resourceFiles);
64
}
65
66
export function copyResourceFile(
67
rootDir: string,
68
srcFile: string,
69
destFile: string,
70
) {
71
// ensure that the resource reference doesn't escape the root dir
72
if (!normalizePath(srcFile).startsWith(normalizePath(rootDir))) {
73
return;
74
}
75
76
ensureDirSync(dirname(destFile));
77
copyFileIfNewer(srcFile, destFile);
78
79
if (extname(srcFile).toLowerCase() === ".css") {
80
handleCssReferences(rootDir, srcFile, destFile);
81
}
82
}
83
84
export function fixupCssReferences(
85
css: string,
86
offset: string,
87
onRef: (ref: string) => string,
88
) {
89
// fixup / copy refs from url()
90
let destCss = css.replaceAll(
91
kCssUrlRegex,
92
(_match, p1: string, p2: string) => {
93
const ref = p2.startsWith("/") ? `${offset}${p2.slice(1)}` : p2;
94
return `url(${p1 || ""}${onRef(ref)}${p1 || ""})`;
95
},
96
);
97
98
// fixup / copy refs from @import
99
destCss = destCss.replaceAll(
100
kCssImportRegex,
101
(_match, p1: string, p2: string) => {
102
const ref = p2.startsWith("/") ? `${offset}${p2.slice(1)}` : p2;
103
return `@import ${p1 || ""}${onRef(ref)}${p1 || ""}`;
104
},
105
);
106
107
return destCss;
108
}
109
110
// fixup root ('/') css references and also copy references to other
111
// stylesheet or resources (e.g. images) to alongside the destFile
112
function handleCssReferences(
113
rootDir: string,
114
srcFile: string,
115
destFile: string,
116
) {
117
// read the css
118
const css = Deno.readTextFileSync(destFile);
119
120
// offset for root references
121
const offset = relative(dirname(srcFile), rootDir);
122
123
// function that can be used to copy a ref
124
const copyRef = (ref: string) => {
125
const refPath = join(dirname(srcFile), ref);
126
if (safeExistsSync(refPath)) {
127
const refDestPath = join(dirname(destFile), ref);
128
copyResourceFile(rootDir, refPath, refDestPath);
129
}
130
return ref;
131
};
132
133
const destCss = fixupCssReferences(css, offset, copyRef);
134
135
// write the css if necessary
136
if (destCss !== css) {
137
Deno.writeTextFileSync(destFile, destCss);
138
}
139
}
140
141