How to extends Theia's classes inside extensions

I have two extensions that I’m building (let’s call them extension-a and extension-b). Both of them need to add some features to @theia/mini-browser.

More specifically, they need to customize mini-browser’s layout, so they’ll need to make some changes to MiniBrowserContent inside @theia/mini-browser/lib/browser/mini-browser-content.

So, I’ve created a new class called MiniBrowserContentA inside extension-a which extends MiniBrowserContent class. The implementation looks somewhat like this:

// extension-a/src/browser/mini-browser-content-a
import { MiniBrowserContent } from "@theia/mini-browser/lib/browser/mini-browser-content";
class MiniBrowserContentA extends MiniBrowserContent {

  createToolbar(
    parent: HTMLElement
  ): HTMLDivElement & Readonly<{ input: HTMLInputElement }> {
    const toolbar = super.createToolbar(parent);
    /* Make some changes to toolbar */
    return toolbar;
  }
}

and then I’m rebinding MiniBrowserContentA to MiniBrowserContent like this:

// extension-a/src/browser/extension-a-frontend-module
import { MiniBrowserContent } from "@theia/mini-browser/lib/browser/mini-browser-content";
import { MiniBrowserContentA } from "./mini-browser-content-a"

export default new ContainerModule(
  (
    bind: interfaces.Bind,
    unbind: interfaces.Unbind,
    isBound: interfaces.IsBound,
    rebind: interfaces.Rebind
  ) => {
    rebind(MiniBrowserContent).to(MinibrowserContentA);
  }
);

Now, in extension-b, I need to again extend MiniBrowserContent class, and so the implementation will look somewhat like this for extension-b

// extension-b/src/browser/mini-browser-content-b
import { MiniBrowserContent } from "@theia/mini-browser/lib/browser/mini-browser-content";
class MiniBrowserContentB extends MiniBrowserContent {

  createToolbar(
    parent: HTMLElement
  ): HTMLDivElement & Readonly<{ input: HTMLInputElement }> {
    const toolbar = super.createToolbar(parent);
    /* Make some changes to toolbar */
    return toolbar;
  }
}
// extension-b/src/browser/extension-b-frontend-module
import { MiniBrowserContent } from "@theia/mini-browser/lib/browser/mini-browser-content";
import { MiniBrowserContentB } from "./mini-browser-content-b"

export default new ContainerModule(
  (
    bind: interfaces.Bind,
    unbind: interfaces.Unbind,
    isBound: interfaces.IsBound,
    rebind: interfaces.Rebind
  ) => {
    rebind(MiniBrowserContent).to(MinibrowserContentB);
  }
);

Now, if I include both these extensions into my project via the package.json file in browser-app, then only the extension which comes later will have its changes to MiniBrowserContent class reflected in the final theia build. In this case, only extension-b's changes will be reflected.

// browser-app/package.json
"dependencies": {
  "extension-a": "0.0.0",
  "extension-b": "0.0.0"
}

To make sure, both extension-a and extension-b changes to MiniBrowserContent class are reflected, I need to import MiniBrowserContentA class into extension-b like this:

// extension-b/src/browser/mini-browser-content-b
import { MiniBrowserContentA } from "extension-a/lib/browser/mini-browser-content-a";
class MiniBrowserContentB extends MiniBrowserContentA {

  createToolbar(
    parent: HTMLElement
  ): HTMLDivElement & Readonly<{ input: HTMLInputElement }> {
    const toolbar = super.createToolbar(parent);
    /* Make some changes to toolbar */
    return toolbar;
  }
}

My question is, is there any better way to extend Theia’s base classes into multiple extension so that during the final build, all extensions contributions are reflected? Because, if I implement according to the above mentioned way, then extension-b will always need to depend upon extension-a to work. This doesn’t seem like a good solution.

I could’ve also used bind instead of rebind, but container.get(MiniBrowserContent) is used inside @theia/mini-browser/lib/browser/mini-browser-frontend-module, so it won’t work.

Please let me know what’s the best way to accomplish this task.

Closing as the question is already asked on our GitHub Discussions: