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