## TL;DR
Put the most specific mapper entries first, write each key as a full regex, and point values at [rootDir]-anchored paths. Jest never reads tsconfig paths or bundler alias settings, so every alias needs its own mapper entry. Double-check you edited the config file Jest actually loads.

## Problem
Tests fail with Cannot find module for an aliased import like @app/utils, even though the app builds fine.

## Steps
1. Open the Jest config Jest actually uses and confirm the moduleNameMapper block is in it, not in a stale or unused config file. Expected: npx jest --showConfig prints your mapper entries.
2. Write each key as a regex with start and end anchors, for example "^@app/(.*)$". Expected: the key matches the full import specifier.
3. Map the value to a [rootDir]-anchored path with a $1 capture, for example "[rootDir]/src/$1". Expected: the resolved path points at a real file.
4. Order entries from most specific to most generic, with catch-all patterns last. Expected: npx jest --showConfig lists specific patterns above generic ones.
5. Run one failing test file. Expected: Cannot find module is gone for the aliased import.

## When to use
- Jest 29 or 30 reports Cannot find module for imports using @, ~, or other aliases.
- Aliases work in the bundler or tsc but not under Jest.
- You added a new alias prefix and new tests fail while old ones pass.

## When not to use
- The error comes from webpack, Vite, or tsc itself; fix the alias at the source instead.
- Relative imports fail too; that is a roots or testMatch problem, not a mapper problem.
- You use Vitest; it reads vite resolve.alias, so configure the alias there.

## Tool compatibility
- Jest 29.x: moduleNameMapper keys are regex strings, values support $N captures.
- Jest 30.x: same behavior; --showConfig is still the fastest way to verify.
- Works with babel-jest and ts-jest; ts-jest does not read tsconfig paths automatically either.

## Variant phrasings
### jest cannot find module for @ alias
Same fix. The @ prefix is just a naming habit; the mapper entry is "^@/(.*)$" pointed at your source dir.
### moduleNameMapper ignored
Almost always the wrong config file, or a second config (package.json vs jest.config.js) shadowing the first. --showConfig tells you which file won.

## Why it happens
Jest resolves modules with its own resolver and never looks at tsconfig.json paths or bundler alias settings. A mapper key that is not anchored, or a generic pattern listed first, silently matches the wrong thing or nothing at all.

## Edge cases
- Monorepo packages: the value path must be anchored per package; a repo-root [rootDir] in a package config points at the wrong dir.
- If the alias maps to a directory index file, add the trailing path explicitly; Jest does not guess index files through a mapper value.
- ESM projects: mapper values still work, but extensionless imports may also need extensionsToTreatAsEsm or a custom resolver.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_L3wJeZdL9tuP6d4PQ0gdag
