rspack_core 0.7.11

rspack core
Documentation
use std::path::Path;

use rspack_collections::{DatabaseItem, IdentifierMap};
use rspack_error::Result;
use rspack_hash::RspackHashDigest;
use rspack_paths::ArcPathSet;
use rspack_tasks::within_compiler_context;
use rustc_hash::{FxHashMap, FxHashSet};

use crate::{
  ChunkGraph, ChunkKind, Compilation, Compiler, RuntimeSpec,
  chunk_graph_chunk::ChunkId,
  chunk_graph_module::ModuleId,
  compilation::build_module_graph::ModuleExecutor,
  fast_set,
  incremental::{Incremental, IncrementalPasses},
  recover_artifact,
};

impl Compiler {
  pub async fn rebuild(
    &mut self,
    changed_files: std::collections::HashSet<String>,
    deleted_files: std::collections::HashSet<String>,
  ) -> Result<()> {
    within_compiler_context(
      self.compiler_context.clone(),
      self.rebuild_inner(changed_files, deleted_files),
    )
    .await?;
    Ok(())
  }
  #[tracing::instrument("Compiler:rebuild", skip_all, fields(
    compiler.changed_files = ?changed_files.iter().cloned().collect::<Vec<_>>(),
    compiler.deleted_files = ?deleted_files.iter().cloned().collect::<Vec<_>>()
  ))]
  async fn rebuild_inner(
    &mut self,
    changed_files: std::collections::HashSet<String>,
    deleted_files: std::collections::HashSet<String>,
  ) -> Result<()> {
    let records = CompilationRecords::record(&self.compilation);

    // build without stats
    {
      let mut modified_files: ArcPathSet = ArcPathSet::default();
      modified_files.extend(changed_files.iter().map(|files| Path::new(files).into()));
      let mut removed_files: ArcPathSet = ArcPathSet::default();
      removed_files.extend(deleted_files.iter().map(|files| Path::new(files).into()));

      let mut all_files = modified_files.clone();
      all_files.extend(removed_files.clone());

      self.plugin_driver.clear_cache(self.compilation.id());

      let mut new_compilation = Compilation::new(
        self.id,
        self.options.clone(),
        self.platform.clone(),
        self.plugin_driver.clone(),
        self.buildtime_plugin_driver.clone(),
        self.resolver_factory.clone(),
        self.loader_resolver_factory.clone(),
        Some(records),
        Incremental::new_hot(self.options.experiments.incremental),
        Some(ModuleExecutor::default()),
        modified_files,
        removed_files,
        self.input_filesystem.clone(),
        self.intermediate_filesystem.clone(),
        self.output_filesystem.clone(),
        true,
        self.compiler_context.clone(),
      );
      new_compilation.hot_index = self.compilation.hot_index + 1;

      if new_compilation
        .incremental
        .mutations_readable(IncrementalPasses::MAKE)
      {
        // recover module graph from last compilation
        self
          .compilation
          .recover_module_graph_to_new_compilation(&mut new_compilation);

        // seal stage used
        new_compilation.build_chunk_graph_artifact =
          std::mem::take(&mut self.compilation.build_chunk_graph_artifact);

        // reuse module executor
        new_compilation.module_executor = std::mem::take(&mut self.compilation.module_executor);
      }

      // Recover artifacts based on their associated incremental passes
      let incremental = &new_compilation.incremental;

      // Wrapped artifacts (SharedArtifact<T>, BindingCell<T>, DerefOption<T>)
      recover_artifact(
        incremental,
        &mut new_compilation.async_modules_artifact,
        &mut self.compilation.async_modules_artifact,
      );
      recover_artifact(
        incremental,
        &mut new_compilation.dependencies_diagnostics_artifact,
        &mut self.compilation.dependencies_diagnostics_artifact,
      );
      recover_artifact(
        incremental,
        &mut new_compilation.code_generation_results,
        &mut self.compilation.code_generation_results,
      );
      recover_artifact(
        incremental,
        &mut new_compilation.side_effects_optimize_artifact,
        &mut self.compilation.side_effects_optimize_artifact,
      );

      // Direct type artifacts
      recover_artifact(
        incremental,
        &mut new_compilation.module_ids_artifact,
        &mut self.compilation.module_ids_artifact,
      );
      recover_artifact(
        incremental,
        &mut new_compilation.named_chunk_ids_artifact,
        &mut self.compilation.named_chunk_ids_artifact,
      );
      recover_artifact(
        incremental,
        &mut new_compilation.cgm_hash_artifact,
        &mut self.compilation.cgm_hash_artifact,
      );
      recover_artifact(
        incremental,
        &mut new_compilation.cgm_runtime_requirements_artifact,
        &mut self.compilation.cgm_runtime_requirements_artifact,
      );
      recover_artifact(
        incremental,
        &mut new_compilation.cgc_runtime_requirements_artifact,
        &mut self.compilation.cgc_runtime_requirements_artifact,
      );
      recover_artifact(
        incremental,
        &mut new_compilation.chunk_hashes_artifact,
        &mut self.compilation.chunk_hashes_artifact,
      );
      recover_artifact(
        incremental,
        &mut new_compilation.chunk_render_artifact,
        &mut self.compilation.chunk_render_artifact,
      );

      // Cache artifacts (custom recover impl calls start_next_generation)
      recover_artifact(
        incremental,
        &mut new_compilation.chunk_render_cache_artifact,
        &mut self.compilation.chunk_render_cache_artifact,
      );
      recover_artifact(
        incremental,
        &mut new_compilation.code_generate_cache_artifact,
        &mut self.compilation.code_generate_cache_artifact,
      );
      recover_artifact(
        incremental,
        &mut new_compilation.process_runtime_requirements_cache_artifact,
        &mut self.compilation.process_runtime_requirements_cache_artifact,
      );

      // FOR BINDING SAFETY:
      // Update `compilation` for each rebuild.
      // Make sure `thisCompilation` hook was called before any other hooks that leverage `JsCompilation`.
      fast_set(&mut self.compilation, new_compilation);
      self.cache.before_compile(&mut self.compilation).await;
      self.compile().await?;
    }

    self.compile_done().await?;
    self.cache.after_compile(&self.compilation).await;

    #[cfg(allocative)]
    crate::utils::snapshot_allocative("rebuild");

    Ok(())
  }
}

