Hook List
In the Modern.js engineering system, there are three types of plugins: CLI, Runtime, and Server. Each type of plugin can utilize different Hooks.
In this chapter, all available Hooks are listed, and you can use the corresponding Hook based on your needs.
CLI Common Hooks
The following are the common CLI Hooks that can be used in both Modern.js Framework and Modern.js Module.
beforeConfig
- Functionality: Running tasks before the config process
- Execution phase: Before the config process
- Hook model:
AsyncWorkflow
- Type:
AsyncWorkflow<void, void>
- Example:
import type { CliPlugin } from '@modern-js/core';
export const myPlugin = (): CliPlugin => ({
setup(api) {
return {
beforeConfig: () => {
// do something
},
};
},
});
config
- Functionality: Collect configuration
- Execution phase: After parsing the configuration in
modern.config.ts
- Hook model:
ParallelWorkflow
- Type:
ParallelWorkflow<void, unknown>
- Example:
import type { CliPlugin } from '@modern-js/core';
export const myPlugin = (): CliPlugin => ({
setup(api) {
return {
config: () => {
return {
/** some config */
};
},
};
},
});
If you need to set the configuration of the Modern.js Framework, please use the CliPlugin<AppTools>
type exported by @modern-js/app-tools
:
import type { CliPlugin, AppTools } from '@modern-js/app-tools';
export const myPlugin = (): CliPlugin => ({
setup(api) {
return {
config: () => {
return {
output: {
polyfill: 'usage',
},
};
},
};
},
});
The configuration returned by the config
hook will be collected and merged by Modern.js, resulting in a NormalizedConfig
. When merging configurations, the priority is as follows, from high to low:
- Configuration defined by the user in the
modern.config.*
file.
- Configuration defined by the plugin through the
config
hook.
- Default configuration of Modern.js.
prepare
- Functionality: Preparatory process for running the main process.
- Execution phase: After configuration validation.
- Hook model:
AsyncWorkflow
- Type:
AsyncWorkflow<void, void>
- Example:
import type { CliPlugin } from '@modern-js/core';
export const myPlugin = (): CliPlugin => ({
setup(api) {
return {
prepare: () => {
// do something
},
};
},
});
afterPrepare
- function: Running tasks after the prepare process
- Execution Phase: After the prepare process
- Hook model:
AsyncWorkflow
- type:
AsyncWorkflow<void, void>
- Usage:
import type { CliPlugin } from '@modern-js/core';
export const myPlugin = (): CliPlugin => ({
setup(api) {
return {
afterPrepare: () => {
// do something
},
};
},
});
commands
- Functionality: Add new CLI commands for the commander.
- Execution phase: After the
prepare
Hook has run.
- Hook model:
AsyncWorkflow
- Type:
AsyncWorkflow<{ program: Command; }, void>
- Example:
import type { CliPlugin } from '@modern-js/core';
export const myPlugin = (): CliPlugin => ({
setup(api) {
return {
commands: ({ program }) => {
program.command('foo').action(async () => {
// do something
console.log('foo');
});
},
};
},
});
Move the plugin to modern.config.ts
:
modern.config.ts
import myPlugin from './config/plugin/myPlugin';
export const myPlugin = defineConfig({
plugins: [myPlugin()],
});
run modern foo
:
beforeExit
- Functionality: Reset some file states before exiting the process.
- Execution phase: Before the process exits.
- Hook model:
Workflow
- Type:
Workflow<void, void>
- Example:
import type { CliPlugin } from '@modern-js/core';
export const myPlugin = (): CliPlugin => ({
setup(api) {
return {
beforeExit: () => {
// do something
},
};
},
});
TIP
Since the callback function when exiting the process in Node.js is synchronous, the type of beforeExit
Hook is Workflow
and cannot perform asynchronous operations.
CLI Framework Hooks
The following are the CLI Hooks of the framework, which can only be used in Modern.js Framework and cannot be used in Modern.js Module.
You need to import the CliPlugin
and AppTools
types from @modern-js/app-tools
to get accurate type hints for Hooks.
beforeDev
- Functionality: Tasks before running the main dev process.
- Execution phase: Before the project starts when the
dev
command is run.
- Hook model:
AsyncWorkflow
- Type:
AsyncWorkflow<void, unknown>
- Example:
import type { CliPlugin, AppTools } from '@modern-js/app-tools';
export const myPlugin = (): CliPlugin<AppTools> => ({
setup(api) {
return {
beforeDev: () => {
// do something
},
};
},
});
afterDev
- Function: Tasks to be executed after the main process of
dev
command
- Execution stage: It is executed after each compilation is completed when running the
dev
command
- Hook model:
AsyncWorkflow
- Type:
AsyncWorkflow<{ isFirstCompile: boolean }, unknown>
- Example:
import type { CliPlugin, AppTools } from '@modern-js/app-tools';
export const myPlugin = (): CliPlugin<AppTools> => ({
setup(api) {
return {
afterDev: () => {
// do something
},
};
},
});
afterDev
will be executed after each compilation is completed, you can use the isFirstCompile
param to determine whether it is the first compilation:
import type { CliPlugin, AppTools } from '@modern-js/app-tools';
export const myPlugin = (): CliPlugin<AppTools> => ({
setup(api) {
return {
afterDev: ({ isFirstCompile }) => {
if (isFirstCompile) {
// do something
}
},
};
},
});
beforeBuild
- Function: A callback function triggered before executing production environment builds. You can access the final configuration array of the underlying bundler through the
bundlerConfigs
parameter.
- If the current bundler is webpack, you will get the webpack Compiler object.
- If the current bundler is Rspack, you will get the Rspack Compiler object.
- The configuration array may contain one or multiple configurations, depending on whether you have enabled features such as SSR.
- Execution stage: Executed after running the
build
command and before the actual build process begins.
- Hook model:
AsyncWorkflow
.
- Type:
type BeforeBuild = AsyncWorkflow<{
bundlerConfigs: WebpackConfig[] | RspackConfig[];
}>;
import type { CliPlugin, AppTools } from '@modern-js/app-tools';
export const myPlugin = (): CliPlugin<AppTools> => ({
setup(api) {
return {
beforeBuild: ({ bundlerConfigs }) => {
console.log('Before build.');
console.log(bundlerConfigs);
},
};
},
});
afterBuild
- Function: A callback function triggered after running the production build. You can access the build result information through the
stats
parameter.
- If the current bundler is webpack, you will get webpack Stats.
- If the current bundler is Rspack, you will get Rspack Stats.
- Execution stage: It is executed after running the
build
command and completing the build.
- Hook model:
AsyncWorkflow
.
- Type:
type AfterBuild = AsyncWorkflow<{
stats?: Stats | MultiStats;
}>;
import type { CliPlugin, AppTools } from '@modern-js/app-tools';
export const myPlugin = (): CliPlugin<AppTools> => ({
setup(api) {
return {
afterBuild: ({ stats }) => {
console.log('After build.');
console.log(stats);
},
};
},
});
beforeCreateCompiler
- Function: A callback function triggered before creating the underlying Compiler instance, and you can get the final configuration array of the underlying bundler through the
bundlerConfigs
parameter:
- If the current bundler is webpack, you will get the webpack Compiler object.
- If the current bundler is Rspack, you will get the Rspack Compiler object.
- The configuration array may contain one or multiple configurations, depending on whether you have enabled features such as SSR.
- Execution stage: Executed before creating the Compiler instance when running the
dev
or build
command.
- Hook model:
AsyncWorkflow
.
- Type:
type BeforeCreateCompiler = AsyncWorkflow<
{ bundlerConfigs: Configuration[] },
unknown
>;
import type { CliPlugin, AppTools } from '@modern-js/app-tools';
export const myPlugin = (): CliPlugin<AppTools> => ({
setup(api) {
return {
beforeCreateCompiler: ({ bundlerConfigs }) => {
console.log('Before create compiler.');
console.log(bundlerConfigs);
},
};
},
});
afterCreateCompiler
- Function: A callback function triggered after creating a Compiler instance and before executing the build. You can access the Compiler instance object through the
compiler
parameter:
- If the current bundler is webpack, you will get the webpack Compiler object.
- If the current bundler is Rspack, you will get the Rspack Compiler object.
- Execution stage: Executed after creating the Compiler object.
- Hook model:
AsyncWorkflow
.
- Type:
type AfterCreateCompiler = AsyncWorkflow<
{ compiler: Compiler | MultiCompiler | undefined },
unknown
>;
import type { CliPlugin, AppTools } from '@modern-js/app-tools';
export const myPlugin = (): CliPlugin<AppTools> => ({
setup(api) {
return {
afterCreateCompiler: ({ compiler }) => {
console.log('After create compiler.');
console.log(compiler);
},
};
},
});
beforePrintInstructions
- Function: Provides access to the log information that will be printed within middleware functions and allows modification of the log information.
- Execution stage: Executed before printing the log information.
- Hook model:
AsyncWaterfall
- Type:
AsyncWaterfall<{ instructions: string }>
- Example:
import type { CliPlugin, AppTools } from '@modern-js/app-tools';
export const myPlugin = (): CliPlugin<AppTools> => ({
setup(api) {
return {
beforePrintInstructions: ({ instructions }) => {
// do something
return {
instructions: [...instructions, 'some new message'],
};
},
};
},
});
modifyFileSystemRoutes
- Function: Used for modifying the content of the generated front-end page routing files, which must be serializable.
- Execution stage: Executed before generating the front-end routing files, triggered during the
prepare
stage.
- Hook model:
AsyncWaterfall
- Type:
AsyncWaterfall<{ entrypoint: Entrypoint; routes: Route[]; }>
- Example:
import type { CliPlugin, AppTools } from '@modern-js/app-tools';
export const myPlugin = (): CliPlugin<AppTools> => ({
setup(api) {
return {
modifyFileSystemRoutes({ entrypoint, routes }) {
return {
entrypoint,
routes: [
...routes,
{
path: '/custom_page',
component: require.resolve('./Component'),
exact: true,
},
],
};
},
};
},
});
This adds a new page route for the front-end.
modifyServerRoutes
- Function: Used for modifying the content of the generated server routes.
- Execution stage: Executed before generating the server routing files, triggered during the
prepare
stage.
- Hook model:
AsyncWaterfall
- Type:
AsyncWaterfall<{ routes: ServerRoute[]; }>
- Example:
import type { CliPlugin, AppTools } from '@modern-js/app-tools';
export const myPlugin = (): CliPlugin<AppTools> => ({
setup(api) {
return {
modifyServerRoutes({ routes }) {
return {
routes: [
...routes,
{
urlPath: '/api/foo',
isApi: true,
entryPath: '',
isSPA: false,
isSSR: false,
},
],
};
},
};
},
});
htmlPartials
- Function: Used for customizing the generated HTML page template.
- Execution stage: Triggered during the
prepare
stage.
- Hook model:
AsyncWaterfall
- Type:
AsyncWaterfall<{ entrypoint: Entrypoint; partials: HtmlPartials; }>
- Example:
import type { CliPlugin, AppTools } from '@modern-js/app-tools';
export const myPlugin = (): CliPlugin<AppTools> => ({
setup(api) {
return {
async htmlPartials({ entrypoint, partials }) {
partials.head.push('<script>console.log("test")</script>');
return {
entrypoint,
partials,
};
},
};
},
});
This adds a new Script tag to the HTML template.
Runtime
NOTE
Currently, the Runtime plugin is not fully open, and the API does not guarantee stability. Use with caution.
The Runtime plugin is mainly used by developers to add behaviors before application rendering and to modify components that need to be rendered.
beforeRender
- Function: Add behaviors before application rendering
- Execution stage: Rendering (SSR/CSR).
- Hook model:
AsyncWorkflow
- Type:
AsyncWorkflow<RuntimeContext, void>
- Example:
import type { Plugin } from '@modern-js/runtime';
export const myPlugin = (): Plugin => ({
setup(api) {
return {
beforeRender(context) {
// do something
},
};
},
});
wrapRoot
- Function: Modify components that need to be rendered
- Execution stage: Rendering (SSR/CSR).
- Hook model:
Waterfall
- Type:
Waterfall<React.ComponentType<any>>
- Example:
import { createContext } from 'react';
import type { Plugin } from '@modern-js/runtime';
export const myPlugin = (): Plugin => ({
setup(api) {
const FooContext = createContext('');
return {
wrapRoot(App) {
const AppWrapper = (props: any) => {
return (
<FooContext.Provider value={'test'}>
<App {...props} />
</FooContext.Provider>
);
};
return AppWrapper;
},
};
},
});