Hone logo
Hone
Problems

Implement Tree Shaking for React Components

This challenge asks you to implement a simplified tree shaking mechanism for a React component library. Tree shaking is a process that eliminates unused code from your application, leading to smaller bundle sizes and faster loading times. In this scenario, you'll simulate this by identifying and "removing" components that are not imported or used within a given application structure.

Problem Description

Your task is to build a function that simulates tree shaking for a collection of React components. Given a library of components and a representation of an application's component usage, you need to determine which components from the library are actually imported and used in the application. The function should return a subset of the original component library, containing only the components that are deemed "reachable" or "used."

Key Requirements:

  1. Input:
    • componentLibrary: An object where keys are component names (strings) and values are the actual React component functions or classes.
    • appUsage: An object representing the application's component imports and usage. This can be structured to reflect how components are imported and then rendered. A simplified representation could be an object where keys are file paths or module names, and values are arrays of component names that are imported and/or rendered within that module.
  2. Logic: Traverse the appUsage structure to identify all component names that are explicitly imported and/or used. A component is considered "used" if it appears in the appUsage data.
  3. Output: A new object containing only the components from componentLibrary that were identified as used in appUsage. The keys should be the component names, and the values should be the corresponding component functions/classes.
  4. Assumptions:
    • We are only concerned with direct imports and usage. No complex dynamic imports or side effects that might indirectly use components need to be considered.
    • Component names in appUsage directly map to keys in componentLibrary.

Expected Behavior:

The function should accurately identify and include only the components that are present in the appUsage data. Components in componentLibrary that are not referenced in appUsage should be excluded from the output.

Edge Cases:

  • Empty componentLibrary or appUsage.
  • appUsage referencing components that do not exist in componentLibrary. (These should be ignored gracefully).
  • appUsage with no components referenced.

Examples

Example 1:

// Sample React components
const Button = () => "Button";
const Modal = () => "Modal";
const Card = () => "Card";
const Header = () => "Header";

const componentLibrary = {
  Button: Button,
  Modal: Modal,
  Card: Card,
  Header: Header,
};

// Application usage
const appUsage = {
  './src/App.tsx': ['Button', 'Header'],
  './src/components/UserProfile.tsx': ['Card', 'Button'],
  './src/utils/helpers.ts': [], // No components used here
};

// Expected Output:
const prunedLibrary = {
  Button: Button,
  Header: Header,
  Card: Card,
};

/*
Explanation:
The 'Button' component is used in './src/App.tsx' and './src/components/UserProfile.tsx'.
The 'Header' component is used in './src/App.tsx'.
The 'Card' component is used in './src/components/UserProfile.tsx'.
The 'Modal' component is not mentioned in 'appUsage', so it's excluded.
*/

Example 2:

// Sample React components
const InputField = () => "InputField";
const Select = () => "Select";

const componentLibrary = {
  InputField: InputField,
  Select: Select,
};

// Application usage
const appUsage = {
  './src/forms/ContactForm.tsx': ['InputField'],
};

// Expected Output:
const prunedLibrary = {
  InputField: InputField,
};

/*
Explanation:
Only 'InputField' is used in the provided application usage. 'Select' is not used.
*/

Example 3 (Edge Case: No Usage):

// Sample React components
const Button = () => "Button";
const Modal = () => "Modal";

const componentLibrary = {
  Button: Button,
  Modal: Modal,
};

// Application usage
const appUsage = {
  './src/App.tsx': [],
  './src/utils/helpers.ts': [],
};

// Expected Output:
const prunedLibrary = {};

/*
Explanation:
Even though 'Button' and 'Modal' exist in the library, they are not referenced in the appUsage.
Therefore, the pruned library is empty.
*/

Example 4 (Edge Case: Unknown Component Reference):

// Sample React components
const Button = () => "Button";

const componentLibrary = {
  Button: Button,
};

// Application usage
const appUsage = {
  './src/App.tsx': ['Button', 'NonExistentComponent'],
};

// Expected Output:
const prunedLibrary = {
  Button: Button,
};

/*
Explanation:
'Button' is found and used. 'NonExistentComponent' is referenced but doesn't exist in the componentLibrary,
so it's safely ignored and does not cause an error or inclusion.
*/

Constraints

  • The componentLibrary and appUsage objects will be valid JavaScript objects.
  • Component names (keys in componentLibrary and strings in appUsage arrays) will be strings.
  • Values in componentLibrary will be functions (representing React components).
  • Performance is not a critical concern for this simulation, but the solution should be reasonably efficient. An O(N*M) or better complexity (where N is the number of components and M is the number of usage entries) is acceptable.

Notes

  • Think about how to efficiently collect all unique component names used across all files in appUsage.
  • Consider using a Set data structure to store the used component names to automatically handle duplicates.
  • This challenge is a simplified model. Real-world tree shaking involves static analysis of code, parsing abstract syntax trees (ASTs), and understanding module resolution, which are far more complex. Your goal here is to grasp the concept of identifying unused code based on explicit references.
  • The values in componentLibrary are represented as simple functions for this challenge, but they would typically be React component definitions.
Loading editor...
typescript