Introduce clearer boundry between extensions (#7164)

- Bundled extensions are always enabled, and are always compatible
- Have bundled extensions be loaded asyncronously to support
  typescript dynamic import (which is typed) as opposed to require

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton authored and GitHub committed 2023-04-05 10:21:38 -04:00
1 parent 351f9d492f
commit 058494bc73
29 files changed
+440 -550

No files matched your search

@@ -10,7 +10,7 @@ import { broadcastMessage, ipcMainHandle, ipcRendererOn } from "../../common/ipc
import { toJS } from "../../common/utils";
import { isErrnoException } from "@k8slens/utilities";
import type { ExtensionLoader } from "../extension-loader";
import type { InstalledExtension, LensExtensionId, LensExtensionManifest } from "@k8slens/legacy-extensions";
import type { InstalledExtension, LensExtensionId, LensExtensionManifest, ExternalInstalledExtension } from "@k8slens/legacy-extensions";
import type { ExtensionInstallationStateStore } from "../extension-installation-state-store/extension-installation-state-store";
import { extensionDiscoveryStateChannel } from "../../common/ipc/extension-handling";
import { requestInitialExtensionDiscovery } from "../../renderer/ipc";
@@ -73,10 +73,6 @@ interface ExtensionDiscoveryChannelMessage {
*/
const isDirectoryLike = (lstat: Stats) => lstat.isDirectory() || lstat.isSymbolicLink();
interface LoadFromFolderOptions {
isBundled?: boolean;
}
interface ExtensionDiscoveryEvents {
add: (ext: InstalledExtension) => void;
remove: (extId: LensExtensionId) => void;
@@ -271,7 +267,7 @@ export class ExtensionDiscovery {
* @param extensionId The ID of the extension to uninstall.
*/
async uninstallExtension(extensionId: LensExtensionId): Promise<void> {
const extension = this.extensions.get(extensionId) ?? this.dependencies.extensionLoader.getExtension(extensionId);
const extension = this.extensions.get(extensionId) ?? this.dependencies.extensionLoader.getExtensionById(extensionId);
if (!extension) {
return void this.dependencies.logger.warn(`${logModule} could not uninstall extension, not found`, { id: extensionId });
@@ -330,24 +326,26 @@ export class ExtensionDiscovery {
* Returns InstalledExtension from path to package.json file.
* Also updates this.packagesJson.
*/
protected async getByManifest(manifestPath: string, { isBundled = false } = {}): Promise<InstalledExtension | null> {
protected async loadExtensionFromFolder(folderPath: string): Promise<ExternalInstalledExtension | null> {
const manifestPath = this.dependencies.joinPaths(folderPath, manifestFilename);
try {
const manifest = await this.dependencies.readJsonFile(manifestPath) as unknown as LensExtensionManifest;
const id = isBundled ? manifestPath : this.getInstalledManifestPath(manifest.name);
const isEnabled = this.dependencies.isExtensionEnabled({ id, isBundled });
const id = this.getInstalledManifestPath(manifest.name);
const isEnabled = this.dependencies.isExtensionEnabled(id);
const extensionDir = this.dependencies.getDirnameOfPath(manifestPath);
const npmPackage = this.dependencies.joinPaths(extensionDir, `${manifest.name}-${manifest.version}.tgz`);
const absolutePath = this.dependencies.isProduction && await this.dependencies.pathExists(npmPackage)
? npmPackage
: extensionDir;
const isCompatible = isBundled || this.dependencies.isCompatibleExtension(manifest);
const isCompatible = this.dependencies.isCompatibleExtension(manifest);
return {
id,
absolutePath,
manifestPath: id,
manifest,
isBundled,
isBundled: false,
isEnabled,
isCompatible,
};
@@ -363,14 +361,14 @@ export class ExtensionDiscovery {
}
}
async ensureExtensions(): Promise<Map<LensExtensionId, InstalledExtension>> {
async ensureExtensions(): Promise<Map<LensExtensionId, ExternalInstalledExtension>> {
const userExtensions = await this.loadFromFolder(this.localFolderPath);
return this.extensions = new Map(userExtensions.map(extension => [extension.id, extension]));
}
async loadFromFolder(folderPath: string): Promise<InstalledExtension[]> {
const extensions: InstalledExtension[] = [];
async loadFromFolder(folderPath: string): Promise<ExternalInstalledExtension[]> {
const extensions: ExternalInstalledExtension[] = [];
const paths = await this.dependencies.readDirectory(folderPath);
for (const fileName of paths) {
@@ -403,16 +401,6 @@ export class ExtensionDiscovery {
return extensions;
}
/**
* Loads extension from absolute path, updates this.packagesJson to include it and returns the extension.
* @param folderPath Folder path to extension
*/
async loadExtensionFromFolder(folderPath: string, { isBundled = false }: LoadFromFolderOptions = {}): Promise<InstalledExtension | null> {
const manifestPath = this.dependencies.joinPaths(folderPath, manifestFilename);
return this.getByManifest(manifestPath, { isBundled });
}
toJSON(): ExtensionDiscoveryChannelMessage {
return toJS({
isLoaded: this.isLoaded,