Skip to content

Commit

Permalink
application-package: refine configuration typings
Browse files Browse the repository at this point in the history
Typings now support both a `partial` and `resolved` version for our
configurations. A `resolved` configuration has `undefined` values
replaced by available defaults, a `partial` configuration does not.

Co-Authored-By: Nathanael Demacon <[email protected]>
  • Loading branch information
paul-marechal and quantumsheep committed May 20, 2021
1 parent 76f74f1 commit 956e15a
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 37 deletions.
94 changes: 61 additions & 33 deletions dev-packages/application-package/src/application-props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,27 @@

import type { BrowserWindowConstructorOptions } from 'electron';

/**
* Base configuration for the Theia application.
*/
export interface ApplicationConfig {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
readonly [key: string]: any;
}

/**
* Helper interface that generates two versions of an interface given two inputs:
* @param WithDefault key/values that may be left undefined but will be resolved to a default when missing.
* @param Rest key/values that may or may not be left undefined but will stay that way.
* @returns a pseudo-interface you can query 'resolved' or 'partial' to get different versions of the same interface.
*/
interface ConfigHelper<WithDefault extends object, Rest extends object> {
/** Query this field like `Config['partial']` to get the type with optional fields. */
partial: Partial<WithDefault> & Rest & ApplicationConfig
/** Query this field like `Config['resolved']` to get the type without undefined values for fields with defaults. */
resolved: Required<WithDefault> & Rest & ApplicationConfig
}

export interface NpmRegistryProps {

/**
Expand Down Expand Up @@ -76,37 +97,22 @@ export namespace ApplicationProps {
...NpmRegistryProps.DEFAULT,
target: 'browser',
backend: {
config: {}
config: BackendApplicationConfig.DEFAULT,
},
frontend: {
config: {
applicationName: 'Eclipse Theia',
defaultTheme: 'dark',
defaultIconTheme: 'none'
}
config: FrontendApplicationConfig.DEFAULT,
},
generator: {
config: {
preloadTemplate: ''
}
config: GeneratorConfig.DEFAULT,
}
};

}

/**
* Base configuration for the Theia application.
*/
export interface ApplicationConfig {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
readonly [key: string]: any;
}

/**
* Application configuration for the frontend. The following properties will be injected into the `index.html`.
*/
export interface FrontendApplicationConfig extends ApplicationConfig {

export type FrontendApplicationConfig = _FrontendApplicationConfig['resolved'];
type _FrontendApplicationConfig = ConfigHelper<{
/**
* The default theme for the application. If not given, defaults to `dark`. If invalid theme is given, also defaults to `dark`.
*/
Expand All @@ -121,47 +127,69 @@ export interface FrontendApplicationConfig extends ApplicationConfig {
* The name of the application. `Eclipse Theia` by default.
*/
readonly applicationName: string;

}, {
/**
* Electron specific configuration.
*/
readonly electron?: Readonly<ElectronFrontendApplicationConfig>;
}>;
export namespace FrontendApplicationConfig {
export const DEFAULT: FrontendApplicationConfig = {
applicationName: 'Eclipse Theia',
defaultTheme: 'dark',
defaultIconTheme: 'none'
};
export type Partial = _FrontendApplicationConfig['partial'];
}

export interface ElectronFrontendApplicationConfig {

export type ElectronFrontendApplicationConfig = _ElectronFrontendApplicationConfig['resolved'];
type _ElectronFrontendApplicationConfig = ConfigHelper<{
/**
* If set to `true`, reloading the current browser window won't be possible with the `Ctrl/Cmd + R` keybinding.
* It is `false` by default. Has no effect if not in an electron environment.
*/
readonly disallowReloadKeybinding?: boolean;

readonly disallowReloadKeybinding: boolean;
}, {
/**
* Override or add properties to the electron `windowOptions`.
*/
readonly windowOptions?: BrowserWindowConstructorOptions;
}>;
export namespace ElectronFrontendApplicationConfig {
export const DEFAULT: ElectronFrontendApplicationConfig = {
disallowReloadKeybinding: false,
};
export type Partial = _ElectronFrontendApplicationConfig['partial'];
}

/**
* Application configuration for the backend.
*/
export interface BackendApplicationConfig extends ApplicationConfig {

export type BackendApplicationConfig = _BackendApplicationConfig['resolved'];
type _BackendApplicationConfig = ConfigHelper<{}, {
/**
* If true and in Electron mode, only one instance of the application is allowed to run at a time.
*/
singleInstance?: boolean;

readonly singleInstance?: boolean;
}>;
export namespace BackendApplicationConfig {
export const DEFAULT: BackendApplicationConfig = {};
export type Partial = _BackendApplicationConfig['partial'];
}

/**
* Configuration for the generator.
*/
export interface GeneratorConfig {

export type GeneratorConfig = _GeneratorConfig['resolved'];
type _GeneratorConfig = ConfigHelper<{
/**
* Template to use for extra preload content markup (file path or HTML)
* Template to use for extra preload content markup (file path or HTML). Defaults to `''`.
*/
readonly preloadTemplate: string;

}, {}>;
export namespace GeneratorConfig {
export const DEFAULT: GeneratorConfig = {
preloadTemplate: ''
};
export type Partial = _GeneratorConfig['partial'];
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,18 @@ export class FrontendApplicationConfigProvider {
return config;
}

static set(config: FrontendApplicationConfig): void {
static set(config: FrontendApplicationConfig.Partial): void {
if (FrontendApplicationConfigProvider.doGet() !== undefined) {
throw new Error('The configuration is already set.');
}
const resolved: FrontendApplicationConfig = {
...FrontendApplicationConfig.DEFAULT,
...config
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const globalObject = window as any;
const key = FrontendApplicationConfigProvider.KEY;
globalObject[key] = config;
globalObject[key] = resolved;
}

private static doGet(): FrontendApplicationConfig | undefined {
Expand Down
8 changes: 6 additions & 2 deletions packages/core/src/node/backend-application-config-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,18 @@ export class BackendApplicationConfigProvider {
return config;
}

static set(config: BackendApplicationConfig): void {
static set(config: BackendApplicationConfig.Partial): void {
if (BackendApplicationConfigProvider.doGet() !== undefined) {
throw new Error('The configuration is already set.');
}
const resolved: BackendApplicationConfig = {
...BackendApplicationConfig.DEFAULT,
...config
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const globalObject = global as any;
const key = BackendApplicationConfigProvider.KEY;
globalObject[key] = config;
globalObject[key] = resolved;
}

private static doGet(): BackendApplicationConfig | undefined {
Expand Down

0 comments on commit 956e15a

Please sign in to comment.