Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save ScriptedAlchemy/0663a05786187699c622ac7dd8825f65 to your computer and use it in GitHub Desktop.
Save ScriptedAlchemy/0663a05786187699c622ac7dd8825f65 to your computer and use it in GitHub Desktop.
Rspack Export Usage Tracking Research Documentation

Export Info Dependency Analysis

Overview

The ExportInfoDependency is a critical component in rspack's export tracking system that provides runtime access to export metadata during code generation. It acts as a bridge between the analysis phase (where export usage information is collected) and the runtime phase (where this information is used for optimizations).

Core Functionality

Purpose

  • Runtime Export Information Access: Provides a way to inject export metadata into the generated code at runtime
  • Tree-shaking Support: Enables conditional code inclusion/exclusion based on export usage
  • Export State Querying: Allows runtime queries about export states (used, unused, mangled, inlined)

Key Components

1. ExportInfoDependency Structure

pub struct ExportInfoDependency {
  start: u32,           // Start position in source code
  end: u32,             // End position in source code  
  export_name: Vec<Atom>, // Nested export path (e.g., ["default", "foo"])
  property: Atom,       // Property being queried ("used", "canMangle", etc.)
}

2. Property Types

The dependency supports several property queries:

  • usedExports: Returns array of used export names or boolean for namespace usage
  • canMangle: Whether the export name can be mangled for minification
  • inlinable: Whether the export can be inlined
  • used: Boolean indicating if export is used
  • useInfo: Detailed usage state (Used, Unused, OnlyPropertiesUsed, NoInfo, Unknown)
  • provideInfo: Whether export is provided (Provided, NotProvided, Unknown)

Implementation Details

Template Rendering Process

1. Export Name Resolution

fn get_property(&self, context: &TemplateContext) -> Option<String> {
    let export_name = &self.export_name;
    let prop = &self.property;
    let module_graph = compilation.get_module_graph();
    let module_identifier = module.identifier();
    
    // Special handling for usedExports query
    if export_name.is_empty() && prop == "usedExports" {
        let exports_info = module_graph.get_prefetched_exports_info(
            &module_identifier, 
            PrefetchExportsInfoMode::AllExports
        );
        let used_exports = exports_info.get_used_exports(*runtime);
        // Return serialized used exports data
    }
}

2. Export Information Retrieval

The system uses prefetched export information for performance:

let exports_info = module_graph.get_prefetched_exports_info(
    &module_identifier,
    PrefetchExportsInfoMode::NamedNestedExports(export_name),
);

3. Property-Specific Logic

Each property type has specialized handling:

Can Mangle Query:

"canMangle" => {
    let can_mangle = if let Some(export_info) = 
        exports_info.get_read_only_export_info_recursive(export_name) {
        ExportInfoGetter::can_mangle(export_info)
    } else {
        ExportInfoGetter::can_mangle(exports_info.other_exports_info())
    };
    can_mangle.map(|v| v.to_string())
}

Usage State Query:

"useInfo" => {
    let used_state = ExportsInfoGetter::get_used(&exports_info, export_name, *runtime);
    Some((match used_state {
        UsageState::Used => "true",
        UsageState::OnlyPropertiesUsed => "true", 
        UsageState::Unused => "false",
        UsageState::NoInfo => "undefined",
        UsageState::Unknown => "null",
    }).to_owned())
}

Integration with Tree-Shaking

Export Usage Tracking

The dependency integrates with rspack's export analysis plugins:

  1. FlagDependencyExportsPlugin: Determines what exports are provided
  2. FlagDependencyUsagePlugin: Tracks which exports are actually used
  3. ExportInfoDependency: Provides runtime access to this information

Runtime Conditional Logic

Generated code can use export information for conditional execution:

// Example generated code
if (__webpack_exports_info__.canMangle) {
    // Use mangled export name
} else {
    // Use original export name
}

// Usage-based inclusion
if (__webpack_exports_info__.used) {
    // Include export-related code
}

Usage Patterns

1. Basic Export Usage Query

// Source code with export info dependency
const isUsed = __webpack_exports_info__.used;
if (isUsed) {
    // Conditionally include code
}

2. Nested Export Access

// Query nested export property
const canMangleNested = __webpack_exports_info__.nested.property.canMangle;

3. All Used Exports

// Get array of all used exports
const usedExports = __webpack_exports_info__.usedExports;

Performance Considerations

1. Prefetching Strategy

The system uses prefetching to avoid repeated module graph queries:

  • PrefetchExportsInfoMode::AllExports for general queries
  • PrefetchExportsInfoMode::NamedNestedExports for specific export paths

2. Caching

Export information is cached in the compilation's module graph to avoid recomputation.

3. Runtime Efficiency

Properties are resolved at build time and injected as literals, avoiding runtime computation.

Integration Points

1. Module Graph

  • Uses ModuleGraph::get_prefetched_exports_info() for export data
  • Integrates with ExportsInfoGetter for standardized access patterns

2. Template System

  • Implements DependencyTemplate for code generation
  • Uses TemplateReplaceSource for code replacement

3. Export Analysis Pipeline

  • Consumes data from flag dependency plugins
  • Provides bridge between analysis and runtime phases

Error Handling

1. Missing Export Information

Returns "undefined" when export information is not available.

2. Invalid Property Access

Gracefully handles unknown property names by returning None.

3. Runtime Safety

All generated property accesses are safe and won't throw runtime errors.

Export Analysis API Guidelines

Based on ShareUsagePlugin investigation findings, here are key guidelines for proper export analysis API usage:

Correct API Usage Patterns

ExportsInfoGetter vs ExportInfoGetter

// Use ExportsInfoGetter::prefetch() for efficient bulk operations
let prefetched = ExportsInfoGetter::prefetch(
    &exports_info,
    module_graph,
    PrefetchExportsInfoMode::AllExports,
);

// Use ExportInfoGetter::get_used() for individual export usage checking
let export_info_data = prefetched.get_read_only_export_info(&export_atom);
let usage_state = ExportInfoGetter::get_used(export_info_data, runtime_spec);

// NOT: ExportsInfoGetter::get_used() - this is incorrect API usage

Advanced Dependency Analysis for ConsumeShared Modules

// Use incoming connections to analyze ConsumeShared module usage
for connection in module_graph.get_incoming_connections(consume_shared_id) {
    if let Some(dependency) = module_graph.dependency_by_id(&connection.dependency_id) {
        // Extract specific export names using get_referenced_exports()
        let referenced_exports = dependency.get_referenced_exports(
            module_graph,
            &rspack_core::ModuleGraphCacheArtifact::default(),
            None,
        );
        
        // Process ExtendedReferencedExport patterns
        for export_ref in referenced_exports {
            match export_ref {
                ExtendedReferencedExport::Array(names) => {
                    // Handle multiple specific exports
                    for name in names {
                        let export_name = name.to_string();
                        // Process specific export usage
                    }
                },
                ExtendedReferencedExport::Export(export_info) => {
                    // Handle namespace or complex export patterns
                    if export_info.name.is_empty() {
                        // Namespace usage detected
                    } else {
                        // Specific named exports
                        for name in export_info.name {
                            // Process named export
                        }
                    }
                },
            }
        }
    }
}

Prefetch Mode Selection

// For comprehensive analysis (recommended for plugins)
PrefetchExportsInfoMode::AllExports

// For specific export analysis
PrefetchExportsInfoMode::NamedNestedExports(&export_names)

// For minimal analysis (performance-critical scenarios)
PrefetchExportsInfoMode::Default

ProvidedExports Handling Best Practices

// Proper handling of all ProvidedExports variants
match provided_exports {
    ProvidedExports::ProvidedNames(names) => {
        // Iterate over provided exports - this is the correct approach
        for name in names {
            let export_atom = rspack_util::atom::Atom::from(name.as_str());
            // Process each specific export
        }
    },
    ProvidedExports::ProvidedAll => {
        // Module provides all exports dynamically
        // Handle with wildcard or comprehensive analysis
    },
    ProvidedExports::Unknown => {
        // Cannot determine exports statically
        // Preserve unknown status, don't assume empty
    }
}

ConsumeShared Module Special Considerations

When dealing with ConsumeShared modules in export analysis:

// ConsumeShared modules are proxy modules - empty usage is expected
if module.module_type() == &ModuleType::ConsumeShared {
    // Real usage data comes from:
    // 1. Incoming dependencies (modules that import from ConsumeShared)
    // 2. Fallback module analysis
    // 3. Module connection analysis using get_referenced_exports()
    
    // Enhanced analysis using incoming connections
    let mut used_exports = Vec::new();
    let mut uses_namespace = false;
    
    for connection in module_graph.get_incoming_connections(module_id) {
        if let Some(dependency) = module_graph.dependency_by_id(&connection.dependency_id) {
            let referenced_exports = dependency.get_referenced_exports(
                module_graph,
                &rspack_core::ModuleGraphCacheArtifact::default(),
                None,
            );
            
            for export_ref in referenced_exports {
                match export_ref {
                    ExtendedReferencedExport::Array(names) => {
                        for name in names {
                            used_exports.push(name.to_string());
                        }
                    },
                    ExtendedReferencedExport::Export(export_info) => {
                        if export_info.name.is_empty() {
                            uses_namespace = true;
                        } else {
                            for name in export_info.name {
                                used_exports.push(name.to_string());
                            }
                        }
                    },
                }
            }
        }
    }
    
    // Don't expect direct usage data from the proxy module itself
}

Adding Exports and Setting Referenced Exports

Creating and Adding Exports

1. Via Dependency's get_exports Method

The primary way to declare exports is through the dependency's get_exports method:

fn get_exports(&self, _mg: &ModuleGraph, _mg_cache: &ModuleGraphCacheArtifact) -> Option<ExportsSpec> {
    Some(ExportsSpec {
        exports: ExportsOfExportsSpec::Names(vec![
            ExportNameOrSpec::ExportSpec(ExportSpec {
                name: "myExport".into(),
                can_mangle: Some(false),
                terminal_binding: Some(true),
                priority: Some(1),
                inlinable: Some(EvaluatedInlinableValue::String("inline_value".into())),
                ..Default::default()
            })
        ]),
        priority: Some(1),
        terminal_binding: Some(true),
        ..Default::default()
    })
}

2. ExportsSpec Structure

pub struct ExportsSpec {
    pub exports: ExportsOfExportsSpec,
    pub priority: Option<u8>,
    pub can_mangle: Option<bool>,
    pub terminal_binding: Option<bool>,
    pub from: Option<ModuleGraphConnection>,  // For re-exports
    pub dependencies: Option<Vec<ModuleIdentifier>>,
    pub hide_export: Option<FxHashSet<Atom>>,
    pub exclude_exports: Option<FxHashSet<Atom>>,
}

pub enum ExportsOfExportsSpec {
    UnknownExports,               // For dynamic exports
    NoExports,                    // Module has no exports
    Names(Vec<ExportNameOrSpec>), // Specific named exports
}

3. Programmatically Adding Exports at Runtime

// Access module's exports info
let exports_info = module_graph.get_exports_info(&module_id);

// Add a new export
let export_info = exports_info.get_export_info(&mut module_graph, &"newExport".into());
let export_data = export_info.as_data_mut(&mut module_graph);

// Configure the export
export_data.set_provided(Some(ExportProvided::Provided));
export_data.set_can_mangle_provide(Some(true));
export_data.set_terminal_binding(true);

// Set target if it's a re-export
ExportInfoSetter::set_target(
    export_data,
    Some(dependency_id),
    Some(target_module_id),
    Some(&Nullable::Value(vec!["targetExport".into()])),
    Some(1), // priority
);

Setting Referenced Exports

1. Via Dependency's get_referenced_exports Method

Dependencies specify which exports they reference:

fn get_referenced_exports(
    &self,
    _module_graph: &ModuleGraph,
    _module_graph_cache: &ModuleGraphCacheArtifact,
    _runtime: Option<&RuntimeSpec>,
) -> Vec<ExtendedReferencedExport> {
    vec![
        ExtendedReferencedExport::Array(vec!["specific".into(), "export".into()]), // obj.specific.export
        ExtendedReferencedExport::Export(ReferencedExport::new(
            vec!["another".into()],
            false, // can't mangle
            true,  // can inline
        )),
    ]
}

2. Referenced Export Types

pub enum ExtendedReferencedExport {
    Array(Vec<Atom>),      // Path to specific export
    Export(ReferencedExport), // Export with additional metadata
}

pub struct ReferencedExport {
    pub name: Vec<Atom>,   // Export path
    pub can_mangle: bool,
    pub can_inline: bool,
}

// Helper functions
pub fn create_no_exports_referenced() -> Vec<ExtendedReferencedExport> {
    vec![]
}

pub fn create_exports_object_referenced() -> Vec<ExtendedReferencedExport> {
    vec![ExtendedReferencedExport::Array(vec![])]  // Empty array = entire exports object
}

3. Marking Exports as Used

// Mark specific export as used
let export_info = exports_info.get_export_info(&mut module_graph, &"exportName".into());
ExportInfoSetter::set_used(
    export_info.as_data_mut(&mut module_graph),
    UsageState::Used,
    Some(&runtime),
);

// Mark entire exports object as used
exports_info.set_used_in_unknown_way(&mut module_graph, Some(&runtime));

Integration Timing

  1. Export Declaration: During dependency parsing via get_exports()
  2. Export Processing: In FlagDependencyExportsPlugin::finish_modules()
  3. Usage Analysis: During dependency analysis via get_referenced_exports()
  4. Usage Processing: In FlagDependencyUsagePlugin::optimize_dependencies()

Conclusion

The ExportInfoDependency is a sophisticated system that enables rspack to provide runtime access to compile-time export analysis results. It's essential for advanced tree-shaking scenarios where conditional code inclusion depends on export usage patterns. The system is designed for performance with prefetching and caching strategies while maintaining type safety and error resilience.

Key Takeaways from ShareUsagePlugin Investigation:

  • Use the correct API patterns for export analysis (ExportsInfoGetter vs ExportInfoGetter)
  • Handle ProvidedExports variants properly in pattern matching
  • Understand ConsumeShared proxy module behavior (empty usage is expected)
  • Use appropriate prefetch modes for different analysis scenarios
  • Focus on dependency analysis for accurate ConsumeShared usage tracking
  • Latest Enhancement: Use module_graph.get_incoming_connections() and dependency.get_referenced_exports() for advanced dependency analysis
  • Pattern Matching: Handle ExtendedReferencedExport::Array and ExtendedReferencedExport::Export patterns for comprehensive export extraction
  • Cross-reference Analysis: Compare extracted usage with provided exports for accurate filtering and optimization decisions

ESM Export Tracking and Dependencies

Overview

Rspack implements comprehensive ESM (ECMAScript Module) export tracking through a sophisticated dependency system that handles both direct exports and re-exports. This system enables precise tree-shaking and export optimization by tracking how exports are defined, used, and transformed throughout the build process.

Core ESM Export Dependencies

1. ESMExportSpecifierDependency

Purpose: Handles direct named exports within a module

pub struct ESMExportSpecifierDependency {
    name: Atom,        // Export name (e.g., "foo" in `export { foo }`)
    value: Atom,       // Local variable/value being exported
    inline: Option<EvaluatedInlinableValue>, // Inlining information
    range: DependencyRange, // Source code position
}

Code Generation Process:

impl DependencyTemplate for ESMExportSpecifierDependencyTemplate {
    fn render(&self, dep: &dyn DependencyCodeGeneration, context: &mut TemplateContext) {
        // 1. Get used name after mangling/optimization
        let used_name = ExportsInfoGetter::get_used_name(
            GetUsedNameParam::WithNames(&exports_info),
            *runtime,
            std::slice::from_ref(&dep.name),
        );
        
        // 2. Generate export definition
        if let Some(UsedName::Normal(used)) = used_name {
            init_fragments.push(Box::new(ESMExportInitFragment::new(
                module.get_exports_argument(),
                vec![(used, dep.value.to_string().into())],
            )));
        }
    }
}

Module Federation Integration: The system includes special handling for ConsumeShared modules:

// Check if this dependency is related to a ConsumeShared module
let consume_shared_info = {
    if let Some(parent_module_id) = module_graph.get_parent_module(&dep.id) {
        if let Some(parent_module) = module_graph.module_by_identifier(parent_module_id) {
            if parent_module.module_type() == &ModuleType::ConsumeShared {
                parent_module.get_consume_shared_key()
            }
        }
    }
};

// Generate conditional exports for tree-shaking
let export_content = if let Some(ref share_key) = consume_shared_info {
    format!(
        "/* @common:if [condition=\"treeShake.{}.{}\"] */ {} /* @common:endif */",
        share_key, dep.name, dep.value
    )
} else {
    dep.value.to_string()
};

2. ESMExportImportedSpecifierDependency

Purpose: Handles re-exports from other modules (export { foo } from './module')

Complex Mode System: The dependency implements a sophisticated mode system to handle different re-export scenarios:

pub enum ExportMode {
    Missing,                           // Target module not found
    Unused(ExportModeUnused),         // Export is not used
    EmptyStar(ExportModeEmptyStar),   // Star re-export with no exports
    ReexportDynamicDefault(ExportModeReexportDynamicDefault), // Dynamic default re-export
    ReexportNamedDefault(ExportModeReexportNamedDefault),     // Named default re-export
    ReexportNamespaceObject(ExportModeReexportNamespaceObject), // Namespace re-export
    ReexportFakeNamespaceObject(ExportModeFakeNamespaceObject), // Fake namespace
    ReexportUndefined(ExportModeReexportUndefined),           // Undefined re-export
    NormalReexport(ExportModeNormalReexport),                 // Normal re-export
    DynamicReexport(ExportModeDynamicReexport),               // Dynamic re-export
}

Mode Determination Logic:

fn get_mode(&self, module_graph: &ModuleGraph, runtime: Option<&RuntimeSpec>) -> ExportMode {
    // 1. Check if export is unused
    let is_name_unused = if let Some(ref name) = name {
        ExportsInfoGetter::get_used(&exports_info_data, std::slice::from_ref(name), runtime)
            == UsageState::Unused
    };
    if is_name_unused {
        return ExportMode::Unused(ExportModeUnused { name: "*".into() });
    }
    
    // 2. Handle special default export cases
    if let Some(name) = name.as_ref() && ids.first() == Some("default") {
        match imported_exports_type {
            ExportsType::Dynamic => {
                return ExportMode::ReexportDynamicDefault(ExportModeReexportDynamicDefault {
                    name: name.clone(),
                });
            }
            ExportsType::DefaultOnly | ExportsType::DefaultWithNamed => {
                return ExportMode::ReexportNamedDefault(ExportModeReexportNamedDefault {
                    name: name.clone(),
                    partial_namespace_export_info: exports_info_data.get_read_only_export_info(name).id(),
                });
            }
        }
    }
    
    // 3. Determine star re-export behavior
    let StarReexportsInfo { exports, checked, ignored_exports, hidden } = 
        self.get_star_reexports(module_graph, runtime, Some(exports_info), imported_module_identifier);
    
    // 4. Return appropriate mode based on analysis
}

3. ESMImportSpecifierDependency

Purpose: Handles import statements that may be used in exports

Referenced Exports Analysis:

fn get_referenced_exports(&self, module_graph: &ModuleGraph, runtime: Option<&RuntimeSpec>) -> Vec<ExtendedReferencedExport> {
    let mut ids = self.get_ids(module_graph);
    
    // Handle namespace imports
    if ids.is_empty() {
        return self.get_referenced_exports_in_destructuring(None);
    }
    
    // Handle default export special cases
    if let Some(id) = ids.first() && id == "default" {
        let exports_type = get_exports_type(module_graph, &self.id, parent_module);
        match exports_type {
            ExportsType::DefaultOnly | ExportsType::DefaultWithNamed => {
                if ids.len() == 1 {
                    return self.get_referenced_exports_in_destructuring(None);
                }
                ids = &ids[1..];
                namespace_object_as_context = true;
            }
            ExportsType::Dynamic => {
                return create_exports_object_referenced();
            }
        }
    }
    
    // Handle property access and destructuring
    if self.call && !self.direct_import && (namespace_object_as_context || ids.len() > 1) {
        if ids.len() == 1 {
            return create_exports_object_referenced();
        }
        ids = &ids[..ids.len() - 1];
    }
    
    self.get_referenced_exports_in_destructuring(Some(ids))
}

Export Fragment Generation

Init Fragment System

ESM exports use an init fragment system for code generation:

pub struct ESMExportInitFragment {
    exports_argument: String,        // __webpack_exports__
    export_map: Vec<(Atom, Atom)>, // [(export_name, export_value)]
}

impl InitFragment for ESMExportInitFragment {
    fn generate(&self, context: &mut GenerateContext) -> String {
        let exports = self.export_map
            .iter()
            .map(|(name, value)| format!("{}: {}", property_name(name), value))
            .collect::<Vec<_>>()
            .join(", ");
            
        format!(
            "/* ESM exports */ {}({}, {{ {} }});\n",
            RuntimeGlobals::DEFINE_PROPERTY_GETTERS,
            self.exports_argument,
            exports
        )
    }
}

Conditional Export Generation

For module federation and tree-shaking:

fn get_reexport_fragment(&self, ctxt: &mut TemplateContext, comment: &str, key: String, name: &str, value_key: ValueKey) -> ESMExportInitFragment {
    // Check for ConsumeShared module context
    let consume_shared_info = self.get_consume_shared_context(module_graph);
    
    // Generate conditional export content
    let export_content = if let Some(ref share_key) = consume_shared_info {
        format!(
            "/* @common:if [condition=\"treeShake.{}.{}\"] */ /* {comment} */ {return_value} /* @common:endif */",
            share_key, key
        )
    } else {
        format!(
            "/* EXPORT_BEGIN:{} */ /* {comment} */ {return_value} /* EXPORT_END:{} */",
            key, key
        )
    };
    
    ESMExportInitFragment::new(module.get_exports_argument(), vec![(key.into(), export_content.into())])
}

Star Re-export Handling

Star Re-export Information Collection

pub struct StarReexportsInfo {
    exports: Option<HashSet<Atom>>,    // Available exports
    checked: Option<HashSet<Atom>>,    // Exports that need runtime checks
    ignored_exports: HashSet<Atom>,    // Exports to ignore
    hidden: Option<HashSet<Atom>>,     // Hidden exports
}

