Skip to content

Commit 13b078a

Browse files
authored
refactor(nsis): make ambiguous types strict (#6868)
* refactor(nsis): make ambiguous types strict to list the environment variables that can be used in the NSIS script.
1 parent fa72861 commit 13b078a

File tree

4 files changed

+147
-20
lines changed

4 files changed

+147
-20
lines changed

.changeset/serious-buttons-exist.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"app-builder-lib": patch
3+
---
4+
5+
refactor(nsis): make ambiguous types strict for nsis DEFINES
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export type Commands = {
2+
OutFile: string
3+
VIProductVersion?: string
4+
VIAddVersionKey: Array<string>
5+
Unicode: boolean
6+
Icon?: string
7+
SetCompress?: "off"
8+
SetCompressor?: "zlib"
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { PortableOptions } from "./nsisOptions"
2+
import { PathLike } from "fs"
3+
/**
4+
* Parameters declared as environment variables in NSIS scripts.
5+
* The documentation vaguely explains "All other electron-builder specific flags (e.g. ONE_CLICK) are still defined."
6+
* Parameters with null values in TypeScript can be treated as Boolean values using "!Ifdef" in NSIS Script.
7+
*/
8+
export type Defines = {
9+
APP_ID: string
10+
APP_GUID: unknown
11+
UNINSTALL_APP_KEY: unknown
12+
PRODUCT_NAME: string
13+
PRODUCT_FILENAME: string
14+
APP_FILENAME: string
15+
APP_DESCRIPTION: string
16+
VERSION: string
17+
18+
PROJECT_DIR: string
19+
BUILD_RESOURCES_DIR: string
20+
21+
APP_PACKAGE_NAME: string
22+
23+
ENABLE_LOGGING_ELECTRON_BUILDER?: null
24+
UNINSTALL_REGISTRY_KEY_2?: string
25+
26+
MUI_ICON?: unknown
27+
MUI_UNICON?: unknown
28+
29+
APP_DIR_64?: string
30+
APP_DIR_ARM64?: string
31+
APP_DIR_32?: string
32+
33+
APP_BUILD_DIR?: string
34+
35+
APP_64?: string
36+
APP_ARM64?: string
37+
APP_32?: string
38+
39+
APP_64_NAME?: string
40+
APP_ARM64_NAME?: string
41+
APP_32_NAME?: string
42+
43+
APP_64_HASH?: string
44+
APP_ARM64_HASH?: string
45+
APP_32_HASH?: string
46+
47+
REQUEST_EXECUTION_LEVEL?: PortableOptions["requestExecutionLevel"]
48+
49+
UNPACK_DIR_NAME?: string | false
50+
51+
SPLASH_IMAGE?: unknown
52+
53+
ESTIMATED_SIZE?: number
54+
55+
COMPRESS?: "auto"
56+
57+
BUILD_UNINSTALLER?: null
58+
UNINSTALLER_OUT_FILE?: PathLike
59+
60+
ONE_CLICK?: null
61+
RUN_AFTER_FINISH?: null
62+
HEADER_ICO?: string
63+
HIDE_RUN_AFTER_FINISH?: null
64+
65+
MUI_HEADERIMAGE?: null
66+
MUI_HEADERIMAGE_RIGHT?: null
67+
MUI_HEADERIMAGE_BITMAP?: string
68+
69+
MUI_WELCOMEFINISHPAGE_BITMAP?: string
70+
MUI_UNWELCOMEFINISHPAGE_BITMAP?: string
71+
72+
MULTIUSER_INSTALLMODE_ALLOW_ELEVATION?: null
73+
74+
INSTALL_MODE_PER_ALL_USERS?: null
75+
INSTALL_MODE_PER_ALL_USERS_REQUIRED?: null
76+
77+
allowToChangeInstallationDirectory?: null
78+
79+
MENU_FILENAME?: string
80+
81+
SHORTCUT_NAME?: string
82+
83+
DELETE_APP_DATA_ON_UNINSTALL?: null
84+
85+
UNINSTALLER_ICON?: string
86+
UNINSTALL_DISPLAY_NAME?: string
87+
88+
RECREATE_DESKTOP_SHORTCUT?: null
89+
90+
DO_NOT_CREATE_DESKTOP_SHORTCUT?: null
91+
92+
DO_NOT_CREATE_START_MENU_SHORTCUT?: null
93+
94+
DISPLAY_LANG_SELECTOR?: null
95+
96+
COMPANY_NAME?: string
97+
98+
APP_PRODUCT_FILENAME?: string
99+
100+
APP_PACKAGE_STORE_FILE?: string
101+
102+
APP_INSTALLER_STORE_FILE?: string
103+
104+
ZIP_COMPRESSION?: null
105+
106+
COMPRESSION_METHOD?: "zip" | "7z"
107+
}

packages/app-builder-lib/src/targets/nsis/NsisTarget.ts

+26-20
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import { WinPackager } from "../../winPackager"
1919
import { archive, ArchiveOptions } from "../archive"
2020
import { appendBlockmap, configureDifferentialAwareArchiveOptions, createBlockmap, createNsisWebDifferentialUpdateInfo } from "../differentialUpdateInfoBuilder"
2121
import { getWindowsInstallationAppPackageName, getWindowsInstallationDirName } from "../targetUtil"
22+
import { Commands } from "./Commands"
23+
import { Defines } from "./Defines"
2224
import { addCustomMessageFileInclude, createAddLangsMacro, LangConfigurator } from "./nsisLang"
2325
import { computeLicensePage } from "./nsisLicense"
2426
import { NsisOptions, PortableOptions } from "./nsisOptions"
@@ -71,7 +73,7 @@ export class NsisTarget extends Target {
7173
return Promise.resolve()
7274
}
7375

74-
get isBuildDifferentialAware() {
76+
get isBuildDifferentialAware(): boolean {
7577
return !this.isPortable && this.options.differentialPackage !== false
7678
}
7779

@@ -115,7 +117,7 @@ export class NsisTarget extends Target {
115117
return "${productName} " + (this.isPortable ? "" : "Setup ") + "${version}.${ext}"
116118
}
117119

118-
private get isPortable() {
120+
private get isPortable(): boolean {
119121
return this.name === "portable"
120122
}
121123

@@ -176,7 +178,7 @@ export class NsisTarget extends Target {
176178

177179
const guid = options.guid || UUID.v5(appInfo.id, ELECTRON_BUILDER_NS_UUID)
178180
const uninstallAppKey = guid.replace(/\\/g, " - ")
179-
const defines: any = {
181+
const defines: Defines = {
180182
APP_ID: appInfo.id,
181183
APP_GUID: guid,
182184
// Windows bug - entry in Software\Microsoft\Windows\CurrentVersion\Uninstall cannot have \ symbols (dir)
@@ -199,7 +201,7 @@ export class NsisTarget extends Target {
199201
defines.UNINSTALL_REGISTRY_KEY_2 = `Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\${guid}`
200202
}
201203

202-
const commands: any = {
204+
const commands: Commands = {
203205
OutFile: `"${installerPath}"`,
204206
VIProductVersion: appInfo.getVersionInWeirdWindowsForm(),
205207
VIAddVersionKey: this.computeVersionKey(),
@@ -231,9 +233,13 @@ export class NsisTarget extends Target {
231233
const file = fileInfo.path
232234
const defineKey = arch === Arch.x64 ? "APP_64" : arch === Arch.arm64 ? "APP_ARM64" : "APP_32"
233235
defines[defineKey] = file
234-
defines[`${defineKey}_NAME`] = path.basename(file)
236+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
237+
const defineNameKey = `${defineKey}_NAME` as "APP_64_NAME" | "APP_ARM64_NAME" | "APP_32_NAME"
238+
defines[defineNameKey] = path.basename(file)
235239
// nsis expect a hexadecimal string
236-
defines[`${defineKey}_HASH`] = Buffer.from(fileInfo.sha512, "base64").toString("hex").toUpperCase()
240+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
241+
const defineHashKey = `${defineKey}_HASH` as "APP_64_HASH" | "APP_ARM64_HASH" | "APP_32_HASH"
242+
defines[defineHashKey] = Buffer.from(fileInfo.sha512, "base64").toString("hex").toUpperCase()
237243

238244
if (this.isWebInstaller) {
239245
await packager.dispatchArtifactCreated(file, this, arch)
@@ -293,8 +299,8 @@ export class NsisTarget extends Target {
293299
}
294300

295301
// prepare short-version variants of defines and commands, to make an uninstaller that doesn't differ much from the previous one
296-
const definesUninstaller: any = { ...defines }
297-
const commandsUninstaller: any = { ...commands }
302+
const definesUninstaller = { ...defines }
303+
const commandsUninstaller = { ...commands }
298304
if (appInfo.shortVersion != null) {
299305
definesUninstaller.VERSION = appInfo.shortVersion
300306
commandsUninstaller.VIProductVersion = appInfo.shortVersionWindows
@@ -335,21 +341,21 @@ export class NsisTarget extends Target {
335341
})
336342
}
337343

338-
protected generateGitHubInstallerName() {
344+
protected generateGitHubInstallerName(): string {
339345
const appInfo = this.packager.appInfo
340346
const classifier = appInfo.name.toLowerCase() === appInfo.name ? "setup-" : "Setup-"
341347
return `${appInfo.name}-${this.isPortable ? "" : classifier}${appInfo.version}.exe`
342348
}
343349

344-
private get isUnicodeEnabled() {
350+
private get isUnicodeEnabled(): boolean {
345351
return this.options.unicode !== false
346352
}
347353

348354
get isWebInstaller(): boolean {
349355
return false
350356
}
351357

352-
private async computeScriptAndSignUninstaller(defines: any, commands: any, installerPath: string, sharedHeader: string, archs: Map<Arch, string>) {
358+
private async computeScriptAndSignUninstaller(defines: Defines, commands: Commands, installerPath: string, sharedHeader: string, archs: Map<Arch, string>): Promise<string> {
353359
const packager = this.packager
354360
const customScriptPath = await packager.getResource(this.options.script, "installer.nsi")
355361
const script = await readFile(customScriptPath || path.join(nsisTemplatesDir, "installer.nsi"), "utf8")
@@ -416,7 +422,7 @@ export class NsisTarget extends Target {
416422
return versionKey
417423
}
418424

419-
protected configureDefines(oneClick: boolean, defines: any): Promise<any> {
425+
protected configureDefines(oneClick: boolean, defines: Defines): Promise<any> {
420426
const packager = this.packager
421427
const options = this.options
422428

@@ -514,7 +520,7 @@ export class NsisTarget extends Target {
514520
return asyncTaskManager.awaitTasks()
515521
}
516522

517-
private configureDefinesForAllTypeOfInstaller(defines: any) {
523+
private configureDefinesForAllTypeOfInstaller(defines: Defines): void {
518524
const appInfo = this.packager.appInfo
519525
const companyName = appInfo.companyName
520526
if (companyName != null) {
@@ -542,11 +548,11 @@ export class NsisTarget extends Target {
542548
}
543549
}
544550

545-
private async executeMakensis(defines: any, commands: any, script: string) {
551+
private async executeMakensis(defines: Defines, commands: Commands, script: string): Promise<void> {
546552
const args: Array<string> = this.options.warningsAsErrors === false ? [] : ["-WX"]
547553
args.push("-INPUTCHARSET", "UTF8")
548554
for (const name of Object.keys(defines)) {
549-
const value = defines[name]
555+
const value = defines[name as keyof Defines]
550556
if (value == null) {
551557
args.push(`-D${name}`)
552558
} else {
@@ -555,7 +561,7 @@ export class NsisTarget extends Target {
555561
}
556562

557563
for (const name of Object.keys(commands)) {
558-
const value = commands[name]
564+
const value = commands[name as keyof Commands]
559565
if (Array.isArray(value)) {
560566
for (const c of value) {
561567
args.push(`-X${name} ${c}`)
@@ -591,7 +597,7 @@ export class NsisTarget extends Target {
591597
})
592598
}
593599

594-
private async computeCommonInstallerScriptHeader() {
600+
private async computeCommonInstallerScriptHeader(): Promise<string> {
595601
const packager = this.packager
596602
const options = this.options
597603
const scriptGenerator = new NsisScriptGenerator()
@@ -640,7 +646,7 @@ export class NsisTarget extends Target {
640646
return scriptGenerator.build()
641647
}
642648

643-
private async computeFinalScript(originalScript: string, isInstaller: boolean, archs: Map<Arch, string>) {
649+
private async computeFinalScript(originalScript: string, isInstaller: boolean, archs: Map<Arch, string>): Promise<string> {
644650
const packager = this.packager
645651
const options = this.options
646652
const langConfigurator = new LangConfigurator(options)
@@ -703,7 +709,7 @@ export class NsisTarget extends Target {
703709
}
704710
}
705711

706-
async function generateForPreCompressed(preCompressedFileExtensions: Array<string>, dir: string, arch: Arch, scriptGenerator: NsisScriptGenerator) {
712+
async function generateForPreCompressed(preCompressedFileExtensions: Array<string>, dir: string, arch: Arch, scriptGenerator: NsisScriptGenerator): Promise<void> {
707713
const resourcesDir = path.join(dir, "resources")
708714
const dirInfo = await statOrNull(resourcesDir)
709715
if (dirInfo == null || !dirInfo.isDirectory()) {
@@ -728,7 +734,7 @@ async function generateForPreCompressed(preCompressedFileExtensions: Array<strin
728734
}
729735
}
730736

731-
async function ensureNotBusy(outFile: string) {
737+
async function ensureNotBusy(outFile: string): Promise<void> {
732738
function isBusy(wasBusyBefore: boolean): Promise<boolean> {
733739
return new Promise((resolve, reject) => {
734740
fs.open(outFile, "r+", (error, fd) => {

0 commit comments

Comments
 (0)