1
1
import { AsarFileInfo , listPackage , statFile , AsarOptions } from "asar-electron-builder"
2
- import { statOrNull } from "./util/util"
3
- import { lstat , readdir , readFile , Stats , createWriteStream , ensureDir , createReadStream } from "fs-extra-p"
2
+ import { statOrNull , debug } from "./util/util"
3
+ import { lstat , readdir , readFile , Stats , createWriteStream , ensureDir , createReadStream , readJson } from "fs-extra-p"
4
4
import { Promise as BluebirdPromise } from "bluebird"
5
5
import * as path from "path"
6
6
import pathSorter = require( "path-sort" )
@@ -14,7 +14,8 @@ const Filesystem = require("asar-electron-builder/lib/filesystem")
14
14
//noinspection JSUnusedLocalSymbols
15
15
const __awaiter = require ( "./util/awaiter" )
16
16
17
- const concurrency = { concurrency : 50 }
17
+ const MAX_FILE_REQUESTS = 32
18
+ const concurrency = { concurrency : MAX_FILE_REQUESTS }
18
19
const NODE_MODULES_PATTERN = path . sep + "node_modules" + path . sep
19
20
20
21
function walk ( dirPath : string , consumer : ( file : string , stat : Stats ) => void , filter : ( file : string ) => boolean , addRootToResult ?: boolean ) : BluebirdPromise < Array < string > > {
@@ -122,72 +123,100 @@ async function order(src: string, filenames: Array<string>, options: any) {
122
123
return filenamesSorted
123
124
}
124
125
125
- async function createPackageFromFiles ( src : string , dest : string , files : Array < string > , metadata : Map < string , Stats > , options : any ) {
126
- // search auto unpacked dir
127
- const autoUnpackDirs = new Set < string > ( )
128
-
129
- const createDirPromises : Array < Promise < any > > = [ ensureDir ( path . dirname ( dest ) ) ]
130
- const unpackedDest = `${ dest } .unpacked`
131
-
132
- if ( options . smartUnpack !== false ) {
133
- for ( let file of files ) {
134
- const index = file . lastIndexOf ( NODE_MODULES_PATTERN )
135
- if ( index < 0 ) {
136
- continue
137
- }
126
+ async function detectUnpackedDirs ( src : string , files : Array < string > , metadata : Map < string , Stats > , autoUnpackDirs : Set < string > , createDirPromises : Array < Promise < any > > , unpackedDest : string , packageFileToData : Map < string , BluebirdPromise < string > > ) {
127
+ const packageJsonStringLength = "package.json" . length
128
+ const readPackageJsonPromises : Array < Promise < any > > = [ ]
129
+ for ( let file of files ) {
130
+ const index = file . lastIndexOf ( NODE_MODULES_PATTERN )
131
+ if ( index < 0 ) {
132
+ continue
133
+ }
138
134
139
- const nextSlashIndex = file . indexOf ( path . sep , index + NODE_MODULES_PATTERN . length + 1 )
140
- if ( nextSlashIndex < 0 ) {
141
- continue
142
- }
135
+ const nextSlashIndex = file . indexOf ( path . sep , index + NODE_MODULES_PATTERN . length + 1 )
136
+ if ( nextSlashIndex < 0 ) {
137
+ continue
138
+ }
143
139
144
- if ( ! metadata . get ( file ) ! . isFile ( ) ) {
145
- continue
146
- }
140
+ if ( ! metadata . get ( file ) ! . isFile ( ) ) {
141
+ continue
142
+ }
147
143
148
- const nodeModuleDir = file . substring ( 0 , nextSlashIndex )
149
- if ( autoUnpackDirs . has ( nodeModuleDir ) ) {
150
- const fileParent = path . dirname ( file )
151
- if ( fileParent != nodeModuleDir && ! autoUnpackDirs . has ( fileParent ) ) {
152
- autoUnpackDirs . add ( fileParent )
153
- createDirPromises . push ( ensureDir ( path . join ( unpackedDest , path . relative ( src , fileParent ) ) ) )
154
- }
155
- continue
156
- }
144
+ const nodeModuleDir = file . substring ( 0 , nextSlashIndex )
157
145
158
- const ext = path . extname ( file )
159
- let shouldUnpack = false
160
- if ( ext === ".dll" || ext === ".exe" ) {
161
- shouldUnpack = true
162
- }
163
- else if ( ext === "" ) {
164
- shouldUnpack = await isBinaryFile ( file )
165
- }
146
+ if ( file . length == ( nodeModuleDir . length + 1 + packageJsonStringLength ) && file . endsWith ( "package.json" ) ) {
147
+ const promise = readJson ( file )
166
148
167
- if ( ! shouldUnpack ) {
168
- continue
149
+ if ( readPackageJsonPromises . length > MAX_FILE_REQUESTS ) {
150
+ await BluebirdPromise . all ( readPackageJsonPromises )
151
+ readPackageJsonPromises . length = 0
169
152
}
153
+ readPackageJsonPromises . push ( promise )
154
+ packageFileToData . set ( file , promise )
155
+ }
170
156
171
- log ( `${ path . relative ( src , nodeModuleDir ) } is not packed into asar archive - contains executable code` )
172
- autoUnpackDirs . add ( nodeModuleDir )
157
+ if ( autoUnpackDirs . has ( nodeModuleDir ) ) {
173
158
const fileParent = path . dirname ( file )
174
- if ( fileParent != nodeModuleDir ) {
159
+ if ( fileParent != nodeModuleDir && ! autoUnpackDirs . has ( fileParent ) ) {
175
160
autoUnpackDirs . add ( fileParent )
176
- // create parent dir to be able to copy file later without directory existence check
161
+
162
+ if ( createDirPromises . length > MAX_FILE_REQUESTS ) {
163
+ await BluebirdPromise . all ( createDirPromises )
164
+ createDirPromises . length = 0
165
+ }
177
166
createDirPromises . push ( ensureDir ( path . join ( unpackedDest , path . relative ( src , fileParent ) ) ) )
178
167
}
168
+ continue
179
169
}
180
- }
181
170
182
- const unpackDir = options . unpackDir == null ? null : new Minimatch ( options . unpackDir )
183
- const unpack = options . unpack == null ? null : new Minimatch ( options . unpack , {
184
- matchBase : true
185
- } )
171
+ const ext = path . extname ( file )
172
+ let shouldUnpack = false
173
+ if ( ext === ".dll" || ext === ".exe" ) {
174
+ shouldUnpack = true
175
+ }
176
+ else if ( ext === "" ) {
177
+ shouldUnpack = await isBinaryFile ( file )
178
+ }
179
+
180
+ if ( ! shouldUnpack ) {
181
+ continue
182
+ }
186
183
184
+ log ( `${ path . relative ( src , nodeModuleDir ) } is not packed into asar archive - contains executable code` )
185
+ autoUnpackDirs . add ( nodeModuleDir )
186
+ const fileParent = path . dirname ( file )
187
+ if ( fileParent != nodeModuleDir ) {
188
+ autoUnpackDirs . add ( fileParent )
189
+ // create parent dir to be able to copy file later without directory existence check
190
+ createDirPromises . push ( ensureDir ( path . join ( unpackedDest , path . relative ( src , fileParent ) ) ) )
191
+ }
192
+ }
193
+
194
+ if ( readPackageJsonPromises . length > 0 ) {
195
+ await BluebirdPromise . all ( readPackageJsonPromises )
196
+ }
187
197
if ( createDirPromises . length > 0 ) {
188
198
await BluebirdPromise . all ( createDirPromises )
189
199
createDirPromises . length = 0
190
200
}
201
+ }
202
+
203
+ async function createPackageFromFiles ( src : string , dest : string , files : Array < string > , metadata : Map < string , Stats > , options : any ) {
204
+ // search auto unpacked dir
205
+ const autoUnpackDirs = new Set < string > ( )
206
+
207
+ const createDirPromises : Array < Promise < any > > = [ ensureDir ( path . dirname ( dest ) ) ]
208
+ const unpackedDest = `${ dest } .unpacked`
209
+ const changedFiles = new Map < string , string > ( )
210
+
211
+ const packageFileToData = new Map < string , BluebirdPromise < string > > ( )
212
+ if ( options . smartUnpack !== false ) {
213
+ await detectUnpackedDirs ( src , files , metadata , autoUnpackDirs , createDirPromises , unpackedDest , packageFileToData )
214
+ }
215
+
216
+ const unpackDir = options . unpackDir == null ? null : new Minimatch ( options . unpackDir )
217
+ const unpack = options . unpack == null ? null : new Minimatch ( options . unpack , {
218
+ matchBase : true
219
+ } )
191
220
192
221
const toPack : Array < string > = [ ]
193
222
const filesystem = new Filesystem ( src )
@@ -217,7 +246,7 @@ async function createPackageFromFiles(src: string, dest: string, files: Array<st
217
246
218
247
copyPromises . push ( copyFile ( file , path . join ( unpackedDest , path . relative ( src , file ) ) , stat ) )
219
248
// limit concurrency
220
- if ( copyPromises . length > 50 ) {
249
+ if ( copyPromises . length > MAX_FILE_REQUESTS ) {
221
250
await BluebirdPromise . all ( copyPromises )
222
251
copyPromises . length = 0
223
252
}
@@ -226,6 +255,11 @@ async function createPackageFromFiles(src: string, dest: string, files: Array<st
226
255
toPack . push ( file )
227
256
}
228
257
258
+ const packageDataPromise = packageFileToData . get ( file )
259
+ if ( packageDataPromise != null ) {
260
+ cleanupPackageJson ( file , stat , packageDataPromise . value ( ) , changedFiles )
261
+ }
262
+
229
263
filesystem . insertFile ( file , shouldUnpack , stat )
230
264
}
231
265
else if ( stat . isDirectory ( ) ) {
@@ -259,10 +293,31 @@ async function createPackageFromFiles(src: string, dest: string, files: Array<st
259
293
}
260
294
261
295
await BluebirdPromise . all ( copyPromises )
262
- await writeAsarFile ( filesystem , dest , toPack )
296
+ await writeAsarFile ( filesystem , dest , toPack , changedFiles )
263
297
}
264
298
265
- function writeAsarFile ( filesystem : any , dest : string , toPack : Array < string > ) : Promise < any > {
299
+ function cleanupPackageJson ( file : string , stat : Stats , data : any , changedFiles : Map < string , string > ) {
300
+ try {
301
+ let writeFile = false
302
+ for ( let prop of Object . getOwnPropertyNames ( data ) ) {
303
+ if ( prop [ 0 ] === "_" || prop === "dist" || prop === "gitHead" || prop === "keywords" ) {
304
+ delete data [ prop ]
305
+ writeFile = true
306
+ }
307
+ }
308
+
309
+ if ( writeFile ) {
310
+ const value = JSON . stringify ( data , null , 2 )
311
+ changedFiles . set ( file , value )
312
+ stat . size = Buffer . byteLength ( value )
313
+ }
314
+ }
315
+ catch ( e ) {
316
+ debug ( e )
317
+ }
318
+ }
319
+
320
+ function writeAsarFile ( filesystem : any , dest : string , toPack : Array < string > , changedFiles : Map < string , string > ) : Promise < any > {
266
321
const headerPickle = pickle . createEmpty ( )
267
322
headerPickle . writeString ( JSON . stringify ( filesystem . header ) )
268
323
const headerBuf = headerPickle . toBuffer ( )
@@ -283,7 +338,15 @@ function writeAsarFile(filesystem: any, dest: string, toPack: Array<string>): Pr
283
338
return
284
339
}
285
340
286
- const readStream = createReadStream ( list [ index ] )
341
+ const file = list [ index ]
342
+
343
+ const data = changedFiles . get ( file )
344
+ if ( data != null ) {
345
+ writeStream . write ( data , ( ) => w ( list , index + 1 ) )
346
+ return
347
+ }
348
+
349
+ const readStream = createReadStream ( file )
287
350
readStream . on ( "error" , reject )
288
351
readStream . once ( "end" , ( ) => w ( list , index + 1 ) )
289
352
readStream . pipe ( writeStream , {
0 commit comments