Plugin Hooks

本章介绍关于 Modern.js Module 支持的生命周期钩子。

目前主要包含以下几类生命周期钩子:

  • 配置钩子:用于处理用户配置。
  • 构建钩子:仅在执行 build 命令构建源码产物时触发。
  • buildPlatform 钩子:仅在执行 build --platform 命令生成其他构建产物时触发。
  • 调试钩子:运行 dev 命令时会触发的钩子。

这里详细解释了 Hook 模型

配置钩子

resolveModuleUserConfig

用于修改用户配置。

类型:AsyncWaterfall

export const myPlugin = (): CliPlugin<ModuleTools> => ({
  name: 'my-plugin',

  setup() {
    return {
      resolveModuleUserConfig(config: ModuleUserConfig): ModuleUserConfig {},
    };
  },
});

构建钩子

在执行 build 命令的时候,会按照顺序触发以下 Hooks:

  • beforeBuild
  • beforeBuildTask
  • afterBuildTask
  • afterBuild

beforeBuild

执行整体构建流程之前触发。

类型:ParallelWorkflow

export const myPlugin = (): CliPlugin<ModuleTools> => ({
  name: 'my-plugin',

  setup() {
    return {
      beforeBuild(options: Options): void {},
    };
  },
});

参数类型:

type Options = {
  options: { config: BaseBuildConfig[]; cliOptions: BuildCommandOptions };
};

export interface BuildCommandOptions {
  config: string;
  clear?: boolean;
  dts?: boolean;
  platform?: boolean | string[];
  tsconfig: string;
  watch?: boolean;
}

BuildConfig 类型参考 API 配置

beforeBuildTask

根据构建配置,Modern.js Module 会将整体构建分成多个子构建任务。该 Hook 将会在每一个构建子任务之前触发。

类型:AsyncWaterfall

export const myPlugin = (): CliPlugin<ModuleTools> => ({
  name: 'my-plugin',

  setup() {
    return {
      beforeBuildTask(config: BaseBuildConfig): BaseBuildConfig {
        return config;
      },
    };
  },
});

afterBuildTask

类型:ParallelWorkflow,每一个构建子任务结束之后触发。

export const myPlugin = (): CliPlugin<ModuleTools> => ({
  name: 'my-plugin',

  setup() {
    return {
      afterBuildTask(options: BuildTaskResult): void {
        // ...
      },
    };
  },
});

参数和返回值类型:

export interface BuildTaskResult {
  status: 'success' | 'fail';
  message?: string;
  config: BaseBuildConfig;
}

afterBuild

类型:ParallelWorkflow,整体构建流程结束之后触发。

export const myPlugin = (): CliPlugin<ModuleTools> => ({
  name: 'my-plugin',

  setup() {
    return {
      afterBuild(options: BuildResult): void {
        // ...
      },
    };
  },
});

参数和返回值类型:

export interface BuildResult {
  status: 'success' | 'fail';
  message?: string;
  config: BuildConfig;
  commandOptions: BuildCommandOptions;
  totalDuration: number;
}

buildPlatform 钩子

module-tools 还提供了 build --platform 命令来执行特定的构建任务。

例如在安装了 Doc 插件后,就可以执行 build --platform 或者 build --platform doc 来执行 doc 的构建任务。因为 doc 插件基于 buildPlatform Hooks 实现了该功能。

在执行 build --platform 后会按照以下顺序触发 Hooks:

  • registerBuildPlatform
  • beforeBuildPlatform
  • buildPlatform
  • afterBuildPlatform

registerBuildPlatform

获取在执行 build --platform 命令时候需要运行的任务信息。

export const myPlugin = (): CliPlugin<ModuleTools> => ({
  name: 'my-plugin',

  setup() {
    return {
      registerBuildPlatform(): RegisterBuildPlatformResult {
        // ...
        return {
          platform: 'doc',
          build() {
            // logic
          },
        };
      },
    };
  },
});

入参和返回的参数类型:

export interface RegisterBuildPlatformResult {
  platform: string | string[];
  build: (
    currentPlatform: string, // 当前运行的 platform 构建任务
    context: { isTsProject: boolean },
  ) => void | Promise<void>;
}

beforeBuildPlatform

当执行 build --platform 命令的时候,会触发所有已注册的构建任务。beforeBuildPlatform 会在执行整体的构建任务之前触发。

export const myPlugin = (): CliPlugin<ModuleTools> => ({
  name: 'my-plugin',

  setup() {
    return {
      beforeBuildPlatform(platforms: RegisterBuildPlatformResult[]): void {
        console.info(`have ${platforms.length} platform tasks`);
      },
    };
  },
});

入参和返回的参数类型:

export interface RegisterBuildPlatformResult {
  platform: string | string[];
  build: (
    currentPlatform: string, // 当前运行的 platform 构建任务
    context: { isTsProject: boolean },
  ) => void | Promise<void>;
}

buildPlatform

当执行 build --platform 命令的时候,会触发所有已注册的构建任务。buildPlatform 会在每个构建任务执行之前触发。

