Modern.js adopts a highly extensible, plugin-based architecture, where its core functionalities and extended capabilities are implemented through plugins. The plugin system not only ensures the framework's flexibility but also provides developers with powerful customization options. This document focuses on how to write Modern.js plugins, helping you quickly get started with plugin development.
Modern.js adheres to the design philosophy of "everything is a plugin," modularizing the framework's various functional components and assembling and extending them through plugins. This design brings several advantages, including:
Modern.js provides three main plugin types, corresponding to different stages of application development:
CLI Plugins:
modern command).plugins field in modern.config.ts.Runtime Plugins:
plugins field in src/modern.runtime.ts.A typical Modern.js plugin consists of the following key parts:
import type { Plugin } from '@modern-js/plugin';
const myPlugin: Plugin = {
name: 'my-awesome-plugin', // The unique identifier of the plugin (required)
// Plugin dependencies and execution order (optional)
pre: [], // List of plugin names to execute before this plugin, defaults to an empty array
post: [], // List of plugin names to execute after this plugin, defaults to an empty array
required: [], // List of required plugins; if a dependent plugin is not registered, an error will be thrown, defaults to an empty array
usePlugins: [], // List of plugin instances used internally, defaults to an empty array
// Register new Hooks (optional)
registryHook: {},
// The entry function of the plugin (required)
setup(api) {
// The core logic of the plugin, calling plugin APIs through the api object
api.modifyWebpackConfig(config => {
/* ... */
});
api.onPrepare(() => {
/* ... */
});
// ... Other API calls
},
};
export default myPlugin;Field Descriptions:
namestring
The plugin names declared in pre, post, and required refer to this name field.
setup(api: PluginAPI) => MaybePromise<void>apiPluginAPIprestring[]pre will be executed before this plugin.poststring[]post will be executed after this plugin.requiredstring[]
If unregistered plugin names are configured in pre or post, these plugin names will be automatically ignored and will not affect the execution of other plugins.
If you need to explicitly declare that the plugins that the current plugin depends on must exist, you need to use the required field.
usePluginsPlugin
Plugins declared in usePlugins are executed before the current plugin by default. To execute them after, use the post declaration.
registryHooksRecord<string, PluginHook<(...args: any[]) => any>>The core of the Modern.js plugin system is its Hook model, which defines the communication mechanism between plugins. Modern.js mainly provides two types of Hooks:
async/await.createAsyncHook.Example:
// Define Hooks
import { createAsyncHook } from '@modern-js/plugin';
export type AfterPrepareFn = () => Promise<void> | void;
export const onAfterPrepare = createAsyncHook<AfterPrepareFn>();
// Register Hooks in the plugin
const myPlugin = () => ({
name: 'my-plugin',
registryHooks: {
onAfterPrepare,
},
setup: api => {
api.onPrepare(async () => {
// Use the registered Hooks in the plugin
const hooks = api.getHooks();
await hooks.onAfterPrepare.call();
});
},
});
// Use Hook in other plugins
const myPlugin2 = () => ({
name: 'my-plugin-2',
setup: api => {
api.onAfterPrepare(async () => {
// TODO
});
},
});createSyncHook.Example:
// Define Hooks
import { createSyncHook } from '@modern-js/plugin';
type RouteObject = {
/** TODO **/
};
const modifyRoutes = createSyncHook<(routes: RouteObject[]) => RouteObject[]>();
// Register Hooks in the plugin
const myPlugin = () => ({
name: 'my-plugin',
registryHooks: {
modifyRoutes,
},
setup: api => {
api.onPrepare(async () => {
const routes = {};
// Use registered Hooks in the plugin
const hooks = api.getHooks();
const routesResult = hooks.modifyRoutes.call(routes);
});
},
});
// Other plugins use Hooks
const myPlugin2 = () => ({
name: 'my-plugin',
setup: api => {
api.modifyRoutes(async routes => {
// Modify routes
return routes;
});
},
});plugin-xxx or @scope/plugin-xxx).Following these best practices can help you develop high-quality, easy-to-maintain, and easy-to-use Modern.js plugins.