3.5 KiB
3.5 KiB
Dependency Resolution
Overview
The Custom Resolver system includes a robust dependency resolution mechanism that ensures plugins are processed in the correct order based on their dependencies. This document explains how the resolution process works and how to work with complex dependency graphs.
How Dependency Resolution Works
- Graph Construction: The system builds a directed graph of plugin dependencies during initialization.
- Cycle Detection: The graph is checked for cycles to prevent infinite loops.
- Topological Sort: Plugins are ordered such that all dependencies are processed before the plugins that depend on them.
- State Resolution: During processing, each plugin receives the resolved states of its dependencies.
Example: Resolving Dependencies
Consider the following plugin configuration:
const resolver = new CustomResolver(view, {
basePrice: new LastWriteWinsPlugin(),
discount: new LastWriteWinsPlugin(),
taxRate: new LastWriteWinsPlugin(),
tax: new TaxCalculatorPlugin(), // Depends on basePrice and taxRate
total: new TotalPricePlugin() // Depends on basePrice, discount, and tax
});
The system will automatically determine the correct processing order:
basePrice
,discount
,taxRate
(no dependencies)tax
(depends on basePrice and taxRate)total
(depends on basePrice, discount, and tax)
Handling Circular Dependencies
Circular dependencies are detected during initialization and will result in an error. For example:
// This will throw an error during initialization
const resolver = new CustomResolver(view, {
a: new SomePlugin({ deps: ['b'] }),
b: new SomePlugin({ deps: ['a'] }) // Circular dependency!
});
Advanced Dependency Patterns
Optional Dependencies
To make a dependency optional, mark it with a ?
suffix:
class MyPlugin extends ResolverPlugin<MyState, 'required' | 'optional?'> {
readonly dependencies = ['required', 'optional?'] as const;
// ...
}
Dynamic Dependencies
For plugins that need to determine dependencies at runtime, you can implement a custom resolver:
class DynamicDepsPlugin extends ResolverPlugin<DynamicState> {
getDependencies(config: any): string[] {
// Determine dependencies based on config
return ['always'];
}
// ...
}
Debugging Dependency Issues
Common Issues
- Missing Dependencies: Ensure all required dependencies are declared
- Type Mismatches: Verify that dependency types match expected types
- Circular Dependencies: Break circular references in your dependency graph
Debugging Tools
- Dependency Graph: Use
resolver.getDependencyGraph()
to inspect the dependency graph - Processing Order: Check
resolver.getProcessingOrder()
to see the resolved execution order - Logging: Enable debug logging to track dependency resolution
Performance Considerations
- Dependency Depth: Deep dependency chains can impact performance
- Graph Complexity: Complex graphs take longer to process
- Caching: The system caches resolved dependencies for performance
Best Practices
- Minimize Dependencies: Only depend on what you need
- Keep Graphs Shallow: Prefer wide, shallow dependency graphs over deep ones
- Document Dependencies: Clearly document all plugin dependencies
- Test Edge Cases: Test with missing or invalid dependencies
- Monitor Performance: Keep an eye on resolution time for large graphs