1
1
import BluebirdPromise from "bluebird-lst"
2
2
import { Arch , getArchSuffix , Target } from "electron-builder-core"
3
3
import { exec , spawn , use } from "electron-builder-util"
4
+ import { deepAssign } from "electron-builder-util/out/deepAssign"
4
5
import { copyDir , copyFile } from "electron-builder-util/out/fs"
5
- import { emptyDir , readFile , writeFile } from "fs-extra-p"
6
+ import { asyncAll } from "electron-builder-util/out/promise"
7
+ import { emptyDir , readdir , readFile , writeFile } from "fs-extra-p"
6
8
import * as path from "path"
7
- import sanitizeFileName from "sanitize-filename"
8
9
import { AppXOptions } from "../options/winOptions"
9
10
import { getSignVendorPath , isOldWin6 } from "../windowsCodeSign"
10
11
import { WinPackager } from "../winPackager"
11
12
13
+ const APPX_ASSETS_DIR_NAME = "appx"
14
+
15
+ const vendorAssetsForDefaultAssets : { [ key : string ] : string ; } = {
16
+ "StoreLogo.png" : "SampleAppx.50x50.png" ,
17
+ "Square150x150Logo.png" : "SampleAppx.150x150.png" ,
18
+ "Square44x44Logo.png" : "SampleAppx.44x44.png" ,
19
+ "Wide310x150Logo.png" : "SampleAppx.310x150.png" ,
20
+ }
21
+
12
22
export default class AppXTarget extends Target {
13
- readonly options : AppXOptions = Object . assign ( { } , this . packager . platformSpecificBuildOptions , this . packager . config . appx )
23
+ readonly options : AppXOptions = deepAssign ( { } , this . packager . platformSpecificBuildOptions , this . packager . config . appx )
14
24
15
25
constructor ( private readonly packager : WinPackager , readonly outDir : string ) {
16
26
super ( "appx" )
@@ -20,7 +30,6 @@ export default class AppXTarget extends Target {
20
30
}
21
31
}
22
32
23
- // no flatten - use asar or npm 3 or yarn
24
33
async build ( appOutDir : string , arch : Arch ) : Promise < any > {
25
34
const packager = this . packager
26
35
@@ -42,43 +51,63 @@ export default class AppXTarget extends Target {
42
51
}
43
52
}
44
53
45
- const appInfo = packager . appInfo
46
-
47
54
const preAppx = path . join ( this . outDir , `pre-appx-${ getArchSuffix ( arch ) } ` )
48
55
await emptyDir ( preAppx )
49
56
50
- const vendorPath = await getSignVendorPath ( )
51
-
52
- const templatePath = path . join ( __dirname , ".." , ".." , "templates" , "appx" )
53
- const safeName = sanitizeFileName ( appInfo . name )
54
57
const resourceList = await packager . resourceList
55
- await BluebirdPromise . all ( [
56
- BluebirdPromise . map ( [ "44x44" , "50x50" , "150x150" , "310x150" ] , size => {
57
- const target = path . join ( preAppx , "assets" , `${ safeName } .${ size } .png` )
58
- if ( resourceList . includes ( `${ size } .png` ) ) {
59
- return copyFile ( path . join ( packager . buildResourcesDir , `${ size } .png` ) , target )
58
+ if ( resourceList . includes ( APPX_ASSETS_DIR_NAME ) ) {
59
+ await copyDir ( path . join ( packager . buildResourcesDir , APPX_ASSETS_DIR_NAME ) , path . join ( preAppx , "assets" ) )
60
+ }
61
+
62
+ let userAssets : Array < string >
63
+ try {
64
+ userAssets = await readdir ( path . join ( packager . buildResourcesDir , APPX_ASSETS_DIR_NAME ) )
65
+ }
66
+ catch ( e ) {
67
+ if ( e . code === "ENOENT" ) {
68
+ userAssets = [ ]
69
+ }
70
+ else {
71
+ throw e
72
+ }
73
+ }
74
+
75
+ const vendorPath = await getSignVendorPath ( )
76
+ await asyncAll ( [
77
+ ( ) => BluebirdPromise . map ( Object . keys ( vendorAssetsForDefaultAssets ) , defaultAsset => {
78
+ if ( ! isDefaultAssetIncluded ( userAssets , defaultAsset ) ) {
79
+ copyFile ( path . join ( vendorPath , "appxAssets" , vendorAssetsForDefaultAssets [ defaultAsset ] ) , path . join ( preAppx , "assets" , defaultAsset ) )
60
80
}
61
- return copyFile ( path . join ( vendorPath , "appxAssets" , `SampleAppx.${ size } .png` ) , target )
62
81
} ) ,
63
- copyDir ( appOutDir , path . join ( preAppx , "app" ) ) ,
64
- this . writeManifest ( templatePath , preAppx , safeName , arch , publisher )
82
+ ( ) => this . writeManifest ( path . join ( __dirname , ".." , ".." , "templates" , "appx" ) , preAppx , arch , publisher ! , userAssets ) ,
83
+ ( ) => copyDir ( appOutDir , path . join ( preAppx , "app" ) ) ,
65
84
] )
66
85
67
86
const destination = path . join ( this . outDir , packager . expandArtifactNamePattern ( this . options , "appx" , arch ) )
68
- const args = [ "pack" , "/o" , "/d" , preAppx , "/p" , destination ]
69
- use ( this . options . makeappxArgs , ( it : Array < string > ) => args . push ( ...it ) )
70
- // wine supports only ia32 binary in any case makeappx crashed on wine
71
- // await execWine(path.join(await getSignVendorPath(), "windows-10", process.platform === "win32" ? process.arch : "ia32", "makeappx.exe"), args)
72
- await spawn ( path . join ( vendorPath , "windows-10" , arch === Arch . ia32 ? "ia32" : "x64" , "makeappx.exe" ) , args )
87
+ const makeAppXArgs = [ "pack" , "/o" , "/d" , preAppx , "/p" , destination ]
88
+
89
+ // we do not use process.arch to build path to tools, because even if you are on x64, ia32 appx tool must be used if you build appx for ia32
90
+ if ( isScaledAssetsProvided ( userAssets ) ) {
91
+ const priConfigPath = path . join ( preAppx , "priconfig.xml" )
92
+ const makePriPath = path . join ( vendorPath , "windows-10" , Arch [ arch ] , "makepri.exe" )
93
+ await spawn ( makePriPath , [ "createconfig" , "/cf" , priConfigPath , "/dq" , "en-US" , "/pv" , "10.0.0" , "/o" ] )
94
+ await spawn ( makePriPath , [ "new" , "/pr" , preAppx , "/cf" , priConfigPath , "/of" , preAppx ] )
73
95
96
+ makeAppXArgs . push ( "/l" )
97
+ }
98
+
99
+ use ( this . options . makeappxArgs , ( it : Array < string > ) => makeAppXArgs . push ( ...it ) )
100
+ // wine supports only ia32 binary in any case makeappx crashed on wine
101
+ await spawn ( path . join ( vendorPath , "windows-10" , Arch [ arch ] , "makeappx.exe" ) , makeAppXArgs )
74
102
await packager . sign ( destination )
103
+
75
104
packager . dispatchArtifactCreated ( destination , this , arch , packager . computeSafeArtifactName ( "appx" ) )
76
105
}
77
106
78
- private async writeManifest ( templatePath : string , preAppx : string , safeName : string , arch : Arch , publisher : string ) {
107
+ private async writeManifest ( templatePath : string , preAppx : string , arch : Arch , publisher : string , userAssets : Array < string > ) {
79
108
const appInfo = this . packager . appInfo
80
109
const manifest = ( await readFile ( path . join ( templatePath , "appxmanifest.xml" ) , "utf8" ) )
81
- . replace ( / \$ \{ ( [ a - z A - Z ] + ) \} / g, ( match , p1 ) : string => {
110
+ . replace ( / \$ \{ ( [ a - z A - Z 0 - 9 ] + ) \} / g, ( match , p1 ) : string => {
82
111
switch ( p1 ) {
83
112
case "publisher" :
84
113
return publisher
@@ -111,8 +140,23 @@ export default class AppXTarget extends Target {
111
140
case "backgroundColor" :
112
141
return this . options . backgroundColor || "#464646"
113
142
114
- case "safeName" :
115
- return safeName
143
+ case "logo" :
144
+ return "assets\\StoreLogo.png"
145
+
146
+ case "square150x150Logo" :
147
+ return "assets\\Square150x150Logo.png"
148
+
149
+ case "square44x44Logo" :
150
+ return "assets\\Square44x44Logo.png"
151
+
152
+ case "lockScreen" :
153
+ return lockScreenTag ( userAssets )
154
+
155
+ case "defaultTile" :
156
+ return defaultTileTag ( userAssets )
157
+
158
+ case "splashScreen" :
159
+ return splashScreenTag ( userAssets )
116
160
117
161
case "arch" :
118
162
return arch === Arch . ia32 ? "x86" : "x64"
@@ -125,6 +169,48 @@ export default class AppXTarget extends Target {
125
169
}
126
170
}
127
171
172
+ function lockScreenTag ( userAssets : Array < string > ) : string {
173
+ if ( isDefaultAssetIncluded ( userAssets , "BadgeLogo.png" ) ) {
174
+ return '<uap:LockScreen Notification="badgeAndTileText" BadgeLogo="assets\\BadgeLogo.png" />'
175
+ }
176
+ else {
177
+ return ""
178
+ }
179
+ }
180
+
181
+ function defaultTileTag ( userAssets : Array < string > ) : string {
182
+ const defaultTiles : Array < string > = [ "<uap:DefaultTile" , 'Wide310x150Logo="assets\\Wide310x150Logo.png"' ]
183
+
184
+ if ( isDefaultAssetIncluded ( userAssets , "LargeTile.png" ) ) {
185
+ defaultTiles . push ( 'Square310x310Logo="assets\\LargeTile.png"' )
186
+ }
187
+ if ( isDefaultAssetIncluded ( userAssets , "SmallTile.png" ) ) {
188
+ defaultTiles . push ( 'Square71x71Logo="assets\\SmallTile.png"' )
189
+ }
190
+
191
+ defaultTiles . push ( "/>" )
192
+ return defaultTiles . join ( " " )
193
+ }
194
+
195
+ function splashScreenTag ( userAssets : Array < string > ) : string {
196
+ if ( isDefaultAssetIncluded ( userAssets , "SplashScreen.png" ) ) {
197
+ return '<uap:SplashScreen Image="assets\\SplashScreen.png" />'
198
+ }
199
+ else {
200
+ return ""
201
+ }
202
+ }
203
+
204
+ function isDefaultAssetIncluded ( userAssets : Array < string > , defaultAsset : string ) {
205
+ const defaultAssetName = defaultAsset . split ( "." ) [ 0 ]
206
+ return userAssets . some ( it => it . includes ( defaultAssetName ) )
207
+ }
208
+
209
+ function isScaledAssetsProvided ( userAssets : Array < string > ) {
210
+ // noinspection SpellCheckingInspection
211
+ return userAssets . some ( it => it . includes ( ".scale-" ) || it . includes ( ".targetsize-" ) )
212
+ }
213
+
128
214
export function quoteString ( s : string ) : string {
129
215
if ( ! s . includes ( "," ) && ! s . includes ( '"' ) ) {
130
216
return s
0 commit comments