---
sidebar_position: 8
---

# Hook 列表

Modern.js 工程体系中包含三类插件：CLI、Runtime、Server，每一类插件可以使用不同的 Hooks。

在本章节中，罗列了所有可用的 Hooks，你可以根据自己的需求来使用对应的 Hook。

## CLI Common Hooks

以下是通用的 CLI Hooks，可以在 Modern.js Framework 以及 Modern.js Module 中使用。

### `beforeConfig`

- 功能：运行收集配置前的任务
- 执行阶段：收集配置前
- Hook 模型：`AsyncWorkflow`
- 类型：`AsyncWorkflow<void, void>`
- 使用示例：

```ts
import type { CliPlugin } from '@modern-js/core';

export const myPlugin = (): CliPlugin => ({
  setup(api) {
    return {
      beforeConfig: () => {
        // do something
      },
    };
  },
});
```

### `config`

- 功能：收集配置
- 执行阶段：解析完 `modern.config.ts` 中的配置之后
- Hook 模型：`ParallelWorkflow`
- 类型：`ParallelWorkflow<void, unknown>`
- 使用示例：

```ts
import type { CliPlugin } from '@modern-js/core';

export const myPlugin = (): CliPlugin => ({
  setup(api) {
    return {
      config: () => {
        return {
          /** some config */
        };
      },
    };
  },
});
```

如果你需要设置 Modern.js Framework 的配置，请使用 `@modern-js/app-tools` 导出的 `CliPlugin<AppTools>` 类型：

```ts
import type { CliPlugin, AppTools } from '@modern-js/app-tools';

export const myPlugin = (): CliPlugin => ({
  setup(api) {
    return {
      config: () => {
        return {
          output: {
            polyfill: 'usage',
          },
        };
      },
    };
  },
});
```

插件在 `config` hook 中返回的配置信息，会被 Modern.js 统一收集与合并，最终生成一份 `NormalizedConfig`。在进行配置合并时，优先级由高到低依次为：

1. 用户在 `modern.config.*` 文件里定义的配置
2. 插件通过 `config` hook 定义的配置
3. Modern.js 默认配置。

### `prepare`

- 功能：运行主流程的前置准备流程
- 执行阶段：校验完配置之后
- Hook 模型：`AsyncWorkflow`
- 类型：`AsyncWorkflow<void, void>`
- 使用示例：

```ts
import type { CliPlugin } from '@modern-js/core';

export const myPlugin = (): CliPlugin => ({
  setup(api) {
    return {
      prepare: () => {
        // do something
      },
    };
  },
});
```

### `afterPrepare`

- 功能：运行前置准备流程的之后的任务
- 执行阶段：前置准备流程之后
- Hook 模型：`AsyncWorkflow`
- 类型：`AsyncWorkflow<void, void>`
- 使用示例：

```ts
import type { CliPlugin } from '@modern-js/core';

export const myPlugin = (): CliPlugin => ({
  setup(api) {
    return {
      afterPrepare: () => {
        // do something
      },
    };
  },
});
```

### `commands`

- 功能：为 commander 添加新的 CLI 命令
- 执行阶段：`prepare` Hook 运行完成之后
- Hook 模型：`AsyncWorkflow`
- 类型：`AsyncWorkflow<{ program: Command; }, void>`
- 使用示例：

```ts
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');
        });
      },
    };
  },
});
```

将上面这个插件添加到 `modern.config.ts` 中：

```ts title="modern.config.ts"
import myPlugin from './config/plugin/myPlugin';

export const myPlugin = defineConfig({
  plugins: [myPlugin()],
});
```

运行 `modern foo` 就可以看到控制台输出：

```sh
$ modern foo
foo
```

### `beforeExit`

- 功能：在退出进程前，重置一些文件状态
- 执行阶段：进程退出之前
- Hook 模型：`Workflow`
- 类型：`Workflow<void, void>`
- 使用示例：

