Skip to content

[Tech] Let's be even more strict when writing TS code #1761

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 13 commits into from
Closed
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@
"react-dom": "17",
"react-i18next": "^11.16.7",
"react-router-dom": "^6.3.0",
"semver": "^7.3.7",
"shlex": "^2.1.2",
"simple-keyboard": "^3.4.136",
"source-map-support": "^0.5.21",
Expand Down
3 changes: 2 additions & 1 deletion public/locales/fr/gamepage.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
"title": "Arrêter l'installation"
},
"uninstall": {
"checkbox": "Remove prefix: {{prefix}}{{newLine}}Note: This can't be undone and will also remove not backed up save files.",
"checkbox": "Voulez-vous également retirer le préfixe ? Ceci ne peut pas être annulé.",
"checkbox_prefix": "Préfixe",
"message": "Voulez-vous désinstaller ce jeu\u202f?",
"title": "Désinstaller"
},
Expand Down
6 changes: 4 additions & 2 deletions src/backend/api/menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ export const moveInstall = async (
args: [appName: string, path: string, runner: Runner]
) => ipcRenderer.invoke('moveInstall', args)
export const changeInstallPath = async (
args: [appName: string, path: string, runner: Runner]
) => ipcRenderer.invoke('changeInstallPath', args)
appName: string,
runner: Runner,
path: string
) => ipcRenderer.invoke('changeInstallPath', appName, runner, path)
export const disableEosOverlay = async (appName: string) =>
ipcRenderer.invoke('disableEosOverlay', appName)
export const enableEosOverlay = async (appName: string) =>
Expand Down
7 changes: 4 additions & 3 deletions src/backend/api/misc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,13 +125,14 @@ export const storeNew = function (
}

export const storeSet = (storeName: string, key: string, value?: unknown) =>
stores[storeName].set(key, value)
stores[storeName]?.set(key, value)

export const storeHas = (storeName: string, key: string) =>
stores[storeName].has(key)
stores[storeName] ? stores[storeName]!.has(key) : false

