diff --git a/src/extensions/example-extension/package.json b/src/extensions/example-extension/package.json index 0311c7e71f..d57a78aab5 100644 --- a/src/extensions/example-extension/package.json +++ b/src/extensions/example-extension/package.json @@ -3,6 +3,9 @@ "version": "1.0.0", "description": "Example extension", "main": "example-extension.ts", + "lens": { + "metadata": {} + }, "dependencies": { } } diff --git a/src/extensions/extension-api.ts b/src/extensions/extension-api.ts index 8f23d6c825..e61aea08fe 100644 --- a/src/extensions/extension-api.ts +++ b/src/extensions/extension-api.ts @@ -1,5 +1,5 @@ // Lens-extensions api developer's kit -export type { LensRendererRuntimeEnv } from "./extension-api.runtime"; +export type { LensRuntimeRendererEnv } from "./lens-runtime"; // APIs export * from "./extension" diff --git a/src/extensions/extension-store.ts b/src/extensions/extension-store.ts index 3cd965654f..43da0103d7 100644 --- a/src/extensions/extension-store.ts +++ b/src/extensions/extension-store.ts @@ -1,9 +1,11 @@ +import type { LensRuntimeRendererEnv } from "./lens-runtime"; import path from "path"; import fs from "fs-extra"; -import { action, comparer, observable, toJS } from "mobx"; +import { action, observable, reaction, toJS } from "mobx"; import { BaseStore } from "../common/base-store"; import { ExtensionId, ExtensionManifest, ExtensionVersion, LensExtension } from "./extension"; import { isDevelopment } from "../common/vars"; +import logger from "../main/logger"; export interface ExtensionStoreModel { version: ExtensionVersion; @@ -11,7 +13,7 @@ export interface ExtensionStoreModel { } export interface ExtensionModel { - id: ExtensionId; + id?: ExtensionId; // available in lens-extension instance version: ExtensionVersion; name: string; manifestPath: string; @@ -39,7 +41,7 @@ export class ExtensionStore extends BaseStore { @observable version: ExtensionVersion = "0.0.0"; @observable extensions = observable.map(); @observable removed = observable.map(); - @observable installed = observable.map([], { equals: comparer.shallow }); + @observable.shallow installed = observable.map([]); get folderPath(): string { if (isDevelopment) { @@ -49,10 +51,28 @@ export class ExtensionStore extends BaseStore { } async load() { - await this.loadInstalledExtensions(); + await this.loadExtensions(); return super.load(); } + enableAutoInitOnLoad(getLensRuntimeEnv: () => LensRuntimeRendererEnv, { delay = 0 } = {}) { + logger.info('[EXTENSIONS-STORE]: enabled: auto-init loaded extensions'); + return reaction(() => Array.from(this.installed.values()), installedExtensions => { + installedExtensions.forEach(({ extensionModule, manifest, manifestPath }) => { + let instance = this.getById(manifestPath); + if (!instance) { + const LensExtension = extensionModule.default; + instance = new LensExtension({ ...manifest }, manifest); + this.extensions.set(manifestPath, instance); // fixme: mobx error + instance.enable(getLensRuntimeEnv()); + } + }) + }, { + fireImmediately: true, + delay: delay, + }) + } + getExtensionByManifest(manifestPath: string): InstalledExtension { let manifestJson: ExtensionManifest; let mainJs: string; @@ -72,21 +92,13 @@ export class ExtensionStore extends BaseStore { } @action - async loadInstalledExtensions() { - const extensions = await this.loadExtensions(this.folderPath); - this.installed.replace(extensions.map(ext => [ext.manifestPath, ext])); - - // todo: remove - if (process.isMainFrame) { - extensions.forEach(({ extensionModule, manifest }) => { - const LensExtension = extensionModule.default; - const instance = new LensExtension({ ...manifest }, manifest); - instance.activate(); - }) - } + async loadExtensions() { + const extensions = await this.loadFromFolder(this.folderPath); + const extManifestMap = new Map(extensions.map(ext => [ext.manifestPath, ext])); + this.installed.replace(extManifestMap); } - async loadExtensions(folderPath: string): Promise { + async loadFromFolder(folderPath: string): Promise { const paths = await fs.readdir(folderPath); const manifestsLoading = paths.map(fileName => { const absPath = path.resolve(folderPath, fileName); diff --git a/src/extensions/extension.ts b/src/extensions/extension.ts index bc9b3d4aab..3300c96c60 100644 --- a/src/extensions/extension.ts +++ b/src/extensions/extension.ts @@ -1,11 +1,11 @@ import type { ExtensionModel } from "./extension-store"; -import type { LensRendererRuntimeEnv } from "./extension-api.runtime"; +import type { LensRuntimeRendererEnv } from "./lens-runtime"; import { readJsonSync } from "fs-extra"; import { action, observable } from "mobx"; import extensionManifest from "./example-extension/package.json" import logger from "../main/logger"; -export type ExtensionId = string; +export type ExtensionId = string; // id or path to "%lens-extension/manifest.json" export type ExtensionVersion = string | number; export type ExtensionManifest = typeof extensionManifest & ExtensionModel; @@ -19,7 +19,7 @@ export class LensExtension implements ExtensionModel { @observable manifest: ExtensionManifest; @observable manifestPath: string; @observable isEnabled = false; - @observable.ref runtime: LensRendererRuntimeEnv; + @observable.ref runtime: Partial = {}; constructor(model: ExtensionModel, manifest: ExtensionManifest) { this.importModel(model, manifest); @@ -38,38 +38,30 @@ export class LensExtension implements ExtensionModel { } } - async activate() { - logger.info(`[EXTENSION]: activate ${this.name}@${this.version}`, this.getMeta()); - } - - async deactivate() { - logger.info(`[EXTENSION]: deactivate ${this.name}@${this.version}`, this.getMeta()); - } - - async enable() { - logger.info(`[EXTENSION]: enable ${this.name}@${this.version}`, this.getMeta()); + async enable(runtime: LensRuntimeRendererEnv) { this.isEnabled = true; + this.runtime = runtime; + logger.info(`[EXTENSION]: enable ${this.name}@${this.version}`, this.getMeta()); } async disable() { - logger.info(`[EXTENSION]: disable ${this.name}@${this.version}`, this.getMeta()); this.isEnabled = false; + this.runtime = {}; + logger.info(`[EXTENSION]: disable ${this.name}@${this.version}`, this.getMeta()); } - async install() { - // todo + // todo + async install(downloadUrl?: string) { + return; } + // todo async uninstall() { - // todo + return; } - async upgrade() { - // todo - } - - async checkNewVersion() { - // todo + async hasNewVersion(): Promise> { + return; } getMeta() { @@ -78,6 +70,7 @@ export class LensExtension implements ExtensionModel { manifest: this.manifest, manifestPath: this.manifestPath, enabled: this.isEnabled, + runtime: Object.keys(this.runtime), } } diff --git a/src/extensions/extension-api.runtime.ts b/src/extensions/lens-runtime.ts similarity index 54% rename from src/extensions/extension-api.runtime.ts rename to src/extensions/lens-runtime.ts index 02792a4f66..d78fc61d9f 100644 --- a/src/extensions/extension-api.runtime.ts +++ b/src/extensions/lens-runtime.ts @@ -1,12 +1,12 @@ -// Lens runtime params provider to hook up into extensions +// Lens runtime for injecting to extension on activation import { apiManager, ApiManager } from "../renderer/api/api-manager"; -export interface LensRendererRuntimeEnv { +export interface LensRuntimeRendererEnv { apiManager: ApiManager; } // todo: expose more public runtime apis: stores, managers, etc. -export function getExtensionRuntime(): LensRendererRuntimeEnv { +export function getLensRuntime(): LensRuntimeRendererEnv { return { apiManager, } diff --git a/src/renderer/lens-app.tsx b/src/renderer/lens-app.tsx index 9b4fffc6a1..1034c7be99 100644 --- a/src/renderer/lens-app.tsx +++ b/src/renderer/lens-app.tsx @@ -11,9 +11,15 @@ import { ErrorBoundary } from "./components/error-boundary"; import { WhatsNew, whatsNewRoute } from "./components/+whats-new"; import { Notifications } from "./components/notifications"; import { ConfirmDialog } from "./components/confirm-dialog"; +import { extensionStore } from "../extensions/extension-store"; +import { getLensRuntime } from "../extensions/lens-runtime"; @observer export class LensApp extends React.Component { + componentDidMount() { + extensionStore.enableAutoInitOnLoad(getLensRuntime); + } + render() { return (