Skip to content

Commit 666dec7

Browse files
Suraj Reddydevelar
authored andcommitted
feat(appx): Improve support for AppX assets
AppX assets need to be placed in the `appx` folder in the buildResources folder (by default this is `build`). The assets should follow these naming conventions: - Logo: `StoreLogo.png` - Square150x150Logo: `Square150x150Logo.png` - Square44x44Logo: `Square44x44Logo.png` - [Optional] BadgeLogo: `BadgeLogo.png` - Wide310x150Logo: `Wide310x150Logo.png` - [Optional] Square310x310Logo: `LargeTile.png` - [Optional] Square71x71Logo: `SmallTile.png` - [Optional] SplashScreen: `SplashScreen.png` All official AppX asset types are supported by the build process. These assets can include scaled assets by using `target size` and `scale` in the name. See https://docs.microsoft.com/en-us/windows/uwp/controls-and-patterns/tiles-and-notifications-app-assets for more information. Default assets will be used for `Logo`, `Square150x150Logo`, `Square44x44Logo` and `Wide310x150Logo` if not provided. For assets marked `[Optional]`, these assets will not be listed in the manifest file if not provided. > **Note:** Support for assets being provided in the default `build` directory with the names `44x44.png`, `50x50.png`, `150x150.png`, `310x150.png` is dropped. Please move your assets to the `build/appxAssets` folder and name them according to the convention above.
1 parent 28f0266 commit 666dec7

File tree

9 files changed

+144
-53
lines changed

9 files changed

+144
-53
lines changed

.idea/dictionaries/develar.xml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,10 @@
2828
"///": "all dependencies for all packages (hoisted)",
2929
"dependencies": {
3030
"7zip-bin": "^2.1.0",
31-
"ajv": "^5.1.5",
31+
"ajv": "^5.1.6",
3232
"ajv-keywords": "^2.1.0",
3333
"archiver": "^1.3.0",
34-
"aws-sdk": "^2.69.0",
34+
"aws-sdk": "^2.71.0",
3535
"bluebird-lst": "^1.0.2",
3636
"chalk": "^1.1.3",
3737
"chromium-pickle-js": "^0.2.0",
@@ -40,7 +40,7 @@
4040
"electron-download-tf": "4.3.1",
4141
"electron-is-dev": "^0.1.2",
4242
"electron-osx-sign": "0.4.6",
43-
"fcopy-pre-bundled": "0.3.3",
43+
"fcopy-pre-bundled": "0.3.4",
4444
"fs-extra-p": "^4.3.0",
4545
"hosted-git-info": "^2.4.2",
4646
"ini": "^1.3.4",
@@ -92,7 +92,7 @@
9292
"source-map-support": "^0.4.15",
9393
"ts-babel": "^3.0.1",
9494
"tslint": "^5.4.3",
95-
"typescript": "next",
95+
"typescript": "2.5.0-dev.20170614",
9696
"whitespace": "^2.1.0",
9797
"xml2js": "^0.4.17"
9898
},