fn get_star_reexports(&self, module_graph: &ModuleGraph, runtime: Option<&RuntimeSpec>) -> StarReexportsInfo {
    let exports_info = module_graph.get_exports_info(parent_module).as_data(module_graph);
    let imported_exports_info = module_graph.get_exports_info(imported_module_identifier).as_data(module_graph);
    
    // Check if we can determine exports statically
    let no_extra_exports = matches!(
        imported_exports_info.other_exports_info().as_data(module_graph).provided(),
        Some(ExportProvided::NotProvided)
    );
    let no_extra_imports = matches!(
        ExportInfoGetter::get_used(exports_info.other_exports_info().as_data(module_graph), runtime),
        UsageState::Unused
    );
    
    if !no_extra_exports && !no_extra_imports {
        return StarReexportsInfo {
            ignored_exports: self.active_exports(module_graph).clone(),
            hidden: self.discover_active_exports_from_other_star_exports(module_graph),
            ..Default::default()
        };
    }
    
    // Collect static export information
    let mut exports = HashSet::default();
    let mut checked = HashSet::default();
    
    // Process each export from the imported module
    for imported_export_info in imported_exports_info.exports() {
        let imported_export_info_data = imported_export_info.as_data(module_graph);
        let imported_export_info_name = imported_export_info_data.name().cloned().unwrap_or_default();
        
        // Skip ignored exports
        if ignored_exports.contains(&imported_export_info_name) ||
           matches!(imported_export_info_data.provided(), Some(ExportProvided::NotProvided)) {
            continue;
        }
        
        // Check if export is used
        let export_info = exports_info.id().get_read_only_export_info(module_graph, &imported_export_info_name);
        if matches!(ExportInfoGetter::get_used(export_info.as_data(module_graph), runtime), UsageState::Unused) {
            continue;
        }
        
        exports.insert(imported_export_info_name.clone());
        
        // Mark for runtime checking if provision is uncertain
        if !matches!(imported_export_info_data.provided(), Some(ExportProvided::Provided)) {
            checked.insert(imported_export_info_name);
        }
    }
    
    StarReexportsInfo {
        ignored_exports,
        exports: Some(exports),
        checked: Some(checked),
        hidden: None,
    }
}

Performance Optimizations

1. Export Information Prefetching

// Prefetch export information to avoid repeated queries
let exports_info = module_graph.get_prefetched_exports_info(
    &module.identifier(),
    PrefetchExportsInfoMode::NamedExports(HashSet::from_iter([&dep.name])),
);

2. Used Name Caching

// Cache used names to avoid recomputation
let used_name = ExportsInfoGetter::get_used_name(
    GetUsedNameParam::WithNames(&exports_info),
    *runtime,
    std::slice::from_ref(&dep.name),
);

3. Mode Caching

// Cache export modes for re-export dependencies
let key = (self.id, runtime.map(|runtime| get_runtime_key(runtime).to_owned()));
module_graph_cache.cached_get_mode(key, || {
    self.get_mode_inner(module_graph, module_graph_cache, runtime)
})

Debug and Tracing

Comprehensive Logging

The system includes detailed logging for module federation scenarios:

tracing::debug!(
    "[RSPACK_EXPORT_DEBUG:ESM_SPECIFIER_DETAILED] Module: {:?}, Type: {:?}, Layer: {:?}, Name: {:?}, Value: {:?}, DependencyId: {:?}",
    module_identifier,
    module.module_type(),
    module.get_layer(),
    dep.name,
    dep.value,
    dep.id()
);

Integration Points

1. Flag Dependency Plugins

  • FlagDependencyExportsPlugin: Populates export provision information
  • FlagDependencyUsagePlugin: Tracks export usage patterns
  • ESM Dependencies: Consume and act on this information

2. Module Graph

  • Central storage for export metadata
  • Provides caching and prefetching mechanisms
  • Maintains relationships between modules and dependencies

3. Template System

  • Generates optimized export code
  • Handles conditional exports for tree-shaking
  • Integrates with init fragment system for code organization

Conclusion

The ESM export tracking system in rspack provides comprehensive analysis and optimization of ES module exports and re-exports. Through sophisticated dependency types, mode analysis, and conditional code generation, it enables precise tree-shaking while maintaining correctness across complex re-export scenarios. The system is designed for performance with extensive caching and prefetching, while providing detailed debugging capabilities for complex module federation use cases.

CommonJS Export Analysis and Dependencies

Overview

Rspack's CommonJS export system handles the complex semantics of CommonJS modules, including module.exports, exports, and this assignment patterns. The system provides comprehensive tracking and optimization capabilities while maintaining compatibility with Node.js module semantics.

Core CommonJS Export Components

1. CommonJsExportsDependency Structure

pub struct CommonJsExportsDependency {
    id: DependencyId,
    range: DependencyRange,              // Source code position of export assignment
    value_range: Option<DependencyRange>, // Position of assigned value
    base: ExportsBase,                   // Type of export base (exports, module.exports, this)
    names: Vec<Atom>,                    // Property path (e.g., ["foo", "bar"] for exports.foo.bar)
}

2. Export Base Types

The system handles multiple CommonJS export patterns:

#[derive(Debug, Clone, Copy)]
pub enum ExportsBase {
    Exports,                    // exports.foo = value
    ModuleExports,             // module.exports.foo = value  
    This,                      // this.foo = value
    DefinePropertyExports,     // Object.defineProperty(exports, ...)
    DefinePropertyModuleExports, // Object.defineProperty(module.exports, ...)
    DefinePropertyThis,        // Object.defineProperty(this, ...)
}

Base Type Classification Methods:

impl ExportsBase {
    pub const fn is_exports(&self) -> bool {
        matches!(self, Self::Exports | Self::DefinePropertyExports)
    }
    
    pub const fn is_module_exports(&self) -> bool {
        matches!(self, Self::ModuleExports | Self::DefinePropertyModuleExports)
    }
    
    pub const fn is_this(&self) -> bool {
        matches!(self, Self::This | Self::DefinePropertyThis)
    }
    
    pub const fn is_expression(&self) -> bool {
        matches!(self, Self::Exports | Self::ModuleExports | Self::This)
    }
    
    pub const fn is_define_property(&self) -> bool {
        matches!(self, Self::DefinePropertyExports | Self::DefinePropertyModuleExports | Self::DefinePropertyThis)
    }
}

Export Information Generation

1. Export Specification Creation

impl Dependency for CommonJsExportsDependency {
    fn get_exports(&self, _mg: &ModuleGraph, _mg_cache: &ModuleGraphCacheArtifact) -> Option<ExportsSpec> {
        let vec = vec![ExportNameOrSpec::ExportSpec(ExportSpec {
            name: self.names[0].clone(),
            can_mangle: Some(false), // CommonJS object properties may not be mangled
            ..Default::default()
        })];
        
        Some(ExportsSpec {
            exports: ExportsOfExportsSpec::Names(vec),
            ..Default::default()
        })
    }
}

Key Characteristics:

  • Non-mangleable: CommonJS exports typically cannot be mangled due to dynamic property access patterns
  • Property-based: Each export is treated as an object property assignment
  • Runtime semantics: Maintains Node.js-compatible behavior

Template Rendering and Code Generation

1. Base Expression Handling

The rendering process varies based on the export base type:

impl DependencyTemplate for CommonJsExportsDependencyTemplate {
    fn render(&self, dep: &dyn DependencyCodeGeneration, source: &mut TemplateReplaceSource, context: &mut TemplateContext) {
        // 1. Determine runtime base expression
        let base = if dep.base.is_exports() {
            runtime_requirements.insert(RuntimeGlobals::EXPORTS);
            exports_argument.to_string()
        } else if dep.base.is_module_exports() {
            runtime_requirements.insert(RuntimeGlobals::MODULE);
            format!("{module_argument}.exports")
        } else if dep.base.is_this() {
            runtime_requirements.insert(RuntimeGlobals::THIS_AS_EXPORTS);
            "this".to_string()
        };
        
        // 2. Handle expression vs defineProperty patterns
        if dep.base.is_expression() {
            self.render_expression_assignment(dep, source, context, base);
        } else if dep.base.is_define_property() {
            self.render_define_property(dep, source, context, base);
        }
    }
}

2. Expression Assignment Rendering

For direct property assignments (exports.foo = value):

fn render_expression_assignment(&self, dep: &CommonJsExportsDependency, source: &mut TemplateReplaceSource, context: &mut TemplateContext, base: String) {
    // Get used name after tree-shaking analysis
    let used = ExportsInfoGetter::get_used_name(
        GetUsedNameParam::WithNames(&exports_info),
        *runtime,
        &dep.names,
    );
    
    if let Some(UsedName::Normal(used)) = used {
        let export_assignment = format!("{}{}", base, property_access(&used, 0));
        let export_name = used.iter().map(|a| a.as_str()).collect::<Vec<_>>().join(".");
        
        // Generate conditional exports for module federation
        let export_content = if let Some(ref share_key) = consume_shared_info {
            format!(
                "/* @common:if [condition=\"treeShake.{}.{}\"] */ {} /* @common:endif */",
                share_key, export_name, export_assignment
            )
        } else {
            format!(
                "/* EXPORT_BEGIN:{} */ {} /* EXPORT_END:{} */",
                export_name, export_assignment, export_name
            )
        };
        
        source.replace(dep.range.start, dep.range.end, &export_content, None);
    } else {
        // Handle unused exports
        let placeholder_var = format!("__webpack_{}_export__", if is_inlined { "inlined" } else { "unused" });
        source.replace(dep.range.start, dep.range.end, &placeholder_var, None);
        
        // Add placeholder variable declaration
        init_fragments.push(NormalInitFragment::new(
            format!("var {placeholder_var};\n"),
            InitFragmentStage::StageConstants,
            0,
            InitFragmentKey::CommonJsExports(placeholder_var),
            None,
        ).boxed());
    }
}

3. DefineProperty Handling

For Object.defineProperty patterns:

fn render_define_property(&self, dep: &CommonJsExportsDependency, source: &mut TemplateReplaceSource, context: &mut TemplateContext, base: String) {
    if let Some(value_range) = &dep.value_range {
        if let Some(UsedName::Normal(used)) = used {
            if !used.is_empty() {
                let export_name = used.last().unwrap();
                
                // Generate defineProperty call with conditional wrapping
                let define_property_start = if let Some(ref share_key) = consume_shared_info {
                    format!(
                        "/* @common:if [condition=\"treeShake.{}.{}\"] */ Object.defineProperty({}{}, {}, (",
                        share_key, export_name, base, property_access(used[0..used.len() - 1].iter(), 0),
                        serde_json::to_string(&used.last()).expect("Unexpected render define property base")
                    )
                } else {
                    format!(
                        "/* EXPORT_BEGIN:{} */ Object.defineProperty({}{}, {}, (",
                        export_name, base, property_access(used[0..used.len() - 1].iter(), 0),
                        serde_json::to_string(&used.last()).expect("Unexpected render define property base")
                    )
                };
                
                source.replace(dep.range.start, value_range.start, &define_property_start, None);
                
                let define_property_end = if consume_shared_info.is_some() {
                    ")) /* @common:endif */"
                } else {
                    &format!(")) /* EXPORT_END:{} */", export_name)
                };
                
                source.replace(value_range.end, dep.range.end, define_property_end, None);
            }
        } else {
            // Handle unused defineProperty exports
            init_fragments.push(NormalInitFragment::new(
                "var __webpack_unused_export__;\n".to_string(),
                InitFragmentStage::StageConstants,
                0,
                InitFragmentKey::CommonJsExports("__webpack_unused_export__".to_owned()),
                None,
            ).boxed());
            
            source.replace(dep.range.start, value_range.start, "__webpack_unused_export__ = (", None);
            source.replace(value_range.end, dep.range.end, ")", None);
        }
    }
}

Module Federation Integration

1. ConsumeShared Module Detection

The system includes sophisticated detection for module federation scenarios:

fn get_consume_shared_context(&self, module_graph: &ModuleGraph, dep_id: &DependencyId) -> Option<String> {
    if let Some(parent_module_id) = module_graph.get_parent_module(dep_id) {
        if let Some(parent_module) = module_graph.module_by_identifier(parent_module_id) {
            if parent_module.module_type() == &ModuleType::ConsumeShared {
                let trait_result = parent_module.get_consume_shared_key();
                
                // Enhanced debugging for ConsumeShared modules
                tracing::debug!(
                    "[RSPACK_EXPORT_DEBUG:CJS_RENDER_DETAILED] Module: {:?}, Type: {:?}, Layer: {:?}, Names: {:?}, Base: {:?}",
                    module_identifier, module.module_type(), module.get_layer(), dep.names, dep.base
                );
                
                return trait_result;
            }
        }
    }
    None
}

2. Conditional Export Generation

For tree-shaking in module federation contexts:

fn generate_conditional_export(&self, share_key: &str, export_name: &str, assignment: &str) -> String {
    format!(
        "/* @common:if [condition=\"treeShake.{}.{}\"] */ {} /* @common:endif */",
        share_key, export_name, assignment
    )
}

Runtime Requirements Management

1. Runtime Globals Insertion

The system manages runtime requirements based on export patterns:

fn insert_runtime_requirements(&self, base: &ExportsBase, runtime_requirements: &mut RuntimeRequirements) {
    match base {
        ExportsBase::Exports | ExportsBase::DefinePropertyExports => {
            runtime_requirements.insert(RuntimeGlobals::EXPORTS);
        }
        ExportsBase::ModuleExports | ExportsBase::DefinePropertyModuleExports => {
            runtime_requirements.insert(RuntimeGlobals::MODULE);
        }
        ExportsBase::This | ExportsBase::DefinePropertyThis => {
            runtime_requirements.insert(RuntimeGlobals::THIS_AS_EXPORTS);
        }
    }
}

2. Property Access Generation

Safe property access generation for nested exports:

fn generate_property_access(names: &[Atom], start_index: usize) -> String {
    names[start_index..]
        .iter()
        .map(|name| {
            if is_valid_identifier(name) {
                format!(".{}", name)
            } else {
                format!("[{}]", serde_json::to_string(name).unwrap())
            }
        })
        .collect::<String>()
}

Export Usage Analysis Integration

1. Used Name Resolution

fn resolve_used_names(&self, module_graph: &ModuleGraph, names: &[Atom], runtime: Option<&RuntimeSpec>) -> Option<UsedName> {
    if names.is_empty() {
        let exports_info = ExportsInfoGetter::prefetch_used_info_without_name(
            &module_graph.get_exports_info(&module.identifier()),
            module_graph,
            runtime,
            false,
        );
        ExportsInfoGetter::get_used_name(
            GetUsedNameParam::WithoutNames(&exports_info),
            runtime,
            names,
        )
    } else {
        let exports_info = module_graph.get_prefetched_exports_info(
            &module.identifier(),
            PrefetchExportsInfoMode::NamedNestedExports(names),
        );
        ExportsInfoGetter::get_used_name(
            GetUsedNameParam::WithNames(&exports_info),
            runtime,
            names,
        )
    }
}

2. Tree-Shaking Integration

The system integrates with flag dependency plugins:

// Export information flows from analysis to code generation:
// 1. FlagDependencyExportsPlugin -> identifies provided exports
// 2. FlagDependencyUsagePlugin -> tracks export usage
// 3. CommonJsExportsDependency -> generates optimized code based on usage

Performance Optimizations

1. Lazy Fragment Creation

// Only create init fragments when needed
if unused_exports.is_empty() {
    // No fragments needed
} else {
    init_fragments.push(create_unused_export_fragment(unused_exports));
}

2. Efficient Property Access

// Use property_access utility for optimized property chain generation
let property_chain = property_access(&used_names, 0);
// Generates: .foo.bar or ["foo"]["bar"] based on identifier validity

3. Runtime Requirement Optimization

// Only insert required runtime globals
match base_type {
    ExportsBase::Exports => runtime_requirements.insert(RuntimeGlobals::EXPORTS),
    ExportsBase::ModuleExports => runtime_requirements.insert(RuntimeGlobals::MODULE),
    // ... other cases
}

Error Handling and Edge Cases

1. Missing Value Range

if let Some(value_range) = &dep.value_range {
    // Handle defineProperty with value
} else {
    panic!("Define property need value range");
}

2. Invalid Export Names

// Handle special characters in export names
let property_name = if is_valid_identifier(&name) {
    name.to_string()
} else {
    serde_json::to_string(&name).expect("Invalid export name")
};

3. Unused Export Handling

// Generate placeholder for unused exports to maintain side effects
let placeholder = if is_inlined {
    "__webpack_inlined_export__"
} else {
    "__webpack_unused_export__"
};

Debugging and Diagnostics

1. Comprehensive Logging

tracing::debug!(
    "[RSPACK_EXPORT_DEBUG:CJS_ASSIGNMENT] Module: {:?}, Export: {}, Assignment: {}, Used: {:?}, Range: {:?}",
    module_identifier, export_name, export_assignment, used, dep.range
);

2. Module Build Information

// Log module context for debugging
if let Some(normal_module) = module.as_normal_module() {
    tracing::debug!(
        "[RSPACK_EXPORT_DEBUG:CJS_MODULE_BUILD_INFO] Request: {:?}, UserRequest: {:?}, RawRequest: {:?}",
        normal_module.request(), normal_module.user_request(), normal_module.raw_request()
    );
}

Conclusion

The CommonJS export system in rspack provides comprehensive support for all CommonJS export patterns while maintaining optimization capabilities through integration with the flag dependency plugin system. The system handles complex scenarios including module federation, tree-shaking, and property mangling while preserving Node.js compatibility. The sophisticated template rendering system ensures optimal code generation for various export patterns, from simple property assignments to complex Object.defineProperty calls.

Flag Dependency Plugins Analysis

Overview

The Flag Dependency plugins are the cornerstone of rspack's tree-shaking system. These plugins work in tandem to collect comprehensive export information and track usage patterns across the module graph, enabling precise dead code elimination and export optimization.

Plugin Architecture

1. Two-Phase Analysis System

// Phase 1: Export Provision Analysis
FlagDependencyExportsPlugin -> Identifies what exports are provided
// Phase 2: Export Usage Analysis  
FlagDependencyUsagePlugin -> Tracks which exports are actually used

Execution Order:

  1. FlagDependencyExportsPlugin runs during CompilationFinishModules
  2. FlagDependencyUsagePlugin runs during CompilationOptimizeDependencies

This ensures that all export information is collected before usage analysis begins.

FlagDependencyExportsPlugin

Core Responsibilities

  1. Export Provision Tracking: Determines what exports each module provides
  2. Export Metadata Collection: Gathers information about mangling capabilities, inlining potential, and terminal bindings
  3. Dependency Chain Analysis: Tracks how exports flow through re-exports and module connections

Implementation Architecture

1. FlagDependencyExportsState

struct FlagDependencyExportsState<'a> {
    mg: &'a mut ModuleGraph<'a>,              // Module graph for export information
    mg_cache: &'a ModuleGraphCacheArtifact,   // Caching for performance
    changed: bool,                            // Tracks if any changes occurred
    current_module_id: ModuleIdentifier,      // Currently processing module
    dependencies: IdentifierMap<IdentifierSet>, // Dependency tracking for invalidation
}

2. Processing Algorithm

Queue-Based Processing:

pub fn apply(&mut self, modules: IdentifierSet) {
    let mut q = Queue::new();
    
    // 1. Initialize all modules and reset export information
    for module_id in modules {
        let exports_info = mgm.exports;
        exports_info.reset_provide_info(self.mg);
        
        // Handle modules without exports
        let is_module_without_exports = 
            module.build_meta().exports_type == BuildMetaExportsType::Unset;
        if is_module_without_exports {
            exports_info.set_unknown_exports_provided(self.mg, false, None, None, None, None);
            continue;
        }
        
        exports_info.set_has_provide_info(self.mg);
        q.enqueue(module_id);
    }
    
    // 2. Process modules until no more changes occur
    while let Some(module_id) = q.dequeue() {
        self.changed = false;
        self.current_module_id = module_id;
        
        // Process all dependencies to collect export specifications
        self.process_dependencies_block(&module_id, &mut exports_specs_from_dependencies);
        
        // Apply collected export specifications
        let exports_info = self.mg.get_exports_info(&module_id);
        for (dep_id, exports_spec) in exports_specs_from_dependencies.iter() {
            self.process_exports_spec(*dep_id, exports_spec, exports_info);
        }
        
        // If changes occurred, notify dependent modules
        if self.changed {
            self.notify_dependencies(&mut q);
        }
    }
}

3. Export Specification Processing

ExportsSpec Analysis:

pub fn process_exports_spec(&mut self, dep_id: DependencyId, export_desc: &ExportsSpec, exports_info: ExportsInfo) {
    match &export_desc.exports {
        ExportsOfExportsSpec::UnknownExports => {
            // Handle dynamic exports (require.context, etc.)
            if exports_info.set_unknown_exports_provided(
                self.mg,
                global_can_mangle.unwrap_or_default(),
                export_desc.exclude_exports.as_ref(),
                global_from.map(|_| dep_id),
                global_from.map(|_| dep_id),
                *global_priority,
            ) {
                self.changed = true;
            }
        }
        ExportsOfExportsSpec::NoExports => {
            // Module provides no exports
        }
        ExportsOfExportsSpec::Names(ele) => {
            // Named exports - most common case
            self.merge_exports(exports_info, ele, DefaultExportInfo {
                can_mangle: *global_can_mangle,
                terminal_binding: global_terminal_binding,
                from: global_from,
                priority: *global_priority,
            }, dep_id);
        }
    }
    
    // Track dependency relationships for invalidation
    if let Some(export_dependencies) = export_dependencies {
        for export_dep in export_dependencies {
            self.dependencies.entry(*export_dep)
                .or_insert_with(IdentifierSet::new)
                .insert(self.current_module_id);
        }
    }
}

4. Export Merging Logic

Complex Export Handling:

pub fn merge_exports(&mut self, exports_info: ExportsInfo, exports: &Vec<ExportNameOrSpec>, global_export_info: DefaultExportInfo, dep_id: DependencyId) {
    for export_name_or_spec in exports {
        let (name, can_mangle, terminal_binding, exports, from, from_export, priority, hidden, inlinable) = 
            self.extract_export_properties(export_name_or_spec, &global_export_info);
        
        let export_info = exports_info.get_export_info(self.mg, &name);
        let export_info_data = export_info.as_data_mut(self.mg);
        
        // Update provision status
        if let Some(provided) = export_info_data.provided() 
            && matches!(provided, ExportProvided::NotProvided | ExportProvided::Unknown) {
            export_info_data.set_provided(Some(ExportProvided::Provided));
            self.changed = true;
        }
        
        // Update mangling capabilities
        if Some(false) != export_info_data.can_mangle_provide() && can_mangle == Some(false) {
            export_info_data.set_can_mangle_provide(Some(false));
            self.changed = true;
        }
        
        // Update inlining capabilities
        if let Some(inlined) = inlinable && !export_info_data.inlinable().can_inline() {
            export_info_data.set_inlinable(Inlinable::Inlined(inlined));
            self.changed = true;
        }
        
        // Handle nested exports (object properties)
        if let Some(exports) = exports {
            let nested_exports_info = ExportInfoSetter::create_nested_exports_info(&export_info, self.mg);
            self.merge_exports(nested_exports_info, exports, global_export_info.clone(), dep_id);
        }
        
        // Set up target relationships for re-exports
        if let Some(from) = from {
            let changed = if hidden {
                ExportInfoSetter::unset_target(export_info_data, &dep_id)
            } else {
                ExportInfoSetter::set_target(
                    export_info_data,
                    Some(dep_id),
                    Some(from.dependency_id),
                    export_name,
                    priority,
                )
            };
            self.changed |= changed;
        }
    }
}

