mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
extensions-api -- in-progress
Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
parent
3a06f76545
commit
546ca123f6
@ -1,4 +1,5 @@
|
|||||||
import { LensExtension } from "@lens"; // todo: handle runtime import
|
// import { LensExtension } from "@lens"; // fixme: provide runtime import
|
||||||
|
import { LensExtension } from "../extension";
|
||||||
|
|
||||||
export default class ExampleExtension extends LensExtension {
|
export default class ExampleExtension extends LensExtension {
|
||||||
async init(): Promise<any> {
|
async init(): Promise<any> {
|
||||||
|
|||||||
@ -21,6 +21,12 @@ export interface ExtensionModel {
|
|||||||
updateUrl?: string;
|
updateUrl?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface InstalledExtension<T extends ExtensionModel = any> {
|
||||||
|
manifestPath: string;
|
||||||
|
manifest: ExtensionManifest;
|
||||||
|
LensExtension: new (model: T, manifest?: ExtensionManifest) => LensExtension;
|
||||||
|
}
|
||||||
|
|
||||||
export class ExtensionStore extends BaseStore<ExtensionStoreModel> {
|
export class ExtensionStore extends BaseStore<ExtensionStoreModel> {
|
||||||
private constructor() {
|
private constructor() {
|
||||||
super({
|
super({
|
||||||
@ -31,7 +37,7 @@ export class ExtensionStore extends BaseStore<ExtensionStoreModel> {
|
|||||||
@observable version: ExtensionVersion = "0.0.0";
|
@observable version: ExtensionVersion = "0.0.0";
|
||||||
@observable extensions = observable.map<ExtensionId, LensExtension>();
|
@observable extensions = observable.map<ExtensionId, LensExtension>();
|
||||||
@observable removed = observable.map<ExtensionId, LensExtension>();
|
@observable removed = observable.map<ExtensionId, LensExtension>();
|
||||||
@observable installed = observable.set<ExtensionManifest>([], { equals: comparer.shallow });
|
@observable installed = observable.map<string, InstalledExtension>([], { equals: comparer.shallow });
|
||||||
|
|
||||||
get folderPath(): string {
|
get folderPath(): string {
|
||||||
if (isDevelopment) {
|
if (isDevelopment) {
|
||||||
@ -45,31 +51,41 @@ export class ExtensionStore extends BaseStore<ExtensionStoreModel> {
|
|||||||
return super.load();
|
return super.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadInstalledExtensions() {
|
getExtensionByManifest(manifestPath: string): InstalledExtension {
|
||||||
const extensions = await this.loadExtensions(this.folderPath);
|
let manifestJson: ExtensionManifest;
|
||||||
this.installed.replace(extensions);
|
let mainJs: string;
|
||||||
|
try {
|
||||||
|
manifestJson = __non_webpack_require__(manifestPath); // eslint-disable-line
|
||||||
|
mainJs = path.resolve(path.dirname(manifestPath), manifestJson.main); // fixme: compile *.ts on the fly
|
||||||
|
const LensExtension = __non_webpack_require__(mainJs).default; // eslint-disable-line
|
||||||
|
return {
|
||||||
|
manifestPath: manifestPath,
|
||||||
|
manifest: manifestJson,
|
||||||
|
LensExtension: LensExtension,
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(`[EXTENSION-STORE]: can't load extension at ${manifestPath}: ${err}`, { manifestJson, mainJs });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadExtensions(basePath: string): Promise<ExtensionManifest[]> {
|
@action
|
||||||
const paths = await fs.readdir(basePath);
|
async loadInstalledExtensions() {
|
||||||
|
const extensions = await this.loadExtensions(this.folderPath);
|
||||||
|
this.installed.replace(extensions.map(ext => [ext.manifestPath, ext]));
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadExtensions(folderPath: string): Promise<InstalledExtension[]> {
|
||||||
|
const paths = await fs.readdir(folderPath);
|
||||||
const manifestsLoading = paths.map(fileName => {
|
const manifestsLoading = paths.map(fileName => {
|
||||||
const absPath = path.resolve(basePath, fileName);
|
const absPath = path.resolve(folderPath, fileName);
|
||||||
const manifestPath = path.resolve(absPath, "package.json");
|
const manifestPath = path.resolve(absPath, "package.json");
|
||||||
return new Promise<ExtensionManifest>(async resolve => {
|
return fs.access(manifestPath, fs.constants.F_OK)
|
||||||
try {
|
.then(() => this.getExtensionByManifest(manifestPath))
|
||||||
const manifestJson = await fs.readJson(manifestPath);
|
.catch(() => null)
|
||||||
resolve({
|
|
||||||
...manifestJson,
|
|
||||||
manifestPath: manifestPath,
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
resolve(null);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
let extensions = await Promise.all(manifestsLoading);
|
let extensions = await Promise.all(manifestsLoading);
|
||||||
extensions = extensions.filter(v => !!v); // filter out files and invalid folders (without manifest.json)
|
extensions = extensions.filter(v => !!v); // filter out files and invalid folders (without manifest.json)
|
||||||
logger.info(`[EXTENSION-STORE]: loaded ${extensions.length} extensions`, { basePath, extensions });
|
logger.info(`[EXTENSION-STORE]: ${extensions.length} extensions loaded`, { folderPath, extensions });
|
||||||
return extensions;
|
return extensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,9 +114,19 @@ export class ExtensionStore extends BaseStore<ExtensionStoreModel> {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
currentExtensions.forEach(model => {
|
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 })
|
||||||
|
return;
|
||||||
|
}
|
||||||
const extension = this.getById(model.id)
|
const extension = this.getById(model.id)
|
||||||
if (!extension) {
|
if (!extension) {
|
||||||
this.extensions.set(model.id, new LensExtension(model));
|
try {
|
||||||
|
const { LensExtension, manifest: manifestJson } = manifest;
|
||||||
|
this.extensions.set(model.id, new LensExtension(model, manifestJson));
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(`[EXTENSION-STORE]: init extension failed: ${err}`, { model, manifest })
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
extension.importModel(model);
|
extension.importModel(model);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import logger from "../main/logger";
|
|||||||
// * Lazy load/unload extension (js/ts?) (from sources: local folder, npm_modules/@lens/some_plugin, etc.)
|
// * 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:
|
// * 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 ?
|
// - opt1: import {someApi} from "@lens" => replaced to import from "$PATH/build/Lens.js" on the fly ?
|
||||||
// - opt2: eval with injected exposed apis / contents.executeJavaScript / script[src] / etc. ?
|
// - opt2: dynamic require() / contents.executeJavaScript / etc. ?
|
||||||
|
|
||||||
export type ExtensionId = string;
|
export type ExtensionId = string;
|
||||||
export type ExtensionVersion = string | number;
|
export type ExtensionVersion = string | number;
|
||||||
@ -28,14 +28,14 @@ export class LensExtension implements ExtensionModel {
|
|||||||
|
|
||||||
whenReady = when(() => this.isReady);
|
whenReady = when(() => this.isReady);
|
||||||
|
|
||||||
constructor(model: ExtensionModel) {
|
constructor(model: ExtensionModel, manifest?: ExtensionManifest) {
|
||||||
this.importModel(model);
|
this.importModel(model, manifest);
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
async importModel({ enabled, manifestPath, ...model }: ExtensionModel) {
|
async importModel({ enabled, manifestPath, ...model }: ExtensionModel, manifest?: ExtensionManifest) {
|
||||||
try {
|
try {
|
||||||
this.manifest = await readJsonSync(manifestPath, { throws: true })
|
this.manifest = manifest || await readJsonSync(manifestPath, { throws: true })
|
||||||
this.manifestPath = manifestPath;
|
this.manifestPath = manifestPath;
|
||||||
this.isEnabled = enabled;
|
this.isEnabled = enabled;
|
||||||
Object.assign(this, model);
|
Object.assign(this, model);
|
||||||
|
|||||||
@ -22,6 +22,7 @@ export default function (): webpack.Configuration {
|
|||||||
path: buildDir,
|
path: buildDir,
|
||||||
filename: '[name].js',
|
filename: '[name].js',
|
||||||
chunkFilename: 'chunks/[name].js',
|
chunkFilename: 'chunks/[name].js',
|
||||||
|
libraryTarget: "commonjs2",
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
extensions: [
|
extensions: [
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user