Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
This commit is contained in:
Jari Kolehmainen committed 2020-10-07 20:17:22 +03:00
1 parent f97fbdf9ed
commit ffa6a982b5
15 files changed
+122 -119

No files matched your search

+9 -10
View File
@@ -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)
}
})
}
+41 -38
View File
@@ -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;
}
+3 -2
View File
@@ -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"
+3 -4
View File
@@ -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);
-5
View File
@@ -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;
+1
View File
@@ -81,6 +81,7 @@ async function main() {
extensionLoader.loadOnMain(getLensRuntime)
extensionLoader.extensions.replace(await extensionManager.load())
extensionLoader.broadcastExtensions()
}
app.on("ready", main);
+6 -2
View File
@@ -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())
}