1- import { AsarFileInfo , listPackage , statFile , AsarOptions , AsarFileMetadata , createPackageFromFiles } from "asar-electron-builder"
1+ import { AsarFileInfo , listPackage , statFile , AsarOptions } from "asar-electron-builder"
22import { statOrNull } from "./util/util"
3- import { lstat , readdir } from "fs-extra-p"
3+ import { lstat , readdir , readFile , mkdirp , Stats } from "fs-extra-p"
44import { Promise as BluebirdPromise } from "bluebird"
55import * as path from "path"
6- import { Stats } from "fs"
76import pathSorter = require( "path-sort" )
7+ import { log } from "./util/log"
8+ import { Minimatch } from "minimatch"
9+
10+ const isBinaryFile : any = BluebirdPromise . promisify ( require ( "isbinaryfile" ) )
811
912//noinspection JSUnusedLocalSymbols
1013const __awaiter = require ( "./util/awaiter" )
1114
1215const concurrency = { concurrency : 50 }
16+ const NODE_MODULES_PATTERN = path . sep + "node_modules" + path . sep
1317
14- function walk ( dirPath : string , consumer : ( file : string , stat : Stats ) => void , filter : ( file : string ) => boolean ) : BluebirdPromise < any > {
18+ function walk ( dirPath : string , consumer : ( file : string , stat : Stats ) => void , filter : ( file : string ) => boolean , addRootToResult ?: boolean ) : BluebirdPromise < Array < string > > {
1519 return readdir ( dirPath )
1620 . then ( names => {
1721 return BluebirdPromise . map ( names , name => {
@@ -21,31 +25,184 @@ function walk(dirPath: string, consumer: (file: string, stat: Stats) => void, fi
2125 }
2226
2327 return lstat ( filePath )
24- . then ( stat => {
28+ . then ( ( stat ) : any => {
2529 consumer ( filePath , stat )
2630 if ( stat . isDirectory ( ) ) {
27- return walk ( filePath , consumer , filter )
31+ return walk ( filePath , consumer , filter , true )
2832 }
29- return null
33+ return filePath
3034 } )
3135 } , concurrency )
3236 } )
37+ . then ( list => {
38+ list . sort ( ( a , b ) => {
39+ // files before directories
40+ if ( Array . isArray ( a ) && Array . isArray ( b ) ) {
41+ return 0
42+ }
43+ else if ( a == null || Array . isArray ( a ) ) {
44+ return 1
45+ }
46+ else if ( b == null || Array . isArray ( b ) ) {
47+ return - 1
48+ }
49+ else {
50+ return a . localeCompare ( b )
51+ }
52+ } )
53+
54+ const result : Array < string > = addRootToResult ? [ dirPath ] : [ ]
55+ for ( let item of list ) {
56+ if ( item != null ) {
57+ if ( Array . isArray ( item ) ) {
58+ result . push . apply ( result , item )
59+ }
60+ else {
61+ result . push ( item )
62+ }
63+ }
64+ }
65+ return result
66+ } )
3367}
3468
3569export async function createAsarArchive ( src : string , resourcesPath : string , options : AsarOptions , filter : ( file : string ) => boolean ) : Promise < any > {
36- const metadata : { [ key : string ] : AsarFileMetadata ; } = { }
37- const files : Array < string > = [ ]
38- await walk ( src , ( it , stat ) => {
39- files . push ( it )
40- metadata [ it ] = {
41- type : stat . isFile ( ) ? "file" : ( stat . isDirectory ( ) ? "directory" : "link" ) ,
42- stat : stat ,
43- }
44- } ,
45- filter )
70+ const metadata = new Map < string , Stats > ( )
71+ const files = await walk ( src , ( it , stat ) => {
72+ metadata . set ( it , stat )
73+ } , filter )
4674
4775 // sort files to minimize file change (i.e. asar file is not changed dramatically on small change)
48- await BluebirdPromise . promisify ( createPackageFromFiles ) ( src , path . join ( resourcesPath , "app.asar" ) , pathSorter ( files ) , metadata , options )
76+ await createPackageFromFiles ( src , path . join ( resourcesPath , "app.asar" ) , options . ordering == null ? files : await order ( src , files , options ) , metadata , options )
77+ }
78+
79+ const Filesystem = require ( "asar-electron-builder/lib/filesystem" )
80+ const writeFilesystem : any = BluebirdPromise . promisify ( require ( "asar-electron-builder/lib/disk" ) . writeFilesystem )
81+
82+ function isUnpackDir ( path : string , pattern : Minimatch , rawPattern : string ) : boolean {
83+ return path . indexOf ( rawPattern ) === 0 || pattern . match ( path )
84+ }
85+
86+ async function order ( src : string , filenames : Array < string > , options : any ) {
87+ const orderingFiles = ( await readFile ( options . ordering , "utf8" ) ) . split ( "\n" ) . map ( function ( line ) {
88+ if ( line . indexOf ( ':' ) !== - 1 ) {
89+ line = line . split ( ':' ) . pop ( ) !
90+ }
91+ line = line . trim ( )
92+ if ( line [ 0 ] === '/' ) {
93+ line = line . slice ( 1 )
94+ }
95+ return line
96+ } )
97+
98+ const ordering : Array < string > = [ ]
99+ for ( let file of orderingFiles ) {
100+ let pathComponents = file . split ( path . sep )
101+ let str = src
102+ for ( let pathComponent of pathComponents ) {
103+ str = path . join ( str , pathComponent )
104+ ordering . push ( str )
105+ }
106+ }
107+
108+ const filenamesSorted : Array < string > = [ ]
109+ let missing = 0
110+ const total = filenames . length
111+ for ( let file of ordering ) {
112+ if ( ! filenamesSorted . includes ( file ) && filenames . includes ( file ) ) {
113+ filenamesSorted . push ( file )
114+ }
115+ }
116+ for ( let file of filenames ) {
117+ if ( ! filenamesSorted . includes ( file ) ) {
118+ filenamesSorted . push ( file )
119+ missing += 1 ;
120+ }
121+ }
122+ log ( `Ordering file has ${ ( ( total - missing ) / total * 100 ) } % coverage.` )
123+ return filenamesSorted
124+ }
125+
126+ async function createPackageFromFiles ( src : string , dest : string , files : Array < string > , metadata : Map < string , Stats > , options : any ) {
127+ // search auto unpacked dir
128+ const autoUnpackDirs = new Set < string > ( )
129+ if ( options . smartUnpack !== false ) {
130+ for ( let file of files ) {
131+ const index = file . lastIndexOf ( NODE_MODULES_PATTERN )
132+ if ( index < 0 ) {
133+ continue
134+ }
135+
136+ const nextSlashIndex = file . indexOf ( path . sep , index + NODE_MODULES_PATTERN . length + 1 )
137+ if ( nextSlashIndex < 0 ) {
138+ continue
139+ }
140+
141+ const nodeModuleDir = file . substring ( 0 , nextSlashIndex )
142+ if ( autoUnpackDirs . has ( nodeModuleDir ) || ! metadata . get ( file ) ! . isFile ( ) ) {
143+ continue
144+ }
145+
146+ const ext = path . extname ( file )
147+ let shouldUnpack = false
148+ if ( ext === ".dll" || ext === ".exe" ) {
149+ shouldUnpack = true
150+ }
151+ else if ( ext === "" ) {
152+ shouldUnpack = await isBinaryFile ( file )
153+ }
154+
155+ if ( ! shouldUnpack ) {
156+ continue
157+ }
158+
159+ log ( `${ path . relative ( src , nodeModuleDir ) } is not packed into asar archive - contains executable code` )
160+ autoUnpackDirs . add ( nodeModuleDir )
161+ }
162+ }
163+
164+ const unpackDir = options . unpackDir == null ? null : new Minimatch ( options . unpackDir )
165+ const unpack = options . unpack == null ? null : new Minimatch ( options . unpack , {
166+ matchBase : true
167+ } )
168+
169+ const regularFiles : Array < any > = [ ]
170+ const filesystem = new Filesystem ( src )
171+ for ( let file of files ) {
172+ const stat = metadata . get ( file ) !
173+ if ( stat . isFile ( ) ) {
174+ let shouldUnpack = unpack != null && unpack . match ( file )
175+ if ( ! shouldUnpack ) {
176+ const dir = path . dirname ( file )
177+ shouldUnpack = autoUnpackDirs . has ( dir ) || ( unpackDir != null && isUnpackDir ( path . relative ( src , dir ) , unpackDir , options . unpackDir ) )
178+ }
179+
180+ regularFiles . push ( {
181+ filename : file ,
182+ unpack : shouldUnpack
183+ } )
184+ filesystem . insertFile ( file , shouldUnpack , stat )
185+ }
186+ else if ( stat . isDirectory ( ) ) {
187+ let unpacked = autoUnpackDirs . has ( file ) || ( unpackDir != null && isUnpackDir ( path . relative ( src , file ) , unpackDir , options . unpackDir ) )
188+ if ( ! unpacked ) {
189+ for ( let d of autoUnpackDirs ) {
190+ if ( file . length > ( d . length + 2 ) && file [ d . length ] === path . sep && file . startsWith ( d ) ) {
191+ unpacked = true
192+ autoUnpackDirs . add ( file )
193+ break
194+ }
195+ }
196+ }
197+ filesystem . insertDirectory ( file , unpacked )
198+ }
199+ else if ( stat . isSymbolicLink ( ) ) {
200+ filesystem . insertLink ( file , stat )
201+ }
202+ }
203+
204+ await mkdirp ( path . dirname ( dest ) )
205+ await writeFilesystem ( dest , filesystem , regularFiles )
49206}
50207
51208export async function checkFileInPackage ( asarFile : string , relativeFile : string ) {
0 commit comments