@@ -3,9 +3,10 @@ import { AsarOptions } from "electron-builder-core"
3
3
import { debug } from "electron-builder-util"
4
4
import { CONCURRENCY , FileCopier , FileTransformer , Filter , MAX_FILE_REQUESTS , statOrNull , walk } from "electron-builder-util/out/fs"
5
5
import { log } from "electron-builder-util/out/log"
6
- import { createReadStream , createWriteStream , ensureDir , readFile , readlink , stat , Stats , writeFile } from "fs-extra-p"
6
+ import { createReadStream , createWriteStream , emptyDir , ensureDir , readFile , readlink , stat , Stats , writeFile } from "fs-extra-p"
7
7
import * as path from "path"
8
8
import { AsarFilesystem , Node , readAsar } from "./asar"
9
+ import { createElectronCompilerHost } from "./fileTransformer"
9
10
10
11
const isBinaryFile : any = BluebirdPromise . promisify ( require ( "isbinaryfile" ) )
11
12
const pickle = require ( "chromium-pickle-js" )
@@ -26,7 +27,7 @@ function addValue(map: Map<string, Array<string>>, key: string, value: string) {
26
27
interface UnpackedFileTask {
27
28
stats : Stats
28
29
src ?: string
29
- data ?: string
30
+ data ?: string | Buffer
30
31
destination : string
31
32
}
32
33
@@ -44,15 +45,18 @@ function writeUnpackedFiles(filesToUnpack: Array<UnpackedFileTask>, fileCopier:
44
45
export class AsarPackager {
45
46
private readonly fs = new AsarFilesystem ( this . src )
46
47
private readonly outFile : string
48
+
49
+ private transformedFiles : Array < string | Buffer | true | null >
50
+ private readonly metadata = new Map < string , Stats > ( )
47
51
48
- constructor ( private readonly src : string , destination : string , private readonly options : AsarOptions , private readonly unpackPattern : Filter | null ) {
52
+ constructor ( private readonly src : string , destination : string , private readonly options : AsarOptions , private readonly unpackPattern : Filter | null , private readonly transformer : FileTransformer ) {
49
53
this . outFile = path . join ( destination , "app.asar" )
50
54
}
51
55
52
56
// sort files to minimize file change (i.e. asar file is not changed dramatically on small change)
53
- async pack ( filter : Filter , transformer : ( ( path : string ) => any ) | null ) {
54
- const metadata = new Map < string , Stats > ( )
55
- const files = await walk ( this . src , filter , ( file , fileStat ) => {
57
+ async pack ( filter : Filter , isElectronCompile : boolean ) {
58
+ const metadata = this . metadata
59
+ let files = await walk ( this . src , filter , ( file , fileStat ) => {
56
60
metadata . set ( file , fileStat )
57
61
if ( fileStat . isSymbolicLink ( ) ) {
58
62
return readlink ( file )
@@ -76,13 +80,61 @@ export class AsarPackager {
76
80
}
77
81
return null
78
82
} )
83
+
84
+ // transform before electron-compile to avoid filtering (cache files in any case should be not transformed)
85
+ const transformer = this . transformer
86
+ this . transformedFiles = await BluebirdPromise . map ( files , it => metadata . get ( it ) ! . isFile ( ) ? transformer ( it ) : null , CONCURRENCY )
87
+
88
+ if ( isElectronCompile ) {
89
+ files = await this . compileUsingElectronCompile ( files )
90
+ }
91
+
92
+ await this . createPackageFromFiles ( this . options . ordering == null ? files : await this . order ( files ) )
93
+ }
94
+
95
+ async compileUsingElectronCompile ( files : Array < string > ) : Promise < Array < string > > {
96
+ log ( "Compiling using electron-compile" )
97
+
98
+ const metadata = this . metadata
99
+ const cacheDir = path . join ( this . src , ".cache" )
100
+ // clear and create cache dir
101
+ await emptyDir ( cacheDir )
102
+ const compilerHost = await createElectronCompilerHost ( this . src , cacheDir )
103
+ const nextSlashIndex = this . src . length + 1
104
+ // pre-compute electron-compile to cache dir - we need to process only subdirectories, not direct files of app dir
105
+ await BluebirdPromise . map ( files , file => {
106
+ if ( file . includes ( "/node_modules/" ) || file . includes ( "/bower_components/" )
107
+ || ! file . includes ( "/" , nextSlashIndex ) // ignore not root files
108
+ || ! metadata . get ( file ) ! . isFile ( ) ) {
109
+ return null
110
+ }
111
+ return compilerHost . compile ( file )
112
+ . then ( ( it : any ) => null )
113
+ } , CONCURRENCY )
79
114
80
- await this . createPackageFromFiles ( this . options . ordering == null ? files : await this . order ( files ) , metadata , transformer )
115
+ await compilerHost . saveConfiguration ( )
116
+
117
+ const cacheFiles = await walk ( cacheDir , ( file , stat ) => ! file . startsWith ( "." ) , ( file , fileStat ) => {
118
+ this . metadata . set ( file , fileStat )
119
+ return null
120
+ } )
121
+
122
+ // add es6-shim.js
123
+ const es6ShimPath = `${ this . src } /es6-shim.js`
124
+ cacheFiles . push ( es6ShimPath )
125
+ metadata . set ( es6ShimPath , < any > { isFile : ( ) => true , isDirectory : ( ) => false } )
126
+
127
+ this . transformedFiles = ( new Array ( cacheFiles . length ) ) . concat ( this . transformedFiles )
128
+
129
+ this . transformedFiles [ cacheFiles . length - 1 ] = await readFile ( path . join ( this . src , "node_modules" , "electron-compile" , "lib" , "es6-shim.js" ) )
130
+
131
+ // cache files should be first (better IO)
132
+ return cacheFiles . concat ( files )
81
133
}
82
134
83
- async detectUnpackedDirs ( files : Array < string > , metadata : Map < string , Stats > , autoUnpackDirs : Set < string > , unpackedDest : string ) {
135
+ async detectUnpackedDirs ( files : Array < string > , autoUnpackDirs : Set < string > , unpackedDest : string ) {
84
136
const dirToCreate = new Map < string , Array < string > > ( )
85
-
137
+ const metadata = this . metadata
86
138
/* tslint:disable:rule1 prefer-const */
87
139
for ( let i = 0 , n = files . length ; i < n ; i ++ ) {
88
140
const file = files [ i ]
@@ -147,19 +199,20 @@ export class AsarPackager {
147
199
}
148
200
}
149
201
150
- private async createPackageFromFiles ( files : Array < string > , metadata : Map < string , Stats > , transformer : FileTransformer | null ) {
202
+ async createPackageFromFiles ( files : Array < string > ) {
203
+ const metadata = this . metadata
151
204
// search auto unpacked dir
152
205
const unpackedDirs = new Set < string > ( )
153
206
const unpackedDest = `${ this . outFile } .unpacked`
154
207
await ensureDir ( path . dirname ( this . outFile ) )
155
208
156
209
if ( this . options . smartUnpack !== false ) {
157
- await this . detectUnpackedDirs ( files , metadata , unpackedDirs , unpackedDest )
210
+ await this . detectUnpackedDirs ( files , unpackedDirs , unpackedDest )
158
211
}
159
212
160
213
const dirToCreateForUnpackedFiles = new Set < string > ( unpackedDirs )
161
214
162
- const transformedFiles = transformer == null ? new Array ( files . length ) : await BluebirdPromise . map ( files , it => metadata . get ( it ) ! . isFile ( ) ? transformer ( it ) : null , CONCURRENCY )
215
+ const transformedFiles = this . transformedFiles
163
216
const filesToUnpack : Array < UnpackedFileTask > = [ ]
164
217
const fileCopier = new FileCopier ( )
165
218
/* tslint:disable:rule1 prefer-const */
@@ -170,9 +223,9 @@ export class AsarPackager {
170
223
const fileParent = path . dirname ( file )
171
224
const dirNode = this . fs . getOrCreateNode ( fileParent )
172
225
173
- const newData = transformedFiles == null ? null : transformedFiles [ i ]
226
+ const newData = transformedFiles == null ? null : < string | Buffer > transformedFiles [ i ]
174
227
const node = this . fs . getOrCreateNode ( file )
175
- node . size = newData == null ? stat . size : Buffer . byteLength ( newData )
228
+ node . size = newData == null ? stat . size : Buffer . byteLength ( < any > newData )
176
229
if ( dirNode . unpacked || ( this . unpackPattern != null && this . unpackPattern ( file , stat ) ) ) {
177
230
node . unpacked = true
178
231
if ( newData != null ) {
@@ -227,10 +280,10 @@ export class AsarPackager {
227
280
await writeUnpackedFiles ( filesToUnpack , fileCopier )
228
281
}
229
282
230
- await this . writeAsarFile ( files , transformedFiles )
283
+ await this . writeAsarFile ( files )
231
284
}
232
285
233
- private writeAsarFile ( files : Array < string > , transformedFiles : Array < string | Buffer | null | true > ) : Promise < any > {
286
+ private writeAsarFile ( files : Array < string > ) : Promise < any > {
234
287
const headerPickle = pickle . createEmpty ( )
235
288
headerPickle . writeString ( JSON . stringify ( this . fs . header ) )
236
289
const headerBuf = headerPickle . toBuffer ( )
@@ -239,6 +292,7 @@ export class AsarPackager {
239
292
sizePickle . writeUInt32 ( headerBuf . length )
240
293
const sizeBuf = sizePickle . toBuffer ( )
241
294
295
+ const transformedFiles = this . transformedFiles
242
296
const writeStream = createWriteStream ( this . outFile )
243
297
return new BluebirdPromise ( ( resolve , reject ) => {
244
298
writeStream . on ( "error" , reject )
0 commit comments