diff --git a/app/commands.js b/app/commands.ts similarity index 91% rename from app/commands.js rename to app/commands.ts index a5e5acb6..4b5fdf15 100644 --- a/app/commands.js +++ b/app/commands.ts @@ -1,9 +1,9 @@ -import {app, Menu} from 'electron'; +import {app, Menu, BrowserWindow} from 'electron'; import {openConfig, getConfig} from './config'; import {updatePlugins} from './plugins'; import {installCLI} from './utils/cli-install'; -const commands = { +const commands: Record void> = { 'window:new': () => { // If window is created on the same tick, it will consume event too setTimeout(app.createWindow, 0); @@ -31,7 +31,7 @@ const commands = { focusedWindow && focusedWindow.rpc.emit('session clear req'); }, 'editor:selectAll': focusedWindow => { - focusedWindow.rpc.emit('term selectAll'); + focusedWindow && focusedWindow.rpc.emit('term selectAll'); }, 'plugins:update': () => { updatePlugins(); @@ -112,20 +112,20 @@ const commands = { }, 'window:hamburgerMenu': () => { if (getConfig().showHamburgerMenu) { - Menu.getApplicationMenu().popup({x: 15, y: 15}); + Menu.getApplicationMenu()!.popup({x: 15, y: 15}); } } }; //Special numeric command -[1, 2, 3, 4, 5, 6, 7, 8, 'last'].forEach(cmdIndex => { +([1, 2, 3, 4, 5, 6, 7, 8, 'last'] as const).forEach(cmdIndex => { const index = cmdIndex === 'last' ? cmdIndex : cmdIndex - 1; commands[`tab:jump:${cmdIndex}`] = focusedWindow => { focusedWindow && focusedWindow.rpc.emit('move jump req', index); }; }); -export const execCommand = (command, focusedWindow) => { +export const execCommand = (command: string, focusedWindow?: BrowserWindow) => { const fn = commands[command]; if (fn) { fn(focusedWindow); diff --git a/app/config/import.js b/app/config/import.ts similarity index 91% rename from app/config/import.js rename to app/config/import.ts index 0fd65668..d98f4bfa 100644 --- a/app/config/import.js +++ b/app/config/import.ts @@ -4,13 +4,13 @@ import {defaultCfg, cfgPath, legacyCfgPath, plugs, defaultPlatformKeyPath} from import {_init, _extractDefault} from './init'; import notify from '../notify'; -let defaultConfig; +let defaultConfig: Record | undefined; -const _write = (path, data) => { +const _write = (path: string, data: any) => { // This method will take text formatted as Unix line endings and transform it // to text formatted with DOS line endings. We do this because the default // text editor on Windows (notepad) doesn't Deal with LF files. Still. In 2017. - const crlfify = str => { + const crlfify = (str: string) => { return str.replace(/\r?\n/g, '\r\n'); }; const format = process.platform === 'win32' ? crlfify(data.toString()) : data; @@ -19,7 +19,7 @@ const _write = (path, data) => { // Saves a file as backup by appending '.backup' or '.backup2', '.backup3', etc. // so as to not override any existing files -const saveAsBackup = src => { +const saveAsBackup = (src: string) => { let attempt = 1; while (attempt < 100) { try { @@ -99,7 +99,7 @@ const _importConf = () => { // Importing platform specific keymap try { const content = readFileSync(defaultPlatformKeyPath(), 'utf8'); - const mapping = JSON.parse(content); + const mapping = JSON.parse(content) as Record; _defaultCfg.keymaps = mapping; } catch (err) { //eslint-disable-next-line no-console @@ -122,14 +122,14 @@ const _importConf = () => { export const _import = () => { const imported = _importConf(); - defaultConfig = imported.defaultCfg; - const result = _init(imported); + defaultConfig = imported?.defaultCfg; + const result = _init(imported!); return result; }; export const getDefaultConfig = () => { if (!defaultConfig) { - defaultConfig = _extractDefault(_importConf().defaultCfg); + defaultConfig = _importConf()?.defaultCfg; } return defaultConfig; }; diff --git a/app/config/init.js b/app/config/init.ts similarity index 79% rename from app/config/init.js rename to app/config/init.ts index c84d1f93..561103f7 100644 --- a/app/config/init.js +++ b/app/config/init.ts @@ -2,16 +2,16 @@ import vm from 'vm'; import notify from '../notify'; import mapKeys from '../utils/map-keys'; -const _extract = script => { - const module = {}; - script.runInNewContext({module}); +const _extract = (script?: vm.Script): Record => { + const module: Record = {}; + script?.runInNewContext({module}); if (!module.exports) { throw new Error('Error reading configuration: `module.exports` not set'); } return module.exports; }; -const _syntaxValidation = cfg => { +const _syntaxValidation = (cfg: string) => { try { return new vm.Script(cfg, {filename: '.hyper.js', displayErrors: true}); } catch (err) { @@ -19,12 +19,12 @@ const _syntaxValidation = cfg => { } }; -const _extractDefault = cfg => { +const _extractDefault = (cfg: string) => { return _extract(_syntaxValidation(cfg)); }; // init config -const _init = cfg => { +const _init = (cfg: {userCfg: string; defaultCfg: Record}) => { const script = _syntaxValidation(cfg.userCfg); if (script) { const _cfg = _extract(script); diff --git a/app/config/open.js b/app/config/open.ts similarity index 84% rename from app/config/open.js rename to app/config/open.ts index 4352cabe..63779bce 100644 --- a/app/config/open.js +++ b/app/config/open.ts @@ -5,12 +5,12 @@ export default () => Promise.resolve(shell.openItem(cfgPath)); // Windows opens .js files with WScript.exe by default // If the user hasn't set up an editor for .js files, we fallback to notepad. if (process.platform === 'win32') { - const Registry = require('winreg'); - const {exec} = require('child_process'); + const Registry = require('winreg') as typeof import('winreg'); + const {exec} = require('child_process') as typeof import('child_process'); const getUserChoiceKey = async () => { // Load FileExts keys for .js files - const keys = await new Promise((resolve, reject) => { + const keys: Winreg.Registry[] = await new Promise((resolve, reject) => { new Registry({ hive: Registry.HKCU, key: '\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\.js' @@ -29,11 +29,11 @@ if (process.platform === 'win32') { }; const hasDefaultSet = async () => { - let userChoice = await getUserChoiceKey(); + const userChoice = await getUserChoiceKey(); if (!userChoice) return false; // Load key values - let values = await new Promise((resolve, reject) => { + const values: string[] = await new Promise((resolve, reject) => { userChoice.values((error, items) => { if (error) { reject(error); @@ -51,7 +51,7 @@ if (process.platform === 'win32') { }; // This mimics shell.openItem, true if it worked, false if not. - const openNotepad = file => + const openNotepad = (file: string) => new Promise(resolve => { exec(`start notepad.exe ${file}`, error => { resolve(!error); diff --git a/app/config/paths.js b/app/config/paths.ts similarity index 96% rename from app/config/paths.js rename to app/config/paths.ts index 2d59d913..000826b2 100644 --- a/app/config/paths.js +++ b/app/config/paths.ts @@ -20,7 +20,7 @@ const applicationDirectory = let cfgDir = applicationDirectory; let cfgPath = join(applicationDirectory, cfgFile); -let legacyCfgPath = join(homeDirectory, cfgFile); // Hyper 2 config location +const legacyCfgPath = join(homeDirectory, cfgFile); // Hyper 2 config location const devDir = resolve(__dirname, '../..'); const devCfg = join(devDir, cfgFile); diff --git a/app/config/windows.js b/app/config/windows.ts similarity index 84% rename from app/config/windows.js rename to app/config/windows.ts index 2706c500..0486fdf4 100644 --- a/app/config/windows.js +++ b/app/config/windows.ts @@ -1,4 +1,5 @@ import Config from 'electron-store'; +import {BrowserWindow} from 'electron'; const defaults = { windowPosition: [50, 50], @@ -15,7 +16,7 @@ export default { const size = cfg.get('windowSize'); return {position, size}; }, - recordState(win) { + recordState(win: BrowserWindow) { cfg.set('windowPosition', win.getPosition()); cfg.set('windowSize', win.getSize()); } diff --git a/app/rpc.ts b/app/rpc.ts index 5f9f39b4..3b562830 100644 --- a/app/rpc.ts +++ b/app/rpc.ts @@ -36,7 +36,7 @@ export class Server extends EventEmitter { super.emit(ev, data); } - emit(ch: string, data: any): any { + emit(ch: string, data: any = {}): any { // This check is needed because data-batching can cause extra data to be // emitted after the window has already closed if (!this.win.isDestroyed()) { diff --git a/app/ui/contextmenu.js b/app/ui/contextmenu.ts similarity index 50% rename from app/ui/contextmenu.js rename to app/ui/contextmenu.ts index 481e130f..be004062 100644 --- a/app/ui/contextmenu.js +++ b/app/ui/contextmenu.ts @@ -2,26 +2,30 @@ import editMenu from '../menus/menus/edit'; import shellMenu from '../menus/menus/shell'; import {execCommand} from '../commands'; import {getDecoratedKeymaps} from '../plugins'; -const separator = {type: 'separator'}; +import {MenuItemConstructorOptions, BrowserWindow} from 'electron'; +const separator: MenuItemConstructorOptions = {type: 'separator'}; -const getCommandKeys = keymaps => - Object.keys(keymaps).reduce((commandKeys, command) => { +const getCommandKeys = (keymaps: Record): Record => + Object.keys(keymaps).reduce((commandKeys: Record, command) => { return Object.assign(commandKeys, { [command]: keymaps[command][0] }); }, {}); // only display cut/copy when there's a cursor selection -const filterCutCopy = (selection, menuItem) => { - if (/^cut$|^copy$/.test(menuItem.role) && !selection) { +const filterCutCopy = (selection: string, menuItem: MenuItemConstructorOptions) => { + if (/^cut$|^copy$/.test(menuItem.role!) && !selection) { return; } return menuItem; }; -export default (createWindow, selection) => { +export default ( + createWindow: (fn?: (win: BrowserWindow) => void, options?: Record) => BrowserWindow, + selection: string +) => { const commandKeys = getCommandKeys(getDecoratedKeymaps()); - const _shell = shellMenu(commandKeys, execCommand).submenu; + const _shell = shellMenu(commandKeys, execCommand).submenu as MenuItemConstructorOptions[]; const _edit = editMenu(commandKeys, execCommand).submenu.filter(filterCutCopy.bind(null, selection)); return _edit .concat(separator, _shell) diff --git a/package.json b/package.json index 5a6a867e..cb3db111 100644 --- a/package.json +++ b/package.json @@ -300,6 +300,8 @@ "@types/color": "3.0.0", "@types/columnify": "^1.5.0", "@types/electron-devtools-installer": "2.2.0", + "@types/fs-extra": "8.0.1", + "@types/mkdirp": "0.5.2", "@types/mousetrap": "^1.6.3", "@types/node": "^12.12.21", "@types/pify": "3.0.2", @@ -311,6 +313,7 @@ "@types/styled-jsx": "2.2.8", "@types/uuid": "3.4.6", "@types/webdriverio": "^4.8.0", + "@types/winreg": "1.2.30", "@typescript-eslint/eslint-plugin": "2.11.0", "@typescript-eslint/parser": "2.12.0", "ava": "2.4.0", diff --git a/yarn.lock b/yarn.lock index d15c554b..f4b238ab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -650,6 +650,13 @@ resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== +"@types/fs-extra@8.0.1": + version "8.0.1" + resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-8.0.1.tgz#a2378d6e7e8afea1564e44aafa2e207dadf77686" + integrity sha512-J00cVDALmi/hJOYsunyT52Hva5TnJeKP5yd1r+mH/ZU0mbYZflR0Z5kw5kITtKTRYMhm1JMClOFYdHnQszEvqw== + dependencies: + "@types/node" "*" + "@types/glob@^7.1.1": version "7.1.1" resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575" @@ -689,6 +696,13 @@ resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== +"@types/mkdirp@0.5.2": + version "0.5.2" + resolved "https://registry.yarnpkg.com/@types/mkdirp/-/mkdirp-0.5.2.tgz#503aacfe5cc2703d5484326b1b27efa67a339c1f" + integrity sha512-U5icWpv7YnZYGsN4/cmh3WD2onMY0aJIiTE6+51TwJCttdHvtCYmkBNOobHlXwrJRL0nkH9jH4kD+1FAdMN4Tg== + dependencies: + "@types/node" "*" + "@types/mousetrap@^1.6.3": version "1.6.3" resolved "https://registry.yarnpkg.com/@types/mousetrap/-/mousetrap-1.6.3.tgz#3159a01a2b21c9155a3d8f85588885d725dc987d" @@ -780,6 +794,11 @@ dependencies: "@types/node" "*" +"@types/winreg@1.2.30": + version "1.2.30" + resolved "https://registry.yarnpkg.com/@types/winreg/-/winreg-1.2.30.tgz#91d6710e536d345b9c9b017c574cf6a8da64c518" + integrity sha1-kdZxDlNtNFucmwF8V0z2qNpkxRg= + "@typescript-eslint/eslint-plugin@2.11.0": version "2.11.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.11.0.tgz#4477c33491ccf0a9a3f4a30ef84978fa0ea0cad2"