```ts
import type { CliPlugin } from '@modern-js/core';

export const myPlugin = (): CliPlugin => ({
  setup(api) {
    return {
      beforeExit: () => {
        // do something
      },
    };
  },
});
```

:::tip
由于 Node.js 中退出进程时的回调函数是同步的，所以 `beforeExit` Hook 的类型是 `Workflow`，不能执行异步操作。
:::

## CLI Framework Hooks

以下是框架的 CLI Hooks，只能在 Modern.js Framework 中使用，不能在 Modern.js Module 中使用。

你需要从 `@modern-js/app-tools` 中导入 `CliPlugin` 和 `AppTools` 类型，以获得准确的 Hooks 类型提示。

### `beforeDev`

- 功能：运行 dev 主流程的之前的任务
- 执行阶段：`dev` 命令运行时，项目开始启动前执行
- Hook 模型：`AsyncWorkflow`
- 类型：`AsyncWorkflow<void, unknown>`
- 使用示例：

```ts
import type { CliPlugin, AppTools } from '@modern-js/app-tools';

export const myPlugin = (): CliPlugin<AppTools> => ({
  setup(api) {
    return {
      beforeDev: () => {
        // do something
      },
    };
  },
});
```

### `afterDev`

- 功能：运行 dev 主流程的之后的任务
- 执行阶段：运行 `dev` 命令时，每一次编译完成后执行
- Hook 模型：`AsyncWorkflow`
- 类型：`AsyncWorkflow<{ isFirstCompile: boolean }, unknown>`
- 使用示例：

```ts
import type { CliPlugin, AppTools } from '@modern-js/app-tools';

export const myPlugin = (): CliPlugin<AppTools> => ({
  setup(api) {
    return {
      afterDev: () => {
        // do something
      },
    };
  },
});
```

`afterDev` 会在每一次编译完成后执行，你可以通过 `isFirstCompile` 参数来判断是否为首次编译：

```ts
import type { CliPlugin, AppTools } from '@modern-js/app-tools';

export const myPlugin = (): CliPlugin<AppTools> => ({
  setup(api) {
    return {
      afterDev: ({ isFirstCompile }) => {
        if (isFirstCompile) {
          // do something
        }
      },
    };
  },
});
```

### `beforeBuild`

- 功能：在执行生产环境构建前触发的回调函数，你可以通过 `bundlerConfigs` 参数获取到底层打包工具的最终配置数组。
  - 如果当前打包工具为 webpack，则获取到的是 webpack 配置数组。
  - 如果当前打包工具为 Rspack，则获取到的是 Rspack 配置数组。
  - 配置数组中可能包含一份或多份配置，这取决于你是否开启了 SSR 等功能。
- 执行阶段：运行 `build` 命令后，在开始构建前执行
- Hook 模型：`AsyncWorkflow`
- 类型：

```ts
type BeforeBuild = AsyncWorkflow<{
  bundlerConfigs: WebpackConfig[] | RspackConfig[];
}>;
```

- 使用示例：

```ts
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`

- 功能：在执行生产环境构建后触发的回调函数，你可以通过 `stats` 参数获取到构建结果信息。
  - 如果当前打包工具为 webpack，则获取到的是 webpack Stats。
  - 如果当前打包工具为 Rspack，则获取到的是 Rspack Stats。
- 执行阶段：运行 `build` 命令运行后，在项目构建完成之后执行
- Hook 模型：`AsyncWorkflow`
- 类型：

```ts
type AfterBuild = AsyncWorkflow<{
  stats?: Stats | MultiStats;
}>;
```

- 使用示例：

```ts
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`

- 功能：在创建底层 Compiler 实例前触发的回调函数，并且你可以通过 `bundlerConfigs` 参数获取到底层打包工具的最终配置数组：
  - 如果当前打包工具为 webpack，则获取到的是 webpack 配置数组。
  - 如果当前打包工具为 Rspack，则获取到的是 Rspack 配置数组。
  - 配置数组中可能包含一份或多份配置，这取决于你是否开启了 SSR 等功能。
