Skip to content

Commit 09497cc

Browse files
committed
feat(nsis): custom icon for file association #409
1 parent e108f5f commit 09497cc

File tree

4 files changed

+31
-11
lines changed

4 files changed

+31
-11
lines changed

docs/Options.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ NSIS only, [in progress](https://github.com/electron-userland/electron-builder/i
168168
| **ext** | <a name="FileAssociation-ext"></a>The extension (minus the leading period). e.g. `png`
169169
| **name** | <a name="FileAssociation-name"></a>The name. e.g. `PNG`
170170
| description | <a name="FileAssociation-description"></a>*windows-only.* The description.
171+
| icon | <a name="FileAssociation-icon"></a>*windows-only.* The path to icon (`.ico`), relative to `build` (build resources directory). Defaults to `${ext}.ico`.
171172

172173
<a name="MetadataDirectories"></a>
173174
## `.directories`

src/metadata.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,11 @@ export interface FileAssociation {
493493
*windows-only.* The description.
494494
*/
495495
readonly description?: string
496+
497+
/*
498+
*windows-only.* The path to icon (`.ico`), relative to `build` (build resources directory). Defaults to `${ext}.ico`.
499+
*/
500+
readonly icon?: string
496501
}
497502

498503
/*

src/targets/nsis.ts

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { WinPackager } from "../winPackager"
2-
import { Arch, NsisOptions } from "../metadata"
2+
import { Arch, NsisOptions, FileAssociation } from "../metadata"
33
import { exec, debug, doSpawn, handleProcess, use } from "../util/util"
44
import * as path from "path"
55
import { Promise as BluebirdPromise } from "bluebird"
@@ -30,10 +30,17 @@ export default class NsisTarget extends Target {
3030

3131
private readonly nsisTemplatesDir = path.join(__dirname, "..", "..", "templates", "nsis")
3232

33+
private readonly fileAssociations: Array<FileAssociation>
34+
3335
constructor(private packager: WinPackager, private outDir: string) {
3436
super("nsis")
3537

3638
this.options = packager.info.devMetadata.build.nsis || Object.create(null)
39+
40+
// CFBundleTypeName
41+
// https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html#//apple_ref/doc/uid/20001431-101685
42+
// CFBundleTypeExtensions
43+
this.fileAssociations = asArray(packager.devMetadata.build.fileAssociations).concat(asArray(packager.platformSpecificBuildOptions.fileAssociations))
3744
}
3845

3946
async build(arch: Arch, appOutDir: string) {
@@ -221,24 +228,26 @@ export default class NsisTarget extends Target {
221228
const binDir = process.platform === "darwin" ? "mac" : (process.platform === "win32" ? "Bin" : "linux")
222229
const nsisPath = await nsisPathPromise
223230

224-
const packager = this.packager
225-
// CFBundleTypeName
226-
// https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html#//apple_ref/doc/uid/20001431-101685
227-
// CFBundleTypeExtensions
228-
const fileAssociations = asArray(packager.devMetadata.build.fileAssociations).concat(asArray(packager.platformSpecificBuildOptions.fileAssociations))
229-
230231
let script = originalScript
231232
const customInclude = await this.getResource(this.options.include, "installer.nsh")
232233
if (customInclude != null) {
233234
script = `!include "${customInclude}"\n!addincludedir "${this.packager.buildResourcesDir}"\n${script}`
234235
}
235236

236-
if (fileAssociations.length !== 0) {
237+
if (this.fileAssociations.length !== 0) {
237238
script = "!include FileAssociation.nsh\n" + script
238239
if (isInstaller) {
239240
let registerFileAssociationsScript = ""
240-
for (let item of fileAssociations) {
241-
const icon = '"$INSTDIR\\${APP_EXECUTABLE_FILENAME},0"'
241+
for (let item of this.fileAssociations) {
242+
const customIcon = await this.getResource(item.icon, `${normalizeExt(item.ext)}.ico`)
243+
let installedIconPath = "${APP_EXECUTABLE_FILENAME},0"
244+
if (customIcon != null) {
245+
installedIconPath = `resources\\${path.basename(customIcon)}`
246+
//noinspection SpellCheckingInspection
247+
registerFileAssociationsScript += ` File "/oname=${installedIconPath}" "${customIcon}"\n`
248+
}
249+
250+
const icon = `"$INSTDIR\\${installedIconPath}"`
242251
const commandText = `"Open with ${this.packager.appInfo.productName}"`
243252
const command = '"$INSTDIR\\${APP_EXECUTABLE_FILENAME} $\\"%1$\\""'
244253
registerFileAssociationsScript += ` !insertmacro APP_ASSOCIATE "${normalizeExt(item.ext)}" "${item.name}" "${item.description || ""}" ${icon} ${commandText} ${command}\n`
@@ -247,7 +256,7 @@ export default class NsisTarget extends Target {
247256
}
248257
else {
249258
let unregisterFileAssociationsScript = ""
250-
for (let item of fileAssociations) {
259+
for (let item of this.fileAssociations) {
251260
unregisterFileAssociationsScript += ` !insertmacro APP_UNASSOCIATE "${normalizeExt(item.ext)}" "${item.name}"\n`
252261
}
253262
script = `!macro unregisterFileAssociations\n${unregisterFileAssociationsScript}!macroend\n${script}`

test/src/nsisTest.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ test.ifDevOrLinuxCi("perMachine, no run after finish", app({
2828
},
2929
}
3030
}
31+
}, {
32+
projectDirCreated: projectDir => {
33+
let headerIconPath = path.join(projectDir, "build", "foo.ico")
34+
return copy(getTestAsset("headerIcon.ico"), headerIconPath)
35+
},
3136
}))
3237

3338
test.ifNotCiOsx("boring", app({

0 commit comments

Comments
 (0)