@teambit/harmony
Version:
abstract extension system
143 lines (118 loc) • 3.8 kB
text/typescript
import { ProviderFn } from '../types';
import { Harmony } from '../harmony';
import { ExtensionManifest } from './extension-manifest';
import { ExtensionInstantiationException } from '../exceptions/extension-init-error';
import { RuntimeDefinition } from '../runtimes';
export type ExtensionProps = {
name: string;
// TODO: changes from any to something meaningful
dependencies: any[];
provider: ProviderFn;
};
/**
* harmony's extension definition. this can be used to define and extend `Harmony` applications.
*/
export class Extension {
constructor(
/**
* manifest of the extension.
*/
readonly manifest: ExtensionManifest
) {}
private _instance = null;
private _loaded = false;
/**
* returns the instance of the extension
*/
get instance() {
return this._instance;
}
get name() {
const metadata = Reflect.getMetadata('harmony:name', this.manifest);
return metadata || this.manifest.id || this.manifest.name;
}
get id() {
return this.name;
}
get dependencies(): Extension[] {
const metadata = Reflect.getMetadata('harmony:dependencies', this.manifest);
return metadata || this.manifest.dependencies || [];
}
get provider() {
const metadata = Reflect.getMetadata('harmony:provider', this.manifest);
return metadata || this.manifest.provider;
}
get files() {
return this.manifest.files;
}
/**
* returns an indication of the extension already loaded (the provider run)
* We don't rely on the instance since an extension provider might return null
*/
get loaded() {
return this._loaded;
}
toString(): string {
return JSON.stringify(this.name);
}
private buildSlotRegistries(slots: ((registerFn: () => void) => any)[], context: Harmony) {
return slots.map((slot) => {
return slot(() => {
return context.current;
});
});
}
get declareRuntime() {
return this.manifest.declareRuntime;
}
getRuntime(runtime: RuntimeDefinition) {
return this.manifest.getRuntime(runtime);
}
getRuntimes() {
return this.manifest.getRuntimes();
}
getSlots(extensionRuntime: any) {
if (extensionRuntime.slots && extensionRuntime.slots.length) {
return extensionRuntime.slots;
}
return this.manifest.slots || [];
}
getConfig(context: Harmony, extensionRuntime: any) {
const defaultConfig = extensionRuntime.defaultConfig || this.manifest.defaultConfig || {};
const config = context.config.get(this.name) || {};
return Object.assign({}, defaultConfig, config);
}
/**
* initiate Harmony in run-time.
*/
async __run(dependencies: any[], context: Harmony, runtime: RuntimeDefinition) {
const name = this.name;
context.initExtension(name);
const extensionRuntime = this.getRuntime(runtime);
if (!extensionRuntime) {
return undefined;
}
// @ts-ignore
const registries = this.buildSlotRegistries(this.getSlots(extensionRuntime), context);
const config = this.getConfig(context, extensionRuntime);
if (!this.loaded) {
if (extensionRuntime.provider) this._instance = await extensionRuntime.provider(dependencies, config, registries, context);
else {
try {
// @ts-ignore
this._instance = new extensionRuntime.manifest(...dependencies);
} catch(err) {
throw new ExtensionInstantiationException(err.toString());
}
};
// @ts-ignore adding the extension ID to the instance.
// this._instance.id = this.manifest.name;
// @ts-ignore adding the extension ID to the instance.
// this._instance.config = config;
this._loaded = true;
return this._instance;
}
context.endExtension();
return Promise.resolve(this.instance);
}
}