packages/electron-builder-util/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
],
1313
"dependencies": {
1414
"fs-extra-p": "^4.3.0",
15-
"fcopy-pre-bundled": "0.3.3",
15+
"fcopy-pre-bundled": "0.3.4",
1616
"is-ci": "^1.0.10",
1717
"stat-mode": "^0.2.2",
1818
"bluebird-lst": "^1.0.2",

packages/electron-builder-util/src/deepAssign.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,6 @@ function assignKey(target: any, from: any, key: string) {
1515
}
1616

1717
const prevValue = target[key]
18-
if (prevValue === null) {
19-
// if explicitly set to null, it means that we want to not use default or inherited value
20-
return
21-
}
22-
2318
if (prevValue == null || value == null || !isObject(prevValue) || !isObject(value)) {
2419
target[key] = value
2520
}

packages/electron-builder/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
"homepage": "https://github.com/electron-userland/electron-builder",
5252
"dependencies": {
5353
"7zip-bin": "^2.1.0",
54-
"ajv": "^5.1.5",
54+
"ajv": "^5.1.6",
5555
"ajv-keywords": "^2.1.0",
5656
"bluebird-lst": "^1.0.2",
5757
"chalk": "^1.1.3",

packages/electron-builder/src/targets/appx.ts

Lines changed: 113 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,26 @@
11
import BluebirdPromise from "bluebird-lst"
22
import { Arch, getArchSuffix, Target } from "electron-builder-core"
33
import { exec, spawn, use } from "electron-builder-util"
4+
import { deepAssign } from "electron-builder-util/out/deepAssign"
45
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"
68
import * as path from "path"
7-
import sanitizeFileName from "sanitize-filename"
89
import { AppXOptions } from "../options/winOptions"
910
import { getSignVendorPath, isOldWin6 } from "../windowsCodeSign"
1011
import { WinPackager } from "../winPackager"
1112

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+
1222
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)
1424

1525
constructor(private readonly packager: WinPackager, readonly outDir: string) {
1626
super("appx")
@@ -20,7 +30,6 @@ export default class AppXTarget extends Target {
2030
}
2131
}
2232

23-
// no flatten - use asar or npm 3 or yarn
2433
async build(appOutDir: string, arch: Arch): Promise<any> {
2534
const packager = this.packager
2635

@@ -42,43 +51,63 @@ export default class AppXTarget extends Target {
4251
}
4352
}
4453

45-
const appInfo = packager.appInfo
46-
4754
const preAppx = path.join(this.outDir, `pre-appx-${getArchSuffix(arch)}`)
4855
await emptyDir(preAppx)
4956

50-
const vendorPath = await getSignVendorPath()
51-
52-
const templatePath = path.join(__dirname, "..", "..", "templates", "appx")
53-
const safeName = sanitizeFileName(appInfo.name)
5457
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))
6080
}
61-
return copyFile(path.join(vendorPath, "appxAssets", `SampleAppx.${size}.png`), target)
6281
}),
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")),
6584
])
6685

