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