Advanced Features
This page covers dynamic plugin loading, health monitoring, developer tools, dependency graph validation, and internationalization namespace isolation.
Dynamic Plugin Loadingβ
The DynamicPluginLoaderService allows loading and unloading plugins at runtime β after the initial application bootstrap.
Loading a Pluginβ
import { DynamicPluginLoaderService } from '@gauzy/plugin-ui';
const loader = inject(DynamicPluginLoaderService);
// Load a plugin definition at runtime
await loader.loadPlugin(myPluginDefinition);
The service:
- Instantiates the plugin's module (or calls
bootstrap()for declarative plugins) - Creates a child
InjectorwithPLUGIN_OPTIONSandPLUGIN_DEFINITIONtokens - Applies declarative registrations (routes, nav, tabs, extensions)
- Calls lifecycle hooks (
ngOnPluginBootstrap) - Records the plugin in the loaded registry
Unloading a Pluginβ
await loader.unloadPlugin('my-plugin-id');
The service:
- Calls
ngOnPluginBeforeDestroy()if implemented - Calls
ngOnPluginDestroy()if implemented - Cleans up: extensions, settings, services, event subscriptions, plugin state
- Destroys the child injector (cleaning up Angular DI resources)
- Removes the plugin from the loaded registry
- Emits
plugin:dynamic-unloadedevent
Reloading a Pluginβ
await loader.reloadPlugin(updatedPluginDefinition);
This is equivalent to unloadPlugin() followed by loadPlugin() β useful for hot-reloading during development.
Full APIβ
| Method / Property | Signature | Description |
|---|---|---|
loadPlugin | (definition: PluginUiDefinition) => Promise<void> | Load a plugin at runtime |
unloadPlugin | (pluginId: string) => Promise<void> | Unload and clean up a plugin |
reloadPlugin | (definition: PluginUiDefinition) => Promise<void> | Unload then reload |
isLoaded | (pluginId: string) => boolean | Check if a plugin is loaded |
loadedPluginIds | string[] (getter) | Current loaded plugin IDs |
loadedPluginIds$ | Observable<string[]> | Reactive loaded plugin IDs |
Events Emittedβ
| Event Type | When |
|---|---|
plugin:dynamic-loaded | After a plugin is successfully loaded |
plugin:dynamic-unloaded | After a plugin is fully unloaded and cleaned up |
From Reactβ
import { useDynamicPlugin } from '@gauzy/ui-react';
function PluginToggle() {
const { load, unload, isLoaded } = useDynamicPlugin('optional-plugin');
return <button onClick={() => isLoaded ? unload() : load()}>Toggle</button>;
}
Health Monitoringβ
The PluginHealthService automatically tracks plugin bootstrap performance and errors.
Boot Time Trackingβ
Every plugin's bootstrap time is recorded automatically. Query it via:
import { PluginHealthService } from '@gauzy/plugin-ui';
const health = inject(PluginHealthService);
// Get boot time for a specific plugin (ms)
const bootTime = health.getBootTime('my-plugin');
// Get all boot times
const allTimes = health.getAllBootTimes();
// Map<string, number> β { 'plugin-a': 45, 'plugin-b': 120, ... }
Error Monitoringβ
Plugin bootstrap errors are captured rather than crashing the application:
// Get errors for a specific plugin
const errors = health.getErrors('my-plugin');
// Get all plugin errors
const allErrors = health.getAllErrors();
// Map<string, Error[]>
// Check if a plugin is healthy
const isHealthy = health.isHealthy('my-plugin'); // no errors recorded
Health Summaryβ
const summary = health.getSummary();
// {
// totalPlugins: 12,
// healthyPlugins: 11,
// failedPlugins: 1,
// totalBootTime: 340,
// slowestPlugin: { id: 'heavy-plugin', bootTime: 120 }
// }
Developer Toolsβ
The PluginDevToolsService aggregates debug information for development and troubleshooting.
Plugin Introspectionβ
import { PluginDevToolsService } from '@gauzy/plugin-ui';
const devtools = inject(PluginDevToolsService);
// Get a full debug snapshot of all plugins
const snapshot = devtools.getSnapshot();
The snapshot includes for each plugin:
- Plugin ID, version, and location
- Boot time and health status
- Number of registered extensions
- Dependency information
- Settings schema
- Translation namespaces
Console Integrationβ
During development, the devtools service is available on the browser console:
// In browser console (development mode)
window.__GAUZY_PLUGIN_DEVTOOLS__.getSnapshot();
window.__GAUZY_PLUGIN_DEVTOOLS__.getPluginInfo('my-plugin');
Dependency Graph Validationβ
The system validates plugin dependencies at bootstrap time to catch configuration errors early.
Automatic Validationβ
When PluginUiModule.bootstrapPlugins() is called, it automatically:
- Validates existence β all
dependsOnreferences point to registered plugins - Detects cycles β circular dependency chains are reported
- Orders bootstrap β plugins are bootstrapped in topological order
Manual Validationβ
import { validatePluginDependencies, logDependencyValidation } from '@gauzy/plugin-ui';
const result = validatePluginDependencies(pluginDefinitions);
if (!result.valid) {
logDependencyValidation(result);
// Logs missing dependencies, circular chains, and ordering issues
}
// result.ordered β correctly ordered plugin IDs
// result.errors β list of validation error messages
// result.cycles β detected circular dependency chains
Version Compatibilityβ
import { checkVersionCompatibility } from '@gauzy/plugin-ui';
const compat = checkVersionCompatibility(pluginDefinitions);
if (!compat.compatible) {
console.error('Version conflicts:', compat.conflicts);
// e.g., "my-plugin requires core-plugin >=2.0.0 but found 1.5.0"
}
This checks peerPlugins version ranges against actual version fields:
export const MyPlugin = defineDeclarativePlugin('my-plugin', {
version: '1.0.0',
peerPlugins: {
'core-plugin': '>=2.0.0',
'data-plugin': '^1.5.0'
}
});
i18n Namespace Isolationβ
Plugin translations are isolated to prevent naming collisions with the host application or other plugins.
How It Worksβ
- Each plugin declares a
translationNamespace(e.g.,'MY_PLUGIN') - Translation keys are automatically scoped under that namespace
filterNewTranslationKeys()ensures plugins cannot override existing core keys- Language change events trigger re-merging of plugin translations
Namespace Helpersβ
import { namespaceTranslations, NamespacedTranslateHelper } from '@gauzy/plugin-ui';
// Wrap raw translations with a namespace
const namespaced = namespaceTranslations('MY_PLUGIN', {
TITLE: 'Dashboard',
SAVE: 'Save'
});
// Result: { MY_PLUGIN: { TITLE: 'Dashboard', SAVE: 'Save' } }
// Use the namespaced translate helper
const helper = new NamespacedTranslateHelper(translateService, 'MY_PLUGIN');
const title = helper.instant('TITLE'); // Translates 'MY_PLUGIN.TITLE'
Translation Lifecycleβ
When a plugin provides translations:
- At bootstrap, translations are merged into the host's translation store for all available languages
- The
onLangChangesubscription re-merges translations when the user switches languages - On plugin unload (dynamic plugins), the subscription is cleaned up via Angular's
DestroyRef filterNewTranslationKeys()strips any keys that already exist in the host store, preventing overrides
Testing Utilitiesβ
The @gauzy/plugin-ui package provides testing helpers for unit and integration tests.
createTestPluginβ
Create a minimal plugin definition for tests:
import { createTestPlugin } from '@gauzy/plugin-ui';
const testPlugin = createTestPlugin('test-plugin', {
extensions: [
{ id: 'test-ext', slotId: 'dashboard-widgets', component: MockComponent }
]
});
MockEventBusβ
A mock event bus that records emitted events for assertions:
import { MockEventBus } from '@gauzy/plugin-ui';
const mockBus = new MockEventBus();
// Use in tests
myService.doSomething(); // internally emits an event
expect(mockBus.emittedEvents).toContainEqual({
name: 'my-plugin:data-changed',
payload: expect.objectContaining({ updatedAt: expect.any(Number) })
});
TestPluginHarnessβ
A test harness that sets up the full plugin environment:
import { TestPluginHarness } from '@gauzy/plugin-ui';
const harness = new TestPluginHarness();
await harness.bootstrap([testPlugin]);
// Access registries
const extensions = harness.extensionRegistry.getExtensions('dashboard-widgets');
expect(extensions).toHaveLength(1);
// Clean up
harness.destroy();
Related Pagesβ
- Plugin Definitions β dependency and version fields
- Plugin Services β events and translations
- API Reference β full type and token reference