Advanced Module Resolution with Custom Types in TypeScript
TypeScript's module resolution system is powerful, but sometimes you need more control, especially when dealing with non-standard module formats or custom build processes. This challenge asks you to create a custom module resolution type that allows you to resolve modules based on a specific, configurable mapping. This is useful for scenarios like resolving modules from a CDN, using a custom package registry, or handling legacy module formats.
Problem Description
You need to implement a CustomModuleResolver type that extends TypeScript's ModuleResolution interface. This resolver will take a moduleMap as a constructor parameter. The moduleMap is an object where keys are module specifiers (strings) and values are the resolved module paths (strings). The resolver should prioritize the moduleMap over the default module resolution behavior. If a module specifier is found in the moduleMap, it should return the corresponding resolved path. Otherwise, it should delegate to the default ModuleResolution.resolveModule.
Key Requirements:
- Extend
ModuleResolution: Your type must extend theModuleResolutioninterface. moduleMap: The constructor must accept amoduleMapobject ({ [key: string]: string }).- Prioritized Resolution: The resolver should first check the
moduleMap. If a match is found, return the mapped path. - Fallback to Default: If no match is found in the
moduleMap, delegate to the defaultModuleResolution.resolveModulemethod. - Correct
thiscontext: Ensurethisrefers to theCustomModuleResolverinstance when callingresolveModule.
Expected Behavior:
When given a module specifier, the resolver should:
- Check if the specifier exists as a key in the
moduleMap. - If it exists, return the corresponding value (resolved path) from the
moduleMap. - If it doesn't exist, call the default
resolveModulemethod with the same specifier and return its result.
Edge Cases to Consider:
- Empty
moduleMap: Should not cause errors; fallback to default resolution. - Circular dependencies within the
moduleMap(e.g.,"a"maps to"b"and"b"maps to"a"). This challenge does not require you to detect or handle circular dependencies. Assume the map is valid. - Specifiers that are relative paths (e.g.,
"./foo"). The resolver should handle these correctly by delegating to the default resolver. - Specifiers that are absolute paths.
Examples
Example 1:
Input: moduleMap = { "my-module": "path/to/my-module.ts" }, specifier = "my-module"
Output: "path/to/my-module.ts"
Explanation: The specifier "my-module" is found in the moduleMap, so the corresponding value is returned.
Example 2:
Input: moduleMap = { "my-module": "path/to/my-module.ts" }, specifier = "another-module"
Output: "node_modules/another-module/index.ts" (or similar, depending on default resolution)
Explanation: The specifier "another-module" is not found in the moduleMap, so the default resolver is called.
Example 3:
Input: moduleMap = {}, specifier = "any-module"
Output: "node_modules/any-module/index.ts" (or similar, depending on default resolution)
Explanation: The moduleMap is empty, so the default resolver is called.
Constraints
- The
moduleMapwill always be a plain JavaScript object. - Module specifiers and resolved paths will be strings.
- Performance is not a primary concern for this challenge. Focus on correctness and clarity.
- You are not required to implement the entire
ModuleResolutioninterface, only theresolveModulemethod.
Notes
- You'll need to understand the basic structure of TypeScript's
ModuleResolutioninterface. Refer to the TypeScript documentation for details. - Consider using a class to implement the
CustomModuleResolverto properly manage themoduleMapandthiscontext. - This challenge focuses on the core logic of module resolution. Error handling and more complex scenarios are beyond the scope of this exercise.
- The default
resolveModulemethod is assumed to be available and functional. You don't need to implement it.