or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

file-matching.mdindex.mdpath-resolution.md
tile.json

path-resolution.mddocs/

Module Path Resolution

Module path resolution functionality for resolving import specifiers using TypeScript's path mapping configuration from compilerOptions.paths and baseUrl.

Capabilities

Path Matcher Creation

Creates a path matcher function that resolves module specifiers according to TypeScript's path mapping rules.

/**
 * Creates a path matcher function based on tsconfig paths configuration
 * @param tsconfig - The parsed tsconfig result from getTsconfig or parseTsconfig
 * @returns Path matcher function or null if no paths/baseUrl configuration exists
 */
function createPathsMatcher(
  tsconfig: TsConfigResult
): ((specifier: string) => string[]) | null;

/**
 * Function that resolves a module specifier to possible file paths
 * @param specifier - The import specifier to resolve (e.g., "@/utils", "lodash")
 * @returns Array of possible absolute file paths to check, empty if no matches
 */
type PathsMatcher = (specifier: string) => string[];

Usage Examples:

import { getTsconfig, createPathsMatcher, type TsConfigResult } from "get-tsconfig";

const tsconfig = getTsconfig("./my-project");
const pathsMatcher = createPathsMatcher(tsconfig);

if (pathsMatcher) {
  // Resolve path-mapped imports
  const utilsPaths = pathsMatcher("@/utils");          // ["/project/src/utils"]
  const componentPaths = pathsMatcher("@/components/Button"); // ["/project/src/components/Button"]
  
  // Relative imports return empty array (handled by normal resolution)
  const relativePaths = pathsMatcher("./local-file"); // []
  
  // Non-matching specifiers fall back to baseUrl resolution
  const basePaths = pathsMatcher("some-module");       // ["/project/src/some-module"] if baseUrl is set
}

Path Mapping Configuration

Path resolution works with TypeScript's compilerOptions.paths and compilerOptions.baseUrl settings:

// Example tsconfig.json with path mapping
{
  "compilerOptions": {
    "baseUrl": "./src",
    "paths": {
      "@/*": ["./utils/*", "./lib/*"],
      "@components/*": ["./components/*"],
      "~/*": ["../shared/*"]
    }
  }
}

Resolution Algorithm

The path matcher implements TypeScript's module resolution logic:

  1. Exact Match: Check for exact pattern matches first
  2. Wildcard Patterns: Process patterns with * wildcards, preferring longer prefixes
  3. Substitution: Replace wildcards in matched patterns with the corresponding part of the specifier
  4. BaseUrl Fallback: If no patterns match and baseUrl is set, try baseUrl + specifier
  5. Empty Result: Return empty array for relative paths or when no configuration exists

Pattern Matching Rules:

  • Patterns can have at most one * wildcard
  • Substitutions can have at most one * wildcard
  • Longer prefix patterns take precedence over shorter ones
  • First exact match takes precedence over wildcard patterns

Wildcard Pattern Examples

// Given tsconfig paths configuration:
// "@lib/*": ["./libraries/*", "./vendor/*"]

const pathsMatcher = createPathsMatcher(tsconfig);

// Resolves "@lib/utils" to:
const paths = pathsMatcher("@lib/utils");
// Returns: ["/project/src/libraries/utils", "/project/src/vendor/utils"]

// The "*" in the pattern matches "utils"
// The "*" in substitutions is replaced with "utils"

BaseUrl Resolution

When no path patterns match, baseUrl provides fallback resolution:

// Given tsconfig with baseUrl: "./src" and no matching paths
const pathsMatcher = createPathsMatcher(tsconfig);

// Non-relative specifier with baseUrl fallback
const paths = pathsMatcher("helpers/format");
// Returns: ["/project/src/helpers/format"]

Integration with Module Resolvers

Common integration pattern with custom module resolution systems:

import { getTsconfig, createPathsMatcher, type TsConfigResult } from "get-tsconfig";
import { existsSync } from "fs";
import { resolve } from "path";

function resolveModule(specifier: string, fromFile: string): string | null {
  const tsconfig = getTsconfig(fromFile);
  const pathsMatcher = createPathsMatcher(tsconfig);
  
  if (pathsMatcher) {
    const candidatePaths = pathsMatcher(specifier);
    
    // Try each candidate path with common extensions
    for (const candidatePath of candidatePaths) {
      const extensions = ['.ts', '.tsx', '.js', '.jsx', '.d.ts'];
      
      for (const ext of extensions) {
        const fullPath = candidatePath + ext;
        if (existsSync(fullPath)) {
          return fullPath;
        }
      }
      
      // Try index files
      const indexPath = resolve(candidatePath, 'index.ts');
      if (existsSync(indexPath)) {
        return indexPath;
      }
    }
  }
  
  // Fallback to standard Node.js resolution
  return null;
}

Implicit BaseUrl Support

get-tsconfig handles TypeScript's implicit baseUrl behavior when paths are specified without baseUrl:

// tsconfig.json with paths but no explicit baseUrl
{
  "compilerOptions": {
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}

The library automatically uses the tsconfig directory as an implicit baseUrl for path resolution.

Error Handling

The path matcher handles various edge cases gracefully:

  • No Configuration: Returns null if no paths or baseUrl are configured
  • Invalid Patterns: Throws errors for patterns with multiple wildcards
  • Relative Specifiers: Returns empty array for relative imports (./file, ../file)
  • Empty Substitutions: Handles missing or empty substitution arrays
import { createPathsMatcher } from "get-tsconfig";

const pathsMatcher = createPathsMatcher(tsconfig);

if (pathsMatcher === null) {
  console.log("No path mapping configuration found");
} else {
  // Safe to use path matcher
  const paths = pathsMatcher("@/utils");
}

Build Tool Integration

Example integration with bundlers and build tools:

import { getTsconfig, createPathsMatcher, type TsConfigResult } from "get-tsconfig";

function createResolver(projectRoot: string) {
  const tsconfig = getTsconfig(projectRoot);
  const pathsMatcher = createPathsMatcher(tsconfig);
  
  return {
    resolve(specifier: string, importer: string): string[] {
      if (pathsMatcher) {
        const mappedPaths = pathsMatcher(specifier);
        if (mappedPaths.length > 0) {
          return mappedPaths;
        }
      }
      
      // Fallback to standard resolution
      return [specifier];
    }
  };
}