Hone logo
Hone
Problems

Jest Module Resolver Simulation

Jest relies on a sophisticated module resolution mechanism to locate and load the correct files for your tests. This challenge asks you to implement a simplified version of Jest's module resolver in TypeScript, allowing you to understand the core logic behind how Jest finds your modules. This is crucial for debugging import issues and for building custom Jest configurations.

Problem Description

Your task is to create a function, resolveModule, that takes a module path (the string used in import or require statements), a reference file path, and a list of potential module directories. This function should simulate how Jest resolves a module path by searching through various locations, considering different file extensions and the possibility of module directories.

Key Requirements:

  1. Relative Path Resolution: If the module path starts with ./ or ../, it should be resolved relative to the referenceFilePath.
  2. Absolute Path Resolution: If the module path is an absolute path, it should be resolved directly.
  3. Node Modules Resolution: If the module path does not start with ./ or ../ and is not an absolute path, it should be treated as an npm package and resolved by searching within the provided moduleDirectories.
    • This involves looking for:
      • package.json main field.
      • Index files (index.js, index.json, index.node).
      • File with the exact name.
  4. File Extensions: When resolving, consider the following file extensions in order: .js, .json, .node, and then .ts (if TypeScript is implicitly supported).
  5. Directory Index: If a directory is found, look for an index file within it with the supported extensions.
  6. Error Handling: If the module cannot be resolved after all checks, the function should return null.

Expected Behavior:

The function should return the absolute path to the resolved module file if found, or null if not.

Important Edge Cases:

  • Module paths pointing to directories.
  • Module paths with no file extension (requiring extension probing).
  • Module paths that resolve to package.json's main field.
  • The order of checking directories for npm packages.

Examples

Example 1: Relative Path Resolution

Input:
modulePath = "./utils/helpers"
referenceFilePath = "/path/to/project/src/tests/myTest.ts"
moduleDirectories = ["node_modules"] // Not relevant for this example

// Assume the following file structure:
// /path/to/project/src/utils/helpers.ts
// /path/to/project/src/utils/helpers/index.js

Output: "/path/to/project/src/utils/helpers.ts"
Explanation: The path is relative to `referenceFilePath`. It first checks for `helpers.ts` and finds it.

Example 2: Package Resolution with main field

Input:
modulePath = "lodash"
referenceFilePath = "/path/to/project/src/index.ts"
moduleDirectories = ["/path/to/project/node_modules", "/another/global/node_modules"]

// Assume the following structure in /path/to/project/node_modules/lodash/:
// /path/to/project/node_modules/lodash/package.json (with "main": "dist/lodash.js")
// /path/to/project/node_modules/lodash/dist/lodash.js

Output: "/path/to/project/node_modules/lodash/dist/lodash.js"
Explanation: "lodash" is treated as an npm package. The resolver finds `node_modules/lodash/package.json` and uses its `main` field to locate the entry point.

Example 3: Package Resolution with Index File

Input:
modulePath = "some-package"
referenceFilePath = "/path/to/project/src/index.ts"
moduleDirectories = ["/path/to/project/node_modules"]

// Assume the following structure in /path/to/project/node_modules/some-package/:
// /path/to/project/node_modules/some-package/lib/index.js

Output: "/path/to/project/node_modules/some-package/lib/index.js"
Explanation: "some-package" is treated as an npm package. Since there's no `main` field in `package.json`, the resolver looks for an index file within the package directory. It finds `lib/index.js`.

Example 4: Absolute Path and Extension Resolution

Input:
modulePath = "/usr/local/lib/my-module.json"
referenceFilePath = "/tmp/test.js"
moduleDirectories = []

// Assume the file exists at the specified absolute path.

Output: "/usr/local/lib/my-module.json"
Explanation: The module path is absolute and directly points to a `.json` file, which is resolved.

Example 5: Non-existent Module

Input:
modulePath = "non-existent-module"
referenceFilePath = "/path/to/project/src/index.ts"
moduleDirectories = ["/path/to/project/node_modules"]

// Assume no module named "non-existent-module" is found.

Output: null
Explanation: The module could not be found in any of the specified directories or via other resolution strategies.

Constraints

  • The file system is assumed to be accessible for checking file existence.
  • You can assume standard Node.js module resolution behavior for npm packages, but you only need to implement the core logic described above.
  • The maximum depth of nested node_modules folders is not a primary concern for this simulation; focus on the provided moduleDirectories.
  • The supported file extensions are .js, .json, .node, and .ts.
  • You should not rely on external libraries that perform module resolution themselves (e.g., require.resolve).

Notes

  • You'll need to use Node.js's built-in path module extensively for manipulating file paths.
  • Consider how to handle directory paths (e.g., /some/directory vs. /some/directory.js).
  • The order of checking extensions and file types is critical.
  • For the moduleDirectories part, remember to iterate through each directory in the provided array and search for the package within it.
  • Simulating package.json's main field resolution is key for npm package imports.
  • When checking for an index file within a directory, ensure you probe with all supported extensions.
Loading editor...
typescript