logo
  • 指南
  • 配置
  • 插件
  • API
  • 示例
  • 社区
  • Modern.js 2.x 文档
  • 简体中文
    • 简体中文
    • English
    • 插件介绍
      插件系统
      CLI 插件
      插件 API
      生命周期
      插件迁移
      运行时插件
      插件 API
      生命周期
      插件迁移
      官方插件
      CLI 插件
      BFF 插件
      SSG 插件
      styled-components 插件
      📝 编辑此页面
      上一页插件系统下一页生命周期

      #插件 API

      本文档详细介绍了 Modern.js CLI 插件的 API。CLI 插件允许您在 Modern.js 项目的构建和开发过程中扩展和定制功能。

      Info

      CLI 插件需要通过 modern.config.ts 中的 plugins 字段配置。

      #插件基础结构

      一个典型的 CLI 插件结构如下:

      import type { CliPlugin, AppTools } from '@modern-js/app-tools';
      
      const myCliPlugin = (): CliPlugin<AppTools> => ({
        name: '@my-org/my-plugin', // 插件名称,确保唯一性
        setup: api => {
          // 在这里使用 API 注册钩子、添加命令等
          api.onBeforeBuild(() => {
            console.log('构建即将开始...');
          });
        },
      });
      
      export default myCliPlugin;

      setup 函数接收一个 api 对象,该对象提供了所有可用的 CLI 插件 API。

      #信息获取

      #api.getAppContext

      获取 Modern.js 应用的上下文信息。

      • 返回值: AppContext 对象,包含以下字段:
      字段名类型描述何时可用
      commandstring当前执行的命令 (e.g., dev, build, deploy)-
      portnumber开发服务器端口号onPrepare 之后
      configFilestring配置文件的绝对路径-
      isProdboolean是否为生产模式-
      appDirectorystring项目根目录的绝对路径-
      srcDirectorystring项目源码目录的绝对路径-
      distDirectorystring项目产物输出目录的绝对路径modifyResolvedConfig 之后
      sharedDirectorystring公共模块目录的绝对路径-
      nodeModulesDirectorystringnode_modules 目录的绝对路径-
      ipstring当前机器的 IPv4 地址-
      packageNamestring项目 package.json 中的 name 字段-
      pluginsobject[]当前已注册的插件列表-
      entrypointsobject[]页面入口信息-
      serverRoutesobject[]服务端路由信息-
      bundlerTypewebpack | rspack当前使用的打包工具类型 (webpack 或 rspack)onPrepare 之后
      metaNamestring框架内部名称-
      apiDirectorystringAPI 模块目录的绝对路径 (BFF 使用)-
      lambdaDirectorystringLambda 模块目录的绝对路径 (BFF 使用)-
      runtimeConfigFilestring运行时配置文件的名称-
      checkedEntriesstring[]指定的入口信息-
      apiOnlyboolean是否为 apiOnly 模式-
      • 示例:
      api.onPrepare(() => {
        const appContext = api.getAppContext();
        console.log(`当前项目运行在 ${appContext.isProd ? '生产' : '开发'} 模式`);
        console.log(`打包工具: ${appContext.bundlerType}`);
      });
      Info

      getAppContext 返回的上下文信息是只读的,无法直接进行修改。


      #api.getConfig

      获取用户在 modern.config.ts 文件中定义的配置。

      • 返回值: 用户定义的配置对象。
      • 示例:
      api.onPrepare(() => {
        const userConfig = api.getConfig();
        if (userConfig.output) {
          console.log('用户自定义了 output 配置');
        }
      });

      #api.getNormalizedConfig

      获取经过 Modern.js 内部处理和插件修改后的最终配置(归一化配置)。

      • 返回值: 归一化后的配置对象。
      • 何时可用: 必须在 modifyResolvedConfig 钩子之后使用。
      • 示例:
      api.modifyResolvedConfig(resolvedConfig => {
        // ... 修改配置 ...
        return resolvedConfig;
      });
      
      api.onBeforeBuild(() => {
        const finalConfig = api.getNormalizedConfig();
        console.log('最终构建配置:', finalConfig);
      });

      #api.isPluginExists

      检查指定的插件是否已注册。

      • 参数:
        • pluginName: string: 要检查的插件名称。
      • 返回值: boolean 值,表示插件是否存在。
      • 示例:
      if (api.isPluginExists('@modern-js/plugin-bff')) {
        console.log('BFF 插件已启用');
      }

      #api.getHooks

      获取所有已注册的钩子函数。

      • 返回值: 包含所有钩子函数的对象。
      • 示例:
      const hooks = api.getHooks();
      // 手动触发 onPrepare 钩子
      hooks.onPrepare.call();
      Warning

      在自定义插件中,只能手动调用对应插件注册的钩子,不能调用官方钩子,以免影响正常应用的执行顺序。


      #配置修改

      #api.config

      修改 Modern.js 的初始配置。

      • 类型: api.config(configFn: () => UserConfig | Promise<UserConfig>)
      • 参数:
        • configFn: 一个返回配置对象或 Promise 的函数。
      • 执行阶段: 解析完 modern.config.ts 中的配置之后。
      • 示例:
      api.config(() => {
        return {
          output: {
            disableTsChecker: true, // 关闭 TypeScript 类型检查
          },
        };
      });

      配置合并优先级(从高到低):

      1. 用户在 modern.config.* 文件中定义的配置。
      2. 插件通过 api.config() 注册的配置。
      3. Modern.js 默认配置。

      #api.modifyBundlerChain

      使用 chain API 修改 webpack 或者 Rspack 配置。

      • 类型: api.modifyBundlerChain(modifyFn: (chain: WebpackChain | RspackChain, utils: WebpackUtils | RspackUtils) => void | Promise<void>)
      • 参数:
        • modifyFn: 修改函数,接收 webpack-chain 或 RspackChain 实例和实用工具作为参数。
      • 执行阶段: 在生成最终的 Webpack 或 Rspack 配置时。
      • 对应 Rsbuild Hook: modifybundlerchain
      • 示例:
      api.modifyBundlerChain((chain, utils) => {
        if (utils.env === 'development') {
          chain.devtool('eval');
        }
        chain.plugin('bundle-analyze').use(BundleAnalyzerPlugin);
      });

      #api.modifyRsbuildConfig

      修改 Rsbuild 的配置。

      • 类型: api.modifyRsbuildConfig(modifyFn: (config: RsbuildConfig, utils: RsbuildUtils) => RsbuildConfig | Promise<RsbuildConfig> | void)
      • 参数:
        • modifyFn: 修改函数,接收 Rsbuild 配置对象和实用工具作为参数,可以返回修改后的配置对象、Promise 或不返回(直接修改原对象)。
      • 执行阶段: 在生成最终的 Rsbuild 配置时。
      • 对应 Rsbuild Hook: modifyRsbuildConfig
      • 示例:
      api.modifyRsbuildConfig((config, utils) => {
        // 添加一个自定义的 Rsbuild 插件
        config.plugins.push(myCustomRsbuildPlugin());
      });

      #api.modifyRspackConfig

      修改 Rspack 的配置。(当使用 Rspack 作为打包工具时)

      • 类型: api.modifyRspackConfig(modifyFn: (config: RspackConfig, utils: RspackUtils) => RspackConfig | Promise<RspackConfig> | void)
      • 参数:
        • modifyFn: 修改函数,接收 Rspack 配置对象和实用工具作为参数,可以返回修改后的配置对象、Promise 或不返回(直接修改原对象)。
      • 执行阶段: 在生成最终的 Rspack 配置时。
      • 对应 Rsbuild Hook: modifyRspackConfig
      • 示例:
      api.modifyRspackConfig((config, utils) => {
        config.builtins.minify = {
          enable: true,
          implementation: utils.rspack.SwcJsMinimizerRspackPlugin,
        }
      });

      #api.modifyWebpackChain

      使用 webpack-chain 修改 Webpack 配置。(当使用 Webpack 作为打包工具时)

      • 类型: api.modifyWebpackChain(modifyFn: (chain: WebpackChain, utils: WebpackUtils) => void | Promise<void>)
      • 参数:
        • modifyFn: 修改函数,接收 webpack-chain 实例和实用工具作为参数。
      • 执行阶段: 在生成最终的 Webpack 配置时。
      • 示例:
      api.modifyWebpackChain((chain, utils) => {
        // 添加一个自定义的 Webpack loader
        chain.module
          .rule('my-loader')
          .test(/\.my-ext$/)
          .use('my-loader')
          .loader(require.resolve('./my-loader'));
      });

      #api.modifyWebpackConfig

      直接修改 Webpack 配置对象。(当使用 Webpack 作为打包工具时)

      • 类型: api.modifyWebpackConfig(modifyFn: (config: WebpackConfig, utils: WebpackUtils) => WebpackConfig | Promise<WebpackConfig> | void)
      • 参数:
        • modifyFn: 修改函数,接收 Webpack 配置对象和实用工具作为参数,可以返回修改后的配置对象、Promise 或不返回(直接修改原对象)。
      • 执行阶段: 在生成最终的 Webpack 配置时。
      • 示例:
      api.modifyWebpackConfig((config, utils) => {
        // 禁用 source map
        config.devtool = false;
      });

      构建配置修改顺序

      • 使用 Rspack 构建:
        modifyRsbuildConfig
        modifyBundlerChain
        tools.bundlerChain
        modifyRspackConfig
        tools.rspack
      • 使用 Webpack 构建:
        modifyBundlerChain
        tools.bundlerChain
        modifyWebpackChain
        tools.webpackChain
        modifyWebpackConfig
        tools.webpack

      #api.modifyServerRoutes

      修改服务器路由配置。

      • 类型 api.modifyServerRoutes(transformFn: (routes: ServerRoute[]) => ServerRoute[])
      • 参数:
        • transformFn: 转换函数,接收当前服务器路由数组作为参数,返回修改后的数组。
      • 执行阶段: 在生成服务器路由文件之前 ( prepare 阶段)。
      • 示例:
      api.modifyServerRoutes(routes => {
        // 添加一个新的 API 路由
        routes.push({
          urlPath: '/api',
          isApi: true,
          entryPath: '',
          isSPA: false,
          isSSR: false,
        });
        return routes;
      });

      #api.modifyHtmlPartials

      修改 HTML 模板片段。

      • 类型 api.modifyHtmlPartials(modifyFn: (partials: HtmlPartials, entrypoint: Entrypoint) => void)
      • 参数:
        • modifyFn: 修改函数,接收 HTML 模板片段对象和当前入口点信息作为参数。
          • partials: 包含 top, head, body 三个部分,每个部分都有append, prepend, replace三个方法。
      • 执行阶段: 在生成 HTML 文件之前 ( prepare 阶段)。
      • 示例:
      api.modifyHtmlPartials(({ entrypoint, partials }) => {
        // 在所有页面的 <head> 标签中添加一个 meta 标签
        if (partials.head && partials.head.append) {
          partials.head.append(`<meta name="my-custom-meta" content="value">`);
        }
      });
      Warning

      当使用完全自定义模板时,该钩子函数将不会执行。


      #构建流程控制

      #api.onPrepare

      在 Modern.js 准备阶段添加额外的逻辑。

      • 类型 api.onPrepare(prepareFn: () => void | Promise<void>)
      • 参数:
        • prepareFn: 准备函数,无参数,可异步。
      • 执行阶段: 在 Modern.js 完成配置校验之后。
      • 示例:
      api.onPrepare(async () => {
        // 执行一些初始化操作,例如检查环境、下载依赖等
        await prepareMyCustomEnvironment();
      });

      #api.addCommand

      添加自定义的 CLI 命令。

      • 类型 api.addCommand(commandFn: ({ program: Command }) => void)
      • 参数:
        • commandFn: 接收 commander 的 program 对象作为参数,用于定义新的命令。
      • 执行阶段: prepare 钩子运行完成后。
      • 示例:
      api.addCommand(({ program }) => {
        program
          .command('my-command')
          .description('我的自定义命令')
          .action(async () => {
            // 执行命令逻辑
            console.log('执行自定义命令...');
          });
      });

      #api.addWatchFiles

      添加额外的文件监听列表(用于开发模式)。

      • 类型 api.addWatchFiles(watchFn: () => string[] | { files: string[]; isPrivate: boolean; })
      • 参数:
        • watchFn: 返回一个包含文件路径的数组,或一个包含 files 和 isPrivate 属性的对象。
          • files: 要监听的文件路径数组。
          • isPrivate: 是否为框架内部文件(影响文件变更时的行为)。
      • 执行阶段: addCommand 钩子运行完成之后。
      • 示例:
      api.addWatchFiles(() => {
        // 监听项目根目录下的 .env 文件
        return [path.resolve(api.getAppContext().appDirectory, '.env')];
      });

      #api.onFileChanged

      在监听文件发生变化时添加额外的逻辑(用于开发模式)。

      • 类型 api.onFileChanged(changeFn: (params: { filename: string; eventType: 'add' | 'change' | 'unlink'; isPrivate: boolean; }) => void)
      • 参数:
        • changeFn: 文件变化处理函数,接收以下参数:
          • filename: 发生变化的文件路径。
          • eventType: 变化类型 (add, change, unlink)。
          • isPrivate: 是否为框架内部文件。
      • 执行阶段: 监听文件变化之后。
      • 示例:
      api.onFileChanged(({ filename, eventType }) => {
        if (eventType === 'change' && filename.endsWith('.ts')) {
          console.log('TypeScript 文件发生变化,可能需要重新编译...');
        }
      });

      #api.onBeforeBuild

      在构建开始之前添加额外的逻辑。

      • 类型 api.onBeforeBuild(buildFn: () => void | Promise<void>)
      • 参数:
        • buildFn: 构建前执行的函数,无参数,可异步。
      • 执行阶段: 在执行构建流程之前。
      • 对应 Rsbuild Hook: onBeforeBuild
      • 示例:
      api.onBeforeBuild(() => {
        // 构建前做一些环境检查
      });

      #api.onAfterBuild

      在构建完成后添加额外的逻辑。

      • 类型: api.onAfterBuild(buildFn: () => void | Promise<void>)
      • 参数:
        • buildFn: 构建后执行的函数,无参数,可异步。
      • 执行阶段: 在执行构建流程之后。
      • 对应 Rsbuild Hook: onAfterBuild
      • 示例:
      api.onAfterBuild(() => {
        // 构建后上传sourceMap
      });

      #api.onDevCompileDone

      在开发服务器编译完成后添加额外的逻辑。

      • 类型: api.onDevCompileDone(compileFn: () => void | Promise<void>)
      • 参数:
        • compileFn: 编译完成后执行的函数。
      • 执行阶段: 开发服务器启动,且首次编译完成后。
      • 对应 Rsbuild Hook: onDevCompileDone
      • 示例:
      api.onDevCompileDone(() => {
        // 首次编译完成,打开浏览器
      });

      #api.onBeforeCreateCompiler

      在创建编译器实例之前添加额外的逻辑。

      • 类型: api.onBeforeCreateCompiler(createFn: () => void | Promise<void>)
      • 参数:
        • createFn: 创建前执行的函数,无参数,可异步。
      • 执行阶段: 在创建 Webpack 或 Rspack 编译器实例之前。
      • 对应 Rsbuild Hook: onBeforeCreateCompiler
      • 示例:
      api.onBeforeCreateCompiler(() => {
        // 可以获取 compiler 相关配置
      });

      #api.onAfterCreateCompiler

      在创建编译器实例之后添加额外的逻辑。

      • 类型: api.onAfterCreateCompiler(createFn: () => void | Promise<void>)
      • 参数:
        • createFn: 创建后执行的函数,无参数,可异步。
      • 执行阶段: 在创建 Webpack 或 Rspack 编译器实例之后。
      • 对应 Rsbuild Hook: onAfterCreateCompiler
      • 示例:
      api.onAfterCreateCompiler(() => {
        // 可以获取 compiler 实例
      });

      #api.onBeforeDev

      在开发服务器启动前添加额外的逻辑。

      • 类型: api.onBeforeDev(devFn: () => void | Promise<void>)
      • 参数:
        • devFn: 开发服务器启动前执行的函数,无参数,可异步。
      • 执行阶段: 在执行 dev 命令启动开发服务器之前。
      • 示例:
      api.onBeforeDev(async () => {
        // 检查端口是否被占用
        await checkPortAvailability(3000);
      });

      #api.onAfterDev

      在开发服务器启动后添加额外的逻辑。

      • 类型: api.onAfterDev(devFn: () => void | Promise<void>)
      • 参数:
        • devFn: 开发服务器启动后执行的函数。
      • 执行阶段: 开发服务器成功启动后。
      • 对应 Rsbuild Hook: onAfterStartDevServer
      • 示例:
      api.onAfterDev(() => {
        // 上报 dev 相关信息
      });

      #api.onBeforeExit

      在进程退出前添加额外的逻辑。

      • 类型: api.onBeforeExit(exitFn: () => void | Promise<void>)
      • 参数:
        • exitFn: 进程退出前执行的函数,无参数,可异步。
      • 执行阶段: 在 Modern.js 进程即将退出时(例如,用户按下 Ctrl+C)。
      • 示例:
      api.onBeforeExit(async () => {
        // 执行一些清理操作,例如关闭数据库连接、删除临时文件等
        await cleanupMyResources();
      });

      #api.onBeforePrintInstructions

      在打印成功信息前添加额外的逻辑。

      • 类型: api.onBeforePrintInstructions(printFn: ({instructions: string}) => {instructions: string} | Promise<{instructions: string}>)
      • 参数:
        • printFn: 修改打印信息的函数, 返回修改后的信息。
      • 执行阶段: 打印成功信息前。
      • 示例:
      api.onBeforePrintInstructions(({ instructions }) => {
        // do something
        return { instructions };
      });

      #其他说明

      • 可以参考 CLI 插件生命周期 了解插件钩子的执行顺序。
      • 可以参考 CLI 插件迁移指南 了解如何从旧版插件迁移到新版。