How Dependencies Add Exports

Dependencies declare their exports through the get_exports() method, which returns an ExportsSpec:

1. Simple Named Export

fn get_exports(&self, _mg: &ModuleGraph, _mg_cache: &ModuleGraphCacheArtifact) -> Option<ExportsSpec> {
    Some(ExportsSpec {
        exports: ExportsOfExportsSpec::Names(vec![
            ExportNameOrSpec::String("myExport".into())
        ]),
        terminal_binding: Some(true),
        ..Default::default()
    })
}

2. Export with Metadata

fn get_exports(&self, _mg: &ModuleGraph, _mg_cache: &ModuleGraphCacheArtifact) -> Option<ExportsSpec> {
    Some(ExportsSpec {
        exports: ExportsOfExportsSpec::Names(vec![
            ExportNameOrSpec::ExportSpec(ExportSpec {
                name: "complexExport".into(),
                can_mangle: Some(false),
                terminal_binding: Some(true),
                priority: Some(1),
                inlinable: Some(EvaluatedInlinableValue::String("value".into())),
                ..Default::default()
            })
        ]),
        ..Default::default()
    })
}

3. Re-export from Another Module

fn get_exports(&self, _mg: &ModuleGraph, _mg_cache: &ModuleGraphCacheArtifact) -> Option<ExportsSpec> {
    Some(ExportsSpec {
        exports: ExportsOfExportsSpec::Names(vec![
            ExportNameOrSpec::ExportSpec(ExportSpec {
                name: "reexported".into(),
                export: Some(Nullable::Value(vec!["originalName".into()])),
                from: Some(target_connection),
                ..Default::default()
            })
        ]),
        from: Some(target_connection),
        ..Default::default()
    })
}

4. Dynamic Exports

fn get_exports(&self, _mg: &ModuleGraph, _mg_cache: &ModuleGraphCacheArtifact) -> Option<ExportsSpec> {
    Some(ExportsSpec {
        exports: ExportsOfExportsSpec::UnknownExports, // For require.context(), etc.
        can_mangle: Some(false),
        ..Default::default()
    })
}

## FlagDependencyUsagePlugin

### Core Responsibilities

1. **Usage State Tracking**: Marks exports as Used, Unused, OnlyPropertiesUsed, etc.
2. **Entry Point Analysis**: Starts usage analysis from application entry points
3. **Transitive Usage**: Follows module dependencies to track usage propagation
4. **Side Effects Handling**: Accounts for modules that must be executed for side effects

### Implementation Architecture

#### 1. FlagDependencyUsagePluginProxy

