Jest Module Name Mapper: Dynamic Resolution
This challenge focuses on creating a flexible module name mapper for Jest. Often, you need to mock or resolve modules to specific paths during testing, and a dynamic mapper allows you to configure these resolutions based on environment variables or other factors. Your task is to build a TypeScript function that generates a Jest module name mapper object based on a provided configuration.
Problem Description
You need to implement a function createJestModuleNameMapper that takes a configuration object and returns a Jest module name mapper object. This mapper will be used in your Jest setup to resolve module paths. The configuration object will contain a mapping of module names to their corresponding resolved paths. The function should handle cases where a module name might not be present in the configuration, returning the original module name in such scenarios. This allows for graceful fallback to the default module resolution behavior when a mapping isn't provided.
Key Requirements:
- The function must accept a configuration object with the following structure:
interface ModuleMapping { [moduleName: string]: string; } - The function must return a Jest module name mapper object, which is a dictionary where keys are module names and values are their resolved paths.
- If a module name is not found in the configuration, the mapper should return the original module name.
- The function should be written in TypeScript.
Expected Behavior:
The createJestModuleNameMapper function should take the configuration object and produce a Jest-compatible module name mapper. This mapper will be used by Jest to resolve module paths during testing. When a module name is found in the configuration, it should be mapped to the corresponding resolved path. If a module name is not found, it should be returned unchanged.
Edge Cases to Consider:
- Empty configuration object.
- Module names with special characters.
- Resolved paths that are relative or absolute.
- Module names that are not strings. (Should be handled gracefully, returning the original value)
Examples
Example 1:
Input: { 'src/utils/math.ts': 'path/to/mock/math.ts', 'src/components/Button.tsx': 'path/to/mock/Button.tsx' }
Output: { '^src/utils/math.ts$': 'path/to/mock/math.ts', '^src/components/Button.tsx$': 'path/to/mock/Button.tsx' }
Explanation: The input configuration maps two module names to their mock paths. The output is a Jest-compatible mapper with the module names wrapped in '^' and '$' and prefixed with '^'.
Example 2:
Input: {}
Output: {}
Explanation: An empty configuration object should result in an empty mapper object.
Example 3:
Input: { 'src/utils/math.ts': 'path/to/mock/math.ts' }
Output: { '^src/utils/math.ts$': 'path/to/mock/math.ts' }
Explanation: A single mapping should be correctly translated to the Jest format.
Example 4:
Input: { 'nonexistent/module': 'path/to/mock' }
Output: { '^nonexistent/module$': 'path/to/mock' }
Explanation: Even if the module doesn't exist in the project, the mapper should still function correctly.
Constraints
- The configuration object will only contain string keys (module names) and string values (resolved paths).
- The function must be performant enough to handle configurations with up to 100 module mappings without significant delay.
- The output must be a valid Jest module name mapper object.
- Module names and resolved paths should be treated as strings.
Notes
- Jest module name mappers require module names to be wrapped in
^and$. For example,src/utils/math.tsbecomes^src/utils/math.ts$. - Consider using a
for...inloop orObject.entries()to iterate over the configuration object. - Think about how to handle cases where the configuration object is empty.
- The goal is to create a reusable function that can be easily integrated into a Jest setup file.
- Error handling is not required for invalid input types (assume the input will always be in the correct format).