Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/build/lib/asar.ts
5240 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import path from 'path';
7
import es from 'event-stream';
8
import pickle from 'chromium-pickle-js';
9
import Filesystem from 'asar/lib/filesystem.js';
10
import VinylFile from 'vinyl';
11
import minimatch from 'minimatch';
12
13
export function createAsar(folderPath: string, unpackGlobs: string[], skipGlobs: string[], duplicateGlobs: string[], destFilename: string): NodeJS.ReadWriteStream {
14
15
const shouldUnpackFile = (file: VinylFile): boolean => {
16
for (let i = 0; i < unpackGlobs.length; i++) {
17
if (minimatch(file.relative, unpackGlobs[i])) {
18
return true;
19
}
20
}
21
return false;
22
};
23
24
const shouldSkipFile = (file: VinylFile): boolean => {
25
for (const skipGlob of skipGlobs) {
26
if (minimatch(file.relative, skipGlob)) {
27
return true;
28
}
29
}
30
return false;
31
};
32
33
// Files that should be duplicated between
34
// node_modules.asar and node_modules
35
const shouldDuplicateFile = (file: VinylFile): boolean => {
36
for (const duplicateGlob of duplicateGlobs) {
37
if (minimatch(file.relative, duplicateGlob)) {
38
return true;
39
}
40
}
41
return false;
42
};
43
44
const filesystem = new Filesystem(folderPath);
45
const out: Buffer[] = [];
46
47
// Keep track of pending inserts
48
let pendingInserts = 0;
49
let onFileInserted = () => { pendingInserts--; };
50
51
// Do not insert twice the same directory
52
const seenDir: { [key: string]: boolean } = {};
53
const insertDirectoryRecursive = (dir: string) => {
54
if (seenDir[dir]) {
55
return;
56
}
57
58
let lastSlash = dir.lastIndexOf('/');
59
if (lastSlash === -1) {
60
lastSlash = dir.lastIndexOf('\\');
61
}
62
if (lastSlash !== -1) {
63
insertDirectoryRecursive(dir.substring(0, lastSlash));
64
}
65
seenDir[dir] = true;
66
filesystem.insertDirectory(dir);
67
};
68
69
const insertDirectoryForFile = (file: string) => {
70
let lastSlash = file.lastIndexOf('/');
71
if (lastSlash === -1) {
72
lastSlash = file.lastIndexOf('\\');
73
}
74
if (lastSlash !== -1) {
75
insertDirectoryRecursive(file.substring(0, lastSlash));
76
}
77
};
78
79
const insertFile = (relativePath: string, stat: { size: number; mode: number }, shouldUnpack: boolean) => {
80
insertDirectoryForFile(relativePath);
81
pendingInserts++;
82
// Do not pass `onFileInserted` directly because it gets overwritten below.
83
// Create a closure capturing `onFileInserted`.
84
filesystem.insertFile(relativePath, shouldUnpack, { stat: stat }, {}).then(() => onFileInserted(), () => onFileInserted());
85
};
86
87
return es.through(function (file) {
88
if (file.stat.isDirectory()) {
89
return;
90
}
91
if (!file.stat.isFile()) {
92
throw new Error(`unknown item in stream!`);
93
}
94
if (shouldSkipFile(file)) {
95
this.queue(new VinylFile({
96
base: '.',
97
path: file.path,
98
stat: file.stat,
99
contents: file.contents
100
}));
101
return;
102
}
103
if (shouldDuplicateFile(file)) {
104
this.queue(new VinylFile({
105
base: '.',
106
path: file.path,
107
stat: file.stat,
108
contents: file.contents
109
}));
110
}
111
const shouldUnpack = shouldUnpackFile(file);
112
insertFile(file.relative, { size: file.contents.length, mode: file.stat.mode }, shouldUnpack);
113
114
if (shouldUnpack) {
115
// The file goes outside of xx.asar, in a folder xx.asar.unpacked
116
const relative = path.relative(folderPath, file.path);
117
this.queue(new VinylFile({
118
base: '.',
119
path: path.join(destFilename + '.unpacked', relative),
120
stat: file.stat,
121
contents: file.contents
122
}));
123
} else {
124
// The file goes inside of xx.asar
125
out.push(file.contents);
126
}
127
}, function () {
128
129
const finish = () => {
130
{
131
const headerPickle = pickle.createEmpty();
132
headerPickle.writeString(JSON.stringify(filesystem.header));
133
const headerBuf = headerPickle.toBuffer();
134
135
const sizePickle = pickle.createEmpty();
136
sizePickle.writeUInt32(headerBuf.length);
137
const sizeBuf = sizePickle.toBuffer();
138
139
out.unshift(headerBuf);
140
out.unshift(sizeBuf);
141
}
142
143
const contents = Buffer.concat(out);
144
out.length = 0;
145
146
this.queue(new VinylFile({
147
base: '.',
148
path: destFilename,
149
contents: contents
150
}));
151
this.queue(null);
152
};
153
154
// Call finish() only when all file inserts have finished...
155
if (pendingInserts === 0) {
156
finish();
157
} else {
158
onFileInserted = () => {
159
pendingInserts--;
160
if (pendingInserts === 0) {
161
finish();
162
}
163
};
164
}
165
});
166
}
167
168