Skip to content

Commit

Permalink
refactor(nsis): make ambiguous types strict (#6868)
Browse files Browse the repository at this point in the history
* refactor(nsis): make ambiguous types strict to list the environment variables that can be used in the NSIS script.
  • Loading branch information
KaminoRyo authored May 19, 2022
1 parent fa72861 commit 13b078a
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 20 deletions.
5 changes: 5 additions & 0 deletions .changeset/serious-buttons-exist.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"app-builder-lib": patch
---

refactor(nsis): make ambiguous types strict for nsis DEFINES
9 changes: 9 additions & 0 deletions packages/app-builder-lib/src/targets/nsis/Commands.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export type Commands = {
OutFile: string
VIProductVersion?: string
VIAddVersionKey: Array<string>
Unicode: boolean
Icon?: string
SetCompress?: "off"
SetCompressor?: "zlib"
}
107 changes: 107 additions & 0 deletions packages/app-builder-lib/src/targets/nsis/Defines.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { PortableOptions } from "./nsisOptions"
import { PathLike } from "fs"
/**
* Parameters declared as environment variables in NSIS scripts.
* The documentation vaguely explains "All other electron-builder specific flags (e.g. ONE_CLICK) are still defined."
* Parameters with null values in TypeScript can be treated as Boolean values using "!Ifdef" in NSIS Script.
*/
export type Defines = {
APP_ID: string
APP_GUID: unknown
UNINSTALL_APP_KEY: unknown
PRODUCT_NAME: string
PRODUCT_FILENAME: string
APP_FILENAME: string
APP_DESCRIPTION: string
VERSION: string

PROJECT_DIR: string
BUILD_RESOURCES_DIR: string

APP_PACKAGE_NAME: string

ENABLE_LOGGING_ELECTRON_BUILDER?: null
UNINSTALL_REGISTRY_KEY_2?: string

MUI_ICON?: unknown
MUI_UNICON?: unknown

APP_DIR_64?: string
APP_DIR_ARM64?: string
APP_DIR_32?: string

APP_BUILD_DIR?: string

APP_64?: string
APP_ARM64?: string
APP_32?: string

APP_64_NAME?: string
APP_ARM64_NAME?: string
APP_32_NAME?: string

APP_64_HASH?: string
APP_ARM64_HASH?: string
APP_32_HASH?: string

REQUEST_EXECUTION_LEVEL?: PortableOptions["requestExecutionLevel"]

UNPACK_DIR_NAME?: string | false

SPLASH_IMAGE?: unknown

ESTIMATED_SIZE?: number

COMPRESS?: "auto"

BUILD_UNINSTALLER?: null
UNINSTALLER_OUT_FILE?: PathLike

ONE_CLICK?: null
RUN_AFTER_FINISH?: null
HEADER_ICO?: string
HIDE_RUN_AFTER_FINISH?: null

MUI_HEADERIMAGE?: null
MUI_HEADERIMAGE_RIGHT?: null
MUI_HEADERIMAGE_BITMAP?: string

MUI_WELCOMEFINISHPAGE_BITMAP?: string
MUI_UNWELCOMEFINISHPAGE_BITMAP?: string

MULTIUSER_INSTALLMODE_ALLOW_ELEVATION?: null

INSTALL_MODE_PER_ALL_USERS?: null
INSTALL_MODE_PER_ALL_USERS_REQUIRED?: null

allowToChangeInstallationDirectory?: null

MENU_FILENAME?: string

SHORTCUT_NAME?: string

DELETE_APP_DATA_ON_UNINSTALL?: null

UNINSTALLER_ICON?: string
UNINSTALL_DISPLAY_NAME?: string

RECREATE_DESKTOP_SHORTCUT?: null

DO_NOT_CREATE_DESKTOP_SHORTCUT?: null

DO_NOT_CREATE_START_MENU_SHORTCUT?: null

DISPLAY_LANG_SELECTOR?: null

COMPANY_NAME?: string

APP_PRODUCT_FILENAME?: string

APP_PACKAGE_STORE_FILE?: string

APP_INSTALLER_STORE_FILE?: string

ZIP_COMPRESSION?: null

COMPRESSION_METHOD?: "zip" | "7z"
}
46 changes: 26 additions & 20 deletions packages/app-builder-lib/src/targets/nsis/NsisTarget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import { WinPackager } from "../../winPackager"
import { archive, ArchiveOptions } from "../archive"
import { appendBlockmap, configureDifferentialAwareArchiveOptions, createBlockmap, createNsisWebDifferentialUpdateInfo } from "../differentialUpdateInfoBuilder"
import { getWindowsInstallationAppPackageName, getWindowsInstallationDirName } from "../targetUtil"
import { Commands } from "./Commands"
import { Defines } from "./Defines"
import { addCustomMessageFileInclude, createAddLangsMacro, LangConfigurator } from "./nsisLang"
import { computeLicensePage } from "./nsisLicense"
import { NsisOptions, PortableOptions } from "./nsisOptions"
Expand Down Expand Up @@ -71,7 +73,7 @@ export class NsisTarget extends Target {
return Promise.resolve()
}

get isBuildDifferentialAware() {
get isBuildDifferentialAware(): boolean {
return !this.isPortable && this.options.differentialPackage !== false
}

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

private get isPortable() {
private get isPortable(): boolean {
return this.name === "portable"
}

Expand Down Expand Up @@ -176,7 +178,7 @@ export class NsisTarget extends Target {

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

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

if (this.isWebInstaller) {
await packager.dispatchArtifactCreated(file, this, arch)
Expand Down Expand Up @@ -293,8 +299,8 @@ export class NsisTarget extends Target {
}

// prepare short-version variants of defines and commands, to make an uninstaller that doesn't differ much from the previous one
const definesUninstaller: any = { ...defines }
const commandsUninstaller: any = { ...commands }
const definesUninstaller = { ...defines }
const commandsUninstaller = { ...commands }
if (appInfo.shortVersion != null) {
definesUninstaller.VERSION = appInfo.shortVersion
commandsUninstaller.VIProductVersion = appInfo.shortVersionWindows
Expand Down Expand Up @@ -335,21 +341,21 @@ export class NsisTarget extends Target {
})
}

protected generateGitHubInstallerName() {
protected generateGitHubInstallerName(): string {
const appInfo = this.packager.appInfo
const classifier = appInfo.name.toLowerCase() === appInfo.name ? "setup-" : "Setup-"
return `${appInfo.name}-${this.isPortable ? "" : classifier}${appInfo.version}.exe`
}

private get isUnicodeEnabled() {
private get isUnicodeEnabled(): boolean {
return this.options.unicode !== false
}

get isWebInstaller(): boolean {
return false
}

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

protected configureDefines(oneClick: boolean, defines: any): Promise<any> {
protected configureDefines(oneClick: boolean, defines: Defines): Promise<any> {
const packager = this.packager
const options = this.options

Expand Down Expand Up @@ -514,7 +520,7 @@ export class NsisTarget extends Target {
return asyncTaskManager.awaitTasks()
}

private configureDefinesForAllTypeOfInstaller(defines: any) {
private configureDefinesForAllTypeOfInstaller(defines: Defines): void {
const appInfo = this.packager.appInfo
const companyName = appInfo.companyName
if (companyName != null) {
Expand Down Expand Up @@ -542,11 +548,11 @@ export class NsisTarget extends Target {
}
}

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

for (const name of Object.keys(commands)) {
const value = commands[name]
const value = commands[name as keyof Commands]
if (Array.isArray(value)) {
for (const c of value) {
args.push(`-X${name} ${c}`)
Expand Down Expand Up @@ -591,7 +597,7 @@ export class NsisTarget extends Target {
})
}

private async computeCommonInstallerScriptHeader() {
private async computeCommonInstallerScriptHeader(): Promise<string> {
const packager = this.packager
const options = this.options
const scriptGenerator = new NsisScriptGenerator()
Expand Down Expand Up @@ -640,7 +646,7 @@ export class NsisTarget extends Target {
return scriptGenerator.build()
}

private async computeFinalScript(originalScript: string, isInstaller: boolean, archs: Map<Arch, string>) {
private async computeFinalScript(originalScript: string, isInstaller: boolean, archs: Map<Arch, string>): Promise<string> {
const packager = this.packager
const options = this.options
const langConfigurator = new LangConfigurator(options)
Expand Down Expand Up @@ -703,7 +709,7 @@ export class NsisTarget extends Target {
}
}

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

async function ensureNotBusy(outFile: string) {
async function ensureNotBusy(outFile: string): Promise<void> {
function isBusy(wasBusyBefore: boolean): Promise<boolean> {
return new Promise((resolve, reject) => {
fs.open(outFile, "r+", (error, fd) => {
Expand Down

0 comments on commit 13b078a

Please sign in to comment.