From b2414542b8e52d1361a73dcea90bf977337cc54a Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 2 Sep 2020 13:24:30 +0300 Subject: [PATCH] extensions-api -- loading extension modules -- part 1 Signed-off-by: Roman --- .gitignore | 1 + package.json | 1 + .../example-extension/example-extension.ts | 10 +++++--- src/extensions/extension-store.ts | 24 +++++++++++-------- src/extensions/extension.ts | 9 ++++--- src/extensions/tsconfig.json | 20 ++++++++++++++++ 6 files changed, 47 insertions(+), 18 deletions(-) create mode 100644 src/extensions/tsconfig.json diff --git a/.gitignore b/.gitignore index a0cd24bad5..1251adbc13 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ static/build/** binaries/client/ binaries/server/ src/extensions/**/*.js +src/extensions/**/*.d.ts locales/**/**.js diff --git a/package.json b/package.json index 06775dfce8..deb531b5ac 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "compile:main": "webpack --config webpack.main.ts", "compile:renderer": "webpack --config webpack.renderer.ts", "compile:i18n": "lingui compile", + "compile-extensions": "tsc --project src/extensions --watch", "build:linux": "yarn compile && electron-builder --linux --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", diff --git a/src/extensions/example-extension/example-extension.ts b/src/extensions/example-extension/example-extension.ts index bb579be32c..1747d70667 100644 --- a/src/extensions/example-extension/example-extension.ts +++ b/src/extensions/example-extension/example-extension.ts @@ -1,8 +1,12 @@ -import { LensExtension } from "@lens"; // fixme: provide runtime import +// import { LensExtension } from "@lens"; // fixme: provide runtime import -export default class ExampleExtension extends LensExtension { +export default class ExampleExtension /*extends LensExtension*/ { async init(): Promise { console.log('Example extension: init') - return super.init(); + // return super.init(); } } + +export const someData = { + title: "it works" +} \ No newline at end of file diff --git a/src/extensions/extension-store.ts b/src/extensions/extension-store.ts index c4450f74e1..95d8932783 100644 --- a/src/extensions/extension-store.ts +++ b/src/extensions/extension-store.ts @@ -4,7 +4,6 @@ import { action, comparer, observable, 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; @@ -24,7 +23,10 @@ export interface ExtensionModel { export interface InstalledExtension { manifestPath: string; manifest: ExtensionManifest; - LensExtension: new (model: T, manifest?: ExtensionManifest) => LensExtension; + extensionModule: { + [name: string]: any; + default: new (model: T, manifest?: ExtensionManifest) => LensExtension + } } export class ExtensionStore extends BaseStore { @@ -56,15 +58,16 @@ export class ExtensionStore extends BaseStore { let mainJs: string; try { manifestJson = __non_webpack_require__(manifestPath); // eslint-disable-line - mainJs = path.resolve(path.dirname(manifestPath), manifestJson.main); // todo: compile *.ts on the fly? - const LensExtension = __non_webpack_require__(mainJs).default; // eslint-disable-line + mainJs = path.resolve(path.dirname(manifestPath), manifestJson.main); + mainJs = mainJs.replace(/\.ts$/i, ".js"); // todo: compile *.ts on the fly? + const extensionModule = __non_webpack_require__(mainJs); // eslint-disable-line return { manifestPath: manifestPath, manifest: manifestJson, - LensExtension: LensExtension, + extensionModule: extensionModule, } } catch (err) { - logger.error(`[EXTENSION-STORE]: can't load extension at ${manifestPath}: ${err}`, { manifestJson, mainJs }); + console.error(`[EXTENSION-STORE]: can't load extension at ${manifestPath}: ${err}`, { manifestJson, mainJs }); } } @@ -85,7 +88,7 @@ export class ExtensionStore extends BaseStore { }); let extensions = await Promise.all(manifestsLoading); extensions = extensions.filter(v => !!v); // filter out files and invalid folders (without manifest.json) - logger.info(`[EXTENSION-STORE]: ${extensions.length} extensions loaded`, { folderPath, extensions }); + console.info(`[EXTENSION-STORE]: ${extensions.length} extensions loaded`, { folderPath, extensions }); return extensions; } @@ -116,16 +119,17 @@ export class ExtensionStore extends BaseStore { currentExtensions.forEach(model => { const manifest = this.installed.get(model.manifestPath); if (!manifest) { - logger.error(`[EXTENSION-STORE]: can't load extension manifest at ${model.manifestPath}`, { model }) + console.error(`[EXTENSION-STORE]: can't load extension manifest at ${model.manifestPath}`, { model }) return; } const extension = this.getById(model.id) if (!extension) { try { - const { LensExtension, manifest: manifestJson } = manifest; + const { manifest: manifestJson, extensionModule } = manifest; + const LensExtension = extensionModule.default; this.extensions.set(model.id, new LensExtension(model, manifestJson)); } catch (err) { - logger.error(`[EXTENSION-STORE]: init extension failed: ${err}`, { model, manifest }) + console.error(`[EXTENSION-STORE]: init extension failed: ${err}`, { model, manifest }) } } else { extension.importModel(model); diff --git a/src/extensions/extension.ts b/src/extensions/extension.ts index 66612a66a6..35b8758989 100644 --- a/src/extensions/extension.ts +++ b/src/extensions/extension.ts @@ -1,14 +1,13 @@ +import type { ExtensionModel } from "./extension-store"; import { readJsonSync } from "fs-extra"; import { action, observable, when } from "mobx"; -import { ExtensionModel } from "./extension-store"; import extensionManifest from "./example-extension/package.json" import logger from "../main/logger"; // TODO: extensions api -// * Lazy load/unload extension (js/ts?) (from sources: local folder, npm_modules/@lens/some_plugin, etc.) -// * figure out how to expose lens external apis to extension: -// - opt1: import {someApi} from "@lens" => replaced to import from "$PATH/build/Lens.js" on the fly ? -// - opt2: dynamic require() / contents.executeJavaScript / etc. ? +// - figure out how to expose/inject lens apis to extension: +// -- replace import "@lens" to real path to "build/Lens.js" or maybe "build/Lens-extensions.api.js" +// -- load extension via NodeJS.require() / webContents.executeJavaScript() export type ExtensionId = string; export type ExtensionVersion = string | number; diff --git a/src/extensions/tsconfig.json b/src/extensions/tsconfig.json new file mode 100644 index 0000000000..504ffe6687 --- /dev/null +++ b/src/extensions/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "jsx": "react", + "target": "ES2017", + "lib": ["ESNext", "DOM", "DOM.Iterable"], + "module": "CommonJS", + "moduleResolution": "Node", + "noImplicitAny": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "allowJs": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "resolveJsonModule": true, + "declaration": true + }, + "files": [ + "./example-extension/example-extension.ts" + ] +}