export const myPlugin = (): CliPlugin<ModuleTools> => ({
  name: 'my-plugin',

  setup() {
    return {
      buildPlatform({ platform }: Options): void {
        console.info(`current task is ${platform}`);
      },
    };
  },
});

入参和返回的参数类型:

export interface Options {
  platform: string;
}

afterBuildPlatform

当执行 build --platform 命令的时候,会触发所有已注册的构建任务。afterBuildPlatform 会在整体 platform 构建任务结束后触发。

export const myPlugin = (): CliPlugin<ModuleTools> => ({
  name: 'my-plugin',

  setup() {
    return {
      afterBuildPlatform(result: BuildPlatformResult): void {
        if (result.status === 'success') {
          console.info(`all platform build task success`);
        } else {
          console.error(result.message);
        }
      },
    };
  },
});

入参和返回的参数类型:

export interface BuildPlatformResult {
  status: 'success' | 'fail';
  message: string | Error | null;
}

调试钩子

在执行 dev 命令的时候,会按照顺序触发以下 Hooks:

  • registerDev: 在获取调试功能信息的时候触发。
  • beforeDev: 开始执行调试整体流程之前触发。
  • beforeDevMenu: 出现调试列表/菜单之前触发。
  • afterDevMenu: 选择调试列表/菜单选项后触发。
  • beforeDevTask: 执行调试任务之前触发。
  • afterDev: 执行 dev 整体流程最后触发。

registerDev

注册调试工具相关的数据。主要包含:

  • 调试工具的名称
  • 显示在菜单列表中的项目名称以及对应的值。
  • dev 子命令的定义。
  • 是否在运行调试任务之前执行源码构建
  • 执行调试任务的函数。
export const myPlugin = (): CliPlugin<ModuleTools> => ({
  name: 'my-plugin',

  setup() {
    return {
      registerDev() {
        return {
          // 调试工具名称
          name: 'storybook',
          // 菜单显示内容
          menuItem: {
            name: 'Storybook',
            value: 'storybook',
          },
          // 定义的 dev 子命令
          subCommands: ['storybook', 'story'],
          async action() {
            // dev logic
          },
        };
      },
    };
  },
});

入参和返回的参数类型:

export interface DevToolData {
  name: string;
  subCommands?: string[];
  menuItem?: {
    name: string;
    value: string;
  };
  action: (
    options: { port?: string },
    context: { isTsProject?: boolean },
  ) => void | Promise<void>;
}

beforeDev

在收集完所有调试工具元数据后,执行 dev 任务之前触发。

export const myPlugin = (): CliPlugin<ModuleTools> => ({
  name: 'my-plugin',

  setup() {
    return {
      beforeDev(metas: DevToolData[]) {
        console.info(`have ${metas.length} dev tools`);
      },
    };
  },
});

入参和返回的参数类型:

export interface DevToolData {
  name: string;
  subCommands?: string[];
  menuItem?: {
    name: string;
    value: string;
  };
  action: (
    options: { port?: string },
    context: { isTsProject?: boolean },
  ) => void | Promise<void>;
}

(before|after)DevMenu

beforeDevMenu 在出现调试列表/菜单之前触发。接收 inquirer question 作为参数。默认值为:

const question = [
  {
    name: 'choiceDevTool',
    message: '选择调试工具',
    type: 'list',
    // 注册的调试信息
    choices,
  },
];

afterDevMenu 选择调试列表/菜单选项后触发。

export const myPlugin = (): CliPlugin<ModuleTools> => ({
  name: 'my-plugin',

  setup() {
    return {
      beforeDevMenu(questions) {
        questions[0].message += '!';
        return questions; // required
      },
      afterDevMenu(options: Options) {
        console.info(`choise ${options.result.choiceDevTool} dev tools`);
      },
    };
  },
});

入参和返回的参数类型:

export type { QuestionCollection } from 'inquirer';

export interface Options {
  result: PromptResult;
  devTools: DevToolData[];
}

export type PromptResult = { choiceDevTool: string };
export interface DevToolData {
  name: string;
  subCommands?: string[];
  menuItem?: {
    name: string;
    value: string;
  };
  action: (
    options: { port?: string },
    context: { isTsProject?: boolean },
  ) => void | Promise<void>;
}

beforeDevTask

执行调试任务之前触发。

export const myPlugin = (): CliPlugin<ModuleTools> => ({
  name: 'my-plugin',

  setup() {
    return {
      beforeDevTask(currentDevData: DevToolData) {
        console.info(`${currentDevData.name} running`);
      },
    };
  },
});

入参和返回的参数类型:

export interface DevToolData {
  name: string;
  subCommands?: string[];
  menuItem?: {
    name: string;
    value: string;
  };
  action: (
    options: { port?: string },
    context: { isTsProject?: boolean },
  ) => void | Promise<void>;
}

afterDev

在中断调试任务进程时触发。

export const myPlugin = (): CliPlugin<ModuleTools> => ({
  name: 'my-plugin',

  setup() {
    return {
      afterDev() {
        console.info(`exit!`);
      },
    };
  },
});