acker-dacker 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -10
- package/package.json +34 -33
- package/src/cli/archive-name.ts +51 -0
- package/src/cli/cli-options.ts +166 -0
- package/src/{cli.ts → cli/cli.ts} +19 -8
- package/src/{git-handoff-selection.ts → git/git-handoff-selection.ts} +54 -2
- package/src/{git-handoff.ts → git/git-handoff.ts} +109 -28
- package/src/{git.ts → git/git.ts} +77 -0
- package/{index.ts → src/index.ts} +1 -1
- package/src/{file-policy.ts → pack/file-policy.ts} +1 -0
- package/src/pack/gitignore.ts +272 -0
- package/src/{repository-pack.ts → pack/repository-pack.ts} +149 -25
- package/src/zip/zip.ts +511 -0
- package/src/cli-options.ts +0 -93
- package/src/gitignore.ts +0 -139
- package/src/zip.ts +0 -327
- /package/src/{path-patterns.ts → pack/path-patterns.ts} +0 -0
package/src/gitignore.ts
DELETED
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
import { globPatternToRegexSource, isPathWithin, normalizeArchivePath } from './path-patterns';
|
|
3
|
-
|
|
4
|
-
type GitIgnoreRule = {
|
|
5
|
-
readonly directoryOnly: boolean;
|
|
6
|
-
readonly matcher: RegExp;
|
|
7
|
-
readonly negated: boolean;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
const parseGitIgnoreRule = (line: string): GitIgnoreRule | null => {
|
|
11
|
-
let pattern = line.replace(/[ \t]+$/u, '');
|
|
12
|
-
if (pattern.length === 0 || pattern.startsWith('#')) {
|
|
13
|
-
return null;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const escapedLeadingMarker = pattern.startsWith('\\#') || pattern.startsWith('\\!');
|
|
17
|
-
if (escapedLeadingMarker) {
|
|
18
|
-
pattern = pattern.slice(1);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
let negated = false;
|
|
22
|
-
if (!escapedLeadingMarker && pattern.startsWith('!')) {
|
|
23
|
-
negated = true;
|
|
24
|
-
pattern = pattern.slice(1);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const directoryOnly = pattern.endsWith('/') && !pattern.endsWith('\\/');
|
|
28
|
-
if (directoryOnly) {
|
|
29
|
-
pattern = pattern.slice(0, -1);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const anchored = pattern.startsWith('/');
|
|
33
|
-
if (anchored) {
|
|
34
|
-
pattern = pattern.slice(1);
|
|
35
|
-
}
|
|
36
|
-
if (pattern.length === 0) {
|
|
37
|
-
return null;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const hasSlash = pattern.includes('/');
|
|
41
|
-
const source = globPatternToRegexSource(pattern);
|
|
42
|
-
const matcher = new RegExp(anchored || hasSlash ? `^${source}$` : `(?:^|/)${source}(?:$|/)`);
|
|
43
|
-
|
|
44
|
-
return { directoryOnly, matcher, negated };
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
const parseGitIgnoreRules = (contents: string): readonly GitIgnoreRule[] => {
|
|
48
|
-
const rules: GitIgnoreRule[] = [];
|
|
49
|
-
for (const line of contents.split(/\r?\n/u)) {
|
|
50
|
-
const rule = parseGitIgnoreRule(line);
|
|
51
|
-
if (rule) {
|
|
52
|
-
rules.push(rule);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return rules;
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
const matchesGitIgnoreRule = (rule: GitIgnoreRule, relativePath: string, isDirectory: boolean): boolean => {
|
|
59
|
-
if (!rule.directoryOnly || isDirectory) {
|
|
60
|
-
return rule.matcher.test(relativePath);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const segments = relativePath.split('/');
|
|
64
|
-
for (let end = 1; end < segments.length; end += 1) {
|
|
65
|
-
if (rule.matcher.test(segments.slice(0, end).join('/'))) {
|
|
66
|
-
return true;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
return false;
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
export class GitIgnoreMatcher {
|
|
73
|
-
private readonly rulesByDirectory = new Map<string, readonly GitIgnoreRule[]>();
|
|
74
|
-
|
|
75
|
-
public constructor(private readonly rootDir: string) {}
|
|
76
|
-
|
|
77
|
-
private async rulesForDirectory(directory: string): Promise<readonly GitIgnoreRule[]> {
|
|
78
|
-
const cachedRules = this.rulesByDirectory.get(directory);
|
|
79
|
-
if (cachedRules) {
|
|
80
|
-
return cachedRules;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const ignoreFile = Bun.file(path.join(directory, '.gitignore'));
|
|
84
|
-
if (!(await ignoreFile.exists())) {
|
|
85
|
-
this.rulesByDirectory.set(directory, []);
|
|
86
|
-
return [];
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
try {
|
|
90
|
-
const rules = parseGitIgnoreRules(await ignoreFile.text());
|
|
91
|
-
this.rulesByDirectory.set(directory, rules);
|
|
92
|
-
return rules;
|
|
93
|
-
} catch {
|
|
94
|
-
this.rulesByDirectory.set(directory, []);
|
|
95
|
-
return [];
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
public async isIgnored(absolutePath: string, isDirectory: boolean): Promise<boolean> {
|
|
100
|
-
const parentDirectory = path.dirname(absolutePath);
|
|
101
|
-
if (!isPathWithin(parentDirectory, this.rootDir)) {
|
|
102
|
-
return false;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const directories: string[] = [];
|
|
106
|
-
let directory = parentDirectory;
|
|
107
|
-
while (true) {
|
|
108
|
-
directories.push(directory);
|
|
109
|
-
if (directory === this.rootDir) {
|
|
110
|
-
break;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const parent = path.dirname(directory);
|
|
114
|
-
if (parent === directory || !isPathWithin(parent, this.rootDir)) {
|
|
115
|
-
return false;
|
|
116
|
-
}
|
|
117
|
-
directory = parent;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
directories.reverse();
|
|
121
|
-
|
|
122
|
-
let ignored = false;
|
|
123
|
-
for (const ruleDirectory of directories) {
|
|
124
|
-
const relativePath = normalizeArchivePath(path.relative(ruleDirectory, absolutePath));
|
|
125
|
-
if (relativePath.length === 0 || relativePath === '..' || relativePath.startsWith('../')) {
|
|
126
|
-
continue;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const rules = await this.rulesForDirectory(ruleDirectory);
|
|
130
|
-
for (const rule of rules) {
|
|
131
|
-
if (matchesGitIgnoreRule(rule, relativePath, isDirectory)) {
|
|
132
|
-
ignored = !rule.negated;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return ignored;
|
|
138
|
-
}
|
|
139
|
-
}
|
package/src/zip.ts
DELETED
|
@@ -1,327 +0,0 @@
|
|
|
1
|
-
import { lstat, mkdir, open, readdir, readlink } from 'node:fs/promises';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { deflateRawSync } from 'node:zlib';
|
|
4
|
-
import { compareStableStrings, normalizeArchivePath } from './path-patterns';
|
|
5
|
-
|
|
6
|
-
type ZipEntryKind = 'file' | 'symlink';
|
|
7
|
-
|
|
8
|
-
type ZipFileEntry = {
|
|
9
|
-
readonly absolutePath: string;
|
|
10
|
-
readonly archivePath: string;
|
|
11
|
-
readonly kind: ZipEntryKind;
|
|
12
|
-
readonly linkTarget?: string;
|
|
13
|
-
readonly mode?: number;
|
|
14
|
-
readonly size: number;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
type WrittenZipEntry = ZipFileEntry & {
|
|
18
|
-
readonly compressedSize: number;
|
|
19
|
-
readonly compressionMethod: number;
|
|
20
|
-
readonly crc32: number;
|
|
21
|
-
readonly localHeaderOffset: number;
|
|
22
|
-
readonly pathBytes: Uint8Array;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
export type ZipCreationOptions = {
|
|
26
|
-
readonly preserveSymlinks: boolean;
|
|
27
|
-
readonly preserveUnixMetadata: boolean;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
const ZIP_MAX_UINT32 = 0xffffffff;
|
|
31
|
-
const ZIP_MAX_UINT16 = 0xffff;
|
|
32
|
-
const ZIP_DOS_DATE_1980_01_01 = 0x21;
|
|
33
|
-
const ZIP_DEFLATE_METHOD = 8;
|
|
34
|
-
const ZIP_STORE_METHOD = 0;
|
|
35
|
-
const ZIP_UNIX_CREATOR_VERSION = 0x0314;
|
|
36
|
-
const MAX_BUFFERED_DEFLATE_BYTES = 64 * 1024 * 1024;
|
|
37
|
-
|
|
38
|
-
const CRC32_TABLE = new Uint32Array(256);
|
|
39
|
-
for (let index = 0; index < CRC32_TABLE.length; index += 1) {
|
|
40
|
-
let value = index;
|
|
41
|
-
for (let bit = 0; bit < 8; bit += 1) {
|
|
42
|
-
value = value & 1 ? 0xedb88320 ^ (value >>> 1) : value >>> 1;
|
|
43
|
-
}
|
|
44
|
-
CRC32_TABLE[index] = value >>> 0;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const updateCrc32 = (crc: number, chunk: Uint8Array): number => {
|
|
48
|
-
let current = crc;
|
|
49
|
-
for (const byte of chunk) {
|
|
50
|
-
current = CRC32_TABLE[(current ^ byte) & 0xff]! ^ (current >>> 8);
|
|
51
|
-
}
|
|
52
|
-
return current >>> 0;
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
const collectZipFiles = async ({
|
|
56
|
-
directory,
|
|
57
|
-
excludedAbsolutePath,
|
|
58
|
-
preserveSymlinks,
|
|
59
|
-
preserveUnixMetadata,
|
|
60
|
-
rootDir,
|
|
61
|
-
}: {
|
|
62
|
-
readonly directory: string;
|
|
63
|
-
readonly excludedAbsolutePath: string;
|
|
64
|
-
readonly preserveSymlinks: boolean;
|
|
65
|
-
readonly preserveUnixMetadata: boolean;
|
|
66
|
-
readonly rootDir: string;
|
|
67
|
-
}): Promise<readonly ZipFileEntry[]> => {
|
|
68
|
-
const entries = await readdir(directory, { withFileTypes: true });
|
|
69
|
-
const files: ZipFileEntry[] = [];
|
|
70
|
-
for (const entry of entries.sort((left, right) => compareStableStrings(left.name, right.name))) {
|
|
71
|
-
const absolutePath = path.join(directory, entry.name);
|
|
72
|
-
if (absolutePath === excludedAbsolutePath) {
|
|
73
|
-
continue;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
if (entry.isSymbolicLink()) {
|
|
77
|
-
if (!preserveSymlinks) {
|
|
78
|
-
continue;
|
|
79
|
-
}
|
|
80
|
-
const linkTarget = await readlink(absolutePath);
|
|
81
|
-
files.push({
|
|
82
|
-
absolutePath,
|
|
83
|
-
archivePath: normalizeArchivePath(path.relative(rootDir, absolutePath)),
|
|
84
|
-
kind: 'symlink',
|
|
85
|
-
linkTarget,
|
|
86
|
-
mode: 0o120777,
|
|
87
|
-
size: new TextEncoder().encode(linkTarget).byteLength,
|
|
88
|
-
});
|
|
89
|
-
continue;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (entry.isDirectory()) {
|
|
93
|
-
files.push(
|
|
94
|
-
...(await collectZipFiles({
|
|
95
|
-
directory: absolutePath,
|
|
96
|
-
excludedAbsolutePath,
|
|
97
|
-
preserveSymlinks,
|
|
98
|
-
preserveUnixMetadata,
|
|
99
|
-
rootDir,
|
|
100
|
-
})),
|
|
101
|
-
);
|
|
102
|
-
continue;
|
|
103
|
-
}
|
|
104
|
-
if (!entry.isFile()) {
|
|
105
|
-
continue;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const fileStats = await lstat(absolutePath);
|
|
109
|
-
if (fileStats.size > ZIP_MAX_UINT32) {
|
|
110
|
-
throw new Error(`Zip entry exceeds 4 GB limit: ${absolutePath}`);
|
|
111
|
-
}
|
|
112
|
-
files.push({
|
|
113
|
-
absolutePath,
|
|
114
|
-
archivePath: normalizeArchivePath(path.relative(rootDir, absolutePath)),
|
|
115
|
-
kind: 'file',
|
|
116
|
-
mode: preserveUnixMetadata ? fileStats.mode : undefined,
|
|
117
|
-
size: fileStats.size,
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
return files;
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
const writeBuffer = async (
|
|
124
|
-
handle: Awaited<ReturnType<typeof open>>,
|
|
125
|
-
buffer: Uint8Array,
|
|
126
|
-
offset: number,
|
|
127
|
-
): Promise<number> => {
|
|
128
|
-
await handle.write(buffer, 0, buffer.length, offset);
|
|
129
|
-
return offset + buffer.length;
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
const writeZipLocalHeader = async (
|
|
133
|
-
handle: Awaited<ReturnType<typeof open>>,
|
|
134
|
-
entry: ZipFileEntry,
|
|
135
|
-
pathBytes: Uint8Array,
|
|
136
|
-
crc32: number,
|
|
137
|
-
compressedSize: number,
|
|
138
|
-
compressionMethod: number,
|
|
139
|
-
offset: number,
|
|
140
|
-
): Promise<number> => {
|
|
141
|
-
const header = Buffer.alloc(30);
|
|
142
|
-
header.writeUInt32LE(0x04034b50, 0);
|
|
143
|
-
header.writeUInt16LE(20, 4);
|
|
144
|
-
header.writeUInt16LE(0, 6);
|
|
145
|
-
header.writeUInt16LE(compressionMethod, 8);
|
|
146
|
-
header.writeUInt16LE(0, 10);
|
|
147
|
-
header.writeUInt16LE(ZIP_DOS_DATE_1980_01_01, 12);
|
|
148
|
-
header.writeUInt32LE(crc32, 14);
|
|
149
|
-
header.writeUInt32LE(compressedSize, 18);
|
|
150
|
-
header.writeUInt32LE(entry.size, 22);
|
|
151
|
-
header.writeUInt16LE(pathBytes.length, 26);
|
|
152
|
-
header.writeUInt16LE(0, 28);
|
|
153
|
-
const nextOffset = await writeBuffer(handle, header, offset);
|
|
154
|
-
return await writeBuffer(handle, pathBytes, nextOffset);
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
const computeFileCrc32 = async (absolutePath: string): Promise<number> => {
|
|
158
|
-
const reader = Bun.file(absolutePath).stream().getReader();
|
|
159
|
-
let crc = 0xffffffff;
|
|
160
|
-
try {
|
|
161
|
-
while (true) {
|
|
162
|
-
const { done, value } = await reader.read();
|
|
163
|
-
if (done) {
|
|
164
|
-
break;
|
|
165
|
-
}
|
|
166
|
-
crc = updateCrc32(crc, value);
|
|
167
|
-
}
|
|
168
|
-
} finally {
|
|
169
|
-
reader.releaseLock();
|
|
170
|
-
}
|
|
171
|
-
return (crc ^ 0xffffffff) >>> 0;
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
const prepareFileBytes = async (entry: ZipFileEntry) => {
|
|
175
|
-
if (entry.kind === 'symlink') {
|
|
176
|
-
const source = new TextEncoder().encode(entry.linkTarget ?? '');
|
|
177
|
-
const compressed = deflateRawSync(source, { level: 9 });
|
|
178
|
-
if (compressed.byteLength >= source.byteLength) {
|
|
179
|
-
return {
|
|
180
|
-
bytes: source,
|
|
181
|
-
compressedSize: source.byteLength,
|
|
182
|
-
compressionMethod: ZIP_STORE_METHOD,
|
|
183
|
-
crc32: (updateCrc32(0xffffffff, source) ^ 0xffffffff) >>> 0,
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
return {
|
|
187
|
-
bytes: compressed,
|
|
188
|
-
compressedSize: compressed.byteLength,
|
|
189
|
-
compressionMethod: ZIP_DEFLATE_METHOD,
|
|
190
|
-
crc32: (updateCrc32(0xffffffff, source) ^ 0xffffffff) >>> 0,
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
if (entry.size > MAX_BUFFERED_DEFLATE_BYTES) {
|
|
195
|
-
return {
|
|
196
|
-
bytes: null,
|
|
197
|
-
compressedSize: entry.size,
|
|
198
|
-
compressionMethod: ZIP_STORE_METHOD,
|
|
199
|
-
crc32: await computeFileCrc32(entry.absolutePath),
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
const source = new Uint8Array(await Bun.file(entry.absolutePath).arrayBuffer());
|
|
203
|
-
const compressed = deflateRawSync(source, { level: 9 });
|
|
204
|
-
if (compressed.byteLength >= source.byteLength) {
|
|
205
|
-
return {
|
|
206
|
-
bytes: source,
|
|
207
|
-
compressedSize: source.byteLength,
|
|
208
|
-
compressionMethod: ZIP_STORE_METHOD,
|
|
209
|
-
crc32: (updateCrc32(0xffffffff, source) ^ 0xffffffff) >>> 0,
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
return {
|
|
213
|
-
bytes: compressed,
|
|
214
|
-
compressedSize: compressed.byteLength,
|
|
215
|
-
compressionMethod: ZIP_DEFLATE_METHOD,
|
|
216
|
-
crc32: (updateCrc32(0xffffffff, source) ^ 0xffffffff) >>> 0,
|
|
217
|
-
};
|
|
218
|
-
};
|
|
219
|
-
|
|
220
|
-
export const createZipFile = async (
|
|
221
|
-
rootDir: string,
|
|
222
|
-
outputFile: string,
|
|
223
|
-
options: ZipCreationOptions = { preserveSymlinks: false, preserveUnixMetadata: false },
|
|
224
|
-
): Promise<void> => {
|
|
225
|
-
const absoluteOutputFile = path.resolve(outputFile);
|
|
226
|
-
const absoluteRootDir = path.resolve(rootDir);
|
|
227
|
-
await mkdir(path.dirname(absoluteOutputFile), { recursive: true });
|
|
228
|
-
const handle = await open(absoluteOutputFile, 'w');
|
|
229
|
-
|
|
230
|
-
try {
|
|
231
|
-
let offset = 0;
|
|
232
|
-
const writtenEntries: WrittenZipEntry[] = [];
|
|
233
|
-
const files = await collectZipFiles({
|
|
234
|
-
directory: absoluteRootDir,
|
|
235
|
-
excludedAbsolutePath: absoluteOutputFile,
|
|
236
|
-
preserveSymlinks: options.preserveSymlinks,
|
|
237
|
-
preserveUnixMetadata: options.preserveUnixMetadata,
|
|
238
|
-
rootDir: absoluteRootDir,
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
for (const entry of files) {
|
|
242
|
-
const pathBytes = new TextEncoder().encode(entry.archivePath);
|
|
243
|
-
if (pathBytes.length > ZIP_MAX_UINT16) {
|
|
244
|
-
throw new Error(`Path exceeds ZIP16 limit: ${entry.archivePath}`);
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
const prepared = await prepareFileBytes(entry);
|
|
248
|
-
const localHeaderOffset = offset;
|
|
249
|
-
offset = await writeZipLocalHeader(
|
|
250
|
-
handle,
|
|
251
|
-
entry,
|
|
252
|
-
pathBytes,
|
|
253
|
-
prepared.crc32,
|
|
254
|
-
prepared.compressedSize,
|
|
255
|
-
prepared.compressionMethod,
|
|
256
|
-
offset,
|
|
257
|
-
);
|
|
258
|
-
|
|
259
|
-
if (prepared.bytes) {
|
|
260
|
-
offset = await writeBuffer(handle, prepared.bytes, offset);
|
|
261
|
-
} else {
|
|
262
|
-
const reader = Bun.file(entry.absolutePath).stream().getReader();
|
|
263
|
-
try {
|
|
264
|
-
while (true) {
|
|
265
|
-
const { done, value } = await reader.read();
|
|
266
|
-
if (done) {
|
|
267
|
-
break;
|
|
268
|
-
}
|
|
269
|
-
offset = await writeBuffer(handle, value, offset);
|
|
270
|
-
}
|
|
271
|
-
} finally {
|
|
272
|
-
reader.releaseLock();
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
writtenEntries.push({
|
|
277
|
-
...entry,
|
|
278
|
-
compressedSize: prepared.compressedSize,
|
|
279
|
-
compressionMethod: prepared.compressionMethod,
|
|
280
|
-
crc32: prepared.crc32,
|
|
281
|
-
localHeaderOffset,
|
|
282
|
-
pathBytes,
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
const centralDirectoryOffset = offset;
|
|
287
|
-
for (const entry of writtenEntries) {
|
|
288
|
-
const header = Buffer.alloc(46);
|
|
289
|
-
header.writeUInt32LE(0x02014b50, 0);
|
|
290
|
-
header.writeUInt16LE(options.preserveUnixMetadata ? ZIP_UNIX_CREATOR_VERSION : 20, 4);
|
|
291
|
-
header.writeUInt16LE(20, 6);
|
|
292
|
-
header.writeUInt16LE(0, 8);
|
|
293
|
-
header.writeUInt16LE(entry.compressionMethod, 10);
|
|
294
|
-
header.writeUInt16LE(0, 12);
|
|
295
|
-
header.writeUInt16LE(ZIP_DOS_DATE_1980_01_01, 14);
|
|
296
|
-
header.writeUInt32LE(entry.crc32, 16);
|
|
297
|
-
header.writeUInt32LE(entry.compressedSize, 20);
|
|
298
|
-
header.writeUInt32LE(entry.size, 24);
|
|
299
|
-
header.writeUInt16LE(entry.pathBytes.length, 28);
|
|
300
|
-
header.writeUInt16LE(0, 30);
|
|
301
|
-
header.writeUInt16LE(0, 32);
|
|
302
|
-
header.writeUInt16LE(0, 34);
|
|
303
|
-
header.writeUInt16LE(0, 36);
|
|
304
|
-
const externalAttributes = options.preserveUnixMetadata
|
|
305
|
-
? ((((entry.mode ?? 0o100644) & 0xffff) << 16) >>> 0)
|
|
306
|
-
: 0;
|
|
307
|
-
header.writeUInt32LE(externalAttributes, 38);
|
|
308
|
-
header.writeUInt32LE(entry.localHeaderOffset, 42);
|
|
309
|
-
offset = await writeBuffer(handle, header, offset);
|
|
310
|
-
offset = await writeBuffer(handle, entry.pathBytes, offset);
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
const centralDirectorySize = offset - centralDirectoryOffset;
|
|
314
|
-
const endHeader = Buffer.alloc(22);
|
|
315
|
-
endHeader.writeUInt32LE(0x06054b50, 0);
|
|
316
|
-
endHeader.writeUInt16LE(0, 4);
|
|
317
|
-
endHeader.writeUInt16LE(0, 6);
|
|
318
|
-
endHeader.writeUInt16LE(writtenEntries.length, 8);
|
|
319
|
-
endHeader.writeUInt16LE(writtenEntries.length, 10);
|
|
320
|
-
endHeader.writeUInt32LE(centralDirectorySize, 12);
|
|
321
|
-
endHeader.writeUInt32LE(centralDirectoryOffset, 16);
|
|
322
|
-
endHeader.writeUInt16LE(0, 20);
|
|
323
|
-
await writeBuffer(handle, endHeader, offset);
|
|
324
|
-
} finally {
|
|
325
|
-
await handle.close();
|
|
326
|
-
}
|
|
327
|
-
};
|
|
File without changes
|