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).
- 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)
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.)
}
The dependency supports several property queries:
usedExports
: Returns array of used export names or boolean for namespace usagecanMangle
: Whether the export name can be mangled for minificationinlinable
: Whether the export can be inlinedused
: Boolean indicating if export is useduseInfo
: Detailed usage state (Used, Unused, OnlyPropertiesUsed, NoInfo, Unknown)provideInfo
: Whether export is provided (Provided, NotProvided, Unknown)
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
}
}
The system uses prefetched export information for performance:
let exports_info = module_graph.get_prefetched_exports_info(
&module_identifier,
PrefetchExportsInfoMode::NamedNestedExports(export_name),
);
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())
}
The dependency integrates with rspack's export analysis plugins:
- FlagDependencyExportsPlugin: Determines what exports are provided
- FlagDependencyUsagePlugin: Tracks which exports are actually used
- ExportInfoDependency: Provides runtime access to this information
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
}
// Source code with export info dependency
const isUsed = __webpack_exports_info__.used;
if (isUsed) {
// Conditionally include code
}
// Query nested export property
const canMangleNested = __webpack_exports_info__.nested.property.canMangle;
// Get array of all used exports
const usedExports = __webpack_exports_info__.usedExports;
The system uses prefetching to avoid repeated module graph queries:
PrefetchExportsInfoMode::AllExports
for general queriesPrefetchExportsInfoMode::NamedNestedExports
for specific export paths
Export information is cached in the compilation's module graph to avoid recomputation.
Properties are resolved at build time and injected as literals, avoiding runtime computation.
- Uses
ModuleGraph::get_prefetched_exports_info()
for export data - Integrates with
ExportsInfoGetter
for standardized access patterns
- Implements
DependencyTemplate
for code generation - Uses
TemplateReplaceSource
for code replacement
- Consumes data from flag dependency plugins
- Provides bridge between analysis and runtime phases
Returns "undefined"
when export information is not available.
Gracefully handles unknown property names by returning None
.
All generated property accesses are safe and won't throw runtime errors.
Based on ShareUsagePlugin investigation findings, here are key guidelines for proper export analysis API usage:
// 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
// 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
}
}
},
}
}
}
}
// For comprehensive analysis (recommended for plugins)
PrefetchExportsInfoMode::AllExports
// For specific export analysis
PrefetchExportsInfoMode::NamedNestedExports(&export_names)
// For minimal analysis (performance-critical scenarios)
PrefetchExportsInfoMode::Default
// 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
}
}
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
}
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()
})
}
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
}
// 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
);
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
)),
]
}
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
}
// 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));
- Export Declaration: During dependency parsing via
get_exports()
- Export Processing: In
FlagDependencyExportsPlugin::finish_modules()
- Usage Analysis: During dependency analysis via
get_referenced_exports()
- Usage Processing: In
FlagDependencyUsagePlugin::optimize_dependencies()
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