mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
1 parent
f97fbdf9ed
commit
ffa6a982b5
15 files changed
+122
-119
No files matched your search
@@ -66,14 +66,13 @@ export class ExtensionLoader {
|
||||
}
|
||||
|
||||
protected autoloadExtensions(getLensRuntimeEnv: () => LensExtensionRuntimeEnv, callback: (instance: LensExtension) => void) {
|
||||
return reaction(() => this.extensions.toJS(), installedExtensions => {
|
||||
installedExtensions.forEach((ext) => {
|
||||
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) {
|
||||
logger.error("[EXTENSION-LOADER] failed to load extension " + ext.manifestPath)
|
||||
return
|
||||
continue
|
||||
}
|
||||
const LensExtensionClass = extensionModule.default;
|
||||
instance = new LensExtensionClass({ ...ext.manifest, manifestPath: ext.manifestPath, id: ext.manifestPath }, ext.manifest);
|
||||
@@ -81,7 +80,7 @@ export class ExtensionLoader {
|
||||
callback(instance)
|
||||
this.instances.set(ext.id, instance)
|
||||
}
|
||||
})
|
||||
}
|
||||
}, {
|
||||
fireImmediately: true,
|
||||
delay: 0,
|
||||
@@ -92,17 +91,17 @@ export class ExtensionLoader {
|
||||
let extEntrypoint = ""
|
||||
return withExtensionPackagesRoot(() => {
|
||||
try {
|
||||
if (ipcRenderer) {
|
||||
extEntrypoint = path.join(path.dirname(extension.manifestPath), extension.manifest.renderer)
|
||||
} else {
|
||||
extEntrypoint = path.join(path.dirname(extension.manifestPath), extension.manifest.main)
|
||||
if (ipcRenderer && extension.manifest.renderer) {
|
||||
extEntrypoint = path.resolve(path.join(path.dirname(extension.manifestPath), extension.manifest.renderer))
|
||||
} else if (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.trace(err)
|
||||
console.error(`[EXTENSION-LOADER]: can't load extension main at ${extEntrypoint}: ${err}`, { extension });
|
||||
console.trace(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2,10 +2,23 @@ import type { ExtensionManifest } from "./lens-extension"
|
||||
import path from "path"
|
||||
import fs from "fs-extra"
|
||||
import logger from "../main/logger"
|
||||
import { withExtensionPackagesRoot, extensionPackagesRoot, InstalledExtension } from "./extension-loader"
|
||||
import npm from "npm"
|
||||
import { extensionPackagesRoot, InstalledExtension } from "./extension-loader"
|
||||
import * as child_process from 'child_process';
|
||||
|
||||
type Dependencies = {
|
||||
[name: string]: string;
|
||||
}
|
||||
|
||||
type PackageJson = {
|
||||
dependencies: Dependencies;
|
||||
}
|
||||
|
||||
export class ExtensionManager {
|
||||
|
||||
protected packagesJson: PackageJson = {
|
||||
dependencies: {}
|
||||
}
|
||||
|
||||
get extensionPackagesRoot() {
|
||||
return extensionPackagesRoot()
|
||||
}
|
||||
@@ -14,11 +27,13 @@ export class ExtensionManager {
|
||||
return path.resolve(__static, "../extensions");
|
||||
}
|
||||
|
||||
get npmPath() {
|
||||
return __non_webpack_require__.resolve('npm/bin/npm-cli')
|
||||
}
|
||||
|
||||
async load() {
|
||||
logger.info("[EXTENSION-MANAGER] loading extensions from " + this.extensionPackagesRoot)
|
||||
|
||||
await fs.ensureDir(path.join(this.extensionPackagesRoot, "node_modules"))
|
||||
await fs.writeFile(path.join(this.extensionPackagesRoot, "package.json"), `{"dependencies": []}`, {mode: 0o600})
|
||||
|
||||
return await this.loadExtensions();
|
||||
}
|
||||
@@ -27,9 +42,7 @@ export class ExtensionManager {
|
||||
let manifestJson: ExtensionManifest;
|
||||
try {
|
||||
manifestJson = __non_webpack_require__(manifestPath)
|
||||
withExtensionPackagesRoot(() => {
|
||||
this.installPackageFromPath(path.dirname(manifestPath))
|
||||
})
|
||||
this.packagesJson.dependencies[manifestJson.name] = path.dirname(manifestPath)
|
||||
|
||||
logger.info("[EXTENSION-MANAGER] installed extension " + manifestJson.name)
|
||||
return {
|
||||
@@ -44,31 +57,17 @@ export class ExtensionManager {
|
||||
}
|
||||
}
|
||||
|
||||
protected installPackageFromPath(path: string): Promise<void> {
|
||||
const origLogger = console.log
|
||||
protected installPackages(): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
npm.load({
|
||||
production: true,
|
||||
global: false,
|
||||
prefix: this.extensionPackagesRoot,
|
||||
dev: false,
|
||||
spin: false,
|
||||
"ignore-scripts": true,
|
||||
loglevel: "silent"
|
||||
}, (err) => {
|
||||
console.log = function() {
|
||||
// just to ignore ts empty function error
|
||||
}
|
||||
npm.commands.install([
|
||||
path
|
||||
], (err) => {
|
||||
console.log = origLogger
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
const child = child_process.fork(this.npmPath, ["install", "--silent"], {
|
||||
cwd: extensionPackagesRoot(),
|
||||
silent: true
|
||||
})
|
||||
child.on("close", () => {
|
||||
resolve()
|
||||
})
|
||||
child.on("error", (err) => {
|
||||
reject(err)
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -80,15 +79,19 @@ export class ExtensionManager {
|
||||
|
||||
async loadFromFolder(folderPath: string): Promise<InstalledExtension[]> {
|
||||
const paths = await fs.readdir(folderPath);
|
||||
const manifestsLoading = paths.map(fileName => {
|
||||
const extensions: InstalledExtension[] = []
|
||||
for (const fileName of paths) {
|
||||
const absPath = path.resolve(folderPath, fileName);
|
||||
const manifestPath = path.resolve(absPath, "package.json");
|
||||
return fs.access(manifestPath, fs.constants.F_OK)
|
||||
.then(async () => await this.getExtensionByManifest(manifestPath))
|
||||
.catch(() => null)
|
||||
});
|
||||
let extensions = await Promise.all(manifestsLoading);
|
||||
extensions = extensions.filter(v => !!v); // filter out files and invalid folders (without manifest.json)
|
||||
await fs.access(manifestPath, fs.constants.F_OK)
|
||||
const ext = await this.getExtensionByManifest(manifestPath).catch(() => null)
|
||||
if (ext) {
|
||||
extensions.push(ext)
|
||||
}
|
||||
}
|
||||
await fs.writeFile(path.join(this.extensionPackagesRoot, "package.json"), JSON.stringify(this.packagesJson), {mode: 0o600})
|
||||
await this.installPackages()
|
||||
|
||||
logger.debug(`[EXTENSION-MANAGER]: ${extensions.length} extensions loaded`, { folderPath, extensions });
|
||||
return extensions;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
// Lens-extensions api developer's kit
|
||||
export type { LensExtensionRuntimeEnv, PageStore } from "./lens-renderer-runtime";
|
||||
export type { LensExtensionRuntimeEnv } from "./lens-renderer-runtime"
|
||||
export type { PageStore } from "./page-store"
|
||||
|
||||
// APIs
|
||||
export * from "./lens-renderer-extension"
|
||||
export { DynamicPageType } from "./page-store";
|
||||
export { DynamicPageType } from "./page-store"
|
||||
|
||||
// TODO: add more common re-usable UI components + refactor interfaces (Props -> ComponentProps)
|
||||
export { default as React } from "react"
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import type { PageStore } from "./lens-renderer-runtime"
|
||||
import type { PageStore } from "./extension-renderer-api"
|
||||
import type { PageRegistration } from "./page-store"
|
||||
import { LensExtension } from "./lens-extension"
|
||||
|
||||
export class LensRendererExtension extends LensExtension {
|
||||
export abstract class LensRendererExtension extends LensExtension {
|
||||
registerPages(pageStore: PageStore) {
|
||||
// mock
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// Runtime helpers
|
||||
protected registerPage(pageStore: PageStore, params: PageRegistration) {
|
||||
const dispose = pageStore.register(params);
|
||||
|
||||
@@ -2,11 +2,6 @@
|
||||
|
||||
import logger from "../main/logger";
|
||||
import { navigate } from "../renderer/navigation";
|
||||
import { PageRegistration } from "./page-store";
|
||||
|
||||
export interface PageStore {
|
||||
register(params: PageRegistration): () => void
|
||||
}
|
||||
|
||||
export interface LensExtensionRuntimeEnv {
|
||||
logger: typeof logger;
|
||||
|
||||
@@ -81,6 +81,7 @@ async function main() {
|
||||
|
||||
extensionLoader.loadOnMain(getLensRuntime)
|
||||
extensionLoader.extensions.replace(await extensionManager.load())
|
||||
extensionLoader.broadcastExtensions()
|
||||
}
|
||||
|
||||
app.on("ready", main);
|
||||
|
||||
@@ -4,6 +4,7 @@ import { BrowserWindow, dialog, ipcMain, shell, webContents } from "electron"
|
||||
import windowStateKeeper from "electron-window-state"
|
||||
import { observable } from "mobx";
|
||||
import { initMenu } from "./menu";
|
||||
import { extensionLoader } from "../extensions/extension-loader";
|
||||
|
||||
export class WindowManager {
|
||||
protected mainView: BrowserWindow;
|
||||
@@ -40,6 +41,9 @@ export class WindowManager {
|
||||
event.preventDefault();
|
||||
shell.openExternal(url);
|
||||
});
|
||||
this.mainView.webContents.on("dom-ready", () => {
|
||||
extensionLoader.broadcastExtensions()
|
||||
})
|
||||
|
||||
// track visible cluster from ui
|
||||
ipcMain.on("cluster-view:current-id", (event, clusterId: ClusterId) => {
|
||||
@@ -72,8 +76,8 @@ export class WindowManager {
|
||||
try {
|
||||
await this.showSplash();
|
||||
await this.mainView.loadURL(`http://localhost:${this.proxyPort}`)
|
||||
this.mainView.show();
|
||||
this.splashWindow.close();
|
||||
this.mainView.show()
|
||||
this.splashWindow.close()
|
||||
} catch (err) {
|
||||
dialog.showErrorBox("ERROR!", err.toString())
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user