React Tree Shaking Simulation
Tree shaking is a crucial optimization technique in modern JavaScript bundlers like Webpack and Parcel. It eliminates unused code from your bundles, resulting in smaller file sizes and faster load times. This challenge asks you to simulate a simplified version of tree shaking logic for a React component system, focusing on identifying and removing unused exports.
Problem Description
You are tasked with creating a function that analyzes a dependency graph of React components and their exports. The function should determine which exports are actually used by other components and return a filtered object containing only the utilized exports. This simulation will help you understand the core principles behind tree shaking without the complexities of a full bundler implementation.
What needs to be achieved:
- Given a dependency graph represented as an object, identify unused exports.
- Return a new object containing only the exports that are actually used by at least one component.
Key Requirements:
- The input will be an object where keys represent component names (strings) and values are objects representing their exports.
- Each export object will contain key-value pairs where keys are export names (strings) and values are boolean indicating whether the export is a named export (true) or a default export (false).
- The dependency graph will also include a "usage" property, which is an object where keys are component names and values are arrays of strings representing the exports used by that component.
- The function should handle both named and default exports.
- The function should correctly identify unused exports even when a component uses an export but doesn't directly import it (e.g., through a higher-order component).
Expected Behavior:
The function should return a new object with the same structure as the input, but with any unused exports removed from each component's export object.
Edge Cases to Consider:
- Empty dependency graph.
- Components with no exports.
- Components that use exports from other components that themselves have unused exports.
- Circular dependencies (though you don't need to explicitly handle them, the logic should still function correctly).
- A component using a default export from another component.
Examples
Example 1:
Input: {
ComponentA: {
export1: true,
export2: true,
default: false
},
ComponentB: {
export1: true,
export3: true,
default: true
},
usage: {
ComponentA: ['export1'],
ComponentB: ['export1', 'export3']
}
}
Output: {
ComponentA: {
export1: true
},
ComponentB: {
export1: true,
export3: true,
default: true
}
}
Explanation: 'export2' is unused in ComponentA, so it's removed. All exports in ComponentB are used.
Example 2:
Input: {
ComponentC: {
export4: true,
default: false
},
ComponentD: {
export5: true,
default: true
},
usage: {
ComponentC: [],
ComponentD: []
}
}
Output: {}
Explanation: Both ComponentC and ComponentD have no used exports, so all exports are removed.
Example 3:
Input: {
ComponentE: {
export6: true,
default: false
},
ComponentF: {
export7: true,
default: true
},
usage: {
ComponentE: ['export6', 'ComponentF'],
ComponentF: []
}
}
Output: {
ComponentE: {
export6: true
},
ComponentF: {
export7: true,
default: true
}
}
Explanation: ComponentF is used by ComponentE, but ComponentF itself has no used exports. Therefore, ComponentF's exports are retained.
Constraints
- The input object will always be valid and well-formed.
- Export names will be strings.
- Component names will be strings.
- The
usagearray will contain strings representing component names or export names. - The function must have a time complexity of O(N*M) where N is the number of components and M is the average number of exports per component. While a more optimized solution is possible, this constraint is to ensure a reasonable level of efficiency for this simulation.
Notes
- This is a simplified simulation. Real-world tree shaking involves more complex analysis, such as static analysis and dead code elimination.
- Focus on the core logic of identifying unused exports based on the provided usage information.
- Consider using a
Setto efficiently track used exports. - The "usage" property can contain component names, indicating that a component uses another component (and therefore all of its exports). You need to consider this when determining which exports are used.
- The goal is to create a function that accurately simulates the behavior of a tree shaking algorithm in a simplified context.