Skip to content
Latchkey

Jest transformIgnorePatterns Not Excluding an ESM Dependency

You added a package to transformIgnorePatterns but Jest still throws on its ESM syntax. The negative-lookahead regex is almost always slightly wrong - a scoped name, a nested hoist, or a missing alternation makes the pattern fail to match.

What this error means

Despite a transformIgnorePatterns: ["/node_modules/(?!pkg)"] entry, Jest still reports an unexpected token inside that package. The regex looks right but does not actually exclude the file Jest is failing on.

Jest output
SyntaxError: Unexpected token 'export'

  at node_modules/.pnpm/@scope+ui@2.0.0/node_modules/@scope/ui/index.js
  // transformIgnorePatterns: ['/node_modules/(?!@scope/ui)']  -> still ignored

Diagnose it: flake, environment, or genuine failure?

Before debugging the assertion, establish whether the test is deterministic. A test that fails only in CI is usually order-dependent, time-dependent, or racing something, and fixing the assertion will not help.

Terminal
# does it fail in isolation?
npx vitest run path/to/file.test.ts

# is it order dependent? run the suite in a random order twice
npx vitest run --sequence.shuffle

# is it a race? run the same file repeatedly
for i in $(seq 1 20); do npx vitest run path/to/file.test.ts || break; done

Common causes

Scoped package name not escaped/grouped

A scoped package like @scope/ui needs the / matched literally and the group anchored correctly. (?!@scope/ui) can miss the path when the slash is consumed elsewhere.

Nested or hoisted copy under a store path

pnpm/Yarn store the package under .pnpm/... or a nested node_modules, so a pattern anchored only at the top-level node_modules/ does not match the real file path.

How to fix it

Write a pattern that matches every install layout

Allow-list multiple packages and account for nested node_modules with a non-greedy prefix.

jest.config.js
// jest.config.js
module.exports = {
  transformIgnorePatterns: [
    'node_modules/(?!(?:\\.pnpm/)?(@scope/ui|other-esm)/)',
  ],
};

Confirm the exact failing path

  1. Read the path in the stack trace - match your regex against that literal string.
  2. For pnpm, include the .pnpm/ store segment in the alternation.
  3. Re-run with --no-cache so an old transform result is not reused.

CI-only causes worth ruling out

  • Runners have fewer cores than a laptop, so timing-sensitive tests that pass locally fail under contention.
  • No TTY and a different locale or timezone. Snapshot tests containing formatted dates or numbers are the usual casualty; pin TZ and LANG in the job.
  • Parallel workers sharing a database, a port, or a temp directory. Give each worker its own namespace.
  • Default timeouts calibrated on a fast machine. A cold runner is slower on first execution, especially before any cache warms.

How to prevent it

  • Test the regex against the real node_modules path, not the package name alone.
  • Account for pnpm/Yarn store layouts in the pattern.
  • Clear the Jest cache when changing transform config.

Frequently asked questions

What causes Jest transformIgnorePatterns not excluding an ESM dependency?
There are 2 common causes: scoped package name not escaped/grouped and nested or hoisted copy under a store path. A scoped package like @scope/ui needs the / matched literally and the group anchored correctly.
How do I fix Jest transformIgnorePatterns not excluding an ESM dependency?
There are 2 fixes depending on which cause you have: write a pattern that matches every install layout and confirm the exact failing path. Work through them in order, since the first is the most common.
What does Jest transformIgnorePatterns not excluding an ESM dependency actually mean?
Despite a transformIgnorePatterns: ["/node_modules/(?!pkg)"] entry, Jest still reports an unexpected token inside that package.
How do I stop Jest transformIgnorePatterns not excluding an ESM dependency happening again?
Test the regex against the real node_modules path, not the package name alone. The prevention section lists 3 changes that keep it from recurring.

Related guides

References

Not every red build is your code. Latchkey repairs the ones that are not, on the runner. Start free → 30-day trial · No credit card