#[derive(Debug)]
pub struct CompilationRecords {
  pub runtimes: RuntimeSpec,
  pub runtime_modules: IdentifierMap<RspackHashDigest>,
  pub chunks: FxHashMap<ChunkId, (RuntimeSpec, FxHashSet<ModuleId>)>,
  pub modules: FxHashMap<ModuleId, FxHashMap<ChunkId, RspackHashDigest>>,
  pub hash: Option<RspackHashDigest>,
}

impl CompilationRecords {
  pub fn record(compilation: &Compilation) -> Self {
    Self {
      runtimes: Self::record_runtimes(compilation),
      runtime_modules: Self::record_runtime_modules(compilation),
      chunks: Self::record_chunks(compilation),
      modules: Self::record_modules(compilation),
      hash: Self::record_hash(compilation),
    }
  }

  fn record_hash(compilation: &Compilation) -> Option<RspackHashDigest> {
    compilation.hash.clone()
  }

  fn record_modules(
    compilation: &Compilation,
  ) -> FxHashMap<ModuleId, FxHashMap<ChunkId, RspackHashDigest>> {
    compilation
      .chunk_graph
      .chunk_graph_module_by_module_identifier
      .keys()
      .filter_map(|identifier| {
        let module_id =
          ChunkGraph::get_module_id(&compilation.module_ids_artifact, *identifier)?.clone();
        let mut hashes = FxHashMap::default();
        for chunk in compilation.chunk_graph.get_module_chunks(*identifier) {
          let chunk = compilation.chunk_by_ukey.expect_get(chunk);
          let chunk_id = chunk.id().expect("should have chunk_id").clone();
          let hash = compilation
            .code_generation_results
            .get_hash(identifier, Some(chunk.runtime()))
            .expect("should have hash");
          hashes.insert(chunk_id, hash.clone());
        }
        Some((module_id, hashes))
      })
      .collect()
  }

  fn record_runtime_modules(compilation: &Compilation) -> IdentifierMap<RspackHashDigest> {
    compilation
      .runtime_modules
      .keys()
      .map(|identifier| {
        (
          *identifier,
          compilation
            .runtime_modules_hash
            .get(identifier)
            .expect("should have runtime module hash")
            .clone(),
        )
      })
      .collect()
  }

  fn record_runtimes(compilation: &Compilation) -> RuntimeSpec {
    compilation
      .get_chunk_graph_entries()
      .filter_map(|entry_ukey| compilation.chunk_by_ukey.get(&entry_ukey))
      .flat_map(|entry_chunk| entry_chunk.runtime().clone())
      .collect()
  }

  fn record_chunks(
    compilation: &Compilation,
  ) -> FxHashMap<ChunkId, (RuntimeSpec, FxHashSet<ModuleId>)> {
    compilation
      .chunk_by_ukey
      .values()
      .filter(|chunk| chunk.kind() != ChunkKind::HotUpdate)
      .map(|chunk| {
        let chunk_id = chunk.expect_id().clone();
        let chunk_runtime = chunk.runtime().clone();
        let chunk_modules: FxHashSet<ModuleId> = compilation
          .chunk_graph
          .get_chunk_modules_identifier(&chunk.ukey())
          .iter()
          .filter_map(|m| ChunkGraph::get_module_id(&compilation.module_ids_artifact, *m))
          .cloned()
          .collect();
        (chunk_id, (chunk_runtime, chunk_modules))
      })
      .collect()
  }
}