Skip to content
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

Update inversify to fix TypeScript 5.0 errors #12425

Merged
merged 11 commits into from
May 26, 2023
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@

- [Previous Changelogs](https://github.com/eclipse-theia/theia/tree/master/doc/changelogs/)

## v1.39.0 - 06/29/2023

<a name="breaking_changes_1.39.0">[Breaking Changes:](#breaking_changes_1.39.0)</a>

- [repo] with the upgrade to Inversify 6.0, a few initialization methods were adjusted. See also [this migration guide entry](https://github.com/eclipse-theia/theia/blob/master/doc/Migration.md#inversify-60). Additionally, other changes include: [#12425](https://github.com/eclipse-theia/theia/pull/12425)
- The type expected by the `PreferenceProxySchema` symbol has been changed from `PromiseLike<PreferenceSchema>` to `() => PromiseLike<PreferenceSchema>`
- The symbol `OnigasmPromise` has been changed to `OnigasmProvider` and injects a function of type `() => Promise<IOnigLib>`
- The symbol `PreferenceTransactionPrelude` has been changed to `PreferenceTransactionPreludeProvider ` and injects a function of type `() => Promise<unknown>`

## v1.38.0 - 05/25/2023

- [application-manager] fixed regression preventing browser-only builds from succeeding [#12491](https://github.com/eclipse-theia/theia/pull/12491)
Expand Down
42 changes: 42 additions & 0 deletions doc/Migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,48 @@ For example:
}
```

### v1.38.0

#### Inversify 6.0

With Inversify 6, the library has introduced a strict split between sync and async dependency injection contexts.
Theia uses the sync dependency injection context, and therefore no async dependencies cannot be used as dependencies in Theia.

This might require a few changes in your Theia extensions, if you've been using async dependencies before. These include:
1. Injecting promises directly into services
2. Classes with `@postConstruct` methods which return a `Promise` instance.

In order to work around 1., you can just wrap the promise inside of a function:

```diff
const PromiseSymbol = Symbol();
const promise = startLongRunningOperation();

-bind(PromiseSymbol).toConstantValue(promise);
+bind(PromiseSymbol).toConstantValue(() => promise);
```

The newly bound function needs to be handled appropriately at the injection side.

For 2., `@postConstruct` methods can be refactored into a sync and an async method:

```diff
-@postConstruct()
-protected async init(): Promise<void> {
- await longRunningOperation();
-}
+@postConstruct()
+protected init(): void {
+ this.doInit();
+}
+
+protected async doInit(): Promise<void> {
+ await longRunningOperation();
+}
```

Note that this release includes a few breaking changes that also perform this refactoring on our own classes.
If you've been overriding some of these `init()` methods, it might make sense to override `doInit()` instead.

### v1.37.0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ class SampleFileWatchingContribution implements FrontendApplicationContribution
protected readonly fileWatchingPreferences: FileWatchingPreferences;

@postConstruct()
protected postConstruct(): void {
protected init(): void {
this.verbose = this.fileWatchingPreferences['sample.file-watching.verbose'];
this.fileWatchingPreferences.onPreferenceChanged(e => {
if (e.preferenceName === 'sample.file-watching.verbose') {
Expand Down
6 changes: 5 additions & 1 deletion packages/console/src/browser/console-widget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,11 @@ export class ConsoleWidget extends BaseWidget implements StatefulWidget {
}

@postConstruct()
protected async init(): Promise<void> {
protected init(): void {
this.doInit();
}

protected async doInit(): Promise<void> {
const { id, title, inputFocusContextKey } = this.options;
const { label, iconClass, caption } = Object.assign({}, title);
this.id = id;
Expand Down
2 changes: 1 addition & 1 deletion packages/core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export class SomeClass {
- `@theia/request/lib/node-request-service` (from [`@theia/request@1.38.0`](https://www.npmjs.com/package/@theia/request/v/1.38.0))
- `fs-extra` (from [`fs-extra@^4.0.2`](https://www.npmjs.com/package/fs-extra))
- `fuzzy` (from [`fuzzy@^0.1.3`](https://www.npmjs.com/package/fuzzy))
- `inversify` (from [`inversify@^5.1.1`](https://www.npmjs.com/package/inversify))
- `inversify` (from [`inversify@^6.0.1`](https://www.npmjs.com/package/inversify))
- `react-dom` (from [`react-dom@^18.2.0`](https://www.npmjs.com/package/react-dom))
- `react-dom/client` (from [`react-dom@^18.2.0`](https://www.npmjs.com/package/react-dom))
- `react-virtuoso` (from [`react-virtuoso@^2.17.0`](https://www.npmjs.com/package/react-virtuoso))
Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
"http-proxy-agent": "^5.0.0",
"https-proxy-agent": "^5.0.0",
"iconv-lite": "^0.6.0",
"inversify": "^5.1.1",
"inversify": "^6.0.1",
"jschardet": "^2.1.1",
"keytar": "7.2.0",
"lodash.debounce": "^4.0.8",
Expand Down
6 changes: 5 additions & 1 deletion packages/core/src/browser/about-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,11 @@ export class AboutDialog extends ReactDialog<void> {
}

@postConstruct()
protected async init(): Promise<void> {
protected init(): void {
this.doInit();
}

protected async doInit(): Promise<void> {
this.applicationInfo = await this.appServer.getApplicationInfo();
this.extensionsInfos = await this.appServer.getExtensionsInfos();
this.update();
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/browser/frontend-application-bindings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export function bindPreferenceService(bind: interfaces.Bind): void {
bind(PreferenceProxyFactory).toFactory(({ container }) => (schema: MaybePromise<PreferenceSchema>, options: PreferenceProxyOptions = {}) => {
const child = container.createChild();
child.bind(PreferenceProxyOptions).toConstantValue(options ?? {});
child.bind(PreferenceProxySchema).toConstantValue(schema);
child.bind(PreferenceProxySchema).toConstantValue(() => schema);
const handler = child.get(InjectablePreferenceProxy);
return new Proxy(Object.create(null), handler); // eslint-disable-line no-null/no-null
});
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/browser/frontend-application-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ import {
BreadcrumbsService,
DefaultBreadcrumbRenderer,
} from './breadcrumbs';
import { RendererHost } from './widgets';
import { DockPanel, RendererHost } from './widgets';
import { TooltipService, TooltipServiceImpl } from './tooltip-service';
import { BackendRequestService, RequestService, REQUEST_SERVICE_PATH } from '@theia/request';
import { bindFrontendStopwatch, bindBackendStopwatch } from './performance';
Expand Down Expand Up @@ -190,7 +190,7 @@ export const frontendApplicationModule = new ContainerModule((bind, _unbind, _is
const hoverService = container.get(HoverService);
return new TabBarRenderer(contextMenuRenderer, tabBarDecoratorService, iconThemeService, selectionService, commandService, corePreferences, hoverService);
});
bind(TheiaDockPanel.Factory).toFactory(({ container }) => options => {
bind(TheiaDockPanel.Factory).toFactory(({ container }) => (options?: DockPanel.IOptions) => {
const corePreferences = container.get<CorePreferences>(CorePreferences);
return new TheiaDockPanel(options, corePreferences);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,11 @@ export class BrowserKeyboardLayoutProvider implements KeyboardLayoutProvider, Ke
}

@postConstruct()
protected async initialize(): Promise<void> {
protected init(): void {
this.doInit();
}

protected async doInit(): Promise<void> {
await this.loadState();
const keyboard = (navigator as NavigatorExtension).keyboard;
if (keyboard && keyboard.addEventListener) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export class InjectablePreferenceProxy<T extends Record<string, JSONValue>> impl

@inject(PreferenceProxyOptions) protected readonly options: PreferenceProxyOptions;
@inject(PreferenceService) protected readonly preferences: PreferenceService;
@inject(PreferenceProxySchema) protected readonly promisedSchema: PreferenceSchema | Promise<PreferenceSchema>;
@inject(PreferenceProxySchema) protected readonly promisedSchema: () => PreferenceSchema | Promise<PreferenceSchema>;
@inject(PreferenceProxyFactory) protected readonly factory: PreferenceProxyFactory;
protected toDispose = new DisposableCollection();
protected _onPreferenceChangedEmitter: Emitter<PreferenceChangeEvent<T>> | undefined;
Expand Down Expand Up @@ -95,10 +95,11 @@ export class InjectablePreferenceProxy<T extends Record<string, JSONValue>> impl

@postConstruct()
protected init(): void {
if (this.promisedSchema instanceof Promise) {
this.promisedSchema.then(schema => this.schema = schema);
const schema = this.promisedSchema();
if (schema instanceof Promise) {
schema.then(resolvedSchema => this.schema = resolvedSchema);
} else {
this.schema = this.promisedSchema;
this.schema = schema;
}
}

Expand Down
26 changes: 12 additions & 14 deletions packages/core/src/common/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
// *****************************************************************************

import { inject, injectable } from 'inversify';
import { inject, injectable, postConstruct } from 'inversify';
import { LoggerWatcher } from './logger-watcher';
import { ILoggerServer, LogLevel, ConsoleLogger, rootLoggerName } from './logger-protocol';

Expand Down Expand Up @@ -235,30 +235,28 @@ export class Logger implements ILogger {
/* A promise resolved when the logger has been created by the backend. */
protected created: Promise<void>;

/**
* Build a new Logger.
*/
constructor(
@inject(ILoggerServer) protected readonly server: ILoggerServer,
@inject(LoggerWatcher) protected readonly loggerWatcher: LoggerWatcher,
@inject(LoggerFactory) protected readonly factory: LoggerFactory,
@inject(LoggerName) protected name: string) {
@inject(ILoggerServer) protected readonly server: ILoggerServer;
@inject(LoggerWatcher) protected readonly loggerWatcher: LoggerWatcher;
@inject(LoggerFactory) protected readonly factory: LoggerFactory;
@inject(LoggerName) protected name: string;

if (name !== rootLoggerName) {
@postConstruct()
protected init(): void {
if (this.name !== rootLoggerName) {
/* Creating a child logger. */
this.created = server.child(name);
this.created = this.server.child(this.name);
} else {
/* Creating the root logger (it already exists at startup). */
this.created = Promise.resolve();
}

/* Fetch the log level so it's cached in the frontend. */
this._logLevel = this.created.then(_ => this.server.getLogLevel(name));
this._logLevel = this.created.then(_ => this.server.getLogLevel(this.name));

/* Update the log level if it changes in the backend. */
loggerWatcher.onLogLevelChanged(event => {
this.loggerWatcher.onLogLevelChanged(event => {
this.created.then(() => {
if (event.loggerName === name) {
if (event.loggerName === this.name) {
this._logLevel = Promise.resolve(event.newLogLevel);
}
});
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/common/messaging/proxy-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,10 @@ export class JsonRpcProxyFactory<T extends object> implements ProxyHandler<T> {
if (p === 'onDidCloseConnection') {
return this.onDidCloseConnectionEmitter.event;
}
if (p === 'then') {
// Prevent inversify from identifying this proxy as a promise object.
return undefined;
}
const isNotify = this.isNotification(p);
return (...args: any[]) => {
const method = p.toString();
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/common/performance/stopwatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export abstract class Stopwatch {
@inject(ILogger)
protected readonly logger: ILogger;

protected constructor(protected readonly defaultLogOptions: LogOptions) {
constructor(protected readonly defaultLogOptions: LogOptions) {
paul-marechal marked this conversation as resolved.
Show resolved Hide resolved
if (!defaultLogOptions.defaultLogLevel) {
defaultLogOptions.defaultLogLevel = DEFAULT_LOG_LEVEL;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export class ElectronKeyboardLayoutChangeNotifier implements KeyboardLayoutChang
}

@postConstruct()
protected initialize(): void {
protected init(): void {
window.electronTheiaCore.onKeyboardLayoutChanged((newLayout: NativeKeyboardLayout) => this.nativeLayoutChanged.fire(newLayout));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,11 @@ export class ElectronContextMenuRenderer extends BrowserContextMenuRenderer {
}

@postConstruct()
protected async init(): Promise<void> {
protected init(): void {
this.doInit();
}

protected async doInit(): Promise<void> {
this.useNativeStyle = await window.electronTheiaCore.getTitleBarStyleAtStartup() === 'native';
}

Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/electron-browser/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ const api: TheiaCoreAPI = {
commandHandlers.set(mainMenuId, handlers);
ipcRenderer.send(CHANNEL_SET_MENU, mainMenuId, convertMenu(menu, handlers));
},
getSecurityToken: () => ipcRenderer.invoke(CHANNEL_GET_SECURITY_TOKEN),
getSecurityToken: () => ipcRenderer.sendSync(CHANNEL_GET_SECURITY_TOKEN),
focusWindow: (name: string) => ipcRenderer.send(CHANNEL_FOCUS_WINDOW, name),
showItemInFolder: fsPath => {
ipcRenderer.send(CHANNEL_SHOW_ITEM_IN_FOLDER, fsPath);
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/electron-common/electron-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export type InternalMenuDto = Omit<MenuDto, 'execute' | 'submenu'> & {
export type WindowEvent = 'maximize' | 'unmaximize' | 'focus';

export interface TheiaCoreAPI {
getSecurityToken: () => Promise<string>;
getSecurityToken: () => string;
attachSecurityToken: (endpoint: string) => Promise<void>;

setMenuBarVisible(visible: boolean, windowName?: string): void;
Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/electron-main/electron-api-main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ export class TheiaMainApi implements ElectronMainApplicationContribution {

onStart(application: ElectronMainApplication): MaybePromise<void> {
// electron security token
ipcMain.handle(CHANNEL_GET_SECURITY_TOKEN, () => this.electronSecurityToken.value);
ipcMain.on(CHANNEL_GET_SECURITY_TOKEN, event => {
event.returnValue = this.electronSecurityToken.value;
});

ipcMain.handle(CHANNEL_ATTACH_SECURITY_TOKEN, (event, endpoint) => session.defaultSession.cookies.set({
url: endpoint,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export class ElectronTokenValidator implements WsRequestValidatorContribution {
protected electronSecurityToken: ElectronSecurityToken;

@postConstruct()
protected postConstruct(): void {
protected init(): void {
this.electronSecurityToken = this.getToken();
}

Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/node/backend-application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,10 @@ export class BackendApplication {
}

@postConstruct()
protected init(): void {
this.configure();
}

protected async configure(): Promise<void> {
// Do not await the initialization because contributions are expected to handle
// concurrent initialize/configure in undefined order if they provide both
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export class BackendApplicationHosts {
}

@postConstruct()
protected postConstruct(): void {
protected init(): void {
const theiaHostsEnv = process.env['THEIA_HOSTS'];
if (theiaHostsEnv) {
theiaHostsEnv.split(',').forEach(host => {
Expand Down
6 changes: 5 additions & 1 deletion packages/debug/src/browser/debug-configuration-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,11 @@ export class DebugConfigurationManager {
protected recentDynamicOptionsTracker: DynamicDebugConfigurationSessionOptions[] = [];

@postConstruct()
protected async init(): Promise<void> {
protected init(): void {
this.doInit();
}

protected async doInit(): Promise<void> {
this.debugConfigurationTypeKey = this.contextKeyService.createKey<string>('debugConfigurationType', undefined);
this.initialized = this.preferences.ready.then(() => {
this.preferences.onPreferenceChanged(e => {
Expand Down
2 changes: 1 addition & 1 deletion packages/debug/src/browser/debug-prefix-configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export class DebugPrefixConfiguration implements CommandContribution, CommandHan
});

@postConstruct()
protected initialize(): void {
protected init(): void {
this.handleDebugStatusBarVisibility();
this.preference.onPreferenceChanged(e => {
if (e.preferenceName === 'debug.showInStatusBar') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,11 @@ export class DebugBreakpointWidget implements Disposable {
}

@postConstruct()
protected async init(): Promise<void> {
protected init(): void {
this.doInit();
}

protected async doInit(): Promise<void> {
this.toDispose.push(this.zone = new MonacoEditorZoneWidget(this.editor.getControl()));
this.zone.containerNode.classList.add('theia-debug-breakpoint-widget');

Expand Down
6 changes: 5 additions & 1 deletion packages/debug/src/browser/editor/debug-exception-widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,11 @@ export class DebugExceptionWidget implements Disposable {
protected readonly toDispose = new DisposableCollection();

@postConstruct()
protected async init(): Promise<void> {
protected init(): void {
this.doInit();
}

protected async doInit(): Promise<void> {
this.toDispose.push(this.zone = new DebugExceptionMonacoEditorZoneWidget(this.editor.getControl()));
this.zone.containerNode.classList.add('theia-debug-exception-widget');
this.containerNodeRoot = createRoot(this.zone.containerNode);
Expand Down
Loading