mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
support extensions in main process
Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
This commit is contained in:
parent
2b6f283e1b
commit
8a793c01db
1
.gitignore
vendored
1
.gitignore
vendored
@ -14,3 +14,4 @@ src/extensions/*/*.js
|
|||||||
src/extensions/*/*.d.ts
|
src/extensions/*/*.d.ts
|
||||||
src/extensions/example-extension/src/**
|
src/extensions/example-extension/src/**
|
||||||
types/extension-api.d.ts
|
types/extension-api.d.ts
|
||||||
|
types/extension-renderer-api.d.ts
|
||||||
|
|||||||
@ -1,51 +0,0 @@
|
|||||||
import { Button, DynamicPageType, Icon, IconProps, LensExtension, React } from "@lens/extensions";
|
|
||||||
import { CoffeeDoodle } from "react-open-doodles";
|
|
||||||
import path from "path";
|
|
||||||
|
|
||||||
export default class ExampleExtension extends LensExtension {
|
|
||||||
onActivate() {
|
|
||||||
console.log('EXAMPLE EXTENSION: ACTIVATED', this.getMeta());
|
|
||||||
this.registerPage({
|
|
||||||
type: DynamicPageType.CLUSTER,
|
|
||||||
path: "/extension-example",
|
|
||||||
menuTitle: "Example Extension",
|
|
||||||
components: {
|
|
||||||
Page: () => <ExtensionPage extension={this}/>,
|
|
||||||
MenuIcon: ExtensionIcon,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
onDeactivate() {
|
|
||||||
console.log('EXAMPLE EXTENSION: DEACTIVATED', this.getMeta());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ExtensionIcon(props: IconProps) {
|
|
||||||
return <Icon {...props} material="pages" tooltip={path.basename(__filename)}/>
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ExtensionPage extends React.Component<{ extension: ExampleExtension }> {
|
|
||||||
deactivate = () => {
|
|
||||||
const { extension } = this.props;
|
|
||||||
extension.runtime.navigate("/")
|
|
||||||
extension.disable();
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { TabLayout } = this.props.extension.runtime.components;
|
|
||||||
const doodleStyle = {
|
|
||||||
width: "200px"
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<TabLayout className="ExampleExtension">
|
|
||||||
<div className="flex column gaps align-flex-start">
|
|
||||||
<div style={doodleStyle}><CoffeeDoodle accent="#3d90ce" /></div>
|
|
||||||
<p>Hello from Example extension!</p>
|
|
||||||
<p>File: <i>{__filename}</i></p>
|
|
||||||
<Button accent label="Deactivate" onClick={this.deactivate}/>
|
|
||||||
</div>
|
|
||||||
</TabLayout>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
11
extensions/example-extension/main.ts
Normal file
11
extensions/example-extension/main.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { LensExtension } from "@lens/extensions";
|
||||||
|
|
||||||
|
export default class ExampleExtensionMain extends LensExtension {
|
||||||
|
onActivate() {
|
||||||
|
console.log('EXAMPLE EXTENSION MAIN: ACTIVATED', this.getMeta());
|
||||||
|
}
|
||||||
|
|
||||||
|
onDeactivate() {
|
||||||
|
console.log('EXAMPLE EXTENSION MAIN: DEACTIVATED', this.getMeta());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,7 +2,8 @@
|
|||||||
"name": "extension-example",
|
"name": "extension-example",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "Example extension",
|
"description": "Example extension",
|
||||||
"main": "dist/index.js",
|
"main": "dist/main.js",
|
||||||
|
"renderer": "dist/renderer.js",
|
||||||
"lens": {
|
"lens": {
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"styles": []
|
"styles": []
|
||||||
|
|||||||
32
extensions/example-extension/page.tsx
Normal file
32
extensions/example-extension/page.tsx
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { Button, Icon, IconProps, LensRendererExtension, React } from "@lens/ui-extensions";
|
||||||
|
import { CoffeeDoodle } from "react-open-doodles";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
export function ExtensionIcon(props: IconProps) {
|
||||||
|
return <Icon {...props} material="pages" tooltip={path.basename(__filename)}/>
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ExtensionPage extends React.Component<{ extension: LensRendererExtension }> {
|
||||||
|
deactivate = () => {
|
||||||
|
const { extension } = this.props;
|
||||||
|
extension.disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const doodleStyle = {
|
||||||
|
width: "200px"
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className="flex column gaps align-flex-start">
|
||||||
|
<div style={doodleStyle}><CoffeeDoodle accent="#3d90ce" /></div>
|
||||||
|
<p>Hello from Example extension!</p>
|
||||||
|
<p>File: <i>{__filename}</i></p>
|
||||||
|
<Button accent label="Deactivate" onClick={this.deactivate}/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function examplePage(ext: LensRendererExtension) {
|
||||||
|
return () => <ExtensionPage extension={ext}/>
|
||||||
|
}
|
||||||
24
extensions/example-extension/renderer.ts
Normal file
24
extensions/example-extension/renderer.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { DynamicPageType, LensRendererExtension, PageStore } from "@lens/ui-extensions";
|
||||||
|
import { examplePage, ExtensionIcon } from "./page"
|
||||||
|
|
||||||
|
export default class ExampleExtension extends LensRendererExtension {
|
||||||
|
onActivate() {
|
||||||
|
console.log('EXAMPLE EXTENSION RENDERER: ACTIVATED', this.getMeta());
|
||||||
|
}
|
||||||
|
|
||||||
|
registerPages(pageStore: PageStore) {
|
||||||
|
this.registerPage(pageStore, {
|
||||||
|
type: DynamicPageType.CLUSTER,
|
||||||
|
path: "/extension-example",
|
||||||
|
menuTitle: "Example Extension",
|
||||||
|
components: {
|
||||||
|
Page: examplePage(this),
|
||||||
|
MenuIcon: ExtensionIcon,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onDeactivate() {
|
||||||
|
console.log('EXAMPLE EXTENSION RENDERER: DEACTIVATED', this.getMeta());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -16,7 +16,8 @@
|
|||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"../../types",
|
"../../types",
|
||||||
"./index.tsx"
|
"./index.ts",
|
||||||
|
"./main.ts"
|
||||||
],
|
],
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"node_modules",
|
"node_modules",
|
||||||
|
|||||||
@ -15,12 +15,12 @@
|
|||||||
"dev-run": "nodemon --watch static/build/main.js --exec \"electron --inspect .\"",
|
"dev-run": "nodemon --watch static/build/main.js --exec \"electron --inspect .\"",
|
||||||
"dev:main": "yarn compile:main --watch",
|
"dev:main": "yarn compile:main --watch",
|
||||||
"dev:renderer": "yarn compile:renderer --watch",
|
"dev:renderer": "yarn compile:renderer --watch",
|
||||||
"dev:extensions": "yarn compile:extensions --watch",
|
"dev:extension-api": "yarn compile:extension-api --watch",
|
||||||
"compile": "env NODE_ENV=production concurrently yarn:compile:*",
|
"compile": "env NODE_ENV=production concurrently yarn:compile:*",
|
||||||
"compile:main": "webpack --config webpack.main.ts",
|
"compile:main": "webpack --config webpack.main.ts",
|
||||||
"compile:renderer": "webpack --config webpack.renderer.ts",
|
"compile:renderer": "webpack --config webpack.renderer.ts",
|
||||||
"compile:i18n": "lingui compile",
|
"compile:i18n": "lingui compile",
|
||||||
"compile:extensions": "rollup --config src/extensions/rollup.config.js",
|
"compile:extension-api": "rollup --config src/extensions/rollup.config.js",
|
||||||
"build:linux": "yarn compile && electron-builder --linux --dir -c.productName=Lens",
|
"build:linux": "yarn compile && electron-builder --linux --dir -c.productName=Lens",
|
||||||
"build:mac": "yarn compile && electron-builder --mac --dir -c.productName=Lens",
|
"build:mac": "yarn compile && electron-builder --mac --dir -c.productName=Lens",
|
||||||
"build:win": "yarn compile && electron-builder --win --dir -c.productName=Lens",
|
"build:win": "yarn compile && electron-builder --win --dir -c.productName=Lens",
|
||||||
@ -320,8 +320,8 @@
|
|||||||
"progress-bar-webpack-plugin": "^2.1.0",
|
"progress-bar-webpack-plugin": "^2.1.0",
|
||||||
"raw-loader": "^4.0.1",
|
"raw-loader": "^4.0.1",
|
||||||
"react": "^16.13.1",
|
"react": "^16.13.1",
|
||||||
"react-dom": "^16.13.1",
|
|
||||||
"react-beautiful-dnd": "^13.0.0",
|
"react-beautiful-dnd": "^13.0.0",
|
||||||
|
"react-dom": "^16.13.1",
|
||||||
"react-router": "^5.2.0",
|
"react-router": "^5.2.0",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
"react-select": "^3.1.0",
|
"react-select": "^3.1.0",
|
||||||
|
|||||||
@ -24,6 +24,7 @@ export const sassCommonVars = path.resolve(rendererDir, "components/vars.scss");
|
|||||||
|
|
||||||
// Extensions
|
// Extensions
|
||||||
export const extensionsLibName = `${appName}-extensions.api`
|
export const extensionsLibName = `${appName}-extensions.api`
|
||||||
|
export const extensionsRendererLibName = `${appName}-extensions-renderer.api`
|
||||||
export const extensionsDir = path.join(contextDir, "src/extensions");
|
export const extensionsDir = path.join(contextDir, "src/extensions");
|
||||||
|
|
||||||
// Special runtime paths
|
// Special runtime paths
|
||||||
@ -39,8 +40,10 @@ defineGlobal("__static", {
|
|||||||
// Special dynamic module aliases
|
// Special dynamic module aliases
|
||||||
if (isProduction && process.resourcesPath) {
|
if (isProduction && process.resourcesPath) {
|
||||||
addAlias("@lens/extensions", path.join(process.resourcesPath, "static", `build/${extensionsLibName}.js`))
|
addAlias("@lens/extensions", path.join(process.resourcesPath, "static", `build/${extensionsLibName}.js`))
|
||||||
|
addAlias("@lens/ui-extensions", path.join(process.resourcesPath, "static", `build/${extensionsRendererLibName}.js`))
|
||||||
} else {
|
} else {
|
||||||
addAlias("@lens/extensions", path.join(contextDir, "static", `build/${extensionsLibName}.js`))
|
addAlias("@lens/extensions", path.join(contextDir, "static", `build/${extensionsLibName}.js`))
|
||||||
|
addAlias("@lens/ui-extensions", path.join(contextDir, "static", `build/${extensionsRendererLibName}.js`))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apis
|
// Apis
|
||||||
|
|||||||
@ -1,14 +1,5 @@
|
|||||||
// Lens-extensions api developer's kit
|
// Lens-extensions api developer's kit
|
||||||
export type { LensRuntimeRendererEnv } from "./lens-runtime";
|
export type { LensExtensionRuntimeEnv } from "./lens-runtime";
|
||||||
|
|
||||||
// APIs
|
// APIs
|
||||||
export * from "./lens-extension"
|
export * from "./lens-main-extension"
|
||||||
export { DynamicPageType } from "./register-page";
|
|
||||||
|
|
||||||
// TODO: add more common re-usable UI components + refactor interfaces (Props -> ComponentProps)
|
|
||||||
export { default as React } from "react"
|
|
||||||
export * from "../renderer/components/icon"
|
|
||||||
export * from "../renderer/components/tooltip"
|
|
||||||
export * from "../renderer/components/button"
|
|
||||||
export * from "../renderer/components/tabs"
|
|
||||||
export * from "../renderer/components/badge"
|
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
import type { ExtensionId, LensExtension, ExtensionManifest, ExtensionModel } from "./lens-extension"
|
import type { ExtensionId, LensExtension, ExtensionManifest, ExtensionModel } from "./lens-extension"
|
||||||
|
import type { LensRendererExtension } from "./lens-renderer-extension"
|
||||||
import { broadcastIpc } from "../common/ipc"
|
import { broadcastIpc } from "../common/ipc"
|
||||||
import type { LensRuntimeRendererEnv } from "./lens-runtime"
|
import type { LensExtensionRuntimeEnv } from "./lens-runtime"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import { observable, reaction, toJS, } from "mobx"
|
import { observable, reaction, toJS, } from "mobx"
|
||||||
import logger from "../main/logger"
|
import logger from "../main/logger"
|
||||||
import { app, remote, ipcRenderer } from "electron"
|
import { app, remote, ipcRenderer } from "electron"
|
||||||
|
import { pageStore } from "./page-store";
|
||||||
|
|
||||||
export interface InstalledExtension extends ExtensionModel {
|
export interface InstalledExtension extends ExtensionModel {
|
||||||
manifestPath: string;
|
manifestPath: string;
|
||||||
@ -42,8 +44,28 @@ export class ExtensionLoader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
autoEnableOnLoad(getLensRuntimeEnv: () => LensRuntimeRendererEnv, { delay = 0 } = {}) {
|
loadOnClusterRenderer(getLensRuntimeEnv: () => LensExtensionRuntimeEnv) {
|
||||||
logger.info('[EXTENSIONS-LOADER]: auto-activation loaded extensions: ON');
|
logger.info('[EXTENSIONS-LOADER]: load on cluster renderer')
|
||||||
|
this.autoloadExtensions(getLensRuntimeEnv, (instance: LensRendererExtension) => {
|
||||||
|
instance.registerPages(pageStore)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
loadOnMainRenderer(getLensRuntimeEnv: () => LensExtensionRuntimeEnv) {
|
||||||
|
logger.info('[EXTENSIONS-LOADER]: load on main renderer')
|
||||||
|
this.autoloadExtensions(getLensRuntimeEnv, (instance: LensRendererExtension) => {
|
||||||
|
instance.registerPages(pageStore)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
loadOnMain(getLensRuntimeEnv: () => LensExtensionRuntimeEnv) {
|
||||||
|
logger.info('[EXTENSIONS-LOADER]: load on main')
|
||||||
|
this.autoloadExtensions(getLensRuntimeEnv, (instance: LensExtension) => {
|
||||||
|
// todo
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
protected autoloadExtensions(getLensRuntimeEnv: () => LensExtensionRuntimeEnv, callback: (instance: LensExtension) => void) {
|
||||||
return reaction(() => this.extensions.toJS(), installedExtensions => {
|
return reaction(() => this.extensions.toJS(), installedExtensions => {
|
||||||
installedExtensions.forEach((ext) => {
|
installedExtensions.forEach((ext) => {
|
||||||
let instance = this.instances.get(ext.manifestPath)
|
let instance = this.instances.get(ext.manifestPath)
|
||||||
@ -55,23 +77,32 @@ export class ExtensionLoader {
|
|||||||
}
|
}
|
||||||
const LensExtensionClass = extensionModule.default;
|
const LensExtensionClass = extensionModule.default;
|
||||||
instance = new LensExtensionClass({ ...ext.manifest, manifestPath: ext.manifestPath, id: ext.manifestPath }, ext.manifest);
|
instance = new LensExtensionClass({ ...ext.manifest, manifestPath: ext.manifestPath, id: ext.manifestPath }, ext.manifest);
|
||||||
instance.enable(getLensRuntimeEnv());
|
instance.enable(getLensRuntimeEnv())
|
||||||
|
callback(instance)
|
||||||
this.instances.set(ext.id, instance)
|
this.instances.set(ext.id, instance)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, {
|
}, {
|
||||||
fireImmediately: true,
|
fireImmediately: true,
|
||||||
delay: delay,
|
delay: 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
protected requireExtension(extension: InstalledExtension) {
|
protected requireExtension(extension: InstalledExtension) {
|
||||||
|
let extEntrypoint = ""
|
||||||
return withExtensionPackagesRoot(() => {
|
return withExtensionPackagesRoot(() => {
|
||||||
try {
|
try {
|
||||||
const extMain = path.join(path.dirname(extension.manifestPath), extension.manifest.main)
|
if (ipcRenderer) {
|
||||||
return __non_webpack_require__(extMain)
|
extEntrypoint = path.join(path.dirname(extension.manifestPath), extension.manifest.renderer)
|
||||||
|
} else {
|
||||||
|
extEntrypoint = path.join(path.dirname(extension.manifestPath), extension.manifest.main)
|
||||||
|
}
|
||||||
|
if (extEntrypoint !== "") {
|
||||||
|
return __non_webpack_require__(extEntrypoint)
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(`[EXTENSION-LOADER]: can't load extension main at ${extension.manifestPath}: ${err}`, { extension });
|
console.trace(err)
|
||||||
|
console.error(`[EXTENSION-LOADER]: can't load extension main at ${extEntrypoint}: ${err}`, { extension });
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -84,7 +115,7 @@ export class ExtensionLoader {
|
|||||||
const extension = this.getById(id);
|
const extension = this.getById(id);
|
||||||
if (extension) {
|
if (extension) {
|
||||||
const instance = this.instances.get(extension.id)
|
const instance = this.instances.get(extension.id)
|
||||||
if (instance) { await instance.uninstall() }
|
if (instance) { await instance.disable() }
|
||||||
this.extensions.delete(id);
|
this.extensions.delete(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
14
src/extensions/extension-renderer-api.ts
Normal file
14
src/extensions/extension-renderer-api.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// Lens-extensions api developer's kit
|
||||||
|
export type { LensExtensionRuntimeEnv, PageStore } from "./lens-runtime";
|
||||||
|
|
||||||
|
// APIs
|
||||||
|
export * from "./lens-renderer-extension"
|
||||||
|
export { DynamicPageType } from "./page-store";
|
||||||
|
|
||||||
|
// TODO: add more common re-usable UI components + refactor interfaces (Props -> ComponentProps)
|
||||||
|
export { default as React } from "react"
|
||||||
|
export * from "../renderer/components/icon"
|
||||||
|
export * from "../renderer/components/tooltip"
|
||||||
|
export * from "../renderer/components/button"
|
||||||
|
export * from "../renderer/components/tabs"
|
||||||
|
export * from "../renderer/components/badge"
|
||||||
@ -1,5 +1,4 @@
|
|||||||
import type { LensRuntimeRendererEnv } from "./lens-runtime";
|
import type { LensExtensionRuntimeEnv } from "./lens-runtime";
|
||||||
import type { PageRegistration } from "./register-page";
|
|
||||||
import { readJsonSync } from "fs-extra";
|
import { readJsonSync } from "fs-extra";
|
||||||
import { action, observable, toJS } from "mobx";
|
import { action, observable, toJS } from "mobx";
|
||||||
import logger from "../main/logger";
|
import logger from "../main/logger";
|
||||||
@ -19,7 +18,8 @@ export interface ExtensionModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ExtensionManifest extends ExtensionModel {
|
export interface ExtensionManifest extends ExtensionModel {
|
||||||
main: string;
|
main?: string;
|
||||||
|
renderer?: string;
|
||||||
description?: string; // todo: add more fields similar to package.json + some extra
|
description?: string; // todo: add more fields similar to package.json + some extra
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ export class LensExtension implements ExtensionModel {
|
|||||||
@observable manifest: ExtensionManifest;
|
@observable manifest: ExtensionManifest;
|
||||||
@observable manifestPath: string;
|
@observable manifestPath: string;
|
||||||
@observable isEnabled = false;
|
@observable isEnabled = false;
|
||||||
@observable.ref runtime: LensRuntimeRendererEnv;
|
@observable.ref runtime: LensExtensionRuntimeEnv;
|
||||||
|
|
||||||
constructor(model: ExtensionModel, manifest: ExtensionManifest) {
|
constructor(model: ExtensionModel, manifest: ExtensionManifest) {
|
||||||
this.importModel(model, manifest);
|
this.importModel(model, manifest);
|
||||||
@ -52,10 +52,14 @@ export class LensExtension implements ExtensionModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async enable(runtime: LensRuntimeRendererEnv) {
|
async migrate(appVersion: string) {
|
||||||
|
// mock
|
||||||
|
}
|
||||||
|
|
||||||
|
async enable(runtime: LensExtensionRuntimeEnv) {
|
||||||
this.isEnabled = true;
|
this.isEnabled = true;
|
||||||
this.runtime = runtime;
|
this.runtime = runtime;
|
||||||
console.log(`[EXTENSION]: enabled ${this.name}@${this.version}`, this.getMeta());
|
logger.info(`[EXTENSION]: enabled ${this.name}@${this.version}`, this.getMeta());
|
||||||
this.onActivate();
|
this.onActivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +69,7 @@ export class LensExtension implements ExtensionModel {
|
|||||||
this.runtime = null;
|
this.runtime = null;
|
||||||
this.disposers.forEach(cleanUp => cleanUp());
|
this.disposers.forEach(cleanUp => cleanUp());
|
||||||
this.disposers.length = 0;
|
this.disposers.length = 0;
|
||||||
console.log(`[EXTENSION]: disabled ${this.name}@${this.version}`, this.getMeta());
|
logger.info(`[EXTENSION]: disabled ${this.name}@${this.version}`, this.getMeta());
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: add more hooks
|
// todo: add more hooks
|
||||||
@ -77,27 +81,12 @@ export class LensExtension implements ExtensionModel {
|
|||||||
// mock
|
// mock
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo
|
|
||||||
async install(downloadUrl?: string) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo
|
|
||||||
async uninstall() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
async hasNewVersion(): Promise<Partial<ExtensionModel>> {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
getMeta() {
|
getMeta() {
|
||||||
return toJS({
|
return toJS({
|
||||||
id: this.id,
|
id: this.id,
|
||||||
manifest: this.manifest,
|
manifest: this.manifest,
|
||||||
manifestPath: this.manifestPath,
|
manifestPath: this.manifestPath,
|
||||||
enabled: this.isEnabled,
|
enabled: this.isEnabled
|
||||||
runtime: this.runtime,
|
|
||||||
}, {
|
}, {
|
||||||
recurseEverything: true
|
recurseEverything: true
|
||||||
})
|
})
|
||||||
@ -116,12 +105,4 @@ export class LensExtension implements ExtensionModel {
|
|||||||
recurseEverything: true,
|
recurseEverything: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Runtime helpers
|
|
||||||
protected registerPage(params: PageRegistration, autoDisable = true) {
|
|
||||||
const dispose = this.runtime.dynamicPages.register(params);
|
|
||||||
if (autoDisable) {
|
|
||||||
this.disposers.push(dispose);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
11
src/extensions/lens-main-extension.ts
Normal file
11
src/extensions/lens-main-extension.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { LensExtension } from "./lens-extension"
|
||||||
|
|
||||||
|
export class LensMainExtension extends LensExtension {
|
||||||
|
async registerAppMenus() {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
async registerPrometheusProviders(registry: any) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/extensions/lens-renderer-extension.ts
Normal file
16
src/extensions/lens-renderer-extension.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import type { PageStore } from "./lens-runtime"
|
||||||
|
import type { PageRegistration } from "./page-store"
|
||||||
|
import { LensExtension } from "./lens-extension"
|
||||||
|
|
||||||
|
export class LensRendererExtension extends LensExtension {
|
||||||
|
registerPages(pageStore: PageStore) {
|
||||||
|
// mock
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Runtime helpers
|
||||||
|
protected registerPage(pageStore: PageStore, params: PageRegistration) {
|
||||||
|
const dispose = pageStore.register(params);
|
||||||
|
this.disposers.push(dispose)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,26 +1,18 @@
|
|||||||
// Lens renderer runtime params available to extensions after activation
|
// Lens extension runtime params available to extensions after activation
|
||||||
|
|
||||||
import logger from "../main/logger";
|
import logger from "../main/logger";
|
||||||
import { dynamicPages } from "./register-page";
|
import { PageRegistration } from "./page-store";
|
||||||
import { TabLayout } from "../renderer/components/layout/tab-layout";
|
|
||||||
import { navigate } from "../renderer/navigation";
|
|
||||||
|
|
||||||
export interface LensRuntimeRendererEnv {
|
export interface PageStore {
|
||||||
navigate: typeof navigate;
|
register(params: PageRegistration): () => void
|
||||||
logger: typeof logger;
|
|
||||||
dynamicPages: typeof dynamicPages
|
|
||||||
components: {
|
|
||||||
TabLayout: typeof TabLayout
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getLensRuntime(): LensRuntimeRendererEnv {
|
export interface LensExtensionRuntimeEnv {
|
||||||
|
logger: typeof logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getLensRuntime(): LensExtensionRuntimeEnv {
|
||||||
return {
|
return {
|
||||||
logger,
|
logger,
|
||||||
navigate,
|
|
||||||
dynamicPages,
|
|
||||||
components: {
|
|
||||||
TabLayout // fixme: refactor, import as pure component from "@lens/extensions"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,7 +21,7 @@ export interface PageComponents {
|
|||||||
MenuIcon: React.ComponentType<IconProps>;
|
MenuIcon: React.ComponentType<IconProps>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PagesStore {
|
export class PageStore {
|
||||||
protected pages = observable.array<PageRegistration>([], { deep: false });
|
protected pages = observable.array<PageRegistration>([], { deep: false });
|
||||||
|
|
||||||
@computed get globalPages() {
|
@computed get globalPages() {
|
||||||
@ -43,4 +43,4 @@ export class PagesStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const dynamicPages = new PagesStore();
|
export const pageStore = new PageStore();
|
||||||
@ -22,6 +22,19 @@ const config: RollupOptions = {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const rendererConfig: RollupOptions = {
|
||||||
|
input: "src/extensions/extension-renderer-api.ts",
|
||||||
|
output: [
|
||||||
|
{ file: "types/extension-renderer-api.d.ts", format: "es", }
|
||||||
|
],
|
||||||
|
plugins: [
|
||||||
|
dts(),
|
||||||
|
dtsModuleWrap({ name: "@lens/ui-extensions" }),
|
||||||
|
ignoreImport({ extensions: ['.scss'] }),
|
||||||
|
json(),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
function dtsModuleWrap({ name }: { name: string }): Plugin {
|
function dtsModuleWrap({ name }: { name: string }): Plugin {
|
||||||
return {
|
return {
|
||||||
name,
|
name,
|
||||||
@ -56,4 +69,4 @@ function dtsModuleWrap({ name }: { name: string }): Plugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default config;
|
export default [config, rendererConfig];
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import { workspaceStore } from "../common/workspace-store";
|
|||||||
import { tracker } from "../common/tracker";
|
import { tracker } from "../common/tracker";
|
||||||
import { extensionManager } from "../extensions/extension-manager";
|
import { extensionManager } from "../extensions/extension-manager";
|
||||||
import { extensionLoader } from "../extensions/extension-loader";
|
import { extensionLoader } from "../extensions/extension-loader";
|
||||||
|
import { getLensRuntime } from "../extensions/lens-runtime";
|
||||||
import logger from "./logger"
|
import logger from "./logger"
|
||||||
|
|
||||||
const workingDir = path.join(app.getPath("appData"), appName);
|
const workingDir = path.join(app.getPath("appData"), appName);
|
||||||
@ -78,6 +79,7 @@ async function main() {
|
|||||||
// create window manager and open app
|
// create window manager and open app
|
||||||
windowManager = new WindowManager(proxyPort);
|
windowManager = new WindowManager(proxyPort);
|
||||||
|
|
||||||
|
extensionLoader.loadOnMain(getLensRuntime)
|
||||||
extensionLoader.extensions.replace(await extensionManager.load())
|
extensionLoader.extensions.replace(await extensionManager.load())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,13 +4,11 @@ import { render, unmountComponentAtNode } from "react-dom";
|
|||||||
import { isMac } from "../common/vars";
|
import { isMac } from "../common/vars";
|
||||||
import { userStore } from "../common/user-store";
|
import { userStore } from "../common/user-store";
|
||||||
import { workspaceStore } from "../common/workspace-store";
|
import { workspaceStore } from "../common/workspace-store";
|
||||||
import { extensionLoader } from "../extensions/extension-loader";
|
|
||||||
import { clusterStore } from "../common/cluster-store";
|
import { clusterStore } from "../common/cluster-store";
|
||||||
import { i18nStore } from "./i18n";
|
import { i18nStore } from "./i18n";
|
||||||
import { themeStore } from "./theme.store";
|
import { themeStore } from "./theme.store";
|
||||||
import { App } from "./components/app";
|
import { App } from "./components/app";
|
||||||
import { LensApp } from "./lens-app";
|
import { LensApp } from "./lens-app";
|
||||||
import { getLensRuntime } from "../extensions/lens-runtime";
|
|
||||||
|
|
||||||
type AppComponent = React.ComponentType & {
|
type AppComponent = React.ComponentType & {
|
||||||
init?(): void;
|
init?(): void;
|
||||||
@ -31,7 +29,6 @@ export async function bootstrap(App: AppComponent) {
|
|||||||
|
|
||||||
// Register additional store listeners
|
// Register additional store listeners
|
||||||
clusterStore.registerIpcListener();
|
clusterStore.registerIpcListener();
|
||||||
extensionLoader.autoEnableOnLoad(getLensRuntime);
|
|
||||||
|
|
||||||
// init app's dependencies if any
|
// init app's dependencies if any
|
||||||
if (App.init) {
|
if (App.init) {
|
||||||
|
|||||||
@ -36,7 +36,9 @@ import { getHostedCluster, getHostedClusterId } from "../../common/cluster-store
|
|||||||
import logger from "../../main/logger";
|
import logger from "../../main/logger";
|
||||||
import { clusterIpc } from "../../common/cluster-ipc";
|
import { clusterIpc } from "../../common/cluster-ipc";
|
||||||
import { webFrame } from "electron";
|
import { webFrame } from "electron";
|
||||||
import { dynamicPages } from "../../extensions/register-page";
|
import { pageStore } from "../../extensions/page-store";
|
||||||
|
import { extensionLoader } from "../../extensions/extension-loader";
|
||||||
|
import { getLensRuntime } from "../../extensions/lens-runtime";
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class App extends React.Component {
|
export class App extends React.Component {
|
||||||
@ -47,6 +49,7 @@ export class App extends React.Component {
|
|||||||
await Terminal.preloadFonts()
|
await Terminal.preloadFonts()
|
||||||
await clusterIpc.activate.invokeFromRenderer(clusterId, frameId);
|
await clusterIpc.activate.invokeFromRenderer(clusterId, frameId);
|
||||||
await getHostedCluster().whenReady; // cluster.refresh() is done at this point
|
await getHostedCluster().whenReady; // cluster.refresh() is done at this point
|
||||||
|
extensionLoader.loadOnClusterRenderer(getLensRuntime)
|
||||||
}
|
}
|
||||||
|
|
||||||
get startURL() {
|
get startURL() {
|
||||||
@ -74,7 +77,7 @@ export class App extends React.Component {
|
|||||||
<Route component={CustomResources} {...crdRoute}/>
|
<Route component={CustomResources} {...crdRoute}/>
|
||||||
<Route component={UserManagement} {...usersManagementRoute}/>
|
<Route component={UserManagement} {...usersManagementRoute}/>
|
||||||
<Route component={Apps} {...appsRoute}/>
|
<Route component={Apps} {...appsRoute}/>
|
||||||
{dynamicPages.clusterPages.map(({ path, components: { Page } }) => {
|
{pageStore.clusterPages.map(({ path, components: { Page } }) => {
|
||||||
return <Route key={path} path={path} component={Page}/>
|
return <Route key={path} path={path} component={Page}/>
|
||||||
})}
|
})}
|
||||||
<Redirect exact from="/" to={this.startURL}/>
|
<Redirect exact from="/" to={this.startURL}/>
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import { ClusterSettings, clusterSettingsRoute } from "../+cluster-settings";
|
|||||||
import { clusterViewRoute, clusterViewURL, getMatchedCluster, getMatchedClusterId } from "./cluster-view.route";
|
import { clusterViewRoute, clusterViewURL, getMatchedCluster, getMatchedClusterId } from "./cluster-view.route";
|
||||||
import { clusterStore } from "../../../common/cluster-store";
|
import { clusterStore } from "../../../common/cluster-store";
|
||||||
import { hasLoadedView, initView, lensViews, refreshViews } from "./lens-views";
|
import { hasLoadedView, initView, lensViews, refreshViews } from "./lens-views";
|
||||||
import { dynamicPages } from "../../../extensions/register-page";
|
import { pageStore } from "../../../extensions/page-store";
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class ClusterManager extends React.Component {
|
export class ClusterManager extends React.Component {
|
||||||
@ -63,7 +63,7 @@ export class ClusterManager extends React.Component {
|
|||||||
<Route component={AddCluster} {...addClusterRoute} />
|
<Route component={AddCluster} {...addClusterRoute} />
|
||||||
<Route component={ClusterView} {...clusterViewRoute} />
|
<Route component={ClusterView} {...clusterViewRoute} />
|
||||||
<Route component={ClusterSettings} {...clusterSettingsRoute} />
|
<Route component={ClusterSettings} {...clusterSettingsRoute} />
|
||||||
{dynamicPages.globalPages.map(({ path, components: { Page } }) => {
|
{pageStore.globalPages.map(({ path, components: { Page } }) => {
|
||||||
return <Route key={path} path={path} component={Page}/>
|
return <Route key={path} path={path} component={Page}/>
|
||||||
})}
|
})}
|
||||||
<Redirect exact to={this.startUrl} />
|
<Redirect exact to={this.startUrl} />
|
||||||
|
|||||||
@ -11,9 +11,15 @@ import { ErrorBoundary } from "./components/error-boundary";
|
|||||||
import { WhatsNew, whatsNewRoute } from "./components/+whats-new";
|
import { WhatsNew, whatsNewRoute } from "./components/+whats-new";
|
||||||
import { Notifications } from "./components/notifications";
|
import { Notifications } from "./components/notifications";
|
||||||
import { ConfirmDialog } from "./components/confirm-dialog";
|
import { ConfirmDialog } from "./components/confirm-dialog";
|
||||||
|
import { extensionLoader } from "../extensions/extension-loader";
|
||||||
|
import { getLensRuntime } from "../extensions/lens-runtime";
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class LensApp extends React.Component {
|
export class LensApp extends React.Component {
|
||||||
|
static async init() {
|
||||||
|
extensionLoader.loadOnMainRenderer(getLensRuntime)
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<I18nProvider i18n={_i18n}>
|
<I18nProvider i18n={_i18n}>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { appName, buildDir, extensionsDir, extensionsLibName, htmlTemplate, isDevelopment, isProduction, publicPath, rendererDir, sassCommonVars } from "./src/common/vars";
|
import { appName, buildDir, extensionsDir, extensionsLibName, extensionsRendererLibName, htmlTemplate, isDevelopment, isProduction, publicPath, rendererDir, sassCommonVars } from "./src/common/vars";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import webpack from "webpack";
|
import webpack from "webpack";
|
||||||
import HtmlWebpackPlugin from "html-webpack-plugin";
|
import HtmlWebpackPlugin from "html-webpack-plugin";
|
||||||
@ -10,6 +10,7 @@ import ProgressBarPlugin from "progress-bar-webpack-plugin";
|
|||||||
export default [
|
export default [
|
||||||
webpackLensRenderer,
|
webpackLensRenderer,
|
||||||
webpackExtensionsApi,
|
webpackExtensionsApi,
|
||||||
|
webpackExtensionsRendererApi
|
||||||
]
|
]
|
||||||
|
|
||||||
// todo: use common chunks/externals for "react", "react-dom", etc.
|
// todo: use common chunks/externals for "react", "react-dom", etc.
|
||||||
@ -24,6 +25,17 @@ export function webpackExtensionsApi(): webpack.Configuration {
|
|||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function webpackExtensionsRendererApi(): webpack.Configuration {
|
||||||
|
const config = webpackLensRenderer({ showVars: false });
|
||||||
|
config.name = "extensions-renderer-api"
|
||||||
|
config.entry = {
|
||||||
|
[extensionsRendererLibName]: path.resolve(extensionsDir, "extension-renderer-api.ts")
|
||||||
|
};
|
||||||
|
config.output.libraryTarget = "commonjs2"
|
||||||
|
config.devtool = "nosources-source-map";
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
export function webpackLensRenderer({ showVars = true } = {}): webpack.Configuration {
|
export function webpackLensRenderer({ showVars = true } = {}): webpack.Configuration {
|
||||||
if (showVars) {
|
if (showVars) {
|
||||||
console.info('WEBPACK:renderer', require("./src/common/vars"));
|
console.info('WEBPACK:renderer', require("./src/common/vars"));
|
||||||
@ -184,4 +196,4 @@ export function webpackLensRenderer({ showVars = true } = {}): webpack.Configura
|
|||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user