mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
136 lines
4.8 KiB
TypeScript
136 lines
4.8 KiB
TypeScript
import type { ExtensionId, LensExtension, ExtensionManifest, ExtensionModel } from "./lens-extension"
|
|
import type { LensMainExtension } from "./lens-main-extension"
|
|
import type { LensRendererExtension } from "./lens-renderer-extension"
|
|
import type { LensExtensionMainRuntimeEnv } from "./lens-runtime"
|
|
import type { LensExtensionRendererRuntimeEnv } from "./lens-renderer-runtime"
|
|
import path from "path"
|
|
import { broadcastIpc } from "../common/ipc"
|
|
import { observable, reaction, toJS, } from "mobx"
|
|
import logger from "../main/logger"
|
|
import { app, remote, ipcRenderer } from "electron"
|
|
import { pageRegistry } from "./page-registry";
|
|
import { appPreferenceRegistry } from "./app-preference-registry"
|
|
import { menuRegistry } from "./menu-registry";
|
|
import { statusBarRegistry } from "./status-bar-registry";
|
|
|
|
export interface InstalledExtension extends ExtensionModel {
|
|
manifestPath: string;
|
|
manifest: ExtensionManifest;
|
|
}
|
|
|
|
// lazy load so that we get correct userData
|
|
export function extensionPackagesRoot() {
|
|
return path.join((app || remote.app).getPath("userData"))
|
|
}
|
|
|
|
export class ExtensionLoader {
|
|
@observable extensions = observable.map<ExtensionId, InstalledExtension>([], { deep: false });
|
|
@observable instances = observable.map<ExtensionId, LensExtension>([], { deep: false })
|
|
|
|
constructor() {
|
|
if (ipcRenderer) {
|
|
ipcRenderer.on("extensions:loaded", (event, extensions: InstalledExtension[]) => {
|
|
extensions.forEach((ext) => {
|
|
if (!this.getById(ext.manifestPath)) {
|
|
this.extensions.set(ext.manifestPath, ext)
|
|
}
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
loadOnClusterRenderer(getLensRuntimeEnv: () => LensExtensionRendererRuntimeEnv) {
|
|
logger.info('[EXTENSIONS-LOADER]: load on cluster renderer')
|
|
this.autoloadExtensions(getLensRuntimeEnv, (instance: LensRendererExtension) => {
|
|
instance.registerPages(pageRegistry)
|
|
})
|
|
}
|
|
|
|
loadOnMainRenderer(getLensRuntimeEnv: () => LensExtensionRendererRuntimeEnv) {
|
|
logger.info('[EXTENSIONS-LOADER]: load on main renderer')
|
|
this.autoloadExtensions(getLensRuntimeEnv, (instance: LensRendererExtension) => {
|
|
instance.registerPages(pageRegistry)
|
|
instance.registerAppPreferences(appPreferenceRegistry)
|
|
})
|
|
}
|
|
|
|
loadOnMain(getLensRuntimeEnv: () => LensExtensionMainRuntimeEnv) {
|
|
logger.info('[EXTENSIONS-LOADER]: load on main')
|
|
this.autoloadExtensions(getLensRuntimeEnv, (instance: LensMainExtension) => {
|
|
instance.registerAppMenus(menuRegistry);
|
|
instance.registerStatusBarIcon(statusBarRegistry);
|
|
})
|
|
}
|
|
|
|
protected autoloadExtensions(getLensRuntimeEnv: () => object, callback: (instance: LensExtension) => void) {
|
|
return reaction(() => this.extensions.toJS(), (installedExtensions) => {
|
|
for(const [id, ext] of installedExtensions) {
|
|
let instance = this.instances.get(ext.manifestPath)
|
|
if (!instance) {
|
|
const extensionModule = this.requireExtension(ext)
|
|
if (!extensionModule) {
|
|
continue
|
|
}
|
|
const LensExtensionClass = extensionModule.default;
|
|
instance = new LensExtensionClass({ ...ext.manifest, manifestPath: ext.manifestPath, id: ext.manifestPath }, ext.manifest);
|
|
instance.enable(getLensRuntimeEnv())
|
|
callback(instance)
|
|
this.instances.set(ext.id, instance)
|
|
}
|
|
}
|
|
}, {
|
|
fireImmediately: true,
|
|
delay: 0,
|
|
})
|
|
}
|
|
|
|
protected requireExtension(extension: InstalledExtension) {
|
|
let extEntrypoint = ""
|
|
try {
|
|
if (ipcRenderer && extension.manifest.renderer) {
|
|
extEntrypoint = path.resolve(path.join(path.dirname(extension.manifestPath), extension.manifest.renderer))
|
|
} else if (!ipcRenderer && extension.manifest.main) {
|
|
extEntrypoint = path.resolve(path.join(path.dirname(extension.manifestPath), extension.manifest.main))
|
|
}
|
|
if (extEntrypoint !== "") {
|
|
return __non_webpack_require__(extEntrypoint)
|
|
}
|
|
} catch (err) {
|
|
console.error(`[EXTENSION-LOADER]: can't load extension main at ${extEntrypoint}: ${err}`, { extension });
|
|
console.trace(err)
|
|
}
|
|
}
|
|
|
|
getById(id: ExtensionId): InstalledExtension {
|
|
return this.extensions.get(id);
|
|
}
|
|
|
|
async removeById(id: ExtensionId) {
|
|
const extension = this.getById(id);
|
|
if (extension) {
|
|
const instance = this.instances.get(extension.id)
|
|
if (instance) { await instance.disable() }
|
|
this.extensions.delete(id);
|
|
}
|
|
}
|
|
|
|
broadcastExtensions(frameId?: number) {
|
|
broadcastIpc({
|
|
channel: "extensions:loaded",
|
|
frameId: frameId,
|
|
frameOnly: !!frameId,
|
|
args: [this.toJSON().extensions],
|
|
})
|
|
}
|
|
|
|
toJSON() {
|
|
return toJS({
|
|
extensions: Array.from(this.extensions).map(([id, instance]) => instance),
|
|
}, {
|
|
recurseEverything: true,
|
|
})
|
|
}
|
|
}
|
|
|
|
export const extensionLoader = new ExtensionLoader()
|