Jest "SyntaxError: Cannot use import" - Fix --experimental-vm-modules in CI
Jest’s native ESM support is gated behind Node’s --experimental-vm-modules flag. Without it, Jest loads ESM test files through its CommonJS path and they fail to parse import syntax.
What this error means
Jest tests using import (or testing ESM-only packages) fail with a SyntaxError about import, or Jest warns that ESM support needs the experimental flag. The same tests pass once the flag is set.
SyntaxError: Cannot use import statement outside a module
# or
Jest encountered an unexpected token ...
To enable ESM support, run node with --experimental-vm-modulesDiagnose it: what is different about the runner?
A build that passes locally and fails on a runner differs in a small number of predictable ways. Check those before changing build configuration, because the build config is usually not the thing that changed.
- run: |
node --version && npm --version
echo "NODE_ENV=$NODE_ENV CI=$CI"
nproc && free -h && df -h /
ls -la node_modules/.bin | headCommon causes
Jest run without the ESM flag
Native ESM in Jest requires Node’s --experimental-vm-modules. Without it, Jest cannot evaluate ES modules and import syntax fails.
ESM-only dependencies under test
Even with mostly-CJS tests, importing an ESM-only package pulls in the ESM path, which needs the flag.
How to fix it
Pass the flag via NODE_OPTIONS
Enable Node’s experimental VM modules when invoking Jest.
# package.json script
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js"
# or in CI:
NODE_OPTIONS=--experimental-vm-modules npx jestOr transform ESM to CJS
- Configure
transform(babel-jest/ts-jest) to compile ESM/TS down to CommonJS for Jest. - Add ESM-only deps to
transformIgnorePatternsexceptions so they get transformed. - Pick one approach - native ESM (flag) or transform - not both.
The three that account for most of them
- Case sensitivity. Linux runners are case sensitive, macOS is not. An import with the wrong case resolves locally and fails in CI.
- Out of memory. Exit code 137 is a SIGKILL from the kernel, not a build error. Raise
--max-old-space-sizeor use a larger runner. - devDependencies pruned.
NODE_ENV=productionmakesnpm ciskip devDependencies, so the build tool itself goes missing. Set it after install, not before.
How to prevent it
- Set
NODE_OPTIONS=--experimental-vm-modulesfor ESM Jest runs. - Or standardize on a CJS transform for tests.
- Keep the test module strategy consistent across the repo.