```rust
pub struct FlagDependencyUsagePluginProxy<'a> {
    global: bool,                             // Global vs per-runtime analysis
    compilation: &'a mut Compilation,         // Compilation context
    exports_info_module_map: UkeyMap<ExportsInfo, ModuleIdentifier>, // Reverse mapping
}

2. Entry Point Processing

Starting from Entry Dependencies:

fn apply(&mut self) {
    // Initialize all exports info with usage tracking
    for exports_info in self.exports_info_module_map.keys() {
        exports_info.set_has_use_info(mg);
    }
    
    // Process entry points
    for (entry_name, entry) in entries.iter() {
        let runtime = if self.global {
            None
        } else {
            Some(get_entry_runtime(entry_name, &entry.options, &entries))
        };
        
        for &dep in entry.dependencies.iter() {
            self.process_entry_dependency(dep, runtime.clone(), &mut q);
        }
    }
    
    // Process all modules reachable from entries
    while let Some((module_id, runtime)) = q.dequeue() {
        self.process_module(ModuleOrAsyncDependenciesBlock::Module(module_id), runtime, false, &mut q);
    }
}

3. Module Processing Algorithm

Comprehensive Dependency Analysis:

fn process_module(&mut self, block_id: ModuleOrAsyncDependenciesBlock, runtime: Option<RuntimeSpec>, force_side_effects: bool, q: &mut Queue<(ModuleIdentifier, Option<RuntimeSpec>)>) {
    let mut map: IdentifierMap<ProcessModuleReferencedExports> = IdentifierMap::default();
    let mut queue = VecDeque::new();
    queue.push_back(block_id);
    
    // Traverse all dependencies in the module
    while let Some(module_id) = queue.pop_front() {
        let (blocks, dependencies) = self.get_module_dependencies(module_id);
        
        // Process async dependency blocks
        for block_id in blocks {
            if !self.global && has_entrypoint_options(&block_id) {
                let runtime = RuntimeSpec::from_entry_options(options);
                self.process_module(AsyncDependenciesBlock(block_id), runtime, true, q);
            } else {
                queue.push_back(AsyncDependenciesBlock(block_id));
            }
        }
        
        // Process each dependency
        for dep_id in dependencies {
            let connection = module_graph.connection_by_dependency_id(&dep_id);
            let active_state = connection.active_state(&module_graph, runtime.as_ref(), module_graph_cache);
            
            match active_state {
                ConnectionState::Active(false) => continue,
                ConnectionState::TransitiveOnly => {
                    // Module is needed but exports aren't used
                    self.process_module(Module(*connection.module_identifier()), runtime.clone(), false, q);
                    continue;
                }
                _ => {}
            }
            
            // Get referenced exports from dependency
            let referenced_exports = if let Some(md) = dep.as_module_dependency() {
                md.get_referenced_exports(&module_graph, module_graph_cache, runtime.as_ref())
            } else if dep.as_context_dependency().is_some() {
                vec![ExtendedReferencedExport::Array(vec![])]
            } else {
                continue;
            };
            
            // Merge with existing references
            self.merge_referenced_exports(connection.module_identifier(), referenced_exports, &mut map);
        }
    }
    
    // Process all referenced modules
    for (module_id, referenced_exports) in map {
        self.process_referenced_module(module_id, referenced_exports, runtime.clone(), force_side_effects, q);
    }
}

4. Referenced Export Processing

Usage State Application:

fn process_referenced_module(&mut self, module_id: ModuleIdentifier, used_exports: Vec<ExtendedReferencedExport>, runtime: Option<RuntimeSpec>, force_side_effects: bool, queue: &mut Queue<(ModuleIdentifier, Option<RuntimeSpec>)>) {
    let mgm_exports_info = mgm.exports;
    
    if !used_exports.is_empty() {
        // Handle modules without export information
        if matches!(module.build_meta().exports_type, BuildMetaExportsType::Unset) {
            let flag = mgm_exports_info.set_used_without_info(&mut module_graph, runtime.as_ref());
            if flag {
                queue.enqueue((module_id, None));
            }
            return;
        }
        
        // Process each used export
        for used_export_info in used_exports {
            let (used_exports, can_mangle, can_inline) = match used_export_info {
                ExtendedReferencedExport::Array(used_exports) => (used_exports, true, true),
                ExtendedReferencedExport::Export(export) => (export.name, export.can_mangle, export.can_inline),
            };
            
            if used_exports.is_empty() {
                // Unknown usage pattern - mark as used in unknown way
                let flag = mgm_exports_info.set_used_in_unknown_way(&mut module_graph, runtime.as_ref());
                if flag {
                    queue.enqueue((module_id, runtime.clone()));
                }
            } else {
                // Track specific export usage
                let mut current_exports_info = mgm_exports_info;
                for (i, used_export) in used_exports.into_iter().enumerate() {
                    let export_info = current_exports_info.get_export_info(&mut module_graph, &used_export);
                    
                    // Apply mangling and inlining constraints
                    if !can_mangle {
                        export_info.as_data_mut(&mut module_graph).set_can_mangle_use(Some(false));
                    }
                    if !can_inline {
                        export_info.as_data_mut(&mut module_graph).set_inlinable(Inlinable::NoByUse);
                    }
                    
                    let last_one = i == used_exports.len() - 1;
                    let usage_state = if last_one {
                        UsageState::Used
                    } else {
                        UsageState::OnlyPropertiesUsed
                    };
                    
                    // Set usage state conditionally
                    let changed_flag = ExportInfoSetter::set_used_conditionally(
                        export_info.as_data_mut(&mut module_graph),
                        Box::new(|used| used != &usage_state),
                        usage_state,
                        runtime.as_ref(),
                    );
                    
                    if changed_flag {
                        queue.enqueue((module_id, runtime.clone()));
                    }
                    
                    // Continue to nested exports if not the last one
                    if !last_one {
                        if let Some(nested_info) = export_info.as_data(&module_graph).exports_info() {
                            current_exports_info = nested_info;
                        }
                    }
                }
            }
        }
    } else {
        // Module is used for side effects only
        if !force_side_effects && is_side_effect_free(&module) {
            return;
        }
        
        let changed_flag = mgm_exports_info.set_used_for_side_effects_only(&mut module_graph, runtime.as_ref());
        if changed_flag {
            queue.enqueue((module_id, runtime));
        }
    }
}

How Dependencies Set Referenced Exports

Dependencies specify which exports they use through the get_referenced_exports() method:

1. Specific Export References

fn get_referenced_exports(
    &self,
    _module_graph: &ModuleGraph,
    _module_graph_cache: &ModuleGraphCacheArtifact,
    _runtime: Option<&RuntimeSpec>,
) -> Vec<ExtendedReferencedExport> {
    vec![
        // Reference to obj.specific.export
        ExtendedReferencedExport::Array(vec!["specific".into(), "export".into()]),
        // Reference with usage constraints
        ExtendedReferencedExport::Export(ReferencedExport {
            name: vec!["another".into()],
            can_mangle: false, // Cannot be mangled
            can_inline: true,  // Can be inlined
        }),
    ]
}

2. Namespace References

fn get_referenced_exports(
    &self,
    _module_graph: &ModuleGraph,
    _module_graph_cache: &ModuleGraphCacheArtifact,
    _runtime: Option<&RuntimeSpec>,
) -> Vec<ExtendedReferencedExport> {
    // Empty array means reference to entire exports object
    create_exports_object_referenced()
}

3. No References

fn get_referenced_exports(
    &self,
    _module_graph: &ModuleGraph,
    _module_graph_cache: &ModuleGraphCacheArtifact,
    _runtime: Option<&RuntimeSpec>,
) -> Vec<ExtendedReferencedExport> {
    // Module doesn't reference any exports
    create_no_exports_referenced()
}

4. Conditional References

fn get_referenced_exports(
    &self,
    module_graph: &ModuleGraph,
    _module_graph_cache: &ModuleGraphCacheArtifact,
    runtime: Option<&RuntimeSpec>,
) -> Vec<ExtendedReferencedExport> {
    let mut references = Vec::new();
    
    // Conditional logic based on module analysis
    if self.should_reference_default(module_graph) {
        references.push(ExtendedReferencedExport::Array(vec!["default".into()]));
    }
    
    if self.should_reference_named(module_graph) {
        references.push(ExtendedReferencedExport::Array(vec!["namedExport".into()]));
    }
    
    references
}

Referenced Export Types and Helpers

pub enum ExtendedReferencedExport {
    Array(Vec<Atom>),           // Path to specific export
    Export(ReferencedExport),   // Export with metadata
}

pub struct ReferencedExport {
    pub name: Vec<Atom>,   // Export path
    pub can_mangle: bool,  // Mangling constraint
    pub can_inline: bool,  // Inlining hint
}

// Utility functions
pub fn create_no_exports_referenced() -> Vec<ExtendedReferencedExport> {
    vec![]
}

pub fn create_exports_object_referenced() -> Vec<ExtendedReferencedExport> {
    vec![ExtendedReferencedExport::Array(vec![])]  // Empty = entire exports object
}

Performance Optimizations

1. Incremental Processing

FlagDependencyExportsPlugin:

let modules: IdentifierSet = if let Some(mutations) = compilation
    .incremental
    .mutations_read(IncrementalPasses::PROVIDED_EXPORTS) {
    // Only process affected modules
    mutations.get_affected_modules_with_module_graph(&compilation.get_module_graph())
} else {
    // Full rebuild - process all modules
    compilation.get_module_graph().modules().keys().copied().collect()
};

2. Caching Strategy

Module Graph Caching:

// Freeze cache during processing for consistency
self.mg_cache.freeze();
self.process_dependencies_block(&module_id, &mut exports_specs_from_dependencies, self.mg_cache);
self.mg_cache.unfreeze();

3. Change Tracking

Efficient Invalidation:

// Track dependency relationships for targeted invalidation
if let Some(export_dependencies) = export_dependencies {
    for export_dep in export_dependencies {
        match self.dependencies.entry(*export_dep) {
            Entry::Occupied(mut occ) => {
                occ.get_mut().insert(self.current_module_id);
            }
            Entry::Vacant(vac) => {
                vac.insert(IdentifierSet::from_iter([self.current_module_id]));
            }
        }
    }
}

Export State Management

1. ExportProvided States

pub enum ExportProvided {
    Provided,      // Export is definitely provided
    NotProvided,   // Export is definitely not provided
    Unknown,       // Export provision is unknown (dynamic)
}

2. UsageState States

pub enum UsageState {
    Used,                 // Export is used
    OnlyPropertiesUsed,   // Only properties of export are used
    Unused,               // Export is not used
    NoInfo,               // No usage information available
    Unknown,              // Usage is unknown (dynamic)
}

3. Inlinable States

pub enum Inlinable {
    Inlined(EvaluatedInlinableValue), // Can be inlined with specific value
    NoByUse,                          // Cannot inline due to usage pattern
    NoByProvide,                      // Cannot inline due to provision pattern
}

Integration with Module Graph

1. ExportsInfo Structure

The plugins work with the central ExportsInfo data structure:

// Each module has an ExportsInfo that tracks:
// - Individual export information (ExportInfo)
// - Overall export state
// - Nested export structures
// - Usage and provision metadata

2. Target Relationships

For re-exports, the system tracks target relationships:

// Re-export: export { foo } from './module'
// Creates target relationship: current_module.foo -> target_module.foo
let target = get_target(export_info_data, self.mg);
if let Some(target) = target {
    // Track dependency for invalidation
    self.dependencies.entry(target.module)
        .or_insert_with(IdentifierSet::new)
        .insert(self.current_module_id);
}

Error Handling and Edge Cases

1. Modules Without Exports

let is_module_without_exports = module.build_meta().exports_type == BuildMetaExportsType::Unset;
if is_module_without_exports {
    exports_info.set_unknown_exports_provided(self.mg, false, None, None, None, None);
    continue;
}

2. Dynamic Exports

// Handle require.context and other dynamic patterns
ExportsOfExportsSpec::UnknownExports => {
    exports_info.set_unknown_exports_provided(
        self.mg,
        global_can_mangle.unwrap_or_default(),
        export_desc.exclude_exports.as_ref(),
        global_from.map(|_| dep_id),
        global_from.map(|_| dep_id),
        *global_priority,
    );
}

3. Side Effect Handling

// Modules used only for side effects
if !force_side_effects && is_side_effect_free(&module) {
    return;
}
let changed_flag = mgm_exports_info.set_used_for_side_effects_only(&mut module_graph, runtime.as_ref());

Conclusion

The Flag Dependency plugins implement a sophisticated two-phase analysis system that forms the foundation of rspack's tree-shaking capabilities. Through careful export provision tracking and comprehensive usage analysis, these plugins enable precise dead code elimination while handling complex scenarios including re-exports, dynamic imports, side effects, and module federation patterns. The system is optimized for performance with incremental processing, caching strategies, and efficient invalidation mechanisms, making it suitable for large-scale applications with complex dependency graphs.

Export Usage Tracking Integration and Workflow

Overview

This document provides a comprehensive analysis of how rspack's export usage tracking system integrates all components to enable sophisticated tree-shaking and module optimization. The system combines multiple plugins, dependency types, and analysis phases to create a complete picture of export provision and usage across the entire module graph.

System Architecture Overview

Component Hierarchy

┌─────────────────────────────────────────────────────────────┐
│                    Compilation Process                       │
├─────────────────────────────────────────────────────────────┤
│  1. Module Parsing & Dependency Creation                    │
│     ├── ESMExportSpecifierDependency                       │
│     ├── ESMExportImportedSpecifierDependency               │
│     ├── ESMImportSpecifierDependency                       │
│     ├── CommonJsExportsDependency                          │
│     └── ExportInfoDependency                               │
│                                                             │
│  2. Export Provision Analysis (FlagDependencyExportsPlugin) │
│     ├── Collect ExportsSpec from all dependencies          │
│     ├── Populate ExportsInfo with provision data           │
│     ├── Track re-export relationships                      │
│     └── Handle nested and dynamic exports                  │
│                                                             │
│  3. Export Usage Analysis (FlagDependencyUsagePlugin)       │
│     ├── Start from entry points                            │
│     ├── Traverse dependency graph                          │
│     ├── Collect referenced exports                         │
│     └── Mark usage states in ExportsInfo                   │
│                                                             │
│  4. Code Generation & Optimization                          │
│     ├── Query ExportsInfo for used exports                 │
│     ├── Generate optimized export code                     │
│     ├── Apply tree-shaking decisions                       │
│     └── Handle module federation scenarios                 │
└─────────────────────────────────────────────────────────────┘

Detailed Workflow Analysis

Phase 1: Module Parsing and Dependency Creation

1.1 Dependency Creation During Parsing

When rspack parses a module, it creates specific dependency types based on the export/import patterns found:

ESM Export Examples:

// Creates ESMExportSpecifierDependency
export const foo = 'value';
export { bar };

// Creates ESMExportImportedSpecifierDependency  
export { baz } from './module';
export * from './module';

// Creates ESMImportSpecifierDependency
import { used } from './module';

CommonJS Export Examples:

// Creates CommonJsExportsDependency
exports.foo = 'value';
module.exports.bar = 'value';
Object.defineProperty(exports, 'baz', { value: 'value' });

Export Info Access:

// Creates ExportInfoDependency
const isUsed = __webpack_exports_info__.used;
const canMangle = __webpack_exports_info__.canMangle;

1.2 Dependency Registration

Each dependency implements the get_exports() method to describe what it provides:

// ESMExportSpecifierDependency example
fn get_exports(&self, _mg: &ModuleGraph, _mg_cache: &ModuleGraphCacheArtifact) -> Option<ExportsSpec> {
    Some(ExportsSpec {
        exports: ExportsOfExportsSpec::Names(vec![ExportNameOrSpec::ExportSpec(ExportSpec {
            name: self.name.clone(),
            inlinable: self.inline,
            ..Default::default()
        })]),
        priority: Some(1),
        can_mangle: None,
        terminal_binding: Some(true),
        from: None,
        dependencies: None,
        hide_export: None,
        exclude_exports: None,
    })
}

Phase 2: Export Provision Analysis

2.1 FlagDependencyExportsPlugin Execution

Hook: CompilationFinishModules - runs after all modules are parsed

Process:

// 1. Initialize exports info for all modules
for module_id in modules {
    let exports_info = mgm.exports;
    exports_info.reset_provide_info(self.mg);
    exports_info.set_has_provide_info(self.mg);
}

// 2. Collect export specifications from dependencies
for dep_id in module.get_dependencies() {
    let exports_spec = dep.get_exports(self.mg, module_graph_cache);
    exports_specs_from_dependencies.insert(dep_id, exports_spec);
}

// 3. Process and merge export specifications
for (dep_id, exports_spec) in exports_specs_from_dependencies {
    self.process_exports_spec(dep_id, exports_spec, exports_info);
}

2.2 Export Information Population

ExportsInfo Structure Population:

// For each export found in ExportsSpec:
let export_info = exports_info.get_export_info(self.mg, &name);
let export_info_data = export_info.as_data_mut(self.mg);

// Set provision status
export_info_data.set_provided(Some(ExportProvided::Provided));

// Set mangling capabilities
if can_mangle == Some(false) {
    export_info_data.set_can_mangle_provide(Some(false));
}

// Set inlining potential
if let Some(inlined) = inlinable {
    export_info_data.set_inlinable(Inlinable::Inlined(inlined));
}

// Set terminal binding
if terminal_binding {
    export_info_data.set_terminal_binding(true);
}

2.3 Re-export Target Tracking

For re-exports, the system establishes target relationships:

// For: export { foo } from './module'
if let Some(from) = from {
    ExportInfoSetter::set_target(
        export_info_data,
        Some(dep_id),                    // Dependency creating the re-export
        Some(from.dependency_id),        // Connection to target module
        export_name,                     // What export to target
        priority,                        // Priority for conflict resolution
    );
    
    // Track dependency for invalidation
    self.dependencies.entry(target.module)
        .or_insert_with(IdentifierSet::new)
        .insert(self.current_module_id);
}

Phase 3: Export Usage Analysis

3.1 FlagDependencyUsagePlugin Execution

Hook: CompilationOptimizeDependencies - runs during optimization phase

Entry Point Analysis:

// Start from application entry points
for (entry_name, entry) in entries.iter() {
    let runtime = get_entry_runtime(entry_name, &entry.options, &entries);
    
    for &dep in entry.dependencies.iter() {
        self.process_entry_dependency(dep, runtime.clone(), &mut q);
    }
}

// Process global entries
for dep in self.compilation.global_entry.dependencies.clone() {
    self.process_entry_dependency(dep, global_runtime.clone(), &mut q);
}

3.2 Dependency Graph Traversal

Breadth-First Processing:

while let Some((module_id, runtime)) = q.dequeue() {
    // Process the module and all its dependencies
    self.process_module(ModuleOrAsyncDependenciesBlock::Module(module_id), runtime, false, &mut q);
}

fn process_module(&mut self, block_id: ModuleOrAsyncDependenciesBlock, runtime: Option<RuntimeSpec>, force_side_effects: bool, q: &mut Queue<(ModuleIdentifier, Option<RuntimeSpec>)>) {
    // Collect all referenced exports from module dependencies
    let mut referenced_exports_map: IdentifierMap<ProcessModuleReferencedExports> = IdentifierMap::default();
    
    for dep_id in dependencies {
        let connection = module_graph.connection_by_dependency_id(&dep_id);
        let active_state = connection.active_state(&module_graph, runtime.as_ref(), module_graph_cache);
        
        // Get what exports this dependency references
        let referenced_exports = if let Some(md) = dep.as_module_dependency() {
            md.get_referenced_exports(&module_graph, module_graph_cache, runtime.as_ref())
        } else {
            continue;
        };
        
        // Merge with existing references
        self.merge_referenced_exports(connection.module_identifier(), referenced_exports, &mut referenced_exports_map);
    }
    
    // Apply usage information to all referenced modules
    for (module_id, referenced_exports) in referenced_exports_map {
        self.process_referenced_module(module_id, referenced_exports, runtime.clone(), force_side_effects, q);
    }
}

3.3 Usage State Application

Referenced Export Processing:

fn process_referenced_module(&mut self, module_id: ModuleIdentifier, used_exports: Vec<ExtendedReferencedExport>, runtime: Option<RuntimeSpec>, force_side_effects: bool, queue: &mut Queue<(ModuleIdentifier, Option<RuntimeSpec>)>) {
    for used_export_info in used_exports {
        let (used_exports, can_mangle, can_inline) = extract_usage_info(used_export_info);
        
        if used_exports.is_empty() {
            // Unknown usage pattern
            mgm_exports_info.set_used_in_unknown_way(&mut module_graph, runtime.as_ref());
        } else {
            // Specific export usage
            let mut current_exports_info = mgm_exports_info;
            for (i, used_export) in used_exports.into_iter().enumerate() {
                let export_info = current_exports_info.get_export_info(&mut module_graph, &used_export);
                
                // Apply usage constraints
                if !can_mangle {
                    export_info.as_data_mut(&mut module_graph).set_can_mangle_use(Some(false));
                }
                if !can_inline {
                    export_info.as_data_mut(&mut module_graph).set_inlinable(Inlinable::NoByUse);
                }
                
                // Set usage state
                let usage_state = if i == used_exports.len() - 1 {
                    UsageState::Used
                } else {
                    UsageState::OnlyPropertiesUsed
                };
                
                ExportInfoSetter::set_used_conditionally(
                    export_info.as_data_mut(&mut module_graph),
                    Box::new(|used| used != &usage_state),
                    usage_state,
                    runtime.as_ref(),
                );
            }
        }
    }
}

Phase 4: Code Generation and Optimization

4.1 Export Code Generation

During code generation, dependencies query the populated ExportsInfo to make optimization decisions:

ESM Export Generation:

// ESMExportSpecifierDependencyTemplate
fn render(&self, dep: &dyn DependencyCodeGeneration, context: &mut TemplateContext) {
    // Query usage information
    let exports_info = module_graph.get_prefetched_exports_info(
        &module.identifier(),
        PrefetchExportsInfoMode::NamedExports(HashSet::from_iter([&dep.name])),
    );
    
    let used_name = ExportsInfoGetter::get_used_name(
        GetUsedNameParam::WithNames(&exports_info),
        *runtime,
        std::slice::from_ref(&dep.name),
    );
    
    if let Some(UsedName::Normal(used)) = used_name {
        // Export is used - generate export code
        init_fragments.push(Box::new(ESMExportInitFragment::new(
            module.get_exports_argument(),
            vec![(used, dep.value.to_string().into())],
        )));
    } else {
        // Export is unused - generate placeholder or omit
        // This enables tree-shaking
    }
}

CommonJS Export Generation:

// CommonJsExportsDependencyTemplate
fn render(&self, dep: &dyn DependencyCodeGeneration, source: &mut TemplateReplaceSource, context: &mut TemplateContext) {
    let used = ExportsInfoGetter::get_used_name(
        GetUsedNameParam::WithNames(&exports_info),
        *runtime,
        &dep.names,
    );
    
    if let Some(UsedName::Normal(used)) = used {
        let export_assignment = format!("{}{}", base, property_access(&used, 0));
        source.replace(dep.range.start, dep.range.end, &export_assignment, None);
    } else {
        // Generate unused export placeholder
        let placeholder_var = "__webpack_unused_export__";
        source.replace(dep.range.start, dep.range.end, &placeholder_var, None);
    }
}

4.2 Runtime Export Information Access

ExportInfoDependency Integration:

// ExportInfoDependencyTemplate
fn render(&self, dep: &dyn DependencyCodeGeneration, source: &mut TemplateReplaceSource, context: &mut TemplateContext) {
    let value = match dep.property.as_str() {
        "used" => {
            let used = ExportsInfoGetter::get_used(&exports_info, export_name, *runtime);
            Some((!matches!(used, UsageState::Unused)).to_string())
        }
        "canMangle" => {
            let can_mangle = ExportInfoGetter::can_mangle(export_info);
            can_mangle.map(|v| v.to_string())
        }
        "usedExports" => {
            let used_exports = exports_info.get_used_exports(*runtime);
            // Serialize used exports array or boolean
            Some(serialize_used_exports(used_exports))
        }
        _ => None,
    };
    
    source.replace(dep.start, dep.end, value.unwrap_or("undefined".to_owned()).as_str(), None);
}

Advanced Integration Scenarios

1. Module Federation Integration

The system includes special handling for module federation scenarios:

ConsumeShared Module Detection:

// Check if parent module is ConsumeShared
let consume_shared_info = if let Some(parent_module_id) = module_graph.get_parent_module(&dep.id) {
    if let Some(parent_module) = module_graph.module_by_identifier(parent_module_id) {
        if parent_module.module_type() == &ModuleType::ConsumeShared {
            parent_module.get_consume_shared_key()
        } else {
            None
        }
    } else {
        None
    }
} else {
    None
};

Conditional Export Generation:

// Generate tree-shaking macros for module federation
let export_content = if let Some(ref share_key) = consume_shared_info {
    format!(
        "/* @common:if [condition=\"treeShake.{}.{}\"] */ {} /* @common:endif */",
        share_key, export_name, export_value
    )
} else {
    export_value.to_string()
};

2. Nested Export Handling

The system supports complex nested export structures:

Nested ExportsInfo Creation:

// For: export const obj = { prop: { nested: value } }
if let Some(exports) = exports {
    let nested_exports_info = ExportInfoSetter::create_nested_exports_info(&export_info, self.mg);
    self.merge_exports(nested_exports_info, exports, global_export_info.clone(), dep_id);
}

Property Access Tracking:

// Track usage of nested properties
for (i, used_export) in used_exports.into_iter().enumerate() {
    let export_info = current_exports_info.get_export_info(&mut module_graph, &used_export);
    
    if i < used_exports.len() - 1 {
        // Not the final property - mark as OnlyPropertiesUsed
        ExportInfoSetter::set_used_conditionally(
            export_info.as_data_mut(&mut module_graph),
            Box::new(|used| used == &UsageState::Unused),
            UsageState::OnlyPropertiesUsed,
            runtime.as_ref(),
        );
        
        // Continue to nested exports
        if let Some(nested_info) = export_info.as_data(&module_graph).exports_info() {
            current_exports_info = nested_info;
        }
    } else {
        // Final property - mark as Used
        ExportInfoSetter::set_used_conditionally(
            export_info.as_data_mut(&mut module_graph),
            Box::new(|v| v != &UsageState::Used),
            UsageState::Used,
            runtime.as_ref(),
        );
    }
}

3. Dynamic Export Handling

Unknown Exports Processing:

// Handle dynamic exports like require.context()
ExportsOfExportsSpec::UnknownExports => {
    if exports_info.set_unknown_exports_provided(
        self.mg,
        global_can_mangle.unwrap_or_default(),
        export_desc.exclude_exports.as_ref(),    // Known excluded exports
        global_from.map(|_| dep_id),
        global_from.map(|_| dep_id),
        *global_priority,
    ) {
        self.changed = true;
    }
}

Unknown Usage Handling:

// When usage pattern is unknown
if used_exports.is_empty() {
    let flag = mgm_exports_info.set_used_in_unknown_way(&mut module_graph, runtime.as_ref());
    if flag {
        queue.enqueue((module_id, runtime.clone()));
    }
}

Performance and Optimization Strategies

1. Incremental Processing

Affected Module Detection:

let modules: IdentifierSet = if let Some(mutations) = compilation
    .incremental
    .mutations_read(IncrementalPasses::PROVIDED_EXPORTS) {
    // Only process modules affected by changes
    mutations.get_affected_modules_with_module_graph(&compilation.get_module_graph())
} else {
    // Full rebuild
    compilation.get_module_graph().modules().keys().copied().collect()
};

2. Caching and Prefetching

Export Information Prefetching:

// Prefetch commonly accessed export information
let exports_info = module_graph.get_prefetched_exports_info(
    &module.identifier(),
    match access_pattern {
        PrefetchExportsInfoMode::AllExports => all_exports,
        PrefetchExportsInfoMode::NamedExports(names) => specific_exports,
        PrefetchExportsInfoMode::NamedNestedExports(path) => nested_exports,
        PrefetchExportsInfoMode::Default => basic_info,
    },
);

3. Change Propagation

Dependency Invalidation:

// Track which modules depend on export information changes
if self.changed {
    self.notify_dependencies(&mut q);
}

fn notify_dependencies(&mut self, q: &mut Queue<ModuleIdentifier>) {
    if let Some(set) = self.dependencies.get(&self.current_module_id) {
        for mi in set.iter() {
            q.enqueue(*mi);  // Re-process dependent modules
        }
    }
}

Debugging and Diagnostics

1. Comprehensive Logging

The system includes detailed debug logging for complex scenarios:

tracing::debug!(
    "[RSPACK_EXPORT_DEBUG:ESM_SPECIFIER_DETAILED] Module: {:?}, Type: {:?}, Layer: {:?}, Name: {:?}, Value: {:?}",
    module_identifier, module.module_type(), module.get_layer(), dep.name, dep.value
);

2. Export State Visualization

Usage State Reporting:

// Generate comprehensive export usage reports
fn generate_report(&self, compilation: &Compilation) -> Result<ModuleExportReport> {
    let mut modules = HashMap::new();
    
    for (module_id, _module) in module_graph.modules() {
        if let Some(usage_info) = self.analyze_module(&module_graph, &module_id, compilation, &runtimes) {
            modules.insert(module_id.to_string(), usage_info);
        }
    }
    
    Ok(ModuleExportReport {
        modules,
        summary: self.generate_summary(&modules),
        metadata: self.generate_metadata(&runtimes),
        timestamp: current_timestamp(),
    })
}

Plugin Development Integration Points

Key Integration Points for Plugin Developers

Based on ShareUsagePlugin implementation learnings including the latest enhancement for advanced dependency analysis, the following integration points are essential for plugin developers:

1. Compilation Hook Selection

  • CompilationFinishModules: Best for metadata copying and module information manipulation
  • CompilerEmit: Ideal for asset generation and final analysis reporting
  • CompilationOptimizeDependencies: Use when requiring optimization phase data

2. Export Analysis API Usage

// Correct pattern for comprehensive export analysis
let exports_info = module_graph.get_exports_info(module_id);
let prefetched = ExportsInfoGetter::prefetch(
    &exports_info,
    module_graph,
    PrefetchExportsInfoMode::AllExports, // Efficient bulk operations
);

// Individual export analysis
let export_info_data = prefetched.get_read_only_export_info(&export_atom);
let usage_state = ExportInfoGetter::get_used(export_info_data, runtime_spec);

3. Advanced Dependency Analysis (Latest Enhancement)

// Use incoming connections for accurate ConsumeShared analysis
for connection in module_graph.get_incoming_connections(consume_shared_id) {
    if let Some(dependency) = module_graph.dependency_by_id(&connection.dependency_id) {
        // Extract specific export names using get_referenced_exports()
        let referenced_exports = dependency.get_referenced_exports(
            module_graph,
            &rspack_core::ModuleGraphCacheArtifact::default(),
            None,
        );
        
        // Handle ExtendedReferencedExport patterns
        for export_ref in referenced_exports {
            match export_ref {
                ExtendedReferencedExport::Array(names) => {
                    // Multiple specific exports referenced
                    for name in names {
                        let export_name = name.to_string();
                        // Process specific export usage
                    }
                },
                ExtendedReferencedExport::Export(export_info) => {
                    // Single export or namespace reference
                    if export_info.name.is_empty() {
                        // Namespace usage detected
                        uses_namespace = true;
                    } else {
                        // Specific named exports
                        for name in export_info.name {
                            let export_name = name.to_string();
                            // Process named export
                        }
                    }
                },
            }
        }
    }
}

4. ConsumeShared Module Considerations

  • Empty usage arrays on ConsumeShared modules are expected behavior (proxy pattern)
  • Real usage data requires analyzing incoming dependencies using get_referenced_exports() or fallback modules
  • Use dependency graph traversal with ExtendedReferencedExport pattern matching for accurate ConsumeShared analysis
  • Cross-reference extracted usage with provided exports for accurate filtering

For comprehensive plugin development patterns incorporating these integration insights, see 09_plugin_development_patterns.md.

Latest Enhancement: Advanced Dependency Analysis

The ShareUsagePlugin investigation revealed and implemented a significant enhancement to the export usage tracking system:

Enhanced ConsumeShared Analysis

The latest enhancement introduces sophisticated dependency analysis using incoming connections:

// Enhanced analysis using module_graph.get_incoming_connections()
for connection in module_graph.get_incoming_connections(consume_shared_id) {
    if let Some(dependency) = module_graph.dependency_by_id(&connection.dependency_id) {
        // Use dependency.get_referenced_exports() to extract specific export names
        let referenced_exports = dependency.get_referenced_exports(
            module_graph,
            &rspack_core::ModuleGraphCacheArtifact::default(),
            None,
        );
        
        // Handle ExtendedReferencedExport patterns comprehensively
        for export_ref in referenced_exports {
            match export_ref {
                ExtendedReferencedExport::Array(names) => {
                    // Process multiple specific exports
                },
                ExtendedReferencedExport::Export(export_info) => {
                    // Process single export or namespace reference
                },
            }
        }
    }
}

Key Enhancement Features:

  1. Incoming Connection Analysis: Uses module_graph.get_incoming_connections() to find all modules that import from ConsumeShared modules
  2. Referenced Export Extraction: Calls dependency.get_referenced_exports() to extract specific export names being used
  3. Pattern Matching: Handles both ExtendedReferencedExport::Array and ExtendedReferencedExport::Export patterns
  4. Cross-referencing: Compares used exports with provided exports for accurate filtering

Integration with Existing System

This enhancement seamlessly integrates with the existing four-phase compilation process:

  1. Phase 1-2: Standard export discovery and provision analysis
  2. Phase 3: Enhanced usage analysis with incoming connection analysis
  3. Phase 4: Code generation with more accurate usage information

Conclusion

The export usage tracking system in rspack represents a sophisticated integration of multiple components working together to enable precise tree-shaking and export optimization. Through the coordinated effort of dependency analysis, export provision tracking, usage analysis, and optimized code generation, the system can eliminate dead code while maintaining correctness across complex module relationships. The integration handles advanced scenarios including module federation, nested exports, dynamic patterns, and performance-critical optimizations, making it suitable for large-scale applications with complex dependency graphs.

The latest enhancement using get_referenced_exports() and incoming connection analysis further improves the accuracy of ConsumeShared module analysis, providing more precise usage information for tree-shaking decisions.

The workflow demonstrates how modern bundlers can achieve both comprehensive analysis and excellent performance through careful architecture design, incremental processing, and strategic caching. This system serves as a foundation for advanced optimizations while maintaining the flexibility needed for evolving JavaScript module patterns.

The ShareUsagePlugin implementation insights documented throughout this system, including the latest enhancement for advanced dependency analysis using get_referenced_exports(), provide essential patterns for plugin developers working with export analysis, module graph manipulation, and compilation hooks, ensuring robust and efficient plugin development within the rspack ecosystem.

Latest Enhancement Summary:

  1. Plugin Implementation: Successfully created ShareUsagePlugin with proper API usage and advanced dependency analysis
  2. Export Analysis APIs: Correct usage of ExportsInfoGetter, ExportInfoGetter, and dependency analysis with get_referenced_exports()
  3. ConsumeShared Behavior: Confirmed that empty usage arrays are expected, enhanced analysis through incoming connections
  4. Advanced Dependency Analysis: New pattern using get_referenced_exports() for extracting actual usage data from importing modules
  5. Module Federation Integration: Proper integration with existing export metadata copying systems and enhanced usage tracking
  6. Pattern Matching: Comprehensive handling of ExtendedReferencedExport::Array and ExtendedReferencedExport::Export patterns
  7. Cross-reference Analysis: Implementation of usage-to-provided export filtering for accurate optimization decisions

Comprehensive Tree Shaking Analysis in Rspack

Overview

This document provides a comprehensive analysis of Rspack's tree-shaking implementation, consolidating findings from fact-checking existing research docs and exploring the broader codebase. Rspack implements one of the most sophisticated tree-shaking systems in modern JavaScript bundlers.

Core Tree Shaking Architecture

Four-Phase Compilation Process

  1. Export Discovery Phase - FlagDependencyExportsPlugin
  2. Usage Analysis Phase - FlagDependencyUsagePlugin
  3. Side Effect Analysis Phase - SideEffectsFlagPlugin
  4. Optimization Phase - ModuleConcatenationPlugin + MangleExportsPlugin

Key Components and File Locations

Primary Tree Shaking Plugins

FlagDependencyExportsPlugin

  • Location: crates/rspack_plugin_javascript/src/plugin/flag_dependency_exports_plugin.rs
  • Purpose: Analyzes and flags which exports are provided by each module
  • Key Functions:
    • process_exports_spec() - Processes export specifications from dependencies
    • merge_exports() - Merges export information from multiple sources
    • set_unknown_exports_provided() - Handles dynamic exports

FlagDependencyUsagePlugin

  • Location: crates/rspack_plugin_javascript/src/plugin/flag_dependency_usage_plugin.rs
  • Purpose: Tracks which exports are actually used across the dependency graph
  • Key Functions:
    • process_module() - Analyzes module dependencies and usage
    • process_referenced_module() - Marks exports as used based on import patterns
    • set_used_without_info(), set_used_in_unknown_way() - Updates usage state

SideEffectsFlagPlugin

  • Location: crates/rspack_plugin_javascript/src/plugin/side_effects_flag_plugin.rs
  • Purpose: Identifies modules with side effects and optimizes import/export connections
  • Key Components:
    • SideEffectsFlagPluginVisitor - AST visitor to detect side effects in code
    • can_optimize_connection() - Determines if connections can be optimized
    • do_optimize_connection() - Performs connection optimization

Core Data Structures

ExportsInfo System

  • Location: crates/rspack_core/src/exports/exports_info.rs
  • Purpose: Central data structure tracking export information for modules
  • Integration: Links with module graph to provide comprehensive export tracking

ExportInfo

  • Location: crates/rspack_core/src/exports/export_info.rs
  • Purpose: Tracks individual export usage and provide information
  • Key Properties: provided, used_name, target, can_mangle_provide, can_mangle_use

Usage State System

pub enum UsageState {
    Unused = 0,                // Export is not used - can be eliminated
    OnlyPropertiesUsed = 1,    // Only properties of export are used
    NoInfo = 2,                // No usage information available
    Unknown = 3,               // Usage is unknown - assume used
    Used = 4,                  // Export is definitely used
}

pub enum ExportProvided {
    Provided,     // Export is statically confirmed
    NotProvided,  // Export is confirmed to not exist
    Unknown,      // Export status is unknown (e.g., CommonJS)
}

Module Format Support

ESM (ES Modules)

Export Dependencies:

  • esm_export_specifier_dependency.rs - Handles export { name }
  • esm_export_imported_specifier_dependency.rs - Handles export { name } from 'module'
  • esm_export_expression_dependency.rs - Handles export default and expressions

Import Dependencies:

  • esm_import_dependency.rs - Handles import statements
  • esm_import_specifier_dependency.rs - Tracks specific import specifiers

CommonJS

Export Dependencies:

  • common_js_exports_dependency.rs - Handles module.exports assignments
  • common_js_export_require_dependency.rs - Handles module.exports = require()

Import Dependencies:

  • common_js_require_dependency.rs - Tracks require() calls
  • require_resolve_dependency.rs - Handles require.resolve()

Advanced Pattern Detection

Star Re-exports: Complex mode system for export * from 'module' with sophisticated namespace handling Dynamic Exports: Unknown export type handling for module.exports = dynamicValue Mixed Formats: ESM/CommonJS interop with compatibility dependencies

Advanced Features

Module Federation Integration

Location: crates/rspack_plugin_mf/src/sharing/

  • Export Usage Analysis: export_usage_analysis.rs - Advanced usage tracking for federated modules
  • Export Usage Plugin: export_usage_plugin.rs - Generates detailed usage reports
  • Usage Types: export_usage_types.rs - Comprehensive data structures

Performance Optimizations

  1. Prefetched Exports: Bulk analysis using PrefetchExportsInfoMode
  2. Incremental Processing: Queue-based algorithms for dependency processing
  3. Caching Systems:
    • Export info caching
    • Mode caching for complex dependencies
    • Module graph cache artifacts
  4. Change Tracking: Only re-analyze modified modules during rebuilds

Configuration Integration

Optimization Options (crates/rspack_core/src/options/optimizations.rs):

  • side_effects: bool - Controls side effect analysis
  • used_exports: bool | "global" - Enables usage analysis
  • provided_exports: bool - Enables export discovery
  • mangle_exports: bool - Enables export name mangling

Tree Shaking Decision Process

1. Export Discovery

  • Parse module AST to identify all exports
  • Create ExportInfo entries with provision status
  • Handle different export patterns (named, default, re-exports)

2. Usage Analysis

  • Start from entry points and traverse dependency graph
  • Analyze import statements to determine referenced exports
  • Propagate usage information through module connections
  • Handle namespace imports vs named imports differently

3. Side Effect Evaluation

  • Detect modules with side effects using AST analysis
  • Optimize connections by skipping side-effect-free modules
  • Preserve modules with side effects even if exports aren't used

4. Dead Code Elimination

  • Mark unused exports for elimination
  • Generate optimized code without unused exports
  • Maintain source maps and debugging information

Integration with Broader Ecosystem

Webpack Compatibility

  • Maintains compatibility with webpack's tree-shaking semantics
  • Supports similar configuration options and behavior
  • Handles edge cases consistently with webpack

Development Tools

  • Provides detailed usage reports for debugging
  • Supports development mode with preserved export information
  • Integrates with source maps for accurate debugging

Build Performance

  • Implements incremental compilation for fast rebuilds
  • Uses efficient data structures for large codebases
  • Provides parallel processing capabilities

Export Analysis Plugin Development Guidelines

Based on ShareUsagePlugin investigation findings, here are key guidelines for developing export analysis plugins:

API Usage Best Practices

Export Information Access

// Correct: Use ExportsInfoGetter::prefetch() for efficient bulk operations
let prefetched = ExportsInfoGetter::prefetch(
    &exports_info,
    module_graph,
    PrefetchExportsInfoMode::AllExports, // Comprehensive analysis
);

// Correct: Use ExportInfoGetter::get_used() for usage state checking
let export_info_data = prefetched.get_read_only_export_info(&export_atom);
let usage_state = ExportInfoGetter::get_used(export_info_data, runtime_spec);

// Incorrect: Don't use ExportsInfoGetter for individual export usage checking
// let usage_state = ExportsInfoGetter::get_used(&exports_info, ...); // Wrong API

Advanced Dependency Analysis (Latest Enhancement)

// Use module_graph.get_incoming_connections() to analyze how ConsumeShared modules are imported
for connection in module_graph.get_incoming_connections(consume_shared_id) {
    if let Some(dependency) = module_graph.dependency_by_id(&connection.dependency_id) {
        // Call dependency.get_referenced_exports() to extract specific export names
        let referenced_exports = dependency.get_referenced_exports(
            module_graph,
            &rspack_core::ModuleGraphCacheArtifact::default(),
            None,
        );
        
        // Handle ExtendedReferencedExport patterns
        for export_ref in referenced_exports {
            match export_ref {
                ExtendedReferencedExport::Array(names) => {
                    // Multiple specific exports are referenced
                    for name in names {
                        let export_name = name.to_string();
                        // Process specific export usage
                    }
                },
                ExtendedReferencedExport::Export(export_info) => {
                    // Single export or namespace reference
                    if export_info.name.is_empty() {
                        // No specific name indicates namespace usage
                        uses_namespace = true;
                    } else {
                        // Specific named exports
                        for name in export_info.name {
                            let export_name = name.to_string();
                            // Process named export
                        }
                    }
                },
            }
        }
    }
}

ProvidedExports Pattern Matching

// Proper handling of all ProvidedExports variants
match provided_exports {
    ProvidedExports::ProvidedNames(names) => {
        // Iterate over specific exports, not try to enumerate all exports
        for name in names {
            // Process each specific named export
        }
    },
    ProvidedExports::ProvidedAll => {
        // Module provides all possible exports dynamically
        // Handle appropriately for bulk export scenarios
    },
    ProvidedExports::Unknown => {
        // Cannot determine exports statically
        // Preserve unknown status, don't assume empty
    }
}

ConsumeShared Module Considerations

Expected Behavior Patterns

  • Empty Usage Arrays: ConsumeShared modules showing empty usage is correct behavior
  • Proxy Pattern: These modules act as proxies, real usage data is elsewhere
  • Analysis Strategy: Focus on incoming dependencies and fallback modules

Proper Analysis Approach

// For ConsumeShared modules, analyze usage from consumers using get_referenced_exports()
if module.module_type() == &ModuleType::ConsumeShared {
    // 1. Enhanced analysis using incoming connections and get_referenced_exports()
    let mut used_exports = Vec::new();
    let mut uses_namespace = false;
    
    for connection in module_graph.get_incoming_connections(module_id) {
        if let Some(dependency) = module_graph.dependency_by_id(&connection.dependency_id) {
            // Use get_referenced_exports() to extract specific export names
            let referenced_exports = dependency.get_referenced_exports(
                module_graph,
                &rspack_core::ModuleGraphCacheArtifact::default(),
                None,
            );
            
            // Process ExtendedReferencedExport patterns
            for export_ref in referenced_exports {
                match export_ref {
                    ExtendedReferencedExport::Array(names) => {
                        // Multiple specific exports referenced
                        for name in names {
                            used_exports.push(name.to_string());
                        }
                    },
                    ExtendedReferencedExport::Export(export_info) => {
                        // Single export or namespace reference
                        if export_info.name.is_empty() {
                            uses_namespace = true;
                        } else {
                            for name in export_info.name {
                                used_exports.push(name.to_string());
                            }
                        }
                    },
                }
            }
        }
    }
    
    // 2. Find and analyze fallback module
    if let Some(fallback_id) = find_fallback_module(module_graph, module_id) {
        // Use fallback module's export information
    }
    
    // 3. Cross-reference extracted usage with provided exports for accurate filtering
    let filtered_usage = cross_reference_usage_with_provided(used_exports, provided_exports);
}

Plugin Implementation Patterns

Hook Usage for Export Analysis

#[plugin_hook(CompilerEmit for YourExportAnalysisPlugin)]
async fn emit(&self, compilation: &mut Compilation) -> Result<()> {
    // CompilerEmit hook provides access to final module graph
    // All export analysis and usage information is available
    
    let module_graph = compilation.get_module_graph();
    let runtimes: Vec<RuntimeSpec> = compilation
        .chunk_by_ukey
        .values()
        .map(|chunk| chunk.runtime())
        .cloned()
        .collect();
    
    // Generate analysis reports
}

Research Documentation Quality Assessment

Based on comprehensive fact-checking and ShareUsagePlugin investigation, the existing research documentation in research_docs/ is highly accurate and comprehensive:

  • Technical Accuracy: All core concepts, data structures, and implementation details are correct
  • Code Examples: Rust code examples follow proper conventions and show realistic patterns
  • Architecture Coverage: Complete coverage of the tree-shaking pipeline
  • Performance Awareness: Consistently addresses optimization strategies
  • Edge Case Handling: Documents complex scenarios like module federation and dynamic exports
  • API Usage Patterns: ShareUsagePlugin investigation confirmed correct API usage throughout
  • Latest Enhancements: Advanced dependency analysis using get_referenced_exports() documented and validated
  • Pattern Matching: ExtendedReferencedExport handling patterns correctly documented
  • ConsumeShared Analysis: Proxy module behavior and incoming connection analysis properly covered

The documentation represents one of the most thorough analyses of a modern bundler's tree-shaking implementation available.

Future Enhancements

Potential Improvements

  1. Metrics Collection: Add performance benchmarks and detailed timing data
  2. Advanced Diagnostics: Enhanced debugging tools for complex usage patterns
  3. Optimization Heuristics: Machine learning-based optimization suggestions
  4. Cross-Module Analysis: Even more sophisticated inter-module optimization

Areas for Research

  1. Dynamic Import Patterns: Enhanced analysis of dynamic import usage
  2. Web Workers: Tree-shaking optimization for worker contexts
  3. Micro-frontend Architecture: Advanced federation scenarios
  4. Bundle Splitting: Integration with code splitting strategies

This comprehensive analysis demonstrates that Rspack's tree-shaking implementation is among the most advanced in the JavaScript ecosystem, providing sophisticated optimization capabilities while maintaining compatibility and performance.

Module Federation Export Patterns and Proxy Module Implementation

Overview

This document captures advanced patterns discovered during implementation of ConsumeShared module export copying, extending the research documentation with real-world module federation insights.

Current Implementation Status

✅ IMPLEMENTED: ConsumeShared export metadata copying is already implemented and working correctly in rspack.

Implementation Location

  • Primary Implementation: ConsumeSharedModule and ConsumeSharedPlugin
  • Hook Integration: Uses CompilationFinishModules hook for proper timing
  • API Integration: Leverages existing export analysis infrastructure

Key Implementation Details

  • Metadata Copying Methods: copy_metadata_from_fallback() and copy_exports_from_fallback() are implemented
  • Lifecycle Timing: CompilationFinishModules executes after build phase, before optimization
  • Plugin Integration: ConsumeSharedPlugin already has finish_modules hook implementation

Investigation Findings

Key Discoveries from Research

  1. Plugin Integration Status: ConsumeSharedPlugin already has finish_modules hook implementation with proper metadata copying
  2. API Usage Patterns: Investigation revealed correct usage of ExportsInfoGetter, PrefetchExportsInfoMode, and ExportInfoGetter
  3. Export Analysis Results: ShareUsagePlugin investigation showed ConsumeShared modules display empty usage data due to proxy pattern design
  4. Expected Behavior: ConsumeShared modules showing empty export usage arrays is correct behavior, not a bug

Technical API Usage Insights

Correct API Usage Patterns

// ExportsInfoGetter vs ExportInfoGetter usage
let exports_info_getter = ExportsInfoGetter::prefetch(
    &fallback_exports_info,
    module_graph,
    PrefetchExportsInfoMode::AllExports, // Efficient bulk operations
);

// Individual export analysis
let export_info_getter = fallback_exports_info.get_export_info(module_graph, &export_name);
let usage_state = export_info_getter.get_used(); // Usage state analysis

ProvidedExports Handling

match provided_exports {
    ProvidedExports::ProvidedNames(names) => {
        // Handle specific named exports
        for name in names {
            // Copy metadata for each named export
        }
    }
    ProvidedExports::ProvidedAll => {
        // Module provides all possible exports dynamically
        consume_exports_info.set_has_provide_info(module_graph);
    }
    ProvidedExports::Unknown => {
        // Preserve unknown status - cannot determine exports statically
    }
}

Usage Analysis Findings

ConsumeShared Module Behavior

  • Empty Usage Arrays: Expected behavior due to proxy pattern
  • Real Usage Data: Requires analyzing incoming dependencies or fallback module usage
  • Text Analysis vs Runtime: Different data sources may show different results

Data Source Considerations

  • Runtime Analysis: Shows actual usage patterns during execution
  • Text Analysis Scripts: May use different data sources than runtime analysis
  • Dependency Analysis: Real usage data comes from analyzing modules that depend on ConsumeShared modules

ConsumeShared Proxy Module Pattern

Architecture

ConsumeShared modules implement a transparent proxy pattern where they must perfectly mimic their fallback module's export behavior for accurate tree-shaking:

// ConsumeShared Module (Proxy)          Fallback Module (Real Implementation)
//       ↓                                        ↓
//   Export Info ←------ COPY METADATA ----→ Export Info
//   Build Meta  ←------ COPY METADATA ----→ Build Meta
//   Provided    ←------ COPY METADATA ----→ Provided

Two-Phase Metadata Copying

Phase 1: Direct Metadata Copy

// Copy build metadata for compilation consistency
consume_shared.build_meta = fallback_module.build_meta().clone();
consume_shared.build_info = fallback_module.build_info().clone();

Phase 2: Export Information Copy

// Use prefetched export analysis for efficient copying
let prefetched_fallback = ExportsInfoGetter::prefetch(
    &fallback_exports_info,
    module_graph,
    PrefetchExportsInfoMode::AllExports,
);

match prefetched_fallback.get_provided_exports() {
    ProvidedExports::ProvidedNames(export_names) => {
        // Copy each specific export with full metadata
        for export_name in export_names {
            // Copy provided status, can_mangle_provide, nested exports_info
        }
    }
    ProvidedExports::ProvidedAll => {
        // Mark ConsumeShared as providing all exports
    }
    ProvidedExports::Unknown => {
        // Preserve unknown status
    }
}

Plugin Hook Integration Patterns

CompilationFinishModules Hook Usage

Timing: After all modules are built and analyzed, before optimization phases

#[plugin_hook(CompilationFinishModules for ConsumeSharedPlugin)]
async fn finish_modules(&self, compilation: &mut Compilation) -> Result<()> {
    // 1. Collect all ConsumeShared modules
    let consume_shared_modules: Vec<ModuleIdentifier> = /* ... */;
    
    // 2. Process each individually to avoid borrow checker issues
    for consume_shared_id in consume_shared_modules {
        Self::copy_fallback_metadata_to_consume_shared(compilation, &consume_shared_id)?;
    }
}

Borrow Checker Patterns

Problem: Multiple mutable borrows of ModuleGraph Solution: Separate scopes and helper methods

// ❌ Problematic - multiple mutable borrows
let mut module_graph = compilation.get_module_graph_mut();
let module = module_graph.module_by_identifier_mut(&id);
// Still borrowing module_graph mutably

// ✅ Correct - separate scopes
{
    let module_graph = compilation.get_module_graph(); // immutable
    let fallback_id = find_fallback(&module_graph);
}
{
    let mut module_graph = compilation.get_module_graph_mut(); // mutable
    copy_exports(&mut module_graph, &fallback_id, &consume_id);
}

Advanced Export Copying Techniques

Handling Complex Export Types

Named Exports with Metadata

for export_name in export_names {
    let consume_export_info = consume_exports_info.get_export_info(module_graph, &export_name);
    let fallback_export_info = fallback_exports_info.get_export_info(module_graph, &export_name);
    
    // Copy all relevant metadata
    if let Some(provided) = fallback_export_info.as_data(module_graph).provided() {
        consume_export_info.as_data_mut(module_graph).set_provided(Some(provided));
    }
    
    // Copy mangling capabilities
    if let Some(can_mangle) = fallback_export_info.as_data(module_graph).can_mangle_provide() {
        consume_export_info.as_data_mut(module_graph).set_can_mangle_provide(Some(can_mangle));
    }
    
    // Copy nested export structures
    if let Some(nested_exports_info) = fallback_export_info.as_data(module_graph).exports_info() {
        consume_export_info.as_data_mut(module_graph).set_exports_info(Some(nested_exports_info));
    }
}

Setting Complete Provide Info

// Mark as having complete export information
consume_shared_exports_info.set_has_provide_info(module_graph);

// Set "other exports" to not provided for specific export lists
consume_shared_exports_info.set_unknown_exports_provided(
    module_graph,
    false, // not provided
    None,  // no exclude exports
    None,  // no can_mangle
    None,  // no terminal_binding
    None,  // no target_key
);

Module Federation Specific Considerations

Fallback Module Detection

pub fn find_fallback_module_id(&self, module_graph: &ModuleGraph) -> Option<ModuleIdentifier> {
    for dep_id in self.get_dependencies() {
        if let Some(dep) = module_graph.dependency_by_id(dep_id) {
            if matches!(dep.dependency_type(), DependencyType::ConsumeSharedFallback) {
                if let Some(fallback_id) = module_graph.module_identifier_by_dependency_id(dep_id) {
                    return Some(*fallback_id);
                }
            }
        }
    }
    None
}

Context Handling for Direct vs Indirect Fallbacks

let direct_fallback = matches!(&config.import, Some(i) if RELATIVE_REQUEST.is_match(i) | ABSOLUTE_REQUEST.is_match(i));

let context = if direct_fallback {
    self.get_context()  // Use plugin context for direct paths
} else {
    context.clone()     // Use request context for module resolution
};

Performance Considerations

Efficient Export Analysis

  • Use Prefetched Mode: PrefetchExportsInfoMode::AllExports for bulk analysis
  • Batch Processing: Process all ConsumeShared modules in one hook invocation
  • Scope Separation: Avoid holding multiple mutable references

Error Handling

if let Err(e) = consume_shared_module.copy_metadata_from_fallback(&mut module_graph) {
    compilation.push_diagnostic(
        rspack_error::Diagnostic::warn(
            "ConsumeSharedPlugin".into(),
            format!("Failed to copy metadata from fallback module: {}", e),
        )
    );
}

Integration with Tree-Shaking System

Export Tracking Flow

  1. FlagDependencyExportsPlugin: Analyzes fallback module exports
  2. ConsumeSharedPlugin: Copies export metadata to proxy module
  3. FlagDependencyUsagePlugin: Tracks usage through proxy module
  4. Tree-Shaking: Eliminates unused exports based on copied metadata

Benefits for Module Federation

  • Accurate Analysis: Proxy modules reflect real export capabilities
  • Proper Tree-Shaking: Unused exports in shared modules are eliminated
  • Performance: Leverages existing export analysis infrastructure
  • Compatibility: Works with all module formats (ESM, CommonJS)

ShareUsagePlugin Investigation Findings

Based on the comprehensive ShareUsagePlugin implementation investigation, including the latest enhancement for advanced dependency analysis using get_referenced_exports(), the following key findings were discovered about export analysis APIs and ConsumeShared module behavior. For complete plugin development patterns incorporating these learnings, see 09_plugin_development_patterns.md.

Latest Enhancement: Advanced Dependency Analysis

The ShareUsagePlugin has been enhanced with sophisticated dependency analysis that uses incoming connections to extract specific export usage:

// Enhanced ConsumeShared analysis using incoming connections
pub fn analyze_consume_shared_usage_from_consumers(
  module_graph: &ModuleGraph,
  consume_shared_id: &ModuleIdentifier,
  _runtimes: &[RuntimeSpec],
) -> ConsumeSharedUsageInfo {
  let mut used_exports = Vec::new();
  let mut uses_namespace = false;
  let mut import_types = std::collections::HashMap::new();

  // Use incoming connections for accurate dependency analysis
  for connection in module_graph.get_incoming_connections(consume_shared_id) {
    if let Some(dependency) = module_graph.dependency_by_id(&connection.dependency_id) {
      // Use get_referenced_exports to extract specific export names
      let referenced_exports = dependency.get_referenced_exports(
        module_graph,
        &rspack_core::ModuleGraphCacheArtifact::default(),
        None,
      );
      
      // Process referenced exports to extract used export names
      for export_ref in referenced_exports {
        match export_ref {
          ExtendedReferencedExport::Array(names) => {
            // Multiple specific exports are referenced
            for name in names {
              let export_name = name.to_string();
              if !used_exports.contains(&export_name) {
                used_exports.push(export_name.clone());
                import_types.insert(export_name, "named_import".to_string());
              }
            }
          },
          ExtendedReferencedExport::Export(export_info) => {
            // Single export or namespace reference
            if export_info.name.is_empty() {
              // No specific name indicates namespace usage
              uses_namespace = true;
              import_types.insert("*".to_string(), "namespace_import".to_string());
            } else {
              for name in export_info.name {
                let export_name = name.to_string();
                if !used_exports.contains(&export_name) {
                  used_exports.push(export_name.clone());
                  import_types.insert(export_name, "named_import".to_string());
                }
              }
            }
          },
        }
      }
    }
  }

  ConsumeSharedUsageInfo {
    used_exports: if used_exports.is_empty() { None } else { Some(used_exports) },
    uses_namespace: Some(uses_namespace),
    import_types,
  }
}

Key Enhancement Features:

  1. Incoming Connection Analysis: Uses module_graph.get_incoming_connections() to find all modules that import from ConsumeShared modules
  2. Referenced Export Extraction: Calls dependency.get_referenced_exports() to extract specific export names being used
  3. Pattern Matching: Handles both ExtendedReferencedExport::Array and ExtendedReferencedExport::Export patterns
  4. Cross-referencing: Compares used exports with provided exports for accurate filtering

Export Analysis API Patterns

Correct API Usage for Export Analysis

// Use ExportsInfoGetter::prefetch() with appropriate modes
let prefetched = ExportsInfoGetter::prefetch(
    &exports_info,
    module_graph,
    PrefetchExportsInfoMode::AllExports, // For comprehensive analysis
);

// Use ExportInfoGetter::get_used() for usage state checking (not ExportsInfoGetter)
let export_info_data = prefetched.get_read_only_export_info(&export_atom);
let usage_state = ExportInfoGetter::get_used(export_info_data, runtime_spec);

Key API Distinctions:

  • ExportsInfoGetter::prefetch() - Efficient bulk export analysis
  • ExportInfoGetter::get_used() - Individual export usage state checking
  • PrefetchExportsInfoMode::AllExports - Comprehensive analysis mode
  • PrefetchExportsInfoMode::Default - Lightweight analysis mode

ProvidedExports Enum Handling

match provided_exports {
    ProvidedExports::ProvidedNames(names) => {
        // Handle specific named exports
        for name in names {
            let export_atom = rspack_util::atom::Atom::from(name.as_str());
            // Process each specific export
        }
    },
    ProvidedExports::ProvidedAll => {
        // Module provides all possible exports dynamically
        vec!["*".to_string()]
    },
    ProvidedExports::Unknown => {
        // Cannot determine exports statically - preserve unknown status
        vec![] // Empty vec indicates unknown exports
    }
}

ConsumeShared Module Analysis Patterns

Expected Proxy Module Behavior

Key Finding: ConsumeShared modules showing empty usage arrays is correct behavior, not a bug.

// ConsumeShared modules act as proxy modules
// Export usage data is typically empty on proxy modules themselves
// Real usage data requires analyzing:
// 1. Incoming dependencies (modules that depend on ConsumeShared) using get_referenced_exports()
// 2. Fallback modules (the actual implementation)
// 3. Usage through module connections with ExtendedReferencedExport pattern matching

// Enhanced pattern for ConsumeShared analysis with get_referenced_exports():
let consumer_usage = analyze_consume_shared_usage_from_consumers(
    module_graph, 
    consume_shared_id, 
    runtimes
);

// Analysis focuses on:
// - Incoming connections to the ConsumeShared module
// - dependency.get_referenced_exports() for specific export extraction
// - ExtendedReferencedExport::Array and ExtendedReferencedExport::Export pattern handling
// - Cross-referencing used exports with provided exports for accurate filtering

Usage State Interpretation

  • UsageState::Used and UsageState::OnlyPropertiesUsed indicate actual usage
  • Empty usage arrays on ConsumeShared modules are expected due to proxy pattern
  • Real usage must be determined through dependency graph traversal

Plugin Development Insights

Proper Plugin Structure for Export Analysis

#[plugin_hook(CompilerEmit for SharedExportUsagePlugin)]
async fn emit(&self, compilation: &mut Compilation) -> Result<()> {
    let module_graph = compilation.get_module_graph();
    
    // Collect all runtimes for analysis
    let runtimes: Vec<RuntimeSpec> = compilation
        .chunk_by_ukey
        .values()
        .map(|chunk| chunk.runtime())
        .cloned()
        .collect();
    
    // Analyze each module
    for (module_id, _module) in module_graph.modules() {
        let usage_info = analyze_module(
            &module_id,
            &module_graph, 
            &runtimes,
            self.options.detailed_analysis
        )?;
    }
}

Module Graph Traversal Patterns

// Proper module graph traversal for export analysis
let exports_info = module_graph.get_exports_info(module_id);
let prefetched = ExportsInfoGetter::prefetch(
    &exports_info,
    module_graph,
    PrefetchExportsInfoMode::AllExports,
);

// Extract provided exports
let provided_exports = prefetched.get_provided_exports();

// Analyze usage for each export
for export_name in provided_exports_vec {
    let export_atom = rspack_util::atom::Atom::from(export_name.as_str());
    let export_info_data = prefetched.get_read_only_export_info(&export_atom);
    let usage_state = ExportInfoGetter::get_used(export_info_data, runtime_spec);
}

ConsumeShared-Specific Analysis Requirements

// ConsumeShared modules require special handling
if module.module_type() == &ModuleType::ConsumeShared {
    // 1. Find the fallback module
    let fallback_module_id = find_fallback_module(module_graph, module_id);
    
    // 2. Analyze usage from consumers
    let consumer_usage = analyze_consume_shared_usage_from_consumers(
        module_graph, module_id, runtimes
    );
    
    // 3. Get fallback module exports if available
    let (fallback_exports, _) = if let Some(fallback_id) = fallback_module_id {
        get_fallback_module_exports(module_graph, &fallback_id, runtimes, detailed)
    } else {
        (vec!["*".to_string()], Vec::new())
    };
    
    // 4. Merge analysis results
    let merged_usage = merge_consume_shared_usage_data(
        &consumer_usage, &fallback_exports, &fallback_details
    );
}

Data Source Analysis Findings

Text Analysis vs Runtime Analysis

  • Text Analysis Scripts: May show different results than runtime analysis
  • Runtime Analysis: Shows actual usage patterns during execution
  • Dependency Analysis: Most reliable for ConsumeShared modules
  • Module Graph Analysis: Required for accurate proxy module understanding

Dependency Connection Analysis

// Use incoming connections for accurate ConsumeShared analysis
for connection in module_graph.get_incoming_connections(consume_shared_id) {
    if let Some(dependency) = module_graph.dependency_by_id(&connection.dependency_id) {
        let referenced_exports = dependency.get_referenced_exports(
            module_graph,
            &rspack_core::ModuleGraphCacheArtifact::default(),
            None,
        );
        
        // Process referenced exports to extract used export names
        for export_ref in referenced_exports {
            match export_ref {
                ExtendedReferencedExport::Array(names) => {
                    // Multiple specific exports referenced
                },
                ExtendedReferencedExport::Export(export_info) => {
                    // Single export or namespace reference
                },
            }
        }
    }
}

Research Validation and Implementation Status

This documentation now reflects the actual implementation status and investigation findings:

✅ Confirmed Implementation

  • ConsumeShared export metadata copying is fully implemented and working
  • ExportsInfo/ExportInfo usage patterns are correctly implemented
  • Plugin hook timing and integration points work as documented
  • Export specification copying mechanisms function correctly in production

🔍 Investigation Insights

  • API Usage Validation: Correct usage of ExportsInfoGetter vs ExportInfoGetter confirmed
  • Prefetch Mode Usage: PrefetchExportsInfoMode::AllExports used correctly for bulk operations
  • ProvidedExports Handling: All three states (ProvidedNames, ProvidedAll, Unknown) handled properly
  • Usage State Analysis: ExportInfoGetter::get_used() provides accurate usage information

📝 New Behavioral Understanding

  • ConsumeShared Empty Usage: Confirmed as expected behavior, not a bug
  • Proxy Pattern Design: ConsumeShared modules intentionally show empty usage due to proxy architecture
  • Data Source Differences: Text analysis vs runtime analysis may show different results due to different data sources
  • Real Usage Analysis: Actual usage must be determined through dependency analysis or fallback module inspection

🔄 Implementation Completeness

  • Core Functionality: All essential features are implemented
  • Error Handling: Proper error handling and diagnostic reporting in place
  • Performance Optimization: Efficient bulk operations using prefetch modes
  • Integration: Seamless integration with existing export analysis infrastructure

Issues and Solutions Documented

  1. Empty Usage Arrays: Not a bug - expected behavior for ConsumeShared proxy modules
  2. API Usage Patterns: Proper distinction between ExportsInfoGetter and ExportInfoGetter clarified
  3. Data Source Understanding: Different analysis methods (text vs runtime) explained
  4. Lifecycle Timing: CompilationFinishModules hook usage validated as correct

This document now serves as both implementation reference and investigation findings, confirming that the ConsumeShared export metadata copying system is complete and functioning as designed.

Flagging ConsumeShared Module Usage for Export Tracking

To properly track used exports for ConsumeShared modules, you need to flag their dependency usage similar to normal modules. Here's how to implement this pattern:

1. ConsumeShared Dependency Usage Flagging

// In your ConsumeShared dependency implementation
impl Dependency for ConsumeSharedDependency {
    fn get_referenced_exports(
        &self,
        module_graph: &ModuleGraph,
        _module_graph_cache: &ModuleGraphCacheArtifact,
        runtime: Option<&RuntimeSpec>,
    ) -> Vec<ExtendedReferencedExport> {
        // Get the ConsumeShared module this dependency points to
        if let Some(consume_shared_id) = module_graph.module_identifier_by_dependency_id(&self.id) {
            // Find what exports are actually being imported from ConsumeShared
            let mut referenced_exports = Vec::new();
            
            // Check if specific exports are being imported
            if let Some(imported_names) = &self.imported_names {
                for name in imported_names {
                    referenced_exports.push(ExtendedReferencedExport::Array(vec![name.clone()]));
                }
            } else if self.namespace_import {
                // Namespace import - references entire exports object
                referenced_exports.push(ExtendedReferencedExport::Array(vec![]));
            } else {
                // Default import or specific patterns
                referenced_exports.push(ExtendedReferencedExport::Array(vec!["default".into()]));
            }
            
            referenced_exports
        } else {
            create_no_exports_referenced()
        }
    }
}

2. Enhanced ConsumeShared Module Usage Tracking

// Flag ConsumeShared usage by analyzing incoming connections
pub fn flag_consume_shared_usage(
    module_graph: &mut ModuleGraph,
    consume_shared_id: &ModuleIdentifier,
    runtime: Option<&RuntimeSpec>,
) -> Result<()> {
    let consume_shared_exports_info = module_graph.get_exports_info(consume_shared_id);
    
    // Collect usage information from incoming dependencies
    let mut used_exports = Vec::new();
    let mut uses_namespace = false;
    
    // Analyze incoming connections to determine usage
    for connection in module_graph.get_incoming_connections(consume_shared_id) {
        if let Some(dependency) = module_graph.dependency_by_id(&connection.dependency_id) {
            let referenced_exports = dependency.get_referenced_exports(
                module_graph,
                &rspack_core::ModuleGraphCacheArtifact::default(),
                runtime,
            );
            
            for export_ref in referenced_exports {
                match export_ref {
                    ExtendedReferencedExport::Array(names) => {
                        if names.is_empty() {
                            // Namespace usage
                            uses_namespace = true;
                        } else {
                            // Specific exports
                            for name in names {
                                used_exports.push(name);
                            }
                        }
                    },
                    ExtendedReferencedExport::Export(export_info) => {
                        if export_info.name.is_empty() {
                            uses_namespace = true;
                        } else {
                            used_exports.extend(export_info.name);
                        }
                    },
                }
            }
        }
    }
    
    // Apply usage flags to ConsumeShared module's exports
    if uses_namespace {
        // Mark all exports as used in unknown way (namespace usage)
        consume_shared_exports_info.set_used_in_unknown_way(module_graph, runtime);
    } else {
        // Mark specific exports as used
        for export_name in used_exports {
            let export_info = consume_shared_exports_info.get_export_info(module_graph, &export_name);
            ExportInfoSetter::set_used(
                export_info.as_data_mut(module_graph),
                UsageState::Used,
                runtime,
            );
        }
    }
    
    Ok(())
}

3. Integration with FlagDependencyUsagePlugin

// In FlagDependencyUsagePlugin::process_referenced_module
fn process_consume_shared_module(
    &mut self,
    module_id: ModuleIdentifier,
    referenced_exports: Vec<ExtendedReferencedExport>,
    runtime: Option<RuntimeSpec>,
    queue: &mut Queue<(ModuleIdentifier, Option<RuntimeSpec>)>,
) {
    let module_graph = &mut self.compilation.module_graph;
    let consume_shared_exports_info = module_graph.get_exports_info(&module_id);
    
    // Process referenced exports same as normal modules
    for export_ref in referenced_exports {
        match export_ref {
            ExtendedReferencedExport::Array(export_path) => {
                if export_path.is_empty() {
                    // Namespace usage
                    let changed = consume_shared_exports_info.set_used_in_unknown_way(
                        module_graph, 
                        runtime.as_ref()
                    );
                    if changed {
                        queue.enqueue((module_id, runtime.clone()));
                    }
                } else {
                    // Specific export usage
                    let mut current_exports_info = consume_shared_exports_info;
                    for (i, export_name) in export_path.iter().enumerate() {
                        let export_info = current_exports_info.get_export_info(module_graph, export_name);
                        
                        let usage_state = if i == export_path.len() - 1 {
                            UsageState::Used
                        } else {
                            UsageState::OnlyPropertiesUsed
                        };
                        
                        let changed = ExportInfoSetter::set_used_conditionally(
                            export_info.as_data_mut(module_graph),
                            Box::new(|current| current != &usage_state),
                            usage_state,
                            runtime.as_ref(),
                        );
                        
                        if changed {
                            queue.enqueue((module_id, runtime.clone()));
                        }
                        
                        // Continue to nested exports if not the last one
                        if i < export_path.len() - 1 {
                            if let Some(nested_info) = export_info.as_data(module_graph).exports_info() {
                                current_exports_info = nested_info;
                            }
                        }
                    }
                }
            },
            ExtendedReferencedExport::Export(export_info) => {
                // Handle with mangling and inlining constraints
                let export_path = export_info.name;
                if export_path.is_empty() {
                    let changed = consume_shared_exports_info.set_used_in_unknown_way(
                        module_graph, 
                        runtime.as_ref()
                    );
                    if changed {
                        queue.enqueue((module_id, runtime.clone()));
                    }
                } else {
                    // Process with constraints
                    for export_name in export_path {
                        let export_info_obj = consume_shared_exports_info.get_export_info(module_graph, &export_name);
                        let export_data = export_info_obj.as_data_mut(module_graph);
                        
                        // Apply constraints
                        if !export_info.can_mangle {
                            export_data.set_can_mangle_use(Some(false));
                        }
                        if !export_info.can_inline {
                            export_data.set_inlinable(Inlinable::NoByUse);
                        }
                        
                        let changed = ExportInfoSetter::set_used(
                            export_data,
                            UsageState::Used,
                            runtime.as_ref(),
                        );
                        
                        if changed {
                            queue.enqueue((module_id, runtime.clone()));
                        }
                    }
                }
            },
        }
    }
}

4. Complete ConsumeShared Usage Workflow

// Complete workflow for ConsumeShared usage tracking
pub fn track_consume_shared_usage(
    compilation: &mut Compilation,
    consume_shared_id: &ModuleIdentifier,
    runtime: Option<&RuntimeSpec>,
) -> Result<()> {
    let module_graph = compilation.get_module_graph_mut();
    
    // Step 1: Copy provided exports from fallback module
    if let Some(fallback_id) = find_fallback_module(module_graph, consume_shared_id) {
        copy_exports_from_fallback(module_graph, consume_shared_id, &fallback_id)?;
    }
    
    // Step 2: Flag usage based on incoming dependencies
    flag_consume_shared_usage(module_graph, consume_shared_id, runtime)?;
    
    // Step 3: Process through normal usage plugin flow
    // This happens automatically when FlagDependencyUsagePlugin processes the module
    
    Ok(())
}

// Helper: Copy exports from fallback to ConsumeShared
fn copy_exports_from_fallback(
    module_graph: &mut ModuleGraph,
    consume_shared_id: &ModuleIdentifier,
    fallback_id: &ModuleIdentifier,
) -> Result<()> {
    let fallback_exports_info = module_graph.get_exports_info(fallback_id);
    let consume_shared_exports_info = module_graph.get_exports_info(consume_shared_id);
    
    let prefetched_fallback = ExportsInfoGetter::prefetch(
        &fallback_exports_info,
        module_graph,
        PrefetchExportsInfoMode::AllExports,
    );
    
    match prefetched_fallback.get_provided_exports() {
        ProvidedExports::ProvidedNames(export_names) => {
            for export_name in export_names {
                let consume_export_info = consume_shared_exports_info.get_export_info(module_graph, &export_name);
                let fallback_export_info = fallback_exports_info.get_export_info(module_graph, &export_name);
                
                // Copy provision status
                if let Some(provided) = fallback_export_info.as_data(module_graph).provided() {
                    consume_export_info.as_data_mut(module_graph).set_provided(Some(provided));
                }
                
                // Copy other metadata
                if let Some(can_mangle) = fallback_export_info.as_data(module_graph).can_mangle_provide() {
                    consume_export_info.as_data_mut(module_graph).set_can_mangle_provide(Some(can_mangle));
                }
            }
            
            // Mark as having complete provide info
            consume_shared_exports_info.set_has_provide_info(module_graph);
        },
        ProvidedExports::ProvidedAll => {
            consume_shared_exports_info.set_unknown_exports_provided(
                module_graph, true, None, None, None, None
            );
        },
        ProvidedExports::Unknown => {
            // Keep unknown status
        }
    }
    
    Ok(())
}

Key Benefits of This Approach

  1. Normal Usage Tracking: ConsumeShared modules get flagged for usage the same way as regular modules
  2. Accurate Export Data: Provided exports come from fallback module, usage data from actual consumption
  3. Tree-Shaking Ready: Unused exports in ConsumeShared modules can be properly eliminated
  4. Runtime Awareness: Supports runtime-specific usage tracking for code splitting
  5. Constraint Handling: Respects mangling and inlining constraints from dependencies

Integration Timing

  1. Export Provision: Copy exports from fallback module during CompilationFinishModules
  2. Usage Analysis: Flag usage during FlagDependencyUsagePlugin execution
  3. Tree-Shaking: Apply optimizations during code generation based on usage flags

✅ IMPLEMENTATION STATUS: COMPLETE

Enhancement Applied: The FlagDependencyUsagePlugin has been updated with special ConsumeShared module handling.

What Was Implemented

  1. Enhanced FlagDependencyUsagePlugin: Added process_consume_shared_module() method that processes ConsumeShared modules the same way as normal modules for usage tracking.

  2. Special Module Type Detection: The plugin now detects ConsumeShared modules and routes them to enhanced processing:

    if module.module_type() == &rspack_core::ModuleType::ConsumeShared {
      self.process_consume_shared_module(module_id, used_exports, runtime, force_side_effects, queue);
      return;
    }
  3. Complete Usage State Management: ConsumeShared modules now get proper usage state assignment:

    • Specific Exports: Individual exports marked as Used or OnlyPropertiesUsed
    • Namespace Usage: All exports marked as used in unknown way
    • Side Effects: Proper side-effect-only usage tracking
    • Constraints: Mangling and inlining constraints are applied
  4. Tree-Shaking Ready: ConsumeShared modules can now have unused exports properly eliminated during tree-shaking.

Integration with Existing System

  • Export Provision: ConsumeShared modules get provided exports from fallback module (via ConsumeSharedPlugin::finish_modules)
  • Usage Tracking: ConsumeShared modules now get proper usage flags (via enhanced FlagDependencyUsagePlugin)
  • Tree-Shaking: Unused exports in ConsumeShared modules are eliminated during optimization

This implementation ensures that ConsumeShared modules participate fully in the export usage tracking system, enabling proper tree-shaking while maintaining the proxy pattern needed for module federation.

Fallback Module Tree-Shaking Behavior

Correct Behavior: Fallback Modules Are NOT Tree-Shaken

The implementation correctly preserves all exports in fallback modules, which is the desired behavior for module federation:

Why Fallback Modules Should Not Be Tree-Shaken

  1. Fallback Completeness: Fallback modules must remain complete because they serve as the backup when shared modules are unavailable
  2. Runtime Uncertainty: At build time, we don't know which shared module version will be available at runtime
  3. Safety First: The fallback must be able to provide any export that might be needed

How The Implementation Ensures This

  1. ConsumeSharedFallbackDependency: Does NOT implement get_referenced_exports(), so it uses the default that references the entire exports object:

    // Default implementation returns:
    create_exports_object_referenced() // References all exports
  2. Complete Export Preservation: The fallback dependency includes all exports, preventing tree-shaking:

    // ConsumeSharedFallbackDependency inherits default behavior
    impl Dependency for ConsumeSharedFallbackDependency {
      // No custom get_referenced_exports - uses default (all exports)
    }
  3. Proxy Pattern Separation: Tree-shaking works on the ConsumeShared proxy module, not the fallback:

    • ConsumeShared Module: Gets tree-shaken based on actual usage
    • Fallback Module: Remains complete and untouched by tree-shaking

Tree-Shaking Flow for Module Federation

Consumer Code
     ↓ (imports specific exports)
ConsumeShared Module (Proxy)
     ↓ (tree-shaken based on usage)
     ↓ (references ALL exports from fallback)
Fallback Module
     ↓ (NOT tree-shaken - remains complete)

Benefits of This Approach

  1. Safety: Fallback modules provide complete functionality when shared modules fail
  2. Reliability: No risk of missing exports in fallback scenarios
  3. Performance: Tree-shaking still works on the ConsumeShared proxy layer
  4. Flexibility: Fallback modules can handle any usage pattern at runtime

Implementation Summary

The current implementation strikes the perfect balance:

  • ConsumeShared modules: Participate in tree-shaking for optimal bundle size
  • Fallback modules: Remain complete for maximum reliability
  • Module federation: Works correctly with both shared and fallback scenarios

This architectural decision ensures that module federation remains robust while still benefiting from tree-shaking optimizations where appropriate.

Module Metadata Copying Patterns and Best Practices

Overview

This document consolidates comprehensive research into module information copying patterns in the rspack codebase, providing definitive guidance on when and how to copy, merge, and transfer metadata between modules.

Core Metadata Types

1. Build Metadata

  • build_meta: Build-time metadata including ESM info, async module state, side effects
  • build_info: Build information including asset dependencies, file dependencies, context dependencies
  • factory_meta: Factory creation metadata preserved during module transformations

2. Export Information

  • ExportsInfo: Per-module export collection with bulk operations
  • ExportInfo: Individual export metadata including usage state, mangling capabilities, targets
  • ProvidedExports: What exports a module provides (ProvidedNames, ProvidedAll, Unknown)

3. Usage Information

  • UsageState: Export usage state (Unused, OnlyPropertiesUsed, NoInfo, Unknown, Used)
  • Runtime-specific usage: Per-runtime tracking for code splitting scenarios
  • Referenced exports: Dependency-driven usage information

Established Patterns in Rspack Codebase

1. Proxy Module Pattern (ConsumeShared)

Location: /Users/bytedance/RustroverProjects/rspack/crates/rspack_plugin_mf/src/sharing/consume_shared_module.rs

Purpose: Module Federation proxy modules that inherit all metadata from fallback modules

Implementation:

/// Copies metadata from fallback module to make ConsumeShared act as true proxy
pub fn copy_metadata_from_fallback(&mut self, module_graph: &mut ModuleGraph) -> Result<()> {
    if let Some(fallback_id) = self.find_fallback_module_id(module_graph) {
        if let Some(fallback_module) = module_graph.module_by_identifier(&fallback_id) {
            // Phase 1: Copy build metadata
            self.build_meta = fallback_module.build_meta().clone();
            self.build_info = fallback_module.build_info().clone();

            // Phase 2: Copy export information
            self.copy_exports_from_fallback(module_graph, &fallback_id)?;
        }
    }
    Ok(())
}

/// Comprehensive export information copying
fn copy_exports_from_fallback(&self, module_graph: &mut ModuleGraph, fallback_id: &ModuleIdentifier) -> Result<()> {
    let fallback_exports_info = module_graph.get_exports_info(fallback_id);
    let consume_shared_exports_info = module_graph.get_exports_info(&self.identifier());

    // Use prefetched analysis for efficiency
    let prefetched_fallback = ExportsInfoGetter::prefetch(
        &fallback_exports_info,
        module_graph,
        PrefetchExportsInfoMode::AllExports,
    );

    match prefetched_fallback.get_provided_exports() {
        ProvidedExports::ProvidedNames(export_names) => {
            // Copy each specific export with full metadata
            for export_name in export_names {
                let consume_shared_export_info = consume_shared_exports_info.get_export_info(module_graph, &export_name);
                let fallback_export_info = fallback_exports_info.get_export_info(module_graph, &export_name);

                // Copy provided status
                if let Some(provided) = fallback_export_info.as_data(module_graph).provided() {
                    consume_shared_export_info.as_data_mut(module_graph).set_provided(Some(provided));
                }

                // Copy mangling capabilities
                if let Some(can_mangle) = fallback_export_info.as_data(module_graph).can_mangle_provide() {
                    consume_shared_export_info.as_data_mut(module_graph).set_can_mangle_provide(Some(can_mangle));
                }

                // Copy nested export structures
                if let Some(nested_exports_info) = fallback_export_info.as_data(module_graph).exports_info() {
                    consume_shared_export_info.as_data_mut(module_graph).set_exports_info(Some(nested_exports_info));
                }
            }

            // Mark as having complete provide info
            consume_shared_exports_info.set_has_provide_info(module_graph);
            
            // Set unknown exports to not provided
            consume_shared_exports_info.set_unknown_exports_provided(
                module_graph,
                false, // not provided
                None, None, None, None,
            );
        }
        ProvidedExports::ProvidedAll => {
            // Inherit dynamic export capability
            consume_shared_exports_info.set_unknown_exports_provided(
                module_graph,
                true, // provided
                None, None, None, None,
            );
            consume_shared_exports_info.set_has_provide_info(module_graph);
        }
        ProvidedExports::Unknown => {
            // Preserve unknown status - no copying needed
        }
    }

    Ok(())
}

When to Use: Module Federation scenarios where modules must act as transparent proxies

2. Error Recovery Pattern (FixBuildMeta)

Location: /Users/bytedance/RustroverProjects/rspack/crates/rspack_core/src/compiler/make/cutout/fix_build_meta.rs

Purpose: Preserve build metadata when module builds fail

Implementation:

// Save original metadata before rebuild
pub fn analyze_force_build_module(&mut self, artifact: &MakeArtifact, module_identifier: &ModuleIdentifier) {
    let module = module_graph.module_by_identifier(module_identifier).expect("should have module");
    self.origin_module_build_meta.insert(*module_identifier, module.build_meta().clone());
}

// Restore metadata if build fails
pub fn fix_artifact(&mut self, artifact: &mut MakeArtifact, failed_module: &ModuleIdentifier) {
    if let Some(build_meta) = self.origin_module_build_meta.get(failed_module) {
        if let Some(mut module) = module_graph.module_by_identifier_mut(failed_module) {
            if module.first_error().is_some() {
                *module.build_meta_mut() = build_meta.clone();
            }
        }
    }
}

When to Use: Error recovery scenarios, incremental compilation with rollback needs

3. Export Merging Pattern (FlagDependencyExportsPlugin)

Location: /Users/bytedance/RustroverProjects/rspack/crates/rspack_plugin_javascript/src/plugin/flag_dependency_exports_plugin.rs

Purpose: Merge export specifications from dependencies into module export info

Implementation:

pub fn merge_exports(&mut self, exports_info: ExportsInfo, exports: &Vec<ExportNameOrSpec>, global_export_info: DefaultExportInfo, dep_id: DependencyId) {
    for export_name_or_spec in exports {
        let export_info = exports_info.get_export_info(self.mg, &name);
        let export_info_data = export_info.as_data_mut(self.mg);
        
        // Merge provided status
        if matches!(export_info_data.provided(), Some(ExportProvided::NotProvided | ExportProvided::Unknown)) {
            export_info_data.set_provided(Some(ExportProvided::Provided));
            self.changed = true;
        }

        // Merge mangling capabilities
        if Some(false) != export_info_data.can_mangle_provide() && can_mangle == Some(false) {
            export_info_data.set_can_mangle_provide(Some(false));
            self.changed = true;
        }

        // Set target for re-exports
        if let Some(from) = from {
            let changed = ExportInfoSetter::set_target(
                export_info_data,
                Some(dep_id),
                Some(from.dependency_id),
                export_name,
                priority,
            );
            self.changed |= changed;
        }

        // Recursive merge for nested exports
        if let Some(exports) = exports {
            let nested_exports_info = ExportInfoSetter::create_nested_exports_info(&export_info, self.mg);
            self.merge_exports(nested_exports_info, exports, global_export_info.clone(), dep_id);
        }
    }
}

When to Use: Dependency analysis plugins that need to accumulate export information

4. Template Initialization Pattern (ExportInfo)

Location: /Users/bytedance/RustroverProjects/rspack/crates/rspack_core/src/exports/export_info.rs

Purpose: Initialize new ExportInfo from existing template with property inheritance

Implementation:

pub fn new(name: Option<Atom>, init_from: Option<&ExportInfoData>) -> Self {
    let used_name = init_from.and_then(|init_from| init_from.used_name.clone());
    let global_used = init_from.and_then(|init_from| init_from.global_used);
    let used_in_runtime = init_from.and_then(|init_from| init_from.used_in_runtime.clone());
    let has_use_in_runtime_info = init_from.is_some_and(|init_from| init_from.has_use_in_runtime_info);

    let provided = init_from.and_then(|init_from| init_from.provided);
    let terminal_binding = init_from.is_some_and(|init_from| init_from.terminal_binding);
    let can_mangle_provide = init_from.and_then(|init_from| init_from.can_mangle_provide);
    let can_mangle_use = init_from.and_then(|init_from| init_from.can_mangle_use);

    // Target copying with name transformation
    let target = init_from.and_then(|item| {
        if item.target_is_set {
            Some(/* transform targets with new name */)
        } else {
            None
        }
    }).unwrap_or_default();

    ExportInfoData {
        name,
        used_name,
        global_used,
        used_in_runtime,
        has_use_in_runtime_info,
        provided,
        terminal_binding,
        can_mangle_provide,
        can_mangle_use,
        target,
        target_is_set: target.is_set(),
        /* ... other fields */
    }
}

When to Use: Creating new exports based on existing export templates

5. DLL Delegation Pattern (DelegatedModule)

Location: /Users/bytedance/RustroverProjects/rspack/crates/rspack_plugin_dll/src/dll_reference/delegated_module.rs

Purpose: Inherit metadata from DLL manifest

Implementation:

async fn build(&mut self, _build_context: BuildContext, _compilation: Option<&Compilation>) -> Result<BuildResult> {
    // Copy build meta from DLL manifest
    self.build_meta = self.delegate_data.build_meta.clone();
    
    let dependencies = vec![
        Box::new(DelegatedSourceDependency::new(self.source_request.clone())),
        Box::new(StaticExportsDependency::new(/* DLL exports */)) as BoxDependency,
    ];
    
    Ok(BuildResult { dependencies, ..Default::default() })
}

When to Use: DLL reference scenarios where delegated modules inherit DLL characteristics

Best Practices

1. Two-Phase Copying Approach

Phase 1: Build Metadata

// Copy build-time characteristics
proxy_module.build_meta = source_module.build_meta().clone();
proxy_module.build_info = source_module.build_info().clone();

Phase 2: Export Information

// Copy export metadata using prefetched analysis
let prefetched = ExportsInfoGetter::prefetch(&source_exports_info, module_graph, PrefetchExportsInfoMode::AllExports);
// ... detailed export copying

2. Plugin Hook Integration

Recommended Hook: CompilationFinishModules

  • Timing: After all modules are built and analyzed, before optimization
  • Access: Full module graph with build metadata available
  • Safety: Avoids borrow checker issues with sequential processing
#[plugin_hook(CompilationFinishModules for YourPlugin)]
async fn finish_modules(&self, compilation: &mut Compilation) -> Result<()> {
    // Find modules requiring metadata copying
    let target_modules: Vec<ModuleIdentifier> = /* collect */;
    
    // Process each individually to avoid borrow checker issues
    for module_id in target_modules {
        Self::copy_metadata_between_modules(compilation, &module_id)?;
    }
    
    Ok(())
}

3. Efficient Export Information Access

Use Prefetched Analysis:

// Efficient: Bulk prefetch for multiple operations
let prefetched = ExportsInfoGetter::prefetch(
    &exports_info,
    module_graph,
    PrefetchExportsInfoMode::AllExports
);

// Efficient: Individual export access through prefetched wrapper
let export_info_data = prefetched.get_read_only_export_info(&export_atom);
let usage_state = ExportInfoGetter::get_used(export_info_data, runtime);

// Avoid: Repeated individual module graph access
// let export_info = exports_info.get_export_info(module_graph, &export_name); // Less efficient

4. Usage State Management

Setting Usage States:

// Basic usage setting
ExportInfoSetter::set_used(export_info_data, UsageState::Used, runtime);

// Conditional usage setting (only if condition met)
ExportInfoSetter::set_used_conditionally(
    export_info_data,
    Box::new(|current| current != &UsageState::Used),
    UsageState::Used,
    runtime
);

// Disable optimizations when usage unclear
ExportInfoSetter::set_used_without_info(export_info_data, runtime);

Bulk Operations:

// Set all exports used
exports_info.set_all_known_exports_used(module_graph, runtime);

// Mark as having complete usage info
exports_info.set_has_use_info(module_graph);

5. Borrow Checker Patterns

Separate Scope Approach:

// Problematic: Multiple mutable borrows
let mut module_graph = compilation.get_module_graph_mut();
let source_module = module_graph.module_by_identifier(&source_id); // Borrows mutably
let target_module = module_graph.module_by_identifier_mut(&target_id); // Second mutable borrow

// Solution: Separate scopes
let source_metadata = {
    let module_graph = compilation.get_module_graph(); // Immutable borrow
    let source_module = module_graph.module_by_identifier(&source_id)?;
    (source_module.build_meta().clone(), source_module.build_info().clone())
};

{
    let mut module_graph = compilation.get_module_graph_mut(); // New mutable borrow
    let mut target_module = module_graph.module_by_identifier_mut(&target_id)?;
    target_module.build_meta = source_metadata.0;
    target_module.build_info = source_metadata.1;
}

6. Error Handling and Diagnostics

Graceful Failure:

if let Err(e) = module.copy_metadata_from_source(&mut module_graph) {
    compilation.push_diagnostic(
        rspack_error::Diagnostic::warn(
            "ModuleMetadataCopyPlugin".into(),
            format!("Failed to copy metadata: {}", e),
        )
    );
}

Common Use Cases

1. Proxy Modules

  • ConsumeShared: Module Federation proxy modules
  • LazyCompilation: Proxy modules for lazy-loaded content
  • DelegatedModule: DLL reference proxies

2. Module Transformation

  • Concatenation: Preserving metadata during module concatenation
  • Code Splitting: Maintaining export information across chunk boundaries
  • Tree Shaking: Copying usage information for optimization decisions

3. Error Recovery

  • Incremental Compilation: Restoring metadata after failed rebuilds
  • Hot Module Replacement: Preserving module state during updates
  • Build Rollback: Reverting to previous module states

4. Plugin Development

  • Export Analysis: Plugins that analyze and modify export information
  • Module Federation: Sharing modules across application boundaries
  • Custom Transformations: Plugins that create new modules based on existing ones

Performance Considerations

  1. Use Prefetched Analysis: Avoid repeated module graph traversals
  2. Batch Operations: Process multiple modules in single hook invocation
  3. Separate Scopes: Avoid borrow checker conflicts with scope separation
  4. Change Detection: Only propagate changes when necessary
  5. Runtime Specificity: Consider per-runtime vs global usage tracking

This comprehensive guide provides the foundation for implementing robust module metadata copying in rspack plugins and extensions.

Plugin Development Patterns in Rspack

Overview

This document provides comprehensive patterns and best practices for developing rspack plugins, specifically focused on export analysis, module graph manipulation, and compilation hooks. The insights are derived from real-world plugin implementations including ShareUsagePlugin, ConsumeSharedPlugin, and various export analysis tools.

Plugin Structure and Organization

Basic Plugin Architecture

Plugin Declaration and Hooks

use rspack_core::{Plugin, PluginContext, CompilerEmit, CompilerOptions};

#[plugin]
#[derive(Debug)]
pub struct ExportAnalysisPlugin {
    options: ExportAnalysisOptions,
}

#[plugin_hook(CompilerEmit for ExportAnalysisPlugin)]
async fn emit(&self, compilation: &mut Compilation) -> Result<()> {
    // Plugin implementation
    Ok(())
}

Key Patterns:

  • #[plugin] macro for plugin registration
  • #[plugin_hook(HookName for PluginName)] for hook implementation
  • Async functions with Result<()> return type for error handling
  • Plugin structs should derive Debug for diagnostics

Plugin Options and Configuration

#[derive(Debug, Clone)]
pub struct ExportAnalysisOptions {
    pub output_path: Option<String>,
    pub detailed_analysis: bool,
    pub include_patterns: Vec<String>,
    pub exclude_patterns: Vec<String>,
}

impl Default for ExportAnalysisOptions {
    fn default() -> Self {
        Self {
            output_path: None,
            detailed_analysis: false,
            include_patterns: vec!["**/*.js".to_string(), "**/*.ts".to_string()],
            exclude_patterns: vec!["node_modules/**".to_string()],
        }
    }
}

Compilation Hooks and Their Usage

Available Compilation Hooks

CompilerEmit Hook

Timing: After optimization, before asset emission Use Case: Asset generation, analysis reports, final processing

#[plugin_hook(CompilerEmit for MyPlugin)]
async fn emit(&self, compilation: &mut Compilation) -> Result<()> {
    // Generate analysis reports
    let report = self.generate_analysis_report(compilation)?;
    
    // Create assets
    let source = RawSource::from(serde_json::to_string_pretty(&report)?);
    let asset = CompilationAsset::new(
        Some(source), 
        AssetInfo::default().with_development(true)
    );
    
    compilation.emit_asset("analysis-report.json".to_string(), asset);
    Ok(())
}

CompilationFinishModules Hook

Timing: After module building, before optimization Use Case: Module metadata manipulation, export information copying

#[plugin_hook(CompilationFinishModules for MetadataCopyPlugin)]
async fn finish_modules(&self, compilation: &mut Compilation) -> Result<()> {
    let module_graph = compilation.get_module_graph();
    
    // Collect modules that need processing
    let target_modules: Vec<ModuleIdentifier> = module_graph
        .modules()
        .keys()
        .filter(|&id| self.should_process_module(&module_graph, id))
        .copied()
        .collect();
    
    // Process each module individually to avoid borrow checker issues
    for module_id in target_modules {
        self.process_module(compilation, &module_id)?;
    }
    
    Ok(())
}

CompilationOptimizeDependencies Hook

Timing: During optimization phase Use Case: Dependency analysis, usage tracking

#[plugin_hook(CompilationOptimizeDependencies for DependencyOptimizer)]
async fn optimize_dependencies(&self, compilation: &mut Compilation) -> Result<bool> {
    let mut module_graph = compilation.get_module_graph_mut();
    let mut changes_made = false;
    
    // Optimize dependencies based on usage analysis
    for (module_id, _) in module_graph.modules() {
        if self.optimize_module_dependencies(&mut module_graph, module_id)? {
            changes_made = true;
        }
    }
    
    Ok(changes_made) // Return true if further optimization needed
}

Hook Timing and Dependencies

Compilation Lifecycle:
┌─────────────────────────────────────────────────────────────┐
│  Module Building Phase                                      │
│  ├── Module parsing and dependency creation                 │
│  ├── Dependency resolution                                  │
│  └── CompilationFinishModules ← Good for metadata copying   │
│                                                             │
│  Optimization Phase                                         │
│  ├── CompilationOptimizeDependencies ← Usage analysis      │
│  ├── FlagDependencyExportsPlugin                          │
│  ├── FlagDependencyUsagePlugin                            │
│  └── Various optimization passes                           │
│                                                             │
│  Asset Generation Phase                                     │
│  ├── Code generation                                       │
│  ├── CompilerEmit ← Asset creation, report generation      │
│  └── Asset emission                                        │
└─────────────────────────────────────────────────────────────┘

Module Graph Manipulation Patterns

Safe Module Graph Access

Avoiding Borrow Checker Issues

// ❌ Problematic - multiple mutable borrows
let mut module_graph = compilation.get_module_graph_mut();
let module = module_graph.module_by_identifier_mut(&id);
let other_module = module_graph.module_by_identifier_mut(&other_id); // Error!

// ✅ Correct - separate scopes
{
    let module_graph = compilation.get_module_graph(); // immutable
    let module_ids = collect_target_modules(&module_graph);
}
{
    let mut module_graph = compilation.get_module_graph_mut(); // mutable
    for module_id in module_ids {
        process_module(&mut module_graph, &module_id);
    }
}

// ✅ Alternative - helper methods with contained borrows
fn process_modules_individually(
    compilation: &mut Compilation,
    module_ids: &[ModuleIdentifier]
) -> Result<()> {
    for &module_id in module_ids {
        {
            let mut module_graph = compilation.get_module_graph_mut();
            // Process one module at a time
            process_single_module(&mut module_graph, &module_id)?;
        } // Borrow ends here
    }
    Ok(())
}

Module Iteration Patterns

// Collect first, then process
let module_ids: Vec<ModuleIdentifier> = {
    let module_graph = compilation.get_module_graph();
    module_graph
        .modules()
        .keys()
        .filter(|&id| should_process(module_graph, id))
        .copied()
        .collect()
};

// Process with mutable access
for module_id in module_ids {
    let mut module_graph = compilation.get_module_graph_mut();
    process_module(&mut module_graph, &module_id)?;
}

Module Type Detection and Filtering

// Module type checking patterns
fn should_process_module(module_graph: &ModuleGraph, module_id: &ModuleIdentifier) -> bool {
    if let Some(module) = module_graph.module_by_identifier(module_id) {
        match module.module_type() {
            ModuleType::Js => true,
            ModuleType::ConsumeShared => true, // Special handling
            ModuleType::Asset => false,
            _ => false,
        }
    } else {
        false
    }
}

// Layer-based filtering
fn filter_by_layer(module_graph: &ModuleGraph, target_layer: Option<&str>) -> Vec<ModuleIdentifier> {
    module_graph
        .modules()
        .iter()
        .filter_map(|(id, module)| {
            match (module.get_layer(), target_layer) {
                (Some(layer), Some(target)) if layer == target => Some(*id),
                (None, None) => Some(*id),
                _ => None,
            }
        })
        .collect()
}

Export Analysis API Usage

Correct API Usage Patterns

ExportsInfoGetter vs ExportInfoGetter

use rspack_core::{ExportsInfoGetter, ExportInfoGetter, PrefetchExportsInfoMode};

// Use ExportsInfoGetter for bulk operations and prefetching
let exports_info = module_graph.get_exports_info(module_id);
let prefetched = ExportsInfoGetter::prefetch(
    &exports_info,
    module_graph,
    PrefetchExportsInfoMode::AllExports, // Efficient bulk analysis
);

// Extract provided exports
let provided_exports = prefetched.get_provided_exports();

// Use ExportInfoGetter for individual export analysis
for export_name in export_names {
    let export_atom = rspack_util::atom::Atom::from(export_name.as_str());
    let export_info_data = prefetched.get_read_only_export_info(&export_atom);
    let usage_state = ExportInfoGetter::get_used(export_info_data, runtime_spec);
    
    // Process usage state
    match usage_state {
        UsageState::Used => { /* Export is actively used */ },
        UsageState::OnlyPropertiesUsed => { /* Only properties accessed */ },
        UsageState::Unused => { /* Export can be tree-shaken */ },
        _ => { /* Other usage patterns */ }
    }
}

PrefetchExportsInfoMode Usage

// Different prefetch modes for different use cases
let prefetch_mode = match analysis_type {
    AnalysisType::Comprehensive => PrefetchExportsInfoMode::AllExports,
    AnalysisType::Specific(names) => PrefetchExportsInfoMode::NamedExports(names),
    AnalysisType::Nested(path) => PrefetchExportsInfoMode::NamedNestedExports(path),
    AnalysisType::Basic => PrefetchExportsInfoMode::Default,
};

let prefetched = ExportsInfoGetter::prefetch(&exports_info, module_graph, prefetch_mode);

ProvidedExports Handling

use rspack_core::ProvidedExports;

fn extract_provided_exports(provided: &ProvidedExports) -> Vec<String> {
    match provided {
        ProvidedExports::ProvidedNames(names) => {
            // Specific named exports
            names.iter().map(|name| name.as_str().to_string()).collect()
        },
        ProvidedExports::ProvidedAll => {
            // Module provides all possible exports dynamically
            vec!["*".to_string()]
        },
        ProvidedExports::Unknown => {
            // Cannot determine exports statically
            vec![] // Empty indicates unknown exports
        }
    }
}

// Usage in analysis
fn analyze_module_exports(
    module_graph: &ModuleGraph,
    module_id: &ModuleIdentifier,
    runtimes: &[RuntimeSpec]
) -> Result<ModuleExportInfo> {
    let exports_info = module_graph.get_exports_info(module_id);
    let prefetched = ExportsInfoGetter::prefetch(
        &exports_info,
        module_graph,
        PrefetchExportsInfoMode::AllExports,
    );
    
    let provided_exports = prefetched.get_provided_exports();
    let provided_export_names = extract_provided_exports(&provided_exports);
    
    let mut export_details = Vec::new();
    for export_name in &provided_export_names {
        if export_name == "*" {
            // Handle dynamic exports
            continue;
        }
        
        let export_atom = rspack_util::atom::Atom::from(export_name.as_str());
        let export_info_data = prefetched.get_read_only_export_info(&export_atom);
        
        for runtime in runtimes {
            let usage_state = ExportInfoGetter::get_used(export_info_data, Some(runtime));
            export_details.push(ExportDetail {
                name: export_name.clone(),
                usage_state,
                runtime: runtime.clone(),
            });
        }
    }
    
    Ok(ModuleExportInfo {
        module_id: module_id.to_string(),
        provided_exports: provided_export_names,
        export_details,
    })
}

ConsumeShared Module Analysis Patterns

Understanding ConsumeShared Proxy Behavior

// ConsumeShared modules require special analysis patterns with enhanced dependency analysis
fn analyze_consume_shared_module(
    module_graph: &ModuleGraph,
    module_id: &ModuleIdentifier,
    runtimes: &[RuntimeSpec]
) -> Result<ConsumeSharedAnalysis> {
    // 1. ConsumeShared modules act as proxies - their direct usage is often empty
    let direct_exports = get_direct_module_exports(module_graph, module_id, runtimes);
    
    // 2. Find the fallback module for real export information
    let fallback_module_id = find_fallback_module(module_graph, module_id);
    let fallback_exports = if let Some(fallback_id) = fallback_module_id {
        get_module_exports(module_graph, &fallback_id, runtimes, true)?
    } else {
        vec![]
    };
    
    // 3. Enhanced analysis using incoming connections and get_referenced_exports()
    let consumer_usage = analyze_consume_shared_usage_from_consumers(
        module_graph,
        module_id,
        runtimes
    )?;
    
    // 4. Merge all analysis sources
    Ok(ConsumeSharedAnalysis {
        direct_exports,
        fallback_exports,
        consumer_usage,
        proxy_behavior_detected: direct_exports.is_empty() && !fallback_exports.is_empty(),
    })
}

// Enhanced analysis using get_referenced_exports() for precise export extraction
fn analyze_consume_shared_usage_from_consumers(
    module_graph: &ModuleGraph,
    consume_shared_id: &ModuleIdentifier,
    runtimes: &[RuntimeSpec]
) -> Result<Vec<ConsumerUsage>> {
    let mut usage_patterns = Vec::new();
    
    // Get incoming connections to the ConsumeShared module
    for connection in module_graph.get_incoming_connections(consume_shared_id) {
        if let Some(dependency) = module_graph.dependency_by_id(&connection.dependency_id) {
            // Use get_referenced_exports() to extract specific export names being used
            let referenced_exports = dependency.get_referenced_exports(
                module_graph,
                &rspack_core::ModuleGraphCacheArtifact::default(),
                None,
            );
            
            // Process ExtendedReferencedExport patterns for comprehensive export extraction
            for export_ref in referenced_exports {
                match export_ref {
                    ExtendedReferencedExport::Array(names) => {
                        // Multiple specific exports are referenced
                        for name in names {
                            usage_patterns.push(ConsumerUsage {
                                consumer_module: connection.origin_module_identifier.unwrap(),
                                export_name: name.to_string(),
                                usage_type: UsageType::SpecificExport,
                            });
                        }
                    },
                    ExtendedReferencedExport::Export(export_info) => {
                        // Single export or namespace reference
                        if export_info.name.is_empty() {
                            // No specific name indicates namespace usage
                            usage_patterns.push(ConsumerUsage {
                                consumer_module: connection.origin_module_identifier.unwrap(),
                                export_name: "*".to_string(),
                                usage_type: UsageType::NamespaceUsage,
                            });
                        } else {
                            // Specific named exports
                            for name in export_info.name {
                                usage_patterns.push(ConsumerUsage {
                                    consumer_module: connection.origin_module_identifier.unwrap(),
                                    export_name: name.to_string(),
                                    usage_type: UsageType::NamedExport,
                                });
                            }
                        }
                    },
                }
            }
        }
    }
    
    Ok(usage_patterns)
}

Finding Related Modules

// Find fallback module for ConsumeShared modules
fn find_fallback_module(
    module_graph: &ModuleGraph,
    consume_shared_id: &ModuleIdentifier
) -> Option<ModuleIdentifier> {
    if let Some(module) = module_graph.module_by_identifier(consume_shared_id) {
        // Check direct dependencies
        for dep_id in module.get_dependencies() {
            if let Some(dep) = module_graph.dependency_by_id(dep_id) {
                if matches!(dep.dependency_type(), DependencyType::ConsumeSharedFallback) {
                    return module_graph.module_identifier_by_dependency_id(dep_id).copied();
                }
            }
        }
        
        // Also check async dependencies (for lazy loading)
        for block_id in module.get_blocks() {
            if let Some(block) = module_graph.block_by_id(block_id) {
                for dep_id in block.get_dependencies() {
                    if let Some(dep) = module_graph.dependency_by_id(dep_id) {
                        if matches!(dep.dependency_type(), DependencyType::ConsumeSharedFallback) {
                            return module_graph.module_identifier_by_dependency_id(dep_id).copied();
                        }
                    }
                }
            }
        }
    }
    None
}

// Extract share key from ConsumeShared modules
fn extract_share_key(
    module_graph: &ModuleGraph,
    module_id: &ModuleIdentifier
) -> Option<String> {
    if let Some(module) = module_graph.module_by_identifier(module_id) {
        if module.module_type() == &ModuleType::ConsumeShared {
            // Access ConsumeShared-specific methods
            return module.get_consume_shared_key();
        }
    }
    None
}

// Cross-reference used exports with provided exports for accurate filtering
fn cross_reference_usage_with_provided(
    used_exports: Vec<String>,
    provided_exports: &[String]
) -> Vec<String> {
    used_exports
        .into_iter()
        .filter(|export| {
            // Include if it's a namespace usage or if it's actually provided
            export == "*" || provided_exports.contains(export) || provided_exports.contains(&"*".to_string())
        })
        .collect()
}

Asset Generation Patterns

Creating JSON Reports

use rspack_core::{CompilationAsset, RawSource, AssetInfo};
use serde_json;

fn generate_analysis_report(
    &self,
    compilation: &Compilation
) -> Result<AnalysisReport> {
    let module_graph = compilation.get_module_graph();
    let mut modules = HashMap::new();
    
    // Collect runtimes for comprehensive analysis
    let runtimes: Vec<RuntimeSpec> = compilation
        .chunk_by_ukey
        .values()
        .map(|chunk| chunk.runtime())
        .cloned()
        .collect();
    
    // Analyze each module
    for (module_id, _module) in module_graph.modules() {
        if let Some(usage_info) = self.analyze_module(
            &module_graph,
            &module_id,
            &runtimes
        )? {
            modules.insert(module_id.to_string(), usage_info);
        }
    }
    
    Ok(AnalysisReport {
        modules,
        summary: self.generate_summary(&modules),
        metadata: ReportMetadata {
            timestamp: current_timestamp(),
            rspack_version: env!("CARGO_PKG_VERSION").to_string(),
            total_modules: modules.len(),
            runtimes: runtimes.iter().map(|r| r.to_string()).collect(),
        },
    })
}

// Asset creation with proper metadata
#[plugin_hook(CompilerEmit for AnalysisPlugin)]
async fn emit(&self, compilation: &mut Compilation) -> Result<()> {
    let report = self.generate_analysis_report(compilation)?;
    
    // Serialize to JSON with pretty printing
    let json_content = serde_json::to_string_pretty(&report)
        .map_err(|e| rspack_error::Error::from(format!("JSON serialization failed: {}", e)))?;
    
    // Create asset with appropriate metadata
    let source = RawSource::from(json_content);
    let asset_info = AssetInfo::default()
        .with_development(true) // Mark as development asset
        .with_generated(true);  // Mark as generated content
    
    let asset = CompilationAsset::new(Some(source), asset_info);
    
    // Determine output filename
    let filename = self.options.output_path
        .clone()
        .unwrap_or_else(|| "export-analysis.json".to_string());
    
    compilation.emit_asset(filename, asset);
    Ok(())
}

Custom Asset Types

// Create multiple related assets
fn emit_comprehensive_analysis(&self, compilation: &mut Compilation) -> Result<()> {
    let analysis = self.generate_analysis_report(compilation)?;
    
    // Main JSON report
    let json_source = RawSource::from(serde_json::to_string_pretty(&analysis)?);
    compilation.emit_asset(
        "analysis/exports.json".to_string(),
        CompilationAsset::new(Some(json_source), AssetInfo::default())
    );
    
    // Summary CSV
    let csv_content = self.generate_csv_summary(&analysis)?;
    let csv_source = RawSource::from(csv_content);
    compilation.emit_asset(
        "analysis/exports-summary.csv".to_string(),
        CompilationAsset::new(Some(csv_source), AssetInfo::default())
    );
    
    // Debug logs if enabled
    if self.options.include_debug_info {
        let debug_content = self.generate_debug_logs(&analysis)?;
        let debug_source = RawSource::from(debug_content);
        compilation.emit_asset(
            "analysis/debug.txt".to_string(),
            CompilationAsset::new(Some(debug_source), AssetInfo::default())
        );
    }
    
    Ok(())
}

Error Handling and Diagnostics

Proper Error Patterns

use rspack_error::{Diagnostic, DiagnosticKind};

// Convert various error types to rspack errors
fn handle_analysis_error(&self, error: AnalysisError, context: &str) -> rspack_error::Error {
    match error {
        AnalysisError::ModuleNotFound(id) => {
            rspack_error::Error::from(format!(
                "Module not found during {}: {}",
                context, id
            ))
        },
        AnalysisError::SerializationFailed(err) => {
            rspack_error::Error::from(format!(
                "Failed to serialize analysis results in {}: {}",
                context, err
            ))
        },
        AnalysisError::InvalidExportInfo(details) => {
            rspack_error::Error::from(format!(
                "Invalid export information in {}: {}",
                context, details
            ))
        },
    }
}

// Add diagnostics to compilation
fn add_warning(&self, compilation: &mut Compilation, message: String) {
    compilation.push_diagnostic(Diagnostic::warn(
        "ExportAnalysisPlugin".into(),
        message,
    ));
}

fn add_error(&self, compilation: &mut Compilation, message: String) {
    compilation.push_diagnostic(Diagnostic::error(
        "ExportAnalysisPlugin".into(), 
        message,
    ));
}

// Safe module processing with error handling
fn process_module_safely(
    &self,
    compilation: &mut Compilation,
    module_id: &ModuleIdentifier
) -> Result<()> {
    match self.analyze_module_exports(compilation, module_id) {
        Ok(analysis) => {
            self.store_analysis(module_id, analysis);
            Ok(())
        },
        Err(e) => {
            let warning_msg = format!(
                "Failed to analyze exports for module {}: {}. Skipping.",
                module_id, e
            );
            self.add_warning(compilation, warning_msg);
            Ok(()) // Continue processing other modules
        }
    }
}

Comprehensive Error Recovery

// Robust module iteration with error recovery
fn process_all_modules(&self, compilation: &mut Compilation) -> Result<ProcessingStats> {
    let module_graph = compilation.get_module_graph();
    let module_ids: Vec<ModuleIdentifier> = module_graph.modules().keys().copied().collect();
    
    let mut stats = ProcessingStats::default();
    
    for module_id in module_ids {
        match self.process_module_safely(compilation, &module_id) {
            Ok(()) => {
                stats.successful += 1;
            },
            Err(e) => {
                stats.failed += 1;
                stats.errors.push(format!("Module {}: {}", module_id, e));
                
                // Log but continue processing
                tracing::warn!(
                    "Export analysis failed for module {}: {}",
                    module_id, e
                );
            }
        }
    }
    
    // Add summary diagnostic
    if stats.failed > 0 {
        let summary = format!(
            "Export analysis completed with {} successes and {} failures",
            stats.successful, stats.failed
        );
        self.add_warning(compilation, summary);
    }
    
    Ok(stats)
}

Performance Optimization Patterns

Efficient Module Graph Traversal

// Use sets for efficient lookups
use rustc_hash::{FxHashSet, FxHashMap};

fn optimize_module_processing(&self, compilation: &Compilation) -> Result<ProcessingPlan> {
    let module_graph = compilation.get_module_graph();
    
    // Categorize modules for efficient processing
    let mut js_modules = FxHashSet::default();
    let mut consume_shared_modules = FxHashSet::default();
    let mut other_modules = FxHashSet::default();
    
    for (module_id, module) in module_graph.modules() {
        match module.module_type() {
            ModuleType::Js => { js_modules.insert(*module_id); },
            ModuleType::ConsumeShared => { consume_shared_modules.insert(*module_id); },
            _ => { other_modules.insert(*module_id); }
        }
    }
    
    Ok(ProcessingPlan {
        js_modules: js_modules.into_iter().collect(),
        consume_shared_modules: consume_shared_modules.into_iter().collect(),
        other_modules: other_modules.into_iter().collect(),
    })
}

// Batch processing for related modules
fn process_module_batch(
    &self,
    compilation: &mut Compilation,
    batch: &[ModuleIdentifier]
) -> Result<()> {
    // Process all modules in a batch with shared setup
    let shared_context = self.create_analysis_context(compilation)?;
    
    for &module_id in batch {
        self.process_module_with_context(compilation, &module_id, &shared_context)?;
    }
    
    Ok(())
}

Caching and Memoization

use std::collections::HashMap;

// Cache analysis results to avoid recomputation
#[derive(Debug)]
pub struct CachedAnalysisPlugin {
    options: AnalysisOptions,
    cache: std::sync::Mutex<HashMap<String, CachedResult>>,
}

impl CachedAnalysisPlugin {
    fn get_cache_key(&self, module_id: &ModuleIdentifier, runtime: &RuntimeSpec) -> String {
        format!("{}:{}", module_id, runtime)
    }
    
    fn get_cached_analysis(
        &self,
        module_id: &ModuleIdentifier,
        runtime: &RuntimeSpec
    ) -> Option<ModuleExportInfo> {
        let cache = self.cache.lock().ok()?;
        let key = self.get_cache_key(module_id, runtime);
        cache.get(&key).and_then(|cached| {
            if cached.is_valid() {
                Some(cached.result.clone())
            } else {
                None
            }
        })
    }
    
    fn cache_analysis(
        &self,
        module_id: &ModuleIdentifier,
        runtime: &RuntimeSpec,
        result: ModuleExportInfo
    ) {
        if let Ok(mut cache) = self.cache.lock() {
            let key = self.get_cache_key(module_id, runtime);
            cache.insert(key, CachedResult {
                result,
                timestamp: std::time::Instant::now(),
            });
        }
    }
}

Integration Testing Patterns

Plugin Testing Structure

#[cfg(test)]
mod tests {
    use super::*;
    use rspack_testing::{test_fixture, TestCompilation};
    
    #[test]
    fn test_export_analysis_basic() {
        let plugin = ExportAnalysisPlugin::new(ExportAnalysisOptions::default());
        
        test_fixture("export-analysis-basic", |fixture| {
            let mut compilation = TestCompilation::new(fixture);
            compilation.add_plugin(Box::new(plugin));
            
            let result = compilation.build()?;
            
            // Verify analysis results
            assert_eq!(result.assets.len(), 1);
            assert!(result.assets.contains_key("export-analysis.json"));
            
            // Parse and validate report
            let report_content = result.get_asset_content("export-analysis.json")?;
            let report: AnalysisReport = serde_json::from_str(&report_content)?;
            
            assert!(!report.modules.is_empty());
            assert!(report.summary.total_modules > 0);
        });
    }
    
    #[test]
    fn test_consume_shared_analysis() {
        let options = ExportAnalysisOptions {
            detailed_analysis: true,
            ..Default::default()
        };
        let plugin = ExportAnalysisPlugin::new(options);
        
        test_fixture("module-federation-consume-shared", |fixture| {
            let mut compilation = TestCompilation::new(fixture);
            compilation.add_plugin(Box::new(plugin));
            
            let result = compilation.build()?;
            let report_content = result.get_asset_content("export-analysis.json")?;
            let report: AnalysisReport = serde_json::from_str(&report_content)?;
            
            // Find ConsumeShared modules in results
            let consume_shared_modules: Vec<_> = report.modules
                .values()
                .filter(|m| m.module_type == "ConsumeShared")
                .collect();
            
            assert!(!consume_shared_modules.is_empty());
            
            // Verify proxy behavior detection
            for module in consume_shared_modules {
                if module.has_fallback {
                    assert!(
                        module.proxy_behavior_detected,
                        "ConsumeShared with fallback should show proxy behavior"
                    );
                }
            }
        });
    }
}

// Integration test helper
fn create_test_plugin_with_options(options: ExportAnalysisOptions) -> Box<dyn Plugin> {
    Box::new(ExportAnalysisPlugin::new(options))
}

// Mock module graph for unit testing
fn create_mock_module_graph() -> MockModuleGraph {
    let mut graph = MockModuleGraph::new();
    
    // Add test modules
    graph.add_module("./src/index.js", ModuleType::Js);
    graph.add_module("./src/utils.js", ModuleType::Js);
    graph.add_module("shared-library", ModuleType::ConsumeShared);
    
    // Add dependencies
    graph.add_dependency("./src/index.js", "./src/utils.js", DependencyType::EsmImport);
    graph.add_dependency("./src/index.js", "shared-library", DependencyType::ConsumeSharedImport);
    
    graph
}

Best Practices Summary

Plugin Development Guidelines

  1. Hook Selection: Choose appropriate hooks based on timing requirements

    • CompilationFinishModules for metadata manipulation
    • CompilerEmit for asset generation
    • CompilationOptimizeDependencies for analysis requiring optimization data
  2. Borrow Checker Management: Use separate scopes and helper methods to avoid conflicts

  3. Error Handling: Always use Result<()> and provide meaningful error messages

  4. Performance: Use efficient data structures and caching where appropriate

  5. Testing: Include comprehensive integration tests with real module scenarios

Export Analysis Best Practices

  1. API Usage: Use ExportsInfoGetter::prefetch() for bulk operations

  2. ConsumeShared Handling: Understand proxy behavior and analyze through connections using get_referenced_exports()

  3. Runtime Awareness: Always consider multiple runtimes in analysis

  4. Data Validation: Validate export information before processing

  5. Reporting: Provide comprehensive, structured output with metadata

  6. Advanced Dependency Analysis: Use module_graph.get_incoming_connections() and dependency.get_referenced_exports() for precise usage extraction

  7. Pattern Matching: Handle ExtendedReferencedExport::Array and ExtendedReferencedExport::Export patterns comprehensively

  8. Cross-referencing: Compare extracted usage with provided exports for accurate filtering

Common Pitfalls to Avoid

  1. Multiple Mutable Borrows: Structure code to avoid simultaneous mutable module graph access

  2. Incorrect API Usage: Don't confuse ExportsInfoGetter and ExportInfoGetter methods

  3. Missing Error Handling: Always handle potential failures gracefully

  4. ConsumeShared Assumptions: Don't expect direct export usage data from proxy modules

  5. Runtime Ignorance: Consider all applicable runtimes for comprehensive analysis

  6. Incomplete Pattern Matching: Ensure both ExtendedReferencedExport variants are handled properly

  7. Missing Cross-reference: Don't forget to cross-reference extracted usage with provided exports

  8. Shallow Dependency Analysis: Use incoming connections and get_referenced_exports() instead of relying on direct module analysis only

This document provides the foundation for developing robust, efficient plugins for rspack's export analysis and module federation systems.

Rspack Export Usage Tracking Research Documentation

Overview

This collection of research documents provides a comprehensive analysis of rspack's export usage tracking and tree-shaking system. The documentation covers the entire pipeline from dependency creation through export optimization, offering detailed insights into one of the most sophisticated tree-shaking implementations in modern JavaScript bundlers.

Document Structure

Focus: Runtime export information access system

Key Topics:

  • ExportInfoDependency structure and functionality
  • Runtime property queries (used, canMangle, inlinable, etc.)
  • Template rendering and code generation
  • Integration with tree-shaking decisions
  • Performance optimizations and caching strategies

Best For: Understanding how rspack provides runtime access to compile-time export analysis results.

Focus: ECMAScript Module export dependency system

Key Topics:

  • ESMExportSpecifierDependency for direct exports
  • ESMExportImportedSpecifierDependency for re-exports with complex mode system
  • ESMImportSpecifierDependency for import tracking
  • Star re-export handling and conflict resolution
  • Module federation integration with ConsumeShared modules
  • Init fragment system for code generation

Best For: Understanding how ESM exports and re-exports are tracked and optimized.

Focus: CommonJS module export dependency system

Key Topics:

  • CommonJsExportsDependency structure and base types
  • Support for exports, module.exports, and this patterns
  • Object.defineProperty handling
  • Runtime requirement management
  • Export usage analysis integration
  • Template rendering for various CommonJS patterns

Best For: Understanding how CommonJS exports are handled while maintaining Node.js compatibility.

Focus: Core analysis plugins that drive the entire system

Key Topics:

  • FlagDependencyExportsPlugin - export provision analysis
  • FlagDependencyUsagePlugin - export usage tracking
  • Two-phase analysis system architecture
  • Queue-based processing algorithms
  • Export specification merging and target relationship tracking
  • Performance optimizations and incremental processing

Best For: Understanding the foundational plugins that collect and analyze export metadata.

Focus: Complete system integration and end-to-end workflow

Key Topics:

  • Four-phase compilation process
  • Component interaction and data flow
  • Advanced integration scenarios (module federation, nested exports, dynamic patterns)
  • Performance optimization strategies
  • Debugging and diagnostic capabilities
  • Real-world usage patterns and edge cases

Best For: Understanding how all components work together and the complete optimization pipeline.

Focus: Comprehensive overview of Rspack's tree-shaking system with fact-check results

Key Topics:

  • Complete tree-shaking architecture analysis
  • Core plugin locations and implementations
  • Module format support (ESM, CommonJS, mixed)
  • Advanced features (Module Federation, performance optimizations)
  • Research documentation quality assessment
  • Integration with broader ecosystem

Best For: Complete understanding of tree-shaking implementation and verification of documentation accuracy.

Focus: Advanced module federation patterns and proxy module implementation

Key Topics:

  • ConsumeShared proxy module architecture
  • Two-phase metadata copying (build meta + export info)
  • Plugin hook integration patterns and borrow checker solutions
  • Fallback module detection and context handling
  • Performance considerations for bulk export analysis
  • Integration with tree-shaking system
  • ShareUsagePlugin investigation findings and export analysis API patterns
  • Correct API usage for export analysis (ExportsInfoGetter vs ExportInfoGetter)
  • ConsumeShared module behavior analysis and proxy pattern insights
  • Advanced dependency analysis using get_referenced_exports() for incoming connections
  • ExtendedReferencedExport pattern handling for specific export extraction

Best For: Understanding module federation implementation details, advanced export copying patterns, proper export analysis API usage, and dependency analysis techniques for plugin development.

Focus: Comprehensive guide to module information copying patterns and best practices

Key Topics:

  • Core metadata types (build meta, export info, usage states)
  • Established patterns (proxy modules, error recovery, export merging, template initialization)
  • Plugin hook integration and borrow checker solutions
  • Efficient export information access using prefetched analysis
  • Usage state management and bulk operations
  • Performance considerations and error handling

Best For: Plugin developers implementing module metadata copying, understanding rspack's module information management patterns, and building robust proxy modules or transformation plugins.

Focus: Comprehensive patterns and best practices for developing rspack plugins

Key Topics:

  • Plugin structure with #[plugin] and #[plugin_hook] macros
  • Compilation hooks and their proper usage (CompilerEmit, CompilationFinishModules, etc.)
  • Module graph manipulation and borrow checker solutions
  • Export analysis API usage patterns (ExportsInfoGetter vs ExportInfoGetter)
  • ConsumeShared module analysis and proxy behavior understanding
  • Asset generation patterns for reports and diagnostics
  • Error handling and performance optimization strategies
  • Integration testing patterns and common pitfalls to avoid
  • ShareUsagePlugin implementation learnings and API corrections
  • Advanced dependency analysis using module_graph.get_incoming_connections()
  • get_referenced_exports() usage for extracting specific export names
  • ExtendedReferencedExport pattern matching and processing

Best For: Plugin developers working with export analysis, module graph manipulation, compilation hooks, and advanced dependency analysis. Essential for understanding correct API usage patterns and avoiding common development pitfalls.

System Architecture Summary

┌─────────────────────────────────────────────────────────────┐
│                    Complete Export Analysis System          │
├─────────────────────────────────────────────────────────────┤
│  Phase 1: Dependency Creation                               │
│  ├── Parse modules and create typed dependencies            │
│  ├── ESM: ExportSpecifier, ExportImportedSpecifier         │
│  ├── CJS: CommonJsExports                                  │
│  └── Runtime: ExportInfo                                   │
│                                                             │
│  Phase 2: Export Provision (FlagDependencyExportsPlugin)    │
│  ├── Collect ExportsSpec from all dependencies             │
│  ├── Populate ExportsInfo with provision metadata          │
│  ├── Handle re-export targets and nested structures        │
│  └── Queue-based processing with change propagation        │
│                                                             │
│  Phase 3: Usage Analysis (FlagDependencyUsagePlugin)        │
│  ├── Start from entry points and traverse dependencies     │
│  ├── Collect referenced exports from module dependencies   │
│  ├── Apply usage states (Used, Unused, OnlyPropertiesUsed) │
│  └── Handle side effects and transitive dependencies       │
│                                                             │
│  Phase 4: Code Generation & Optimization                    │
│  ├── Query ExportsInfo during template rendering           │
│  ├── Generate optimized export code                        │
│  ├── Apply tree-shaking decisions                          │
│  └── Handle module federation and advanced scenarios       │
└─────────────────────────────────────────────────────────────┘

Key Concepts

Export Information Flow

  1. Dependencies describe what they provide via get_exports()ExportsSpec
  2. FlagDependencyExportsPlugin collects specs and populates ExportsInfo
  3. FlagDependencyUsagePlugin tracks usage and updates ExportsInfo
  4. Template rendering queries ExportsInfo for optimization decisions

Advanced Features

  • Module Federation: Special handling for ConsumeShared modules with conditional exports
  • Nested Exports: Support for deep object property tracking
  • Dynamic Exports: Handling of require.context() and other dynamic patterns
  • Re-export Chains: Complex target relationship tracking
  • Performance: Incremental processing, caching, and change propagation

Data Structures

  • ExportsInfo: Central repository of export metadata per module
  • ExportInfo: Individual export tracking with usage states and capabilities
  • ExportsSpec: Dependency-provided export specifications
  • UsageState: Fine-grained usage classification (Used, Unused, OnlyPropertiesUsed, etc.)

Usage Recommendations

For Understanding Tree-Shaking

Start with 04_flag_dependency_plugins_analysis.md to understand the core analysis system, then read 05_integration_and_workflow.md for the complete picture.

For Implementing Export Dependencies

Begin with 02_esm_export_tracking.md or 03_commonjs_export_analysis.md depending on the module system, then reference 01_export_info_dependency_analysis.md for runtime integration.

For Export Analysis Plugin Development

Start with 09_plugin_development_patterns.md for comprehensive plugin development patterns and ShareUsagePlugin learnings, including advanced dependency analysis using get_referenced_exports(). Read 07_module_federation_export_patterns.md for specific ShareUsagePlugin investigation findings and ConsumeShared module behavior insights. Also review 01_export_info_dependency_analysis.md and 06_comprehensive_tree_shaking_analysis.md for export analysis API guidelines.

For Performance Optimization

Focus on the performance sections in 04_flag_dependency_plugins_analysis.md and 05_integration_and_workflow.md.

For Module Federation

Review the ConsumeShared sections in 02_esm_export_tracking.md and 03_commonjs_export_analysis.md, then read 07_module_federation_export_patterns.md for comprehensive module federation insights and ShareUsagePlugin findings.

Research Methodology

This documentation was created through:

  1. Static Code Analysis: Comprehensive examination of dependency implementations
  2. Plugin Architecture Review: Analysis of flag dependency plugin systems
  3. Integration Pattern Study: Understanding component interactions and data flow
  4. Performance Analysis: Investigation of optimization strategies and caching
  5. Edge Case Documentation: Exploration of complex scenarios and error handling

The documentation provides both high-level architectural understanding and implementation-level details suitable for contributors, plugin developers, and bundler researchers.

Contributing

When updating this documentation:

  1. Maintain consistency with the existing structure and terminology
  2. Include code examples from actual rspack implementations
  3. Document both common use cases and edge cases
  4. Update the integration workflow when adding new components
  5. Keep performance implications in focus for all changes

This research documentation serves as both a learning resource and a foundation for future enhancements to rspack's tree-shaking capabilities.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment