Hone logo
Hone
Problems

Minimal Bundler Core: Dependency Resolution and Module Inclusion

This challenge asks you to implement the core dependency resolution and module inclusion logic of a JavaScript bundler. Bundlers are essential tools in modern JavaScript development, taking multiple JavaScript files and their dependencies and combining them into a single, optimized file for efficient browser loading. This exercise focuses on the foundational aspects of this process.

Problem Description

You are tasked with creating a function bundle that takes a starting JavaScript file path and a dependency map as input. The function should recursively resolve all dependencies of the starting file, ensuring that each module is included only once, and return a single string containing the concatenated code of all resolved modules in the correct order. The dependency map provides a mapping of module names (strings) to their file paths (strings).

Key Requirements:

  • Recursive Dependency Resolution: The function must recursively traverse the dependency graph, resolving all dependencies of each module.
  • Duplicate Module Prevention: Modules should be included only once in the final bundle, even if they are referenced multiple times. A Set should be used to track included modules.
  • Correct Order of Inclusion: Modules should be included in an order that respects dependencies. A module should be included before any of its dependencies.
  • String Concatenation: The final output should be a single string containing the concatenated code of all included modules.
  • Error Handling: If a dependency is not found in the dependency map, the function should throw an error with a descriptive message.

Expected Behavior:

The bundle function should return a string containing the concatenated code of all modules in the dependency graph, starting from the provided entry point. If a dependency is missing, it should throw an error.

Edge Cases to Consider:

  • Circular Dependencies: The bundler should handle circular dependencies gracefully (e.g., by throwing an error or implementing a more sophisticated cycle detection mechanism – for this challenge, throwing an error is sufficient).
  • Empty Modules: Modules with no dependencies should be handled correctly.
  • Modules with Self-References: Modules referencing themselves should be handled correctly (again, throwing an error is acceptable).
  • Non-existent Entry Point: If the entry point file is not in the dependency map, throw an error.

Examples

Example 1:

Input: { './a.js': 'moduleA', './b.js': 'moduleB', './c.js': 'moduleC' } , './a.js'
Dependency Map: { './a.js': './b.js', './b.js': './c.js', './c.js': '' }
Output: "moduleA\nmoduleB\nmoduleC"
Explanation: Starting from './a.js', we include './b.js' which depends on './c.js'.  './c.js' has no dependencies, so it's included last.

Example 2:

Input: { './app.js': 'appModule', './lib/utils.js': 'utilsModule', './lib/api.js': 'apiModule' } , './app.js'
Dependency Map: { './app.js': './lib/utils.js', './lib/utils.js': './lib/api.js', './lib/api.js': '' }
Output: "appModule\nutilsModule\napiModule"
Explanation: Starting from './app.js', we include './lib/utils.js' which depends on './lib/api.js'.  './lib/api.js' has no dependencies, so it's included last.

Example 3: (Circular Dependency)

Input: { './a.js': 'moduleA', './b.js': 'moduleB' } , './a.js'
Dependency Map: { './a.js': './b.js', './b.js': './a.js' }
Output: Error: Circular dependency detected: a.js -> b.js -> a.js
Explanation: The dependency graph contains a cycle, which should be detected and an error thrown.

Constraints

  • The dependency map will contain string keys (file paths) and string values (module names).
  • Module names and file paths will be strings.
  • The bundle function must not take more than 1 second to execute for dependency graphs with up to 10 modules.
  • The input dependency map will not contain duplicate keys.
  • The entry point file path must exist as a key in the dependency map.

Notes

  • You can assume that the module names are simply strings that represent the module's code. You don't need to parse or interpret the module code itself.
  • Focus on the dependency resolution and inclusion logic. Error handling is an important part of the challenge.
  • Consider using a Set to efficiently track included modules and prevent duplicates.
  • Think about how to handle circular dependencies. A simple error throw is acceptable for this challenge.
  • The order of modules in the dependency map is not guaranteed.
  • The module names are just placeholders; you don't need to implement any actual module parsing or execution. They are simply strings to be concatenated.
Loading editor...
javascript