- 执行阶段：在执行 dev 或 build 命令时，创建 Compiler 实例之前执行
- Hook 模型：`AsyncWorkflow`
- 类型：

```ts
type BeforeCreateCompiler = AsyncWorkflow<
  { bundlerConfigs: Configuration[] },
  unknown
>;
```

- 使用示例：

```ts
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`

- 功能：在创建 Compiler 实例后、执行构建前触发的回调函数，并且你可以通过 `compiler` 参数获取到 Compiler 实例对象：
  - 如果当前打包工具为 webpack，则获取到的是 webpack Compiler 对象。
  - 如果当前打包工具为 Rspack，则获取到的是 Rspack Compiler 对象。
- 执行阶段：创建 Compiler 对象之后执行
- Hook 模型：`AsyncWorkflow`
- 类型：

```ts
type AfterCreateCompiler = AsyncWorkflow<
  { compiler: Compiler | MultiCompiler | undefined },
  unknown
>;
```

- 使用示例：

```ts
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`

- 功能：在中间件函数中可以拿到即将打印的日志信息，并对其进行修改
- 执行阶段：打印日志信息之前执行
- Hook 模型：`AsyncWaterfall`
- 类型：`AsyncWaterfall<{ instructions: string }>`
- 使用示例：

```ts
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'],
        };
      },
    };
  },
});
```

{/*
### `checkEntryPoint`

- 功能：增加入口类型
- 执行阶段：生成入口文件之前，[`prepare`](#prepare) 阶段触发
- Hook 模型：`AsyncWaterfall`
- 类型：`AsyncWaterfall<{ path: string; entry: false | string; }>`
- 使用示例：

```ts
import type { CliPlugin, AppTools } from '@modern-js/app-tools';

export const myPlugin = (): CliPlugin<AppTools> => ({
  setup(api) {
    return {
      checkEntryPoint({ path, entry }) {
        return { path, entry: entry || isNewEntry(path) };
      },
    };
  },
});
```

### `modifyEntrypoints`

- 功能：用于修改入口信息，针对于插件新增的入口，可以通过该钩子修改对应的 entrypoint 信息
- 执行阶段：生成入口文件之前，[`prepare`](#prepare) 阶段触发
- Hook 模型：`AsyncWaterfall`
- 类型：`AsyncWaterfall<{ entrypoints: Entrypoint[]; }>`
- 使用示例：

```ts
import type { CliPlugin, AppTools } from '@modern-js/app-tools';

export const myPlugin = (): CliPlugin<AppTools> => ({
  setup(api) {
    return {
      async modifyEntrypoints({ entrypoints }) {
        const newEntryPoints = entrypoints.map(entrypoint => {
            ...
        });
        return { entrypoints: newEntryPoints };
      },
    };
  },
});
```

### `generateEntryCode`

- 功能：用于修改生成的入口文件
- 执行阶段：生成入口文件之后，创建 builder 之前
- Hook 模型：`AsyncWaterfall`
- 类型：`AsyncWorkflow<{ entrypoints: Entrypoint[] }, void>`
- 使用示例：

```ts
import type { CliPlugin, AppTools } from '@modern-js/app-tools';

export const myPlugin = (): CliPlugin<AppTools> => ({
  setup(api) {
    return {
      async generateEntryCode({ entrypoints }) {
        await Promise.all(
            entrypoints.map(async entrypoint => {
               ...
            })
          );
      },
    };
  },
});
```
*/}
### `modifyFileSystemRoutes`

