-
Notifications
You must be signed in to change notification settings - Fork 80
Expand file tree
/
Copy pathgetAppSourceZip.ts
More file actions
103 lines (84 loc) · 3.03 KB
/
getAppSourceZip.ts
File metadata and controls
103 lines (84 loc) · 3.03 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
import fsp from 'node:fs/promises';
import path from 'node:path';
import ignore from 'ignore';
import JSZip from 'jszip';
import { DevvitCommand } from './commands/DevvitCommand.js';
const ALWAYS_IGNORED_PATHS = Object.freeze(['node_modules', '.env', '.git']);
export async function getAppSourceZip(cmd: DevvitCommand): Promise<ArrayBuffer> {
const zip = new JSZip();
const ignoredPaths = await getIgnoredPaths(cmd);
await addDirectoryToZip(cmd.project.root, zip, ignoredPaths);
return await zip.generateAsync({ type: 'arraybuffer' });
}
async function getIgnoredPaths(cmd: DevvitCommand): Promise<ignore.Ignore> {
const ignoreInstance = ignore();
// Ignore everything in .gitignore
const gitignorePath = path.join(cmd.project.root, '.gitignore');
try {
const gitignoreContent = await fsp.readFile(gitignorePath, 'utf-8');
const gitignorePaths = gitignoreContent
.split(/\r?\n/) // System-agnostic line splitting
.map((line) => line.trim());
ignoreInstance.add([...gitignorePaths, ...ALWAYS_IGNORED_PATHS]);
} catch {
// no-op
}
// Ignore any additional paths specified in app config
if (cmd.project.appConfig?.sourceIgnores) {
ignoreInstance.add(cmd.project.appConfig.sourceIgnores);
}
// Finally, force ignore always-ignored paths
return ignoreInstance.add(ALWAYS_IGNORED_PATHS);
}
async function addDirectoryToZip(
dir: string,
zipFolder: JSZip,
ignoredPaths: ignore.Ignore,
relativePath = ''
): Promise<void> {
// Check for nested .gitignore
const nestedGitignorePath = path.join(dir, '.gitignore');
try {
const nestedGitignoreContent = await fsp.readFile(nestedGitignorePath, 'utf-8');
const nestedGitignorePaths = nestedGitignoreContent
.split(/\r?\n/)
.map((line) => line.trim());
ignoredPaths = ignore().add(ignoredPaths).add(nestedGitignorePaths);
} catch {
// no-op
}
const entries = await fsp.readdir(dir, { withFileTypes: true });
// Process entries in parallel
const filePromises: Promise<void>[] = [];
const dirCallbacks: (() => Promise<void>)[] = [];
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
const relativeEntryPath = path.join(relativePath, entry.name);
if (ignoredPaths.ignores(relativeEntryPath)) {
continue;
}
if (entry.isDirectory()) {
const newZipFolder = zipFolder.folder(entry.name);
if (newZipFolder) {
// Defer directory processing to avoid overtaxing the system
dirCallbacks.push(() =>
addDirectoryToZip(fullPath, newZipFolder, ignoredPaths, relativeEntryPath)
);
}
} else if (entry.isFile()) {
// Read files & add them in parallel though
filePromises.push(
(async () => {
const content = await fsp.readFile(fullPath);
zipFolder.file(entry.name, content);
})()
);
}
}
// Wait for all file reads to complete
await Promise.all(filePromises);
// Then, process the directories in serial
for (const dirCallback of dirCallbacks) {
await dirCallback();
}
}