Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
quarto-dev
GitHub Repository: quarto-dev/quarto-cli
Path: blob/main/src/publish/common/bundle.ts
6446 views
1
/*
2
* bundle.ts
3
*
4
* Copyright (C) 2020-2022 Posit Software, PBC
5
*/
6
7
import { dirname, join } from "../../deno_ral/path.ts";
8
import { ensureDirSync } from "../../deno_ral/fs.ts";
9
10
import { PublishFiles } from "../provider-types.ts";
11
import { TempContext } from "../../core/temp-types.ts";
12
import { md5HashBytes } from "../../core/hash.ts";
13
14
import { createTarFromFiles } from "../../deno_ral/tar.ts";
15
16
interface ManifestMetadata {
17
appmode: string;
18
primary_rmd: string | null;
19
primary_html: string;
20
content_category: string | null;
21
has_parameters: boolean;
22
}
23
24
interface Manifest {
25
version: number;
26
locale: string;
27
platform: string;
28
metadata: ManifestMetadata;
29
packages: null;
30
files: Record<string, { checksum: string }>;
31
users: null;
32
}
33
34
export interface BundleData {
35
bundlePath: string;
36
manifest: Manifest;
37
}
38
39
/** Creates a compressed bundle file in the format required by Posit Connect and Cloud.
40
* @param type Whether this is a site or document.
41
* @param files Information on what should be included in the bundle.
42
* @param tempContext Temporary directory where file operations will be performed.
43
* @returns The absolute path of the bundle file.
44
*/
45
export async function createBundle(
46
type: "site" | "document",
47
files: PublishFiles,
48
tempContext: TempContext,
49
): Promise<BundleData> {
50
// create file md5 checksums
51
const manifestFiles: Record<string, { checksum: string }> = {};
52
for (const file of files.files) {
53
const filePath = join(files.baseDir, file);
54
if (Deno.statSync(filePath).isFile) {
55
const fileBytes = Deno.readFileSync(filePath);
56
const checksum = await md5HashBytes(fileBytes);
57
manifestFiles[file] = { checksum };
58
}
59
}
60
61
// create manifest
62
const manifest = {
63
version: 1,
64
locale: "en_US",
65
platform: "4.2.0",
66
metadata: {
67
appmode: "static",
68
primary_rmd: null,
69
primary_html: files.rootFile,
70
content_category: type === "site" ? "site" : null,
71
has_parameters: false,
72
},
73
packages: null,
74
files: manifestFiles,
75
users: null,
76
};
77
78
// stage files into temp dir
79
const stageDir = tempContext.createDir();
80
files.files.forEach((file) => {
81
const filePath = join(files.baseDir, file);
82
if (Deno.statSync(filePath).isFile) {
83
const targetDir = join(stageDir, dirname(file));
84
ensureDirSync(targetDir);
85
Deno.copyFileSync(filePath, join(stageDir, file));
86
}
87
});
88
// write manifest
89
Deno.writeTextFileSync(
90
join(stageDir, "manifest.json"),
91
JSON.stringify(manifest, undefined, 2),
92
);
93
94
// create tar
95
// const tar = new Tar();
96
const tarFiles = [...files.files, "manifest.json"];
97
98
// for (const tarFile of tarFiles) {
99
// await tar.append(pathWithForwardSlashes(tarFile), {
100
// filePath: join(stageDir, tarFile),
101
// });
102
// }
103
104
// // write to temp file
105
const tarFile = tempContext.createFile({ suffix: ".tar" });
106
// const writer = Deno.openSync(tarFile, { write: true, create: true });
107
// await copy(tar.getReader(), writer);
108
// writer.close();
109
110
await createTarFromFiles(tarFile, tarFiles, { baseDir: stageDir });
111
112
// compress to tar.gz
113
const targzFile = `${tarFile}.gz`;
114
const src = await Deno.open(tarFile);
115
const dest = await Deno.open(targzFile, {
116
create: true,
117
write: true,
118
});
119
await src.readable
120
.pipeThrough(new CompressionStream("gzip"))
121
.pipeTo(dest.writable);
122
123
// return tar.gz
124
return {
125
bundlePath: targzFile,
126
manifest,
127
};
128
}
129
130