export const storeGet = (
storeName: string,
key: string,
defaultValue?: unknown
) => stores[storeName].get(key, defaultValue)
) =>
stores[storeName] ? stores[storeName]!.get(key, defaultValue) : defaultValue
8 changes: 6 additions & 2 deletions src/backend/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,14 @@ abstract class GlobalConfig {
return execAsync(`which wine`)
.then(async ({ stdout }) => {
const wineBin = stdout.split('\n')[0]
defaultWine.bin = wineBin

const { stdout: out } = await execAsync(`wine --version`)
const version = out.split('\n')[0]

if (!wineBin || !version) {
return defaultWine
}

defaultWine.bin = wineBin
defaultWine.name = `Wine Default - ${version}`

return {
Expand Down
6 changes: 3 additions & 3 deletions src/backend/dialog/dialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ function showErrorBoxModalAuto(props: {
if (props.event) {
props.event.sender.send('showErrorDialog', props.title, props.error)
} else {
let window: BrowserWindow | null
try {
window = BrowserWindow.getFocusedWindow()
const window =
BrowserWindow.getFocusedWindow() || BrowserWindow.getAllWindows()[0]
if (!window) {
window = BrowserWindow.getAllWindows()[0]
return
}
window.webContents.send('showErrorDialog', props.title, props.error)
} catch (error) {
Expand Down
82 changes: 44 additions & 38 deletions src/backend/gog/games.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {
isLinux
} from '../constants'
import { installedGamesStore, syncStore } from '../gog/electronStores'
import { logError, logInfo, LogPrefix } from '../logger/logger'
import { logDebug, logError, logInfo, LogPrefix } from '../logger/logger'
import { GOGUser } from './user'
import {
getRunnerCallWithoutCredentials,
Expand All @@ -53,7 +53,7 @@ import { showErrorBoxModalAuto } from '../dialog/dialog'

class GOGGame extends Game {
public appName: string
public window = BrowserWindow.getAllWindows()[0]
public window = BrowserWindow.getAllWindows()[0]!
private static instances = new Map<string, GOGGame>()
private constructor(appName: string) {
super()
Expand Down Expand Up @@ -158,7 +158,7 @@ class GOGGame extends Game {
const etaMatch = data.match(/ETA: (\d\d:\d\d:\d\d)/m)
const bytesMatch = data.match(/Downloaded: (\S+) MiB/m)
const progressMatch = data.match(/Progress: (\d+\.\d+) /m)
if (bytesMatch && progressMatch) {
if (bytesMatch && bytesMatch[1] && progressMatch && progressMatch[1]) {
const eta = etaMatch ? etaMatch[1] : null
const bytes = bytesMatch[1]
let percent = parseFloat(progressMatch[1])
Expand Down Expand Up @@ -582,53 +582,56 @@ class GOGGame extends Game {
}
}
public async uninstall(): Promise<ExecResult> {
const array: Array<InstalledInfo> =
const allInstallledInfo: InstalledInfo[] =
(installedGamesStore.get('installed') as Array<InstalledInfo>) || []
const index = array.findIndex((game) => game.appName === this.appName)
if (index === -1) {
const installedInfo = allInstallledInfo.find(
(game) => game.appName === this.appName
)
if (!installedInfo) {
throw Error("Game isn't installed")
}
const newInstalledInfoArray = allInstallledInfo.filter(
(game) => game !== installedInfo
)
const { install_path } = installedInfo

const [object] = array.splice(index, 1)
logInfo(['Removing', object.install_path], { prefix: LogPrefix.Gog })
logInfo(['Removing', install_path], { prefix: LogPrefix.Gog })
// TODO: Run unins000.exe /verysilent /dir=Z:/path/to/game
const uninstallerPath = join(object.install_path, 'unins000.exe')
const uninstallerPath = join(install_path, 'unins000.exe')

const res: ExecResult = { stdout: '', stderr: '' }
if (existsSync(uninstallerPath)) {
const {
winePrefix,
wineVersion: { bin, name },
wineCrossoverBottle
} = GameConfig.get(this.appName).config
let commandPrefix = `WINEPREFIX="${winePrefix}" ${bin}`
if (name.includes('CrossOver')) {
commandPrefix = `CX_BOTTLE=${wineCrossoverBottle} ${bin}`
}
const command = `${
isWindows ? '' : commandPrefix
} "${uninstallerPath}" /verysilent /dir="${isWindows ? '' : 'Z:'}${
object.install_path
}"`
logInfo(['Executing uninstall command', command], {
prefix: LogPrefix.Gog
})
execAsync(command)
.then(({ stdout, stderr }) => {
if (!this.isNative()) {
const { stdout: wineGamePath } = await this.runWineCommand(
`winepath -w ${install_path}`
)
logDebug(['Game path from within Wine:', wineGamePath], {
prefix: LogPrefix.Gog
})
this.runWineCommand(
`${uninstallerPath} /verysilent /dir=${wineGamePath}`
).then(({ stdout, stderr }) => {
res.stdout = stdout
res.stderr = stderr
})
.catch((error) => {
res.error = `${error}`
})
} else {
execAsync(`${uninstallerPath} /verysilent /dir=${install_path}`)
.then(({ stdout, stderr }) => {
res.stdout = stdout
res.stderr = stderr
})
.catch((error) => {
res.error = `${error}`
})
}
} else {
rmSync(object.install_path, { recursive: true })
rmSync(install_path, { recursive: true })
}
installedGamesStore.set('installed', array)
installedGamesStore.set('installed', newInstalledInfoArray)
GOGLibrary.get().refreshInstalled()
await removeShortcuts(this.appName, 'gog')
syncStore.delete(this.appName)
const gameInfo = await this.getGameInfo()
const gameInfo = this.getGameInfo()
const { defaultSteamPath } = await GlobalConfig.get().getSettings()
const steamUserdataDir = join(
defaultSteamPath.replaceAll("'", ''),
Expand Down Expand Up @@ -689,13 +692,18 @@ class GOGGame extends Game {
}

const installedArray = installedGamesStore.get(
'installed'
'installed',
[]
) as InstalledInfo[]
const gameIndex = installedArray.findIndex(
(value) => this.appName === value.appName
)
const gameObject = installedArray[gameIndex]

if (!gameObject) {
return { status: 'error' }
}

if (gameData.install.platform !== 'linux') {
const installInfo = await this.getInstallInfo()
gameObject.buildId = installInfo.game.buildId
Expand Down Expand Up @@ -766,9 +774,7 @@ class GOGGame extends Game {
) as Array<InstalledInfo>
const newInstalled = installed.filter((g) => g.appName !== this.appName)
installedGamesStore.set('installed', newInstalled)
const mainWindow =
BrowserWindow.getFocusedWindow() ?? BrowserWindow.getAllWindows()[0]
mainWindow.webContents.send('refreshLibrary', 'gog')
this.window.webContents.send('refreshLibrary', 'gog')
}
}

Expand Down
13 changes: 6 additions & 7 deletions src/backend/gog/library.ts
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ export class GOGLibrary {
)

if (
!libraryArray[gameObjectIndex]?.gog_save_location &&
!libraryArray[gameObjectIndex]!.gog_save_location &&
this.installedGames.get(appName) &&
this.installedGames.get(appName)?.platform !== 'linux'
) {
Expand All @@ -368,11 +368,10 @@ export class GOGLibrary {
this.installedGames.get(appName)!
)
}

libraryArray[gameObjectIndex].folder_name = gogInfo?.folder_name
libraryArray[gameObjectIndex].gog_save_location =
gameData?.gog_save_location
gameData.folder_name = gogInfo?.folder_name
libraryArray[gameObjectIndex]!.folder_name = gogInfo.folder_name
libraryArray[gameObjectIndex]!.gog_save_location =
gameData.gog_save_location
gameData.folder_name = gogInfo.folder_name
libraryStore.set('games', libraryArray)
this.library.set(appName, gameData)
const info: GogInstallInfo = {
Expand Down Expand Up @@ -428,7 +427,7 @@ export class GOGLibrary {
(value) => value.appName === appName
)

installedArray[gameIndex].install_path = newInstallPath
installedArray[gameIndex]!.install_path = newInstallPath
cachedGameData.install.install_path = newInstallPath
installedGamesStore.set('installed', installedArray)
}
Expand Down
2 changes: 1 addition & 1 deletion src/backend/legendary/eos_overlay/eos_overlay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ async function install() {
logMessagePrefix: 'Getting EOS Overlay install size',
onOutput: (output: string, child: ChildProcess) => {
const downloadMatch = output.match(/Download size: ([\d.]+) MiB/)
if (downloadMatch) {
if (downloadMatch && downloadMatch[1]) {
downloadSize = parseFloat(downloadMatch[1])
// Output is in MiB, we want it in bytes
downloadSize = downloadSize * 1024 ** 2
Expand Down
14 changes: 6 additions & 8 deletions src/backend/legendary/games.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import { showErrorBoxModalAuto } from '../dialog/dialog'

class LegendaryGame extends Game {
public appName: string
public window = BrowserWindow.getAllWindows()[0]
public window = BrowserWindow.getAllWindows()[0]!
private static instances: Map<string, LegendaryGame> = new Map()

private constructor(appName: string) {
Expand Down Expand Up @@ -267,14 +267,14 @@ class LegendaryGame extends Game {

// store the download size, needed for correct calculation
// when cancel/resume downloads
if (downloadSizeMatch) {
if (downloadSizeMatch && downloadSizeMatch[1]) {
this.currentDownloadSize = parseFloat(downloadSizeMatch[1])
}

// parse log for game download progress
const etaMatch = data.match(/ETA: (\d\d:\d\d:\d\d)/m)
const bytesMatch = data.match(/Downloaded: (\S+.) MiB/m)
if (!etaMatch || !bytesMatch) {
if (!etaMatch || !etaMatch[1] || !bytesMatch || !bytesMatch[1]) {
return
}

Expand Down Expand Up @@ -335,7 +335,7 @@ class LegendaryGame extends Game {

const onOutput = (data: string) => {
this.onInstallOrUpdateOutput(
'installing',
'updating',
info.manifest.download_size,
data
)
Expand Down Expand Up @@ -428,7 +428,7 @@ class LegendaryGame extends Game {

const onOutput = (data: string) => {
this.onInstallOrUpdateOutput(
'updating',
'installing',
info.manifest.download_size,
data
)
Expand Down Expand Up @@ -739,9 +739,7 @@ class LegendaryGame extends Game {
'-y',
'--keep-files'
])
const mainWindow =
BrowserWindow.getFocusedWindow() ?? BrowserWindow.getAllWindows()[0]
mainWindow.webContents.send('refreshLibrary', 'legendary')
this.window.webContents.send('refreshLibrary', 'legendary')
} catch (error) {
logError(`Error reading ${installed}, could not complete operation`, {
prefix: LogPrefix.Legendary
Expand Down
6 changes: 4 additions & 2 deletions src/backend/legendary/library.ts
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ export class LegendaryLibrary {
return
}
const latestVersion = currentAsset.build_version
if (currentVersion !== latestVersion) {
if (latestVersion && currentVersion !== latestVersion) {
logDebug(
[
'Update is available for',
Expand Down Expand Up @@ -520,7 +520,9 @@ export class LegendaryLibrary {
namespace,
is_mac_native: info
? platform === 'Mac'
: releaseInfo[0].platform.includes('Mac'),
: releaseInfo[0]
? releaseInfo[0].platform.includes('Mac')
: false,
save_folder: saveFolder,
title,
canRunOffline,
Expand Down
2 changes: 1 addition & 1 deletion src/backend/logger/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ function convertInputToString(param: LogInputType): string {
// Object.prototype.toString.call(value).includes('Error') will catch all
// Error types (Error, EvalError, SyntaxError, ...)
if (Object.prototype.toString.call(value).includes('Error')) {
return value!['stack'] ? value!['stack'] : value!.toString()
return 'stack' in value! ? value['stack'] : value!.toString()
} else if (Object.prototype.toString.call(value).includes('Object')) {
return JSON.stringify(value, null, 2)
} else {
Expand Down
Loading