- 功能：用于修改生成前端页面路由文件中的内容，内容都是需要可序列化的
- 执行阶段：生成前端路由文件之前，[`prepare`](#prepare) 阶段触发
- Hook 模型：`AsyncWaterfall`
- 类型：`AsyncWaterfall<{ entrypoint: Entrypoint; routes: Route[]; }>`
- 使用示例：

```tsx
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,
            },
          ],
        };
      },
    };
  },
});
```

这样就为前端新增了一个页面路由。

### `modifyServerRoutes`

- 功能：用于修改生成服务器路由中的内容
- 执行阶段：生成 Server 路由文件之前，[`prepare`](#prepare) 阶段触发
- Hook 模型：`AsyncWaterfall`
- 类型：`AsyncWaterfall<{ routes: ServerRoute[]; }>`
- 使用示例：

```ts
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`

- 功能：用于定制生成的 HTML 页面模版
- 执行阶段：[`prepare`](#prepare) 阶段触发
- Hook 模型：`AsyncWaterfall`
- 类型：`AsyncWaterfall<{ entrypoint: Entrypoint; partials: HtmlPartials; }>`
- 使用示例：

```ts
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,
        };
      },
    };
  },
});
```

这样就为 HTML 模版中新增了一个 Script 标签。

{/* ## Server

:::note
目前 Server 插件还未完全开放，API 不保证稳定，使用需谨慎。

:::

Modern.js 工程中的 Server 部分也支持了插件。其中的 Hook 将会提供一些特定阶段调用和特殊功能的 Hook。

### `create`

- 功能：在中间件函数中会拿到 Server 初始化用到的指标测量工具配置 `measureOptions` 和日志工具配置 `loggerOptions`，并返回自定义的指标测量工具 `measure` 和日志工具配置 `logger`
- 执行阶段：Server 初始化
- Hook 模型：`AsyncPipeline`
- 类型：`AsyncPipeline<ServerInitInput, InitExtension>`
- 使用示例：

```ts
import type { ServerPlugin } from '@modern-js/server-core';

export const myPlugin = (): ServerPlugin => ({
  setup(api) {
    return {
      create: ({ measureOptions, loggerOptions }) => {
        // do something
      },
    };
  },
});
```

### `prepareWebServer`

- 功能：设置 Web 路由的处理函数，在中间件函数中可以拿到 Web Server 的前置中间件
- 执行阶段：在请求到达的时候
- Hook 模型：`AsyncPipeline`
- 类型：`AsyncPipeline<WebServerStartInput, Adapter>`
- 使用示例：

```ts
import type { ServerPlugin } from '@modern-js/server-core';

export const myPlugin = (): ServerPlugin => ({
  setup(api) {
    return {
      prepareWebServer: ({ middleware }) => {
        // do something

        return (req, res) => {
          // do response
        };
      },
    };
  },
});
```

### `prepareApiServer`

- 功能：设置 API 路由的处理函数，在中间件函数中可以拿到 API Server 的前置中间件
- 执行阶段：请求到达并且 match bff basename 之后执行
- Hook 模型：`AsyncPipeline`
- 类型：`AsyncPipeline<APIServerStartInput, Adapter>`
- 使用示例：

```ts
import type { ServerPlugin } from '@modern-js/server-core';

export const myPlugin = (): ServerPlugin => ({
  setup(api) {
    return {
      prepareApiServer: ({ middleware }) => {
        // do something

        return (req, res) => {
          // do response
        };
      },
    };
  },
});
```*/}

## Runtime

:::note
目前 Runtime 插件还未完全开放，API 不保证稳定，使用需谨慎。

:::

Runtime 插件主要用于开发者自定义应用渲染前行为和修改需要渲染的组件。

### `beforeRender`

- 功能：在应用渲染之前增加行为
- 执行阶段：渲染（SSR/CSR）
- Hook 模型：`AsyncWorkflow`
- 类型：`AsyncWorkflow<RuntimeContext， void>`
- 使用示例：

```ts
import type { Plugin } from '@modern-js/runtime';

export const myPlugin = (): Plugin => ({
  setup(api) {
    return {
      beforeRender(context) {
        // do something
      },
    };
  },
});
```

### `wrapRoot`

- 功能：修改需要渲染的组件
- 执行阶段：渲染（SSR/CSR）
- Hook 模型：`Waterfall`
- 类型：`Waterfall<React.ComponentType<any>>`
- 使用示例：

```ts
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 store={'test'}>
              <App {...props} />
            </FooContext.Provider>
          );
        };
        return AppWrapper
      },
    };
  },
});
```