6786
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])
7395

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)
74102
await packager.sign(destination)
103+
75104
packager.dispatchArtifactCreated(destination, this, arch, packager.computeSafeArtifactName("appx"))
76105
}
77106

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>) {
79108
const appInfo = this.packager.appInfo
80109
const manifest = (await readFile(path.join(templatePath, "appxmanifest.xml"), "utf8"))
81-
.replace(/\$\{([a-zA-Z]+)\}/g, (match, p1): string => {
110+
.replace(/\$\{([a-zA-Z0-9]+)\}/g, (match, p1): string => {
82111
switch (p1) {
83112
case "publisher":
84113
return publisher
@@ -111,8 +140,23 @@ export default class AppXTarget extends Target {
111140
case "backgroundColor":
112141
return this.options.backgroundColor || "#464646"
113142

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)
116160

117161
case "arch":
118162
return arch === Arch.ia32 ? "x86" : "x64"
@@ -125,6 +169,48 @@ export default class AppXTarget extends Target {
125169
}
126170
}
127171

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+
128214
export function quoteString(s: string): string {
129215
if (!s.includes(",") && !s.includes('"')) {
130216
return s

packages/electron-builder/templates/appx/appxmanifest.xml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
<DisplayName>${displayName}</DisplayName>
1313
<PublisherDisplayName>${publisherDisplayName}</PublisherDisplayName>
1414
<Description>${description}</Description>
15-
<Logo>assets\${safeName}.50x50.png</Logo>
15+
<Logo>${logo}</Logo>
1616
</Properties>
1717
<Resources>
1818
<Resource Language="en-us" />
@@ -28,10 +28,12 @@
2828
<uap:VisualElements
2929
BackgroundColor="${backgroundColor}"
3030
DisplayName="${displayName}"
31-
Square150x150Logo="assets\${safeName}.150x150.png"
32-
Square44x44Logo="assets\${safeName}.44x44.png"
31+
Square150x150Logo="${square150x150Logo}"
32+
Square44x44Logo="${square44x44Logo}"
3333
Description="${description}">
34-
<uap:DefaultTile Wide310x150Logo="assets\${safeName}.310x150.png" />
34+
${lockScreen}
35+
${defaultTile}
36+
${splashScreen}
3537
</uap:VisualElements>
3638
</Application>
3739
</Applications>

packages/electron-publisher-s3/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
],
1313
"dependencies": {
1414
"fs-extra-p": "^4.3.0",
15-
"aws-sdk": "^2.68.0",
15+
"aws-sdk": "^2.70.0",
1616
"mime": "^1.3.6",
1717
"electron-publish": "~0.0.0-semantic-release",
1818
"electron-builder-util": "~0.0.0-semantic-release"

yarn.lock

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,12 @@ ajv@^4.9.1:
9191
co "^4.6.0"
9292
json-stable-stringify "^1.0.1"
9393

94-
ajv@^5.1.5:
95-
version "5.1.5"
96-
resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.1.5.tgz#8734931b601f00d4feef7c65738d77d1b65d1f68"
94+
ajv@^5.1.6:
95+
version "5.1.6"
96+
resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.1.6.tgz#4b2f1a19dece93d57ac216037e3e9791c7dd1564"
9797
dependencies:
9898
co "^4.6.0"
99+
json-schema-traverse "^0.3.0"
99100
json-stable-stringify "^1.0.1"
100101

101102
align-text@^0.1.1, align-text@^0.1.3:
@@ -265,9 +266,9 @@ asynckit@^0.4.0:
265266
version "0.4.0"
266267
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
267268

268-
aws-sdk@^2.69.0:
269-
version "2.69.0"
270-
resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.69.0.tgz#ae0cd12fda5e8cc7aa203229a80169167af15add"
269+
aws-sdk@^2.71.0:
270+
version "2.71.0"
271+
resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.71.0.tgz#629e49d48a95874b77ed50ec9b621633ec83a8ef"
271272
dependencies:
272273
buffer "5.0.6"
273274
crypto-browserify "1.0.9"
@@ -1227,9 +1228,9 @@ fb-watchman@^2.0.0:
12271228
dependencies:
12281229
bser "^2.0.0"
12291230

1230-
fcopy-pre-bundled@0.3.3:
1231-
version "0.3.3"
1232-
resolved "https://registry.yarnpkg.com/fcopy-pre-bundled/-/fcopy-pre-bundled-0.3.3.tgz#4e1fce4a94ab3fafcf3bbb3d5567bb152092bf1b"
1231+
fcopy-pre-bundled@0.3.4:
1232+
version "0.3.4"
1233+
resolved "https://registry.yarnpkg.com/fcopy-pre-bundled/-/fcopy-pre-bundled-0.3.4.tgz#7ff1a1c339e877baa86b0856bebb33621cd5620b"
12331234

12341235
feature-detect-es6@^1.3.1:
12351236
version "1.3.1"
@@ -2075,6 +2076,10 @@ jsesc@^1.3.0:
20752076
version "1.3.0"
20762077
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b"
20772078

2079+
json-schema-traverse@^0.3.0:
2080+
version "0.3.0"
2081+
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.0.tgz#0016c0b1ca1efe46d44d37541bcdfc19dcfae0db"
2082+
20782083
json-schema@0.2.3:
20792084
version "0.2.3"
20802085
resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
@@ -3349,7 +3354,7 @@ type-check@~0.3.2:
33493354
dependencies:
33503355
prelude-ls "~1.1.2"
33513356

3352-
typescript@next:
3357+
typescript@2.5.0-dev.20170614:
33533358
version "2.5.0-dev.20170614"
33543359
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.5.0-dev.20170614.tgz#eb77ed212a5e49ebc8392ba0a95713ef87d9f6f1"
33553360

0 commit comments

Comments
 (0)