【Rolldown源码】五、开始入手rolldown核心包

891 阅读2分钟

回顾一下之前的调用流程

Pasted image 20240524104417.png

现在进入到 rolldown(rust)中的代码后可以看到,Buldler对外主要暴露了writegeneratescan三个方法

impl Bundler {
	pub async fn write(&mut self) -> Result<BundleOutput> {}
	pub async fn generate(&mut self) -> Result<BundleOutput> {}
	pub async fn scan(&mut self) -> Result<ScanStageOutput> {}
}

这里我们主要讲write方法,因为在write的流程中基本都包括了scangenerate的实现。

因为整体流程过于复杂,我曾想一条线走到底,需要描述的内容实在是太多了,接下来我们会采用“大胆假设,小心求证”的方式,先给出全流程,然后根据hooks分段来解释流程。

接下来我们先看看rolldown一直设置了哪些hooks,

  1. 从plugin的配置里看(packages/rolldown/src/plugin/index.ts)
export interface Plugin {
  // --- Build hooks ---
  buildStart?: Hook<
    (
      this: PluginContext,
      options: NormalizedInputOptions,
    ) => MaybePromise<NullValue>
  >

  resolveId?: Hook<
    (
      this: PluginContext,
      source: string,
      importer: string | undefined,
      extraOptions: BindingHookResolveIdExtraOptions,
    ) => MaybePromise<ResolveIdResult>
  >

  /**
   * @deprecated
   * This hook is only for rollup plugin compatibility. Please use `resolveId` instead.
   */
  resolveDynamicImport?: Hook<
    (
      this: PluginContext,
      source: string,
      importer: string | undefined,
    ) => MaybePromise<ResolveIdResult>
  >

  load?: Hook<
    (
      this: PluginContext,
      id: string,
    ) => MaybePromise<
      NullValue | string | { code: string; map?: SourceMapInput }
    >
  >

  transform?: Hook<
    (
      this: TransformPluginContext,
      code: string,
      id: string,
    ) => MaybePromise<
      | NullValue
      | string
      | {
          code: string
          map?: string | null | SourceMapInput
        }
    >
  >

  moduleParsed?: Hook<
    (this: PluginContext, moduleInfo: ModuleInfo) => MaybePromise<NullValue>
  >

  buildEnd?: Hook<(this: PluginContext, err?: Error) => MaybePromise<NullValue>>

  // --- Generate hooks ---

  renderStart?: Hook<
    (
      this: PluginContext,
      outputOptions: NormalizedOutputOptions,
      inputOptions: NormalizedInputOptions,
    ) => MaybePromise<NullValue>
  >

  renderChunk?: Hook<
    (
      this: PluginContext,
      code: string,
      chunk: RenderedChunk,
      outputOptions: NormalizedOutputOptions,
    ) => MaybePromise<
      | NullValue
      | string
      | {
          code: string
          map?: string | null | SourceMapInput
        }
    >
  >

  renderError?: Hook<
    (this: PluginContext, error: Error) => MaybePromise<NullValue>
  >

  generateBundle?: Hook<
    (
      this: PluginContext,
      outputOptions: NormalizedOutputOptions,
      bundle: OutputBundle,
      isWrite: boolean,
    ) => MaybePromise<NullValue>
  >

  writeBundle?: Hook<
    (
      this: PluginContext,
      outputOptions: NormalizedOutputOptions,
      bundle: OutputBundle,
    ) => MaybePromise<NullValue>
  >
}
  1. 从rolldown(rust)中结构体PluginDriver里面看,分成build_hooks和output_hooks
// build_hooks.rs
impl PluginDriver {
  pub async fn build_start(&self) -> HookNoopReturn {}

  pub async fn resolve_id(&self, args: &HookResolveIdArgs<'_>) -> HookResolveIdReturn {}

  // Only for rollup compatibility
  pub async fn resolve_dynamic_import(
    &self,
    args: &HookResolveDynamicImportArgs<'_>,
  ) -> HookResolveIdReturn {}

  pub async fn load(&self, args: &HookLoadArgs<'_>) -> HookLoadReturn {}

  pub async fn transform(
    &self,
    args: &HookTransformArgs<'_>,
    sourcemap_chain: &mut Vec<SourceMap>,
    original_code: &str,
  ) -> Result<String> {}

  pub async fn module_parsed(&self, module_info: Arc<ModuleInfo>) -> HookNoopReturn {}

  pub async fn build_end(&self, args: Option<&HookBuildEndArgs>) -> HookNoopReturn {}

// output_hooks.rs
impl PluginDriver {
  pub async fn render_start(&self) -> HookNoopReturn {}

  pub async fn render_chunk(
    &self,
    mut args: HookRenderChunkArgs<'_>,
  ) -> Result<(String, Vec<SourceMap>)> {}

  pub async fn render_error(&self, args: &HookRenderErrorArgs) -> HookNoopReturn {}

  pub async fn generate_bundle(&self, bundle: &mut Vec<Output>, is_write: bool) -> HookNoopReturn {}

  pub async fn write_bundle(&self, bundle: &mut Vec<Output>) -> HookNoopReturn {}
}

每个hooks都对的上,对比一下rollup的[hooks](Plugin Development | Rollup (rollupjs.org)),也能覆盖大部分的核心hooks了,这边就先抛开rollup,开始看看build hook和generate hooks在rolldown中大概是在什么位置(hook具体的含义可以看rollup的文档)

  1. Build hooks
    • buildStart
    • resolveId 12
    • resolveDynamicImport (在resolveId前判断是否为异步引入) 1
    • load 1
    • transform 1
    • moduleParsed 1
    • buildEnd 1
  2. Generate hooks
    • renderStart 1
    • renderChunk 1
    • renderError 1
    • generateBundle 1
    • writeBundle 1

从方法来看,hooks的分布大致如下:

Pasted image 20240529171937.png

如果从方法来讲的话模块还是太大了,以后我会从hooks的阶段来讲解整个流程,但是在调试的时候发现hooks并不是按照上图中的顺序执行的,所以我会先假设是按照这个流程来运行的,其中到特殊的例子的时候会再标注出来,然后根据内容的大小看看是否需要对这个特殊例子再来一篇文章。