add config types

This commit is contained in:
Labhansh Agrawal 2020-04-27 19:02:08 +05:30 committed by Benjamin Staneck
parent b099bb1218
commit 6debd1e7f2
11 changed files with 147 additions and 58 deletions

View file

@ -5,12 +5,13 @@ import _openConfig from './config/open';
import win from './config/windows'; import win from './config/windows';
import {cfgPath, cfgDir} from './config/paths'; import {cfgPath, cfgDir} from './config/paths';
import {getColorMap} from './utils/colors'; import {getColorMap} from './utils/colors';
import {parsedConfig, configOptions} from '../lib/config';
const watchers: any[] = []; const watchers: any[] = [];
let cfg: Record<string, any> = {}; let cfg: parsedConfig = {} as any;
let _watcher: fs.FSWatcher; let _watcher: fs.FSWatcher;
export const getDeprecatedCSS = (config: Record<string, any>) => { export const getDeprecatedCSS = (config: configOptions) => {
const deprecated: string[] = []; const deprecated: string[] = [];
const deprecatedCSS = ['x-screen', 'x-row', 'cursor-node', '::selection']; const deprecatedCSS = ['x-screen', 'x-row', 'cursor-node', '::selection'];
deprecatedCSS.forEach((css) => { deprecatedCSS.forEach((css) => {
@ -124,15 +125,15 @@ export const getWin = win.get;
export const winRecord = win.recordState; export const winRecord = win.recordState;
export const windowDefaults = win.defaults; export const windowDefaults = win.defaults;
export const fixConfigDefaults = (decoratedConfig: any) => { export const fixConfigDefaults = (decoratedConfig: configOptions) => {
const defaultConfig = getDefaultConfig()?.config; const defaultConfig = getDefaultConfig().config!;
decoratedConfig.colors = getColorMap(decoratedConfig.colors) || {}; decoratedConfig.colors = getColorMap(decoratedConfig.colors) || {};
// We must have default colors for xterm css. // We must have default colors for xterm css.
decoratedConfig.colors = Object.assign({}, defaultConfig.colors, decoratedConfig.colors); decoratedConfig.colors = {...defaultConfig.colors, ...decoratedConfig.colors};
return decoratedConfig; return decoratedConfig;
}; };
export const htermConfigTranslate = (config: Record<string, any>) => { export const htermConfigTranslate = (config: configOptions) => {
const cssReplacements: Record<string, string> = { const cssReplacements: Record<string, string> = {
'x-screen x-row([ {.[])': '.xterm-rows > div$1', 'x-screen x-row([ {.[])': '.xterm-rows > div$1',
'.cursor-node([ {.[])': '.terminal-cursor$1', '.cursor-node([ {.[])': '.terminal-cursor$1',

View file

@ -3,8 +3,9 @@ import {sync as mkdirpSync} from 'mkdirp';
import {defaultCfg, cfgPath, legacyCfgPath, plugs, defaultPlatformKeyPath} from './paths'; import {defaultCfg, cfgPath, legacyCfgPath, plugs, defaultPlatformKeyPath} from './paths';
import {_init, _extractDefault} from './init'; import {_init, _extractDefault} from './init';
import notify from '../notify'; import notify from '../notify';
import {rawConfig} from '../../lib/config';
let defaultConfig: Record<string, any> | undefined; let defaultConfig: rawConfig;
const _write = (path: string, data: any) => { const _write = (path: string, data: any) => {
// This method will take text formatted as Unix line endings and transform it // This method will take text formatted as Unix line endings and transform it
@ -92,41 +93,46 @@ const _importConf = () => {
console.error(err); console.error(err);
} }
let defaultCfgRaw = '';
try { try {
const defaultCfgRaw = readFileSync(defaultCfg, 'utf8'); 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<string, string | string[]>;
_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};
}
} catch (err) { } catch (err) {
console.log(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<string, string | string[]>;
_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 = () => { export const _import = () => {
const imported = _importConf(); const imported = _importConf();
defaultConfig = imported?.defaultCfg; defaultConfig = imported.defaultCfg;
const result = _init(imported!); const result = _init(imported!);
return result; return result;
}; };
export const getDefaultConfig = () => { export const getDefaultConfig = () => {
if (!defaultConfig) { if (!defaultConfig) {
defaultConfig = _importConf()?.defaultCfg; defaultConfig = _importConf().defaultCfg;
} }
return defaultConfig; return defaultConfig;
}; };

View file

@ -1,6 +1,7 @@
import vm from 'vm'; import vm from 'vm';
import notify from '../notify'; import notify from '../notify';
import mapKeys from '../utils/map-keys'; import mapKeys from '../utils/map-keys';
import {parsedConfig, rawConfig, configOptions} from '../../lib/config';
const _extract = (script?: vm.Script): Record<string, any> => { const _extract = (script?: vm.Script): Record<string, any> => {
const module: Record<string, any> = {}; const module: Record<string, any> = {};
@ -24,22 +25,24 @@ const _extractDefault = (cfg: string) => {
}; };
// init config // init config
const _init = (cfg: {userCfg: string; defaultCfg: Record<string, any>}) => { const _init = (cfg: {userCfg: string; defaultCfg: rawConfig}): parsedConfig => {
const script = _syntaxValidation(cfg.userCfg); const script = _syntaxValidation(cfg.userCfg);
if (script) { const _cfg = script && (_extract(script) as rawConfig);
const _cfg = _extract(script); return {
if (!_cfg.config) { config: (() => {
if (_cfg?.config) {
return _cfg.config;
} else {
notify('Error reading configuration: `config` key is missing'); notify('Error reading configuration: `config` key is missing');
return cfg.defaultCfg; return cfg.defaultCfg.config || ({} as configOptions);
} }
})(),
// Merging platform specific keymaps with user defined keymaps // 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 // Ignore undefined values in plugin and localPlugins array Issue #1862
_cfg.plugins = (_cfg.plugins && _cfg.plugins.filter(Boolean)) || []; plugins: (_cfg?.plugins && _cfg.plugins.filter(Boolean)) || [],
_cfg.localPlugins = (_cfg.localPlugins && _cfg.localPlugins.filter(Boolean)) || []; localPlugins: (_cfg?.localPlugins && _cfg.localPlugins.filter(Boolean)) || []
return _cfg; };
}
return cfg.defaultCfg;
}; };
export {_init, _extractDefault}; export {_init, _extractDefault};

View file

@ -13,6 +13,7 @@ import {availableExtensions} from './plugins/extensions';
import {install} from './plugins/install'; import {install} from './plugins/install';
import {plugs} from './config/paths'; import {plugs} from './config/paths';
import mapKeys from './utils/map-keys'; import mapKeys from './utils/map-keys';
import {configOptions} from '../lib/config';
// local storage // local storage
const cache = new Config(); const cache = new Config();
@ -187,10 +188,7 @@ if (cache.get('hyper.plugins') !== id || process.env.HYPER_FORCE_UPDATE) {
const baseConfig = config.getConfig(); const baseConfig = config.getConfig();
if (baseConfig['autoUpdatePlugins']) { if (baseConfig['autoUpdatePlugins']) {
// otherwise update plugins every 5 hours // otherwise update plugins every 5 hours
setInterval( setInterval(updatePlugins, ms(baseConfig['autoUpdatePlugins'] === true ? '5h' : baseConfig['autoUpdatePlugins']));
updatePlugins,
ms(baseConfig['autoUpdatePlugins'] === true ? '5h' : (baseConfig['autoUpdatePlugins'] as string))
);
} }
})(); })();
@ -372,7 +370,7 @@ function decorateEntity(base: any, key: string, type: 'object' | 'function') {
return decorated; return decorated;
} }
function decorateObject(base: any, key: string) { function decorateObject<T>(base: T, key: string): T {
return decorateEntity(base, key, 'object'); return decorateEntity(base, key, 'object');
} }
@ -381,14 +379,14 @@ function decorateClass(base: any, key: string) {
} }
export const getDeprecatedConfig = () => { export const getDeprecatedConfig = () => {
const deprecated: Record<string, any> = {}; const deprecated: Record<string, {css: string[]}> = {};
const baseConfig = config.getConfig(); const baseConfig = config.getConfig();
modules.forEach((plugin) => { modules.forEach((plugin) => {
if (!plugin.decorateConfig) { if (!plugin.decorateConfig) {
return; return;
} }
// We need to clone config in case of plugin modifies config directly. // We need to clone config in case of plugin modifies config directly.
let configTmp; let configTmp: configOptions;
try { try {
configTmp = plugin.decorateConfig(JSON.parse(JSON.stringify(baseConfig))); configTmp = plugin.decorateConfig(JSON.parse(JSON.stringify(baseConfig)));
} catch (e) { } catch (e) {

View file

@ -1,14 +1,15 @@
import {CONFIG_LOAD, CONFIG_RELOAD} from '../constants/config'; import {CONFIG_LOAD, CONFIG_RELOAD} from '../constants/config';
import {HyperActions} from '../hyper'; import {HyperActions} from '../hyper';
import {configOptions} from '../config';
export function loadConfig(config: any): HyperActions { export function loadConfig(config: configOptions): HyperActions {
return { return {
type: CONFIG_LOAD, type: CONFIG_LOAD,
config config
}; };
} }
export function reloadConfig(config: any): HyperActions { export function reloadConfig(config: configOptions): HyperActions {
const now = Date.now(); const now = Date.now();
return { return {
type: CONFIG_RELOAD, type: CONFIG_RELOAD,

77
lib/config.d.ts vendored Normal file
View file

@ -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<string, string>;
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<string, string | string[]>;
};
export type parsedConfig = {
config: configOptions;
plugins: string[];
localPlugins: string[];
keymaps: Record<string, string[]>;
};

View file

@ -1,15 +1,17 @@
import {configOptions} from '../config';
export const CONFIG_LOAD = 'CONFIG_LOAD'; export const CONFIG_LOAD = 'CONFIG_LOAD';
export const CONFIG_RELOAD = 'CONFIG_RELOAD'; export const CONFIG_RELOAD = 'CONFIG_RELOAD';
export interface ConfigLoadAction { export interface ConfigLoadAction {
type: typeof CONFIG_LOAD; type: typeof CONFIG_LOAD;
config: any; config: configOptions;
now?: number; now?: number;
} }
export interface ConfigReloadAction { export interface ConfigReloadAction {
type: typeof CONFIG_RELOAD; type: typeof CONFIG_RELOAD;
config: any; config: configOptions;
now: number; now: number;
} }

2
lib/hyper.d.ts vendored
View file

@ -96,7 +96,7 @@ export type uiState = {
rows: number | null; rows: number | null;
scrollback: number; scrollback: number;
selectionColor: string; selectionColor: string;
showHamburgerMenu: string; showHamburgerMenu: boolean | '';
showWindowControls: string; showWindowControls: string;
termCSS: string; termCSS: string;
uiFontFamily: string; uiFontFamily: string;

View file

@ -17,6 +17,7 @@ import {addNotificationMessage} from './actions/notifications';
import {loadConfig, reloadConfig} from './actions/config'; import {loadConfig, reloadConfig} from './actions/config';
import HyperContainer from './containers/hyper'; import HyperContainer from './containers/hyper';
import configureStore from './store/configure-store'; import configureStore from './store/configure-store';
import {configOptions} from './config';
// On Linux, the default zoom was somehow changed with Electron 3 (or maybe 2). // 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 // 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, 'config', {get: () => config});
Object.defineProperty(window, 'plugins', {get: () => plugins}); Object.defineProperty(window, 'plugins', {get: () => plugins});
const fetchFileData = (configData: any) => { const fetchFileData = (configData: configOptions) => {
const configInfo = Object.assign({}, configData, {bellSound: null}); const configInfo: configOptions = {...configData, bellSound: null};
if (!configInfo.bell || configInfo.bell.toUpperCase() !== 'SOUND' || !configInfo.bellSoundURL) { if (!configInfo.bell || configInfo.bell.toUpperCase() !== 'SOUND' || !configInfo.bellSoundURL) {
store_.dispatch(reloadConfig(configInfo)); store_.dispatch(reloadConfig(configInfo));
return; return;

View file

@ -238,7 +238,7 @@ const reducer = (state = initial, action: HyperActions) => {
ret.modifierKeys = config.modifierKeys; ret.modifierKeys = config.modifierKeys;
} }
if (allowedHamburgerMenuValues.has(config.showHamburgerMenu)) { if (allowedHamburgerMenuValues.has(config.showHamburgerMenu as any)) {
ret.showHamburgerMenu = config.showHamburgerMenu; ret.showHamburgerMenu = config.showHamburgerMenu;
} }

View file

@ -363,10 +363,10 @@ const loadModules = () => {
}) })
.filter((mod: any) => Boolean(mod)); .filter((mod: any) => Boolean(mod));
const deprecatedPlugins: Record<string, any> = plugins.getDeprecatedConfig(); const deprecatedPlugins = plugins.getDeprecatedConfig();
Object.keys(deprecatedPlugins).forEach((name) => { Object.keys(deprecatedPlugins).forEach((name) => {
const {css} = deprecatedPlugins[name]; const {css} = deprecatedPlugins[name];
if (css) { if (css.length > 0) {
console.warn(`Warning: "${name}" plugin uses some deprecated CSS classes (${css.join(', ')}).`); console.warn(`Warning: "${name}" plugin uses some deprecated CSS classes (${css.join(', ')}).`);
} }
}); });