jest.resolver.cjs2.1 KBView on GitHub
/**
 * Custom jest resolver that mirrors the tsconfig.json "@/*" path aliases.
 *
 * tsconfig.json defines:
 *   "@/*": ["./", "./modules/cedar-os/src/*", "./modules/cedar-os/src/cedar-os-components/*",
 *           "../server/src/*"]
 *
 * Jest's moduleNameMapper only supports a single replacement per pattern, so it can't
 * replicate this multi-path fallback. This resolver does it by trying each candidate
 * in order and returning the first one that exists.
 *
 * The `../server/src/*` candidate is deliberately NOT mirrored here. It exists purely so
 * tsc can resolve the server's own `@/` imports while it type-checks server sources pulled
 * in via `@zero/server/*`. At runtime a mail test has no business loading backend modules
 * into jsdom, so leaving it out keeps an accidental `@/services/...` from silently resolving
 * to server code instead of failing loudly.
 */

const path = require('path');
const fs = require('fs');

const ROOT = path.resolve(__dirname);

/** Candidate suffixes that @/* expands to, in tsconfig priority order */
const AT_SLASH_ROOTS = [
  ROOT,
  path.join(ROOT, 'modules/cedar-os/src'),
  path.join(ROOT, 'modules/cedar-os/src/cedar-os-components'),
];

/** Extensions to try when resolving without an explicit extension */
const EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '/index.ts', '/index.tsx', '/index.js'];

function tryResolveWithExtensions(base) {
  // If the path already has a known extension, check it directly
  if (/\.(ts|tsx|js|jsx)$/.test(base)) {
    if (fs.existsSync(base)) return base;
    return null;
  }
  for (const ext of EXTENSIONS) {
    const candidate = base + ext;
    if (fs.existsSync(candidate)) return candidate;
  }
  return null;
}

module.exports = (request, options) => {
  // Only intercept @/* imports that the default resolver would fail on
  if (request.startsWith('@/')) {
    const suffix = request.slice(2); // strip '@/'
    for (const root of AT_SLASH_ROOTS) {
      const resolved = tryResolveWithExtensions(path.join(root, suffix));
      if (resolved) return resolved;
    }
  }

  // Fall back to jest's built-in resolver for everything else
  return options.defaultResolver(request, options);
};