|
| 1 | +# Artifacts |
| 2 | + |
| 3 | +Design and usage of the artifact system in Rspack's incremental compilation. |
| 4 | + |
| 5 | +## Overview |
| 6 | + |
| 7 | +Artifacts are data structures that hold intermediate compilation results. They are designed to be recovered across rebuilds during incremental compilation, avoiding redundant recomputation when their associated compilation pass hasn't changed. |
| 8 | + |
| 9 | +## Core Concepts |
| 10 | + |
| 11 | +### ArtifactExt Trait |
| 12 | + |
| 13 | +The `ArtifactExt` trait is the foundation of the artifact system. It associates each artifact with its corresponding incremental pass and provides recovery logic. |
| 14 | + |
| 15 | +```rust |
| 16 | +pub trait ArtifactExt: Sized { |
| 17 | + /// The incremental pass associated with this artifact. |
| 18 | + const PASS: IncrementalPasses; |
| 19 | + |
| 20 | + /// Determines whether this artifact should be recovered from the previous compilation. |
| 21 | + fn should_recover(incremental: &Incremental) -> bool { |
| 22 | + incremental.mutations_readable(Self::PASS) |
| 23 | + } |
| 24 | + |
| 25 | + /// Recovers the artifact from the old compilation to the new compilation. |
| 26 | + fn recover(incremental: &Incremental, new: &mut Self, old: &mut Self) { |
| 27 | + if Self::should_recover(incremental) { |
| 28 | + mem::swap(new, old); |
| 29 | + } |
| 30 | + } |
| 31 | +} |
| 32 | +``` |
| 33 | + |
| 34 | +### recover_artifact Function |
| 35 | + |
| 36 | +A helper function that invokes the trait's recovery method: |
| 37 | + |
| 38 | +```rust |
| 39 | +pub fn recover_artifact<T: ArtifactExt>(incremental: &Incremental, new: &mut T, old: &mut T) { |
| 40 | + T::recover(incremental, new, old); |
| 41 | +} |
| 42 | +``` |
| 43 | + |
| 44 | +## Artifact Types |
| 45 | + |
| 46 | +### Direct Artifacts |
| 47 | + |
| 48 | +Artifacts that directly implement `ArtifactExt`: |
| 49 | + |
| 50 | +| Artifact | PASS | Description | |
| 51 | +| --------------------------------- | ------------------------------ | ------------------------------- | |
| 52 | +| `ModuleIdsArtifact` | `MODULES_IDS` | Module ID mappings | |
| 53 | +| `ChunkNamedIdArtifact` | `CHUNKS_IDS` | Named chunk ID mappings | |
| 54 | +| `CgmHashArtifact` | `MODULES_HASHES` | Module hash data | |
| 55 | +| `CgmRuntimeRequirementsArtifact` | `MODULES_RUNTIME_REQUIREMENTS` | Module runtime requirements | |
| 56 | +| `CgcRuntimeRequirementsArtifact` | `CHUNKS_RUNTIME_REQUIREMENTS` | Chunk runtime requirements | |
| 57 | +| `ChunkHashesArtifact` | `CHUNKS_HASHES` | Chunk hash data | |
| 58 | +| `ChunkRenderArtifact` | `CHUNKS_RENDER` | Chunk render results | |
| 59 | +| `CodeGenerationResults` | `MODULES_CODEGEN` | Code generation results | |
| 60 | +| `SideEffectsOptimizeArtifact` | `SIDE_EFFECTS_OPTIMIZATION` | Side effects optimization data | |
| 61 | +| `AsyncModulesArtifact` | `INFER_ASYNC_MODULES` | Async modules information | |
| 62 | +| `DependenciesDiagnosticsArtifact` | `DEPENDENCIES_DIAGNOSTICS` | Dependencies diagnostics | |
| 63 | +| `ImportedByDeferModulesArtifact` | empty | Deferred module import tracking | |
| 64 | + |
| 65 | +### Cache Artifacts |
| 66 | + |
| 67 | +Artifacts with custom `recover` implementations that call `start_next_generation()`: |
| 68 | + |
| 69 | +| Artifact | PASS | Description | |
| 70 | +| ----------------------------------------- | ------------------------------ | -------------------------- | |
| 71 | +| `ChunkRenderCacheArtifact` | `CHUNKS_RENDER` | Chunk render cache | |
| 72 | +| `CodeGenerateCacheArtifact` | `MODULES_CODEGEN` | Code generation cache | |
| 73 | +| `ProcessRuntimeRequirementsCacheArtifact` | `MODULES_RUNTIME_REQUIREMENTS` | Runtime requirements cache | |
| 74 | + |
| 75 | +### Wrapper Types |
| 76 | + |
| 77 | +Wrapper types that delegate to the inner type's `PASS`: |
| 78 | + |
| 79 | +| Wrapper | Description | |
| 80 | +| ----------------------- | --------------------------------------------- | |
| 81 | +| `DerefOption<T>` | Optional artifact wrapper with deref support | |
| 82 | +| `Arc<AtomicRefCell<T>>` | Shared artifact wrapper for concurrent access | |
| 83 | +| `BindingCell<T>` | JS binding-aware wrapper (napi feature) | |
| 84 | +| `Box<T>` | Simple box wrapper (sys binding) | |
| 85 | + |
| 86 | +## Usage in Rebuild |
| 87 | + |
| 88 | +During rebuild, artifacts are recovered from the old compilation to the new compilation: |
| 89 | + |
| 90 | +```rust |
| 91 | +// In Compiler::rebuild_inner |
| 92 | + |
| 93 | +// Wrapped artifacts |
| 94 | +recover_artifact( |
| 95 | + incremental, |
| 96 | + &mut new_compilation.async_modules_artifact, |
| 97 | + &mut self.compilation.async_modules_artifact, |
| 98 | +); |
| 99 | +recover_artifact( |
| 100 | + incremental, |
| 101 | + &mut new_compilation.code_generation_results, |
| 102 | + &mut self.compilation.code_generation_results, |
| 103 | +); |
| 104 | + |
| 105 | +// Direct type artifacts |
| 106 | +recover_artifact( |
| 107 | + incremental, |
| 108 | + &mut new_compilation.module_ids_artifact, |
| 109 | + &mut self.compilation.module_ids_artifact, |
| 110 | +); |
| 111 | +``` |
| 112 | + |
| 113 | +## Implementing a New Artifact |
| 114 | + |
| 115 | +### Basic Artifact |
| 116 | + |
| 117 | +```rust |
| 118 | +use crate::{ArtifactExt, incremental::IncrementalPasses}; |
| 119 | + |
| 120 | +#[derive(Debug, Default)] |
| 121 | +pub struct MyArtifact { |
| 122 | + // artifact data |
| 123 | +} |
| 124 | + |
| 125 | +impl ArtifactExt for MyArtifact { |
| 126 | + const PASS: IncrementalPasses = IncrementalPasses::MY_PASS; |
| 127 | +} |
| 128 | +``` |
| 129 | + |
| 130 | +### Cache Artifact with Custom Recovery |
| 131 | + |
| 132 | +```rust |
| 133 | +impl ArtifactExt for MyCacheArtifact { |
| 134 | + const PASS: IncrementalPasses = IncrementalPasses::MY_PASS; |
| 135 | + |
| 136 | + fn recover(_incremental: &Incremental, new: &mut Self, old: &mut Self) { |
| 137 | + *new = std::mem::take(old); |
| 138 | + new.start_next_generation(); |
| 139 | + } |
| 140 | +} |
| 141 | +``` |
| 142 | + |
| 143 | +### Wrapped Artifact |
| 144 | + |
| 145 | +For artifacts wrapped in `Arc<AtomicRefCell<T>>`, `DerefOption<T>`, or `BindingCell<T>`, the wrapper automatically delegates to the inner type's `PASS`. |
| 146 | + |
| 147 | +```rust |
| 148 | +// In Compilation struct |
| 149 | +pub my_artifact: Arc<AtomicRefCell<MyArtifact>>, |
| 150 | + |
| 151 | +// Recovery is automatic through the wrapper's ArtifactExt impl |
| 152 | +recover_artifact( |
| 153 | + incremental, |
| 154 | + &mut new_compilation.my_artifact, |
| 155 | + &mut self.compilation.my_artifact, |
| 156 | +); |
| 157 | +``` |
| 158 | + |
| 159 | +## Incremental Passes |
| 160 | + |
| 161 | +Incremental passes are bitflags that control which compilation phases are enabled: |
| 162 | + |
| 163 | +```rust |
| 164 | +bitflags! { |
| 165 | + pub struct IncrementalPasses: u32 { |
| 166 | + const MAKE = 0b0000_0001; |
| 167 | + const MODULES_IDS = 0b0000_0010; |
| 168 | + const CHUNKS_IDS = 0b0000_0100; |
| 169 | + const MODULES_HASHES = 0b0000_1000; |
| 170 | + const MODULES_CODEGEN = 0b0001_0000; |
| 171 | + const MODULES_RUNTIME_REQUIREMENTS = 0b0010_0000; |
| 172 | + const CHUNKS_RUNTIME_REQUIREMENTS = 0b0100_0000; |
| 173 | + const CHUNKS_HASHES = 0b1000_0000; |
| 174 | + const CHUNKS_RENDER = 0b0001_0000_0000; |
| 175 | + // ... additional passes |
| 176 | + } |
| 177 | +} |
| 178 | +``` |
| 179 | + |
| 180 | +## Design Principles |
| 181 | + |
| 182 | +1. **Separation of Concerns**: Each artifact is associated with related incremental pass |
| 183 | +2. **Automatic Recovery**: Wrapper types delegate recovery to inner types |
| 184 | +3. **Custom Recovery**: Cache artifacts can override `recover` for generation management |
| 185 | +4. **Type Safety**: The trait system ensures compile-time correctness |
| 186 | +5. **Performance**: `mem::swap` provides zero-copy artifact transfer |
| 187 | + |
| 188 | +## File Locations |
| 189 | + |
| 190 | +- Rebuild logic: `crates/rspack_core/src/compiler/rebuild.rs` |
| 191 | +- Individual artifacts: `crates/rspack_core/src/artifacts/*.rs` |
0 commit comments