From 6debd1e7f2a0dee3f5b987db89d378a9df58fd5b Mon Sep 17 00:00:00 2001 From: Labhansh Agrawal Date: Mon, 27 Apr 2020 19:02:08 +0530 Subject: [PATCH] add config types --- app/config.ts | 13 +++---- app/config/import.ts | 50 ++++++++++++++------------ app/config/init.ts | 29 +++++++++------- app/plugins.ts | 12 +++---- lib/actions/config.ts | 5 +-- lib/config.d.ts | 77 +++++++++++++++++++++++++++++++++++++++++ lib/constants/config.ts | 6 ++-- lib/hyper.d.ts | 2 +- lib/index.tsx | 5 +-- lib/reducers/ui.ts | 2 +- lib/utils/plugins.ts | 4 +-- 11 files changed, 147 insertions(+), 58 deletions(-) create mode 100644 lib/config.d.ts diff --git a/app/config.ts b/app/config.ts index 183480ac..ab4a3542 100644 --- a/app/config.ts +++ b/app/config.ts @@ -5,12 +5,13 @@ import _openConfig from './config/open'; import win from './config/windows'; import {cfgPath, cfgDir} from './config/paths'; import {getColorMap} from './utils/colors'; +import {parsedConfig, configOptions} from '../lib/config'; const watchers: any[] = []; -let cfg: Record = {}; +let cfg: parsedConfig = {} as any; let _watcher: fs.FSWatcher; -export const getDeprecatedCSS = (config: Record) => { +export const getDeprecatedCSS = (config: configOptions) => { const deprecated: string[] = []; const deprecatedCSS = ['x-screen', 'x-row', 'cursor-node', '::selection']; deprecatedCSS.forEach((css) => { @@ -124,15 +125,15 @@ export const getWin = win.get; export const winRecord = win.recordState; export const windowDefaults = win.defaults; -export const fixConfigDefaults = (decoratedConfig: any) => { - const defaultConfig = getDefaultConfig()?.config; +export const fixConfigDefaults = (decoratedConfig: configOptions) => { + const defaultConfig = getDefaultConfig().config!; decoratedConfig.colors = getColorMap(decoratedConfig.colors) || {}; // We must have default colors for xterm css. - decoratedConfig.colors = Object.assign({}, defaultConfig.colors, decoratedConfig.colors); + decoratedConfig.colors = {...defaultConfig.colors, ...decoratedConfig.colors}; return decoratedConfig; }; -export const htermConfigTranslate = (config: Record) => { +export const htermConfigTranslate = (config: configOptions) => { const cssReplacements: Record = { 'x-screen x-row([ {.[])': '.xterm-rows > div$1', '.cursor-node([ {.[])': '.terminal-cursor$1', diff --git a/app/config/import.ts b/app/config/import.ts index 190807f3..d0a954ef 100644 --- a/app/config/import.ts +++ b/app/config/import.ts @@ -3,8 +3,9 @@ import {sync as mkdirpSync} from 'mkdirp'; import {defaultCfg, cfgPath, legacyCfgPath, plugs, defaultPlatformKeyPath} from './paths'; import {_init, _extractDefault} from './init'; import notify from '../notify'; +import {rawConfig} from '../../lib/config'; -let defaultConfig: Record | undefined; +let defaultConfig: rawConfig; const _write = (path: string, data: any) => { // This method will take text formatted as Unix line endings and transform it @@ -92,41 +93,46 @@ const _importConf = () => { console.error(err); } + let defaultCfgRaw = ''; try { - const defaultCfgRaw = readFileSync(defaultCfg, 'utf8'); - const _defaultCfg = _extractDefault(defaultCfgRaw); - // Importing platform specific keymap - try { - const content = readFileSync(defaultPlatformKeyPath(), 'utf8'); - const mapping = JSON.parse(content) as Record; - _defaultCfg.keymaps = mapping; - } catch (err) { - console.error(err); - } - - // Import user config - try { - const userCfg = readFileSync(cfgPath, 'utf8'); - return {userCfg, defaultCfg: _defaultCfg}; - } catch (err) { - _write(cfgPath, defaultCfgRaw); - return {userCfg: defaultCfgRaw, defaultCfg: _defaultCfg}; - } + defaultCfgRaw = readFileSync(defaultCfg, 'utf8'); } catch (err) { console.log(err); } + const _defaultCfg = _extractDefault(defaultCfgRaw) as rawConfig; + + // Importing platform specific keymap + let content = '{}'; + try { + content = readFileSync(defaultPlatformKeyPath(), 'utf8'); + } catch (err) { + console.error(err); + } + const mapping = JSON.parse(content) as Record; + _defaultCfg.keymaps = mapping; + + // Import user config + let userCfg: string; + try { + userCfg = readFileSync(cfgPath, 'utf8'); + } catch (err) { + _write(cfgPath, defaultCfgRaw); + userCfg = defaultCfgRaw; + } + + return {userCfg, defaultCfg: _defaultCfg}; }; export const _import = () => { const imported = _importConf(); - defaultConfig = imported?.defaultCfg; + defaultConfig = imported.defaultCfg; const result = _init(imported!); return result; }; export const getDefaultConfig = () => { if (!defaultConfig) { - defaultConfig = _importConf()?.defaultCfg; + defaultConfig = _importConf().defaultCfg; } return defaultConfig; }; diff --git a/app/config/init.ts b/app/config/init.ts index 561103f7..db28f151 100644 --- a/app/config/init.ts +++ b/app/config/init.ts @@ -1,6 +1,7 @@ import vm from 'vm'; import notify from '../notify'; import mapKeys from '../utils/map-keys'; +import {parsedConfig, rawConfig, configOptions} from '../../lib/config'; const _extract = (script?: vm.Script): Record => { const module: Record = {}; @@ -24,22 +25,24 @@ const _extractDefault = (cfg: string) => { }; // init config -const _init = (cfg: {userCfg: string; defaultCfg: Record}) => { +const _init = (cfg: {userCfg: string; defaultCfg: rawConfig}): parsedConfig => { const script = _syntaxValidation(cfg.userCfg); - if (script) { - const _cfg = _extract(script); - if (!_cfg.config) { - notify('Error reading configuration: `config` key is missing'); - return cfg.defaultCfg; - } + const _cfg = script && (_extract(script) as rawConfig); + return { + config: (() => { + if (_cfg?.config) { + return _cfg.config; + } else { + notify('Error reading configuration: `config` key is missing'); + return cfg.defaultCfg.config || ({} as configOptions); + } + })(), // Merging platform specific keymaps with user defined keymaps - _cfg.keymaps = mapKeys(Object.assign({}, cfg.defaultCfg.keymaps, _cfg.keymaps)); + keymaps: mapKeys({...cfg.defaultCfg.keymaps, ..._cfg?.keymaps}), // Ignore undefined values in plugin and localPlugins array Issue #1862 - _cfg.plugins = (_cfg.plugins && _cfg.plugins.filter(Boolean)) || []; - _cfg.localPlugins = (_cfg.localPlugins && _cfg.localPlugins.filter(Boolean)) || []; - return _cfg; - } - return cfg.defaultCfg; + plugins: (_cfg?.plugins && _cfg.plugins.filter(Boolean)) || [], + localPlugins: (_cfg?.localPlugins && _cfg.localPlugins.filter(Boolean)) || [] + }; }; export {_init, _extractDefault}; diff --git a/app/plugins.ts b/app/plugins.ts index ff384382..f2cd7a65 100644 --- a/app/plugins.ts +++ b/app/plugins.ts @@ -13,6 +13,7 @@ import {availableExtensions} from './plugins/extensions'; import {install} from './plugins/install'; import {plugs} from './config/paths'; import mapKeys from './utils/map-keys'; +import {configOptions} from '../lib/config'; // local storage const cache = new Config(); @@ -187,10 +188,7 @@ if (cache.get('hyper.plugins') !== id || process.env.HYPER_FORCE_UPDATE) { const baseConfig = config.getConfig(); if (baseConfig['autoUpdatePlugins']) { // otherwise update plugins every 5 hours - setInterval( - updatePlugins, - ms(baseConfig['autoUpdatePlugins'] === true ? '5h' : (baseConfig['autoUpdatePlugins'] as string)) - ); + setInterval(updatePlugins, ms(baseConfig['autoUpdatePlugins'] === true ? '5h' : baseConfig['autoUpdatePlugins'])); } })(); @@ -372,7 +370,7 @@ function decorateEntity(base: any, key: string, type: 'object' | 'function') { return decorated; } -function decorateObject(base: any, key: string) { +function decorateObject(base: T, key: string): T { return decorateEntity(base, key, 'object'); } @@ -381,14 +379,14 @@ function decorateClass(base: any, key: string) { } export const getDeprecatedConfig = () => { - const deprecated: Record = {}; + const deprecated: Record = {}; const baseConfig = config.getConfig(); modules.forEach((plugin) => { if (!plugin.decorateConfig) { return; } // We need to clone config in case of plugin modifies config directly. - let configTmp; + let configTmp: configOptions; try { configTmp = plugin.decorateConfig(JSON.parse(JSON.stringify(baseConfig))); } catch (e) { diff --git a/lib/actions/config.ts b/lib/actions/config.ts index a237cce0..4c68302e 100644 --- a/lib/actions/config.ts +++ b/lib/actions/config.ts @@ -1,14 +1,15 @@ import {CONFIG_LOAD, CONFIG_RELOAD} from '../constants/config'; import {HyperActions} from '../hyper'; +import {configOptions} from '../config'; -export function loadConfig(config: any): HyperActions { +export function loadConfig(config: configOptions): HyperActions { return { type: CONFIG_LOAD, config }; } -export function reloadConfig(config: any): HyperActions { +export function reloadConfig(config: configOptions): HyperActions { const now = Date.now(); return { type: CONFIG_RELOAD, diff --git a/lib/config.d.ts b/lib/config.d.ts new file mode 100644 index 00000000..4c0170ec --- /dev/null +++ b/lib/config.d.ts @@ -0,0 +1,77 @@ +import {FontWeight} from 'xterm'; + +export type configOptions = { + autoUpdatePlugins: boolean | string; + backgroundColor: string; + bell: string; + bellSound: string | null; + bellSoundURL: string | null; + borderColor: string; + colors: { + black: string; + blue: string; + cyan: string; + green: string; + lightBlack: string; + lightBlue: string; + lightCyan: string; + lightGreen: string; + lightMagenta: string; + lightRed: string; + lightWhite: string; + lightYellow: string; + magenta: string; + red: string; + white: string; + yellow: string; + }; + copyOnSelect: boolean; + css: string; + cursorAccentColor: string; + cursorBlink: boolean; + cursorColor: string; + cursorShape: 'BEAM' | 'UNDERLINE' | 'BLOCK'; + defaultSSHApp: boolean; + disableLigatures: boolean; + env: Record; + fontFamily: string; + fontSize: number; + fontWeight: FontWeight; + fontWeightBold: FontWeight; + foregroundColor: string; + letterSpacing: number; + lineHeight: number; + macOptionSelectionMode: string; + modifierKeys: { + altIsMeta: boolean; + cmdIsMeta: boolean; + }; + padding: string; + quickEdit: boolean; + scrollback: number; + selectionColor: string; + shell: string; + shellArgs: string[]; + showHamburgerMenu: boolean | ''; + showWindowControls: string; + termCSS: string; + uiFontFamily: string; + updateChannel: 'stable' | 'canary'; + useConpty: boolean; + webGLRenderer: boolean; + windowSize: [number, number]; +}; + +export type rawConfig = { + config?: configOptions; + plugins?: string[]; + localPlugins?: string[]; + keymaps?: Record; +}; + +export type parsedConfig = { + config: configOptions; + plugins: string[]; + localPlugins: string[]; + keymaps: Record; +}; diff --git a/lib/constants/config.ts b/lib/constants/config.ts index 02c199ac..1b18cfbd 100644 --- a/lib/constants/config.ts +++ b/lib/constants/config.ts @@ -1,15 +1,17 @@ +import {configOptions} from '../config'; + export const CONFIG_LOAD = 'CONFIG_LOAD'; export const CONFIG_RELOAD = 'CONFIG_RELOAD'; export interface ConfigLoadAction { type: typeof CONFIG_LOAD; - config: any; + config: configOptions; now?: number; } export interface ConfigReloadAction { type: typeof CONFIG_RELOAD; - config: any; + config: configOptions; now: number; } diff --git a/lib/hyper.d.ts b/lib/hyper.d.ts index 1524f94d..f4565d8d 100644 --- a/lib/hyper.d.ts +++ b/lib/hyper.d.ts @@ -96,7 +96,7 @@ export type uiState = { rows: number | null; scrollback: number; selectionColor: string; - showHamburgerMenu: string; + showHamburgerMenu: boolean | ''; showWindowControls: string; termCSS: string; uiFontFamily: string; diff --git a/lib/index.tsx b/lib/index.tsx index 81c37b49..a1921fd0 100644 --- a/lib/index.tsx +++ b/lib/index.tsx @@ -17,6 +17,7 @@ import {addNotificationMessage} from './actions/notifications'; import {loadConfig, reloadConfig} from './actions/config'; import HyperContainer from './containers/hyper'; import configureStore from './store/configure-store'; +import {configOptions} from './config'; // On Linux, the default zoom was somehow changed with Electron 3 (or maybe 2). // Setting zoom factor to 1.2 brings back the normal default size @@ -31,8 +32,8 @@ Object.defineProperty(window, 'rpc', {get: () => rpc}); Object.defineProperty(window, 'config', {get: () => config}); Object.defineProperty(window, 'plugins', {get: () => plugins}); -const fetchFileData = (configData: any) => { - const configInfo = Object.assign({}, configData, {bellSound: null}); +const fetchFileData = (configData: configOptions) => { + const configInfo: configOptions = {...configData, bellSound: null}; if (!configInfo.bell || configInfo.bell.toUpperCase() !== 'SOUND' || !configInfo.bellSoundURL) { store_.dispatch(reloadConfig(configInfo)); return; diff --git a/lib/reducers/ui.ts b/lib/reducers/ui.ts index c18acbb2..2e80cd6a 100644 --- a/lib/reducers/ui.ts +++ b/lib/reducers/ui.ts @@ -238,7 +238,7 @@ const reducer = (state = initial, action: HyperActions) => { ret.modifierKeys = config.modifierKeys; } - if (allowedHamburgerMenuValues.has(config.showHamburgerMenu)) { + if (allowedHamburgerMenuValues.has(config.showHamburgerMenu as any)) { ret.showHamburgerMenu = config.showHamburgerMenu; } diff --git a/lib/utils/plugins.ts b/lib/utils/plugins.ts index b9c0b43d..1512a788 100644 --- a/lib/utils/plugins.ts +++ b/lib/utils/plugins.ts @@ -363,10 +363,10 @@ const loadModules = () => { }) .filter((mod: any) => Boolean(mod)); - const deprecatedPlugins: Record = plugins.getDeprecatedConfig(); + const deprecatedPlugins = plugins.getDeprecatedConfig(); Object.keys(deprecatedPlugins).forEach((name) => { const {css} = deprecatedPlugins[name]; - if (css) { + if (css.length > 0) { console.warn(`Warning: "${name}" plugin uses some deprecated CSS classes (${css.join(', ')}).`); } });