插件 API

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

插件基础结构

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

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

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

export default myCliPlugin;

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

信息获取

api.getAppContext

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

  • 返回值: AppContext 对象,包含以下字段:
字段名 类型 描述 何时可用
command string 当前执行的命令 (e.g., devbuilddeploy) -
port number 开发服务器端口号 onPrepare 之后
configFile string 配置文件的绝对路径 -
isProd boolean 是否为生产模式 -
appDirectory string 项目根目录的绝对路径 -
srcDirectory string 项目源码目录的绝对路径 -
distDirectory string 项目产物输出目录的绝对路径 modifyResolvedConfig 之后
sharedDirectory string 公共模块目录的绝对路径 -
nodeModulesDirectory string node_modules 目录的绝对路径 -
ip string 当前机器的 IPv4 地址 -
packageName string 项目 package.json 中的 name 字段 -
plugins object[] 当前已注册的插件列表 -
entrypoints object[] 页面入口信息 -
serverRoutes object[] 服务端路由信息 -
bundlerType webpack | rspack 当前使用的打包工具类型 (webpackrspack) onPrepare 之后
metaName string 框架内部名称 -
apiDirectory string API 模块目录的绝对路径 (BFF 使用) -
lambdaDirectory string Lambda 模块目录的绝对路径 (BFF 使用) -
runtimeConfigFile string 运行时配置文件的名称 -
serverConfigFile string 服务器配置文件的名称 -
checkedEntries string[] 指定的入口信息 -
apiOnly boolean 是否为 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-tailwind')) {
  console.log('Tailwind CSS 插件已启用');
}

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-chainRspackChain 实例和实用工具作为参数。
  • 执行阶段: 在生成最终的 Webpack 或 Rspack 配置时。
  • 对应 Rsbuild Hookmodifybundlerchain
  • 示例:
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 HookmodifyRsbuildConfig
  • 示例:
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 HookmodifyRspackConfig
  • 示例:
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.concat({
    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: 包含 topheadbody 三个部分,每个部分都有appendprependreplace三个方法。
  • 执行阶段: 在生成 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: 接收 commanderprogram 对象作为参数,用于定义新的命令。
  • 执行阶段: prepare 钩子运行完成后。
  • 示例:
api.addCommand(({ program }) => {
  program
    .command('my-command')
    .description('我的自定义命令')
    .action(async () => {
      // 执行命令逻辑
      console.log('执行自定义命令...');
    });
});

api.addWatchFiles

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

  • 类型 api.addWatchFiles(watchFn: () => string[] | { files: string[]; isPrivate: boolean; })
  • 参数:
    • watchFn: 返回一个包含文件路径的数组,或一个包含 filesisPrivate 属性的对象。
      • 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: 变化类型 (addchangeunlink)。
      • isPrivate: 是否为框架内部文件。
  • 执行阶段: 监听文件变化之后。
  • 示例:
api.onFileChanged(({ filename, eventType }) => {
  if (eventType === 'change' && filename.endsWith('.ts')) {
    console.log('TypeScript 文件发生变化,可能需要重新编译...');
  }
});

api.onBeforeBuild

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

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

api.onAfterBuild

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

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

api.onDevCompileDone

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

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

api.onBeforeCreateCompiler

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

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

api.onAfterCreateCompiler

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

  • 类型: api.onAfterCreateCompiler(createFn: () => void | Promise<void>)
  • 参数:
    • createFn: 创建后执行的函数,无参数,可异步。
  • 执行阶段: 在创建 Webpack 或 Rspack 编译器实例之后。
  • 对应 Rsbuild HookonAfterCreateCompiler
  • 示例:
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 HookonAfterStartDevServer
  • 示例:
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 }
});

其他说明