diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/bundle-extension-initializer.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/bundle-extension-initializer.ts index 04d1ac3e8d..545176457a 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/bundle-extension-initializer.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/bundle-extension-initializer.ts @@ -1,35 +1,18 @@ import type { ManifestBase, ManifestBundle } from '../types/index.js'; import type { UmbExtensionRegistry } from '../registry/extension.registry.js'; import { loadManifestPlainJs } from '../functions/load-manifest-plain-js.function.js'; -import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; -import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { UmbExtensionInitializerBase } from './extension-initializer-base.js'; +import type { UmbElement } from '@umbraco-cms/backoffice/element-api'; -export class UmbBundleExtensionInitializer extends UmbControllerBase { - #extensionRegistry; - #bundleMap = new Map(); - - constructor(host: UmbControllerHost, extensionRegistry: UmbExtensionRegistry) { - super(host); - this.#extensionRegistry = extensionRegistry; - this.observe(extensionRegistry.byType('bundle'), (bundles) => { - // Unregister removed bundles: - this.#bundleMap.forEach((existingBundle) => { - if (!bundles.find((b) => b.alias === existingBundle.alias)) { - this.unregisterBundle(existingBundle); - this.#bundleMap.delete(existingBundle.alias); - } - }); - - // Register new bundles: - bundles.forEach((bundle) => { - if (this.#bundleMap.has(bundle.alias)) return; - this.#bundleMap.set(bundle.alias, bundle); - this.instantiateBundle(bundle); - }); - }); +/** + * Extension initializer for the `bundle` extension type + */ +export class UmbBundleExtensionInitializer extends UmbExtensionInitializerBase<'bundle', ManifestBundle> { + constructor(host: UmbElement, extensionRegistry: UmbExtensionRegistry) { + super(host, extensionRegistry, 'bundle'); } - async instantiateBundle(manifest: ManifestBundle) { + async instantiateExtension(manifest: ManifestBundle): Promise { if (manifest.js) { const js = await loadManifestPlainJs(manifest.js); @@ -38,16 +21,16 @@ export class UmbBundleExtensionInitializer extends UmbControllerBase { const value = js[key]; if (Array.isArray(value)) { - this.#extensionRegistry.registerMany(value); + this.extensionRegistry.registerMany(value); } else if (typeof value === 'object') { - this.#extensionRegistry.register(value); + this.extensionRegistry.register(value); } }); } } } - async unregisterBundle(manifest: ManifestBundle) { + async unloadExtension(manifest: ManifestBundle): Promise { if (manifest.js) { const js = await loadManifestPlainJs(manifest.js); @@ -56,9 +39,9 @@ export class UmbBundleExtensionInitializer extends UmbControllerBase { const value = js[key]; if (Array.isArray(value)) { - this.#extensionRegistry.unregisterMany(value.map((v) => v.alias)); + this.extensionRegistry.unregisterMany(value.map((v) => v.alias)); } else if (typeof value === 'object') { - this.#extensionRegistry.unregister((value as ManifestBase).alias); + this.extensionRegistry.unregister((value as ManifestBase).alias); } }); } diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/entry-point-extension-initializer.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/entry-point-extension-initializer.ts index 91409b3ee6..011d0f58f5 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/entry-point-extension-initializer.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/entry-point-extension-initializer.ts @@ -1,43 +1,31 @@ import type { ManifestEntryPoint } from '../types/index.js'; +import { hasInitExport, loadManifestPlainJs } from '../functions/index.js'; import type { UmbExtensionRegistry } from '../registry/extension.registry.js'; -import type { UmbEntryPointModule } from '../models/entry-point.interface.js'; -import { hasBeforeInitExport, hasInitExport, loadManifestPlainJs } from '../functions/index.js'; -import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; +import { UmbExtensionInitializerBase } from './extension-initializer-base.js'; import type { UmbElement } from '@umbraco-cms/backoffice/element-api'; -export class UmbEntryPointExtensionInitializer extends UmbControllerBase { - #host; - #extensionRegistry; - #entryPointMap = new Map(); - - constructor( - host: UmbElement, - extensionRegistry: UmbExtensionRegistry, - initFn: keyof UmbEntryPointModule, - ) { - super(host); - this.#host = host; - this.#extensionRegistry = extensionRegistry; - this.observe(extensionRegistry.byType('entryPoint'), (entryPoints) => { - entryPoints.forEach((entryPoint) => { - if (this.#entryPointMap.has(entryPoint.alias)) return; - this.#entryPointMap.set(entryPoint.alias, entryPoint); - // TODO: Should we unInit a entry point if is removed? - this.instantiateEntryPoint(entryPoint, initFn); - }); - }); +/** + * Extension initializer for the `entryPoint` extension type + */ +export class UmbEntryPointExtensionInitializer extends UmbExtensionInitializerBase<'entryPoint', ManifestEntryPoint> { + constructor(host: UmbElement, extensionRegistry: UmbExtensionRegistry) { + super(host, extensionRegistry, 'entryPoint'); } - async instantiateEntryPoint(manifest: ManifestEntryPoint, initFn: keyof UmbEntryPointModule = 'onInit') { + async instantiateExtension(manifest: ManifestEntryPoint) { if (manifest.js) { const js = await loadManifestPlainJs(manifest.js); // If the extension has known exports, be sure to run those - if (initFn === 'beforeInit' && hasBeforeInitExport(js)) { - js.beforeInit(this.#host, this.#extensionRegistry); - } else if (initFn === 'onInit' && hasInitExport(js)) { - js.onInit(this.#host, this.#extensionRegistry); + if (hasInitExport(js)) { + js.onInit(this.host, this.extensionRegistry); } } } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + unloadExtension(_manifest: ManifestEntryPoint): void { + // No-op + // Entry points are not unloaded, but if they were, this is where you would do it. + } } diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/extension-initializer-base.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/extension-initializer-base.ts new file mode 100644 index 0000000000..39b67f29c4 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/extension-initializer-base.ts @@ -0,0 +1,48 @@ +import type { ManifestBase } from '../types/index.js'; +import type { UmbExtensionRegistry } from '../registry/extension.registry.js'; +import type { SpecificManifestTypeOrManifestBase } from '../types/map.types.js'; +import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; +import type { UmbElement } from '@umbraco-cms/backoffice/element-api'; +import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; + +/** + * Base class for extension initializers, which are responsible for loading and unloading extensions. + */ +export abstract class UmbExtensionInitializerBase< + Key extends string, + T extends ManifestBase = SpecificManifestTypeOrManifestBase, +> extends UmbControllerBase { + protected host; + protected extensionRegistry; + #extensionMap = new Map(); + + constructor(host: UmbElement, extensionRegistry: UmbExtensionRegistry, manifestType: Key) { + super(host); + this.host = host; + this.extensionRegistry = extensionRegistry; + this.observe(extensionRegistry.byType(manifestType), (extensions) => { + this.#extensionMap.forEach((existingExt) => { + if (!extensions.find((b) => b.alias === existingExt.alias)) { + this.unloadExtension(existingExt); + this.#extensionMap.delete(existingExt.alias); + } + }); + + extensions.forEach((extension) => { + if (this.#extensionMap.has(extension.alias)) return; + this.#extensionMap.set(extension.alias, extension); + this.instantiateExtension(extension); + }); + }); + } + + /** + * Perform any logic required to instantiate the extension. + */ + abstract instantiateExtension(manifest: T): Promise | void; + + /** + * Perform any logic required to unload the extension. + */ + abstract unloadExtension(manifest: T): Promise | void; +} diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/index.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/index.ts index 1441726314..b89ddacc4c 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/index.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/index.ts @@ -1,2 +1,3 @@ export * from './bundle-extension-initializer.js'; export * from './entry-point-extension-initializer.js'; +export * from './extension-initializer-base.js';