Skip to content
Comvi

@comvi/core/plugins

View .md

The plugin capability queues plugins for init() to run. Compose it with .use(plugins()) on a @comvi/core instance.

  • Values: plugins, attachPlugins, definePlugin, ensureInstallable, acquirePluginsApi.
  • Types: AcquiredPluginsApi, I18nPluginApi, PluginI18nInstance, I18nPlugin, I18nPluginFactory, PluginOptions.
function plugins(): <T extends I18n<any>>(i18n: T) => T & I18nPluginApi
function attachPlugins<T extends I18n<any>>(i18n: T): T & I18nPluginApi

plugins() takes no configuration, so it is attachPlugins in installer form. Attaching is idempotent and keeps every registered plugin.

src/plugins-instance.ts
import { createI18n } from '@comvi/core';
import { plugins } from '@comvi/core/plugins';
import { inContextEditorPlugin } from '@comvi/plugin-in-context-editor';
export const i18n = createI18n({ locale: 'en' }).use(plugins());
i18n.registerPlugin(inContextEditorPlugin(), { required: false, timeout: 5000 });
function definePlugin(fn: I18nPlugin, options?: PluginOptions): <T extends I18n<any>>(instance: T) => T & I18nPluginApi

attachPlugins(i18n).registerPlugin(fn, options) as an installer, so an inline plugin fits in a .use() chain. A plugin package’s own installer (fetchLoader(), localeDetector()) goes straight into .use() without it.

src/define-plugin.ts
import { createI18n } from '@comvi/core';
import { definePlugin } from '@comvi/core/plugins';
export const i18n = createI18n({ locale: 'en' }).use(
definePlugin((i18n) => {
const off = i18n.on('localeChanged', ({ to }) => console.log(to));
return off;
}),
);
function ensureInstallable<T>(i18n: T, installer: string): T

The guard an installer calls first. During plugin initialization it throws E_INSTALLER_AS_PLUGIN, naming installer, so registerPlugin(fetchLoader()) fails before anything attaches. Outside plugin initialization it returns the instance untouched.

function acquirePluginsApi(instance: WrapperI18nInstance): AcquiredPluginsApi
interface AcquiredPluginsApi {
onMissingKey(cb: (key: string, locale: string, namespace: string) => TranslationResult | void): () => void;
}

The component-facing slice behind every binding’s useI18nPlugins(). It throws missingCapability('plugins'), in development and production, when the instance has no plugin capability. Cached per instance.

The members the capability adds to the i18n instance.

registerPlugin(plugin: I18nPlugin, options?: PluginOptions): this
registerLocaleDetector(detector: () => string | Promise<string>): void
getLanguageDetector(): (() => string | Promise<string>) | undefined
onMissingKey(cb: (key: string, locale: string, namespace: string) => TranslationResult | void): () => void
registerPostProcessor(fn: PostProcessFn): void
setPluginData(key: string, data: unknown): void
getPluginData<T = unknown>(key: string): T | undefined
Member Meaning
registerPlugin(plugin, options?) Queues a plugin; chainable. Call before init(), which drains the queue once. An installer passed here fails at init() with E_INSTALLER_AS_PLUGIN
registerLocaleDetector(fn) Consulted by init() after the plugins ran; a non-function throws
getLanguageDetector() The registered detector, or undefined
onMissingKey(cb) Runtime miss callback; the first defined return wins. Returns an unsubscribe. Separate from the constructor option
registerPostProcessor(fn) Post-processors chain in registration order
setPluginData(key, data) Instance-lifetime scratch space for plugins
getPluginData<T>(key) The stored data, or undefined
Option Type Default Meaning
required boolean true A required plugin’s failure throws out of init(); an optional one’s is reported
timeout number 10000 Milliseconds allowed for the plugin’s initialization
onError (error: Error) => void none Called for this plugin’s error, which also reaches the instance’s onError with source 'plugin'

Plugins run in order during init(), each in a timeout race. A plugin returning anything other than undefined or a cleanup function fails with E_PLUGIN_INIT_RETURN. Cleanups run in reverse order on destroy(). The E_* strings are the production messages; development builds throw a longer one.

type I18nPlugin = (i18n: PluginI18nInstance) => void | Promise<void> | PluginCleanup | Promise<PluginCleanup>
type I18nPluginFactory<T = unknown> = (options?: T) => I18nPlugin
type PluginI18nInstance<D> = WrapperI18nInstance<D> & I18nLoaderApi & I18nPluginApi

PluginCleanup (() => void | Promise<void>) is not exported. A plugin that registers a loader needs loader() or attachLoader composed on the instance as well as plugins().

  • @comvi/core: the i18n instance this capability composes onto.
  • Write a plugin: writing an installer, and what a plugin may do at init().
  • Plugins: every shipped plugin and the installer each one exports.