Introduction to Plugin Mechanisms and Their Comparison 
Differences between Vite Plugin Mechanism and Rollup Plugin Mechanism 
The conventions for Rollup plugins are as follows:
- Plugins should have a clear and understandable name with the rollup-plugin-prefix.
- Include the rollup-pluginkeyword inpackage.json.
- Plugins should be tested. We recommend using mochaoravawhich provide out-of-the-box promise capabilities.
- Use asynchronous methods whenever possible. For example, use fs.readFileinstead offs.readFileSync.
- Plugin documentation should be written in English.
- If possible, ensure the plugin outputs correct source mappings.
- If your plugin uses virtual modules (e.g., for helper functions), prefix module IDs with \0. This prevents other plugins from attempting to process virtual modules.
Rollup plugins are driven by the PluginDriver function, which provides the following hooks:
- hookFirst- Description: Executes the corresponding plugin hooks in a chained Promise manner while keeping the call parameters unchanged. - hookFirstreturns the first non-null or non-undefined value from plugin calls.- Use Cases: - Chain calls return the first value processed by a plugin.
- Supports asynchronous plugins.
- Parameters remain unchanged, plugins are independent.
 - Related plugin hooks: - load,- resolveDynamicImport,- resolveId,- shouldTransformCachedModule- resolveId
 - Specifically used for path resolution. Converting relative paths to absolute paths usually only requires one plugin. ts- // rollup/src/utils/resolveIdViaPlugins.ts export function resolveIdViaPlugins( source: string, importer: string | undefined, pluginDriver: PluginDriver, moduleLoaderResolveId: ( source: string, importer: string | undefined, customOptions: CustomPluginOptions | undefined, isEntry: boolean | undefined, skip: | readonly { importer: string | undefined; plugin: Plugin; source: string; }[] | null ) => Promise<ResolvedId | null>, skip: | readonly { importer: string | undefined; plugin: Plugin; source: string; }[] | null, customOptions: CustomPluginOptions | undefined, isEntry: boolean ): Promise<ResolveIdResult> { let skipped: Set<Plugin> | null = null; let replaceContext: ReplaceContext | null = null; if (skip) { skipped = new Set(); for (const skippedCall of skip) { if ( source === skippedCall.source && importer === skippedCall.importer ) { skipped.add(skippedCall.plugin); } } replaceContext = (pluginContext, plugin): PluginContext => ({ ...pluginContext, resolve: ( source, importer, { custom, isEntry, skipSelf } = BLANK ) => { return moduleLoaderResolveId( source, importer, custom, isEntry, skipSelf ? [...skip, { importer, plugin, source }] : skip ); } }); } return pluginDriver.hookFirst( 'resolveId', [source, importer, { custom: customOptions, isEntry }], replaceContext, skipped ); }- load
 - loadis strongly associated with virtual module loading. Usually, there is a one-to-one relationship between a virtual module and a plugin, so it only needs to be processed by one plugin.ts- // rollup/src/ModuleLoader.ts source = await this.readQueue.run( async () => (await this.pluginDriver.hookFirst('load', [id])) ?? (await fs.readFile(id, 'utf8')) );
- hookFirstSync- Description: - Synchronously executes the corresponding plugin hooks while keeping the call parameters unchanged. - hookFirstSyncreturns the first non-null or non-undefined value from plugin calls.- Use Cases: - Chain calls return the first value processed by a plugin.
- Does not support asynchronous plugins.
- Parameters remain unchanged, plugins are independent.
 - Related plugin hooks: - renderDynamicImport,- resolveAssetUrl,- resolveFileUrl,- resolveImportMeta
- hookParallel- Description: Executes with the same parameters, directly executes for those without return values and collects and executes in parallel for those with return values. Does not wait for the current plugin to complete execution, no return value. - Use Cases: - Tasks need to be completed as much as possible.
- Parameters remain unchanged, does not affect plugins.
- Supports both synchronous and asynchronous plugin hooks.
 - Related plugin hooks: - buildEnd,- buildStart,- moduleParsed,- renderError,- renderStart,- writeBundle,- closeBundle,- closeWatcher,- watchChange
- hookReduceArg0- Description: Only modifies the first parameter, chains asynchronous calls to the corresponding plugin hook, uses a reduce function to decide modifications to the first parameter, with strong sequential dependencies between plugins. - Use Cases: - Need to chain-modify the first parameter of plugins.
- Supports asynchronous plugin calls.
- Chain calls.
 - Related plugin hooks: - options,- generateBundle,- renderChunk,- transform
- hookReduceArg0Sync- Description: Only modifies the first parameter, chains synchronous calls to the corresponding plugin hook, uses a reduce function to decide modifications to the first parameter, with strong sequential dependencies between plugins. - Use Cases: - Need to chain-modify the first parameter of plugins.
- Only supports synchronous plugin calls.
- Chain calls.
 - Related plugin hooks: - augmentChunkHash,- outputOptions
- hookReduceValue- Description: Plugin parameters remain unchanged, plugins are unaware of changes to - initialValue. Determines the value of- initialValuethrough plugin return values and the- reducefunction, with chained asynchronous calls.- Use Cases: - Specifically used to handle user-defined variables (initialValue), meaning it can be considered when variables are affected by plugin return values.
- Plugin parameters remain unchanged, does not affect plugin calls.
- Asynchronous plugins exist.
 - Related plugin hooks: - banner,- footer,- intro,- outro
- Specifically used to handle user-defined variables (
- hookReduceValueSync- Description: Plugin parameters remain unchanged, plugins are unaware of changes to - initialValue. Determines the value of- initialValuethrough plugin return values and the- reducefunction, with chained synchronous calls.- Use Cases: - Specifically used to handle user-defined variables (initialValue), meaning it can be considered when variables are affected by plugin return values.
- Plugin parameters remain unchanged, does not affect plugin calls.
- No asynchronous plugins exist.
 - Related plugin hooks: - augmentChunkHash,- outputOptions
- Specifically used to handle user-defined variables (
- hookSeq- Description: Plugin parameters remain unchanged, chains calls to various plugins. - Use Cases: - Strong plugin order requirements.
- Plugins are independent of each other.
- Asynchronous plugins exist.
 - Related plugin hooks: - options,- generateBundle,- renderChunk,- transform
Rollup Plugin Execution Diagram: Note that Rollup injects context when executing plugins to provide additional capabilities.
this.pluginContexts = new Map(
  this.plugins.map(plugin => [
    plugin,
    getPluginContext(
      plugin,
      pluginCache,
      graph,
      options,
      this.fileEmitter,
      existingPluginNames
    )
  ])
);
function runHook<H extends AsyncPluginHooks>(
  hookName: H,
  args: Parameters<PluginHooks[H]>,
  plugin: Plugin,
  permitValues: boolean,
  hookContext?: ReplaceContext | null
): EnsurePromise<ReturnType<PluginHooks[H]>> {
  const hook = plugin[hookName];
  if (!hook) return undefined as any;
  let context = this.pluginContexts.get(plugin)!;
  if (hookContext) {
    context = hookContext(context, plugin);
  }
  return Promise.resolve().then(() => {
    // ...
  });
}The injected context capabilities include:
- addWatchFile: (id: string) => void - Adds other files to be watched in - watchmode, so that when these files change, the rebuild process will be triggered.- idcan be an absolute path or a relative path to the current working directory. This context method can only be used during the build phase, such as- buildStart,- load,- resolveId,- transform.- Note: Usually used to improve rebuild speed in - watchmode. The- transformhook will only be triggered when the content of a given module actually changes. Using- this.addWatchFilein- transform, if a file change is detected, the- transformhook will re-parse this module (whether rebuild is needed).
- cache 
- emitAsset 
- emitChunk 
- emitFile - Generates new modules that need to be included in the build output. The method returns a - referenceIdthat users can use in various places to reference the newly generated module.- emitFilesupports two formats:ts- interface EmittedChunk { type: 'chunk'; id: string; name?: string; fileName?: string; implicitlyLoadedAfterOneOf?: string[]; importer?: string; preserveSignature?: | 'strict' | 'allow-extension' | 'exports-only' | false; } interface EmittedAsset { type: 'asset'; name?: string; fileName?: string; source?: string | Uint8Array; }- In both formats above, either - fileNameor- namecan be provided. If- fileNameis provided,
- error 
- getAssetFileName 
- getChunkFileName 
- getFileName 
- getModuleIds 
- getModuleInfo 
- getWatchFiles 
- isExternal 
- load 
- meta 
- moduleIds 
- parse 
- resolve 
- resolveId 
- setAssetSource 
- warnv 
Rollup plugins call various hook functions during the build phase and output generation phase to trigger plugin hooks.
The execution flow diagram is as follows: 
Rollup Plugin Mechanism Summary
Advantages: Rollup's plugins are similar to other large frameworks, providing unified interfaces and following the principle of convention over configuration. The 8 types of hook loading functions make Rollup's plugin development very flexible, though this also brings a learning curve.
Compared to Webpack, Rollup's plugin system is unique and does not distinguish between plugin and loader. The core of Rollup's plugin mechanism is the various hook functions during the build phase and output generation phase. Internally, it implements asynchronous hook scheduling based on Promise.
Disadvantages:
- All source code is mixed in one library, with seemingly arbitrary management of modules and utility functions. 
- Cannot directly transplant any of its tools to our projects. In comparison, webpack's plugin system is encapsulated into a plugin - tapable, which is more conducive to our learning and use.
Vite's Role 
Vite leverages Rollup's capabilities during the build phase, therefore it needs to be compatible with Rollup's plugin ecosystem (compatibility of dev phase plugins to the build phase). It implements a similar plugin system by drawing inspiration from Rollup's plugin mechanism.
Therefore, for Vite, it implements the following capabilities:
- Implements scheduling of Rollupplugin hooks.
- Implements a plugin context mechanism similar to Rollup.
- Processes hook return values accordingly.
- Implements hook types.
Webpack Plugin Mechanism 
Role of Tapable 
Tapable is a library similar to EventEmitter in Node.js, but it focuses more on custom event triggering and handling. Through Tapable, we can register custom events and then execute them at appropriate times.
 XiSenao
 XiSenao SenaoXi
 SenaoXi