TypeScript

Fix: Jest SyntaxError Unexpected token export (uuid library)

The fix depends on whether the stale uuid build is yours or a transitive dependency's — here is how to tell, and the exact config for each case.

Mahdi Ben RhoumaMahdi Ben Rhouma
8 min read

TL;DR#

Jest resolved uuid to node_modules/uuid/dist/esm-browser/index.js, which is written as an ES module (export { default as v1 } from './v1.js'), then ran it through Jest's CommonJS wrapper — which cannot parse export. That file is what uuid's package.json exports map hands back on the default branch; Jest's own resolver did not consistently honour that map before version 9 of uuid, which changed its package layout so a plain CommonJS require() lands on the right build instead. Map uuid to its CommonJS build with moduleNameMapper, or upgrade the package to uuid@9 or later. If the error still appears after upgrading your own uuid, a transitive dependency is still pulling in uuid@8.

text
    node_modules/uuid/dist/esm-browser/index.js:1
    ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,jest){export { default as v1 } from './v1.js';
                                                                                      ^^^^^^
 
    SyntaxError: Unexpected token 'export'
 
      at Runtime.createScriptFromCode (../node_modules/jest-runtime/build/index.js:1796:14)
      at Object.require (../node_modules/@nestjs/common/decorators/core/injectable.decorator.js:4:16)

This is the exact trace from the Stack Overflow question (81 votes, 64,696 views): a NestJS + ts-jest + Puppeteer project where tests ran fine until uuid — a transitive dependency of something else in the tree — got resolved to its ESM build the moment Jest's module graph touched it.

Why it happens#

uuid ships several builds in one package: a CommonJS build under dist/, and ESM builds under dist/esm-node/ and dist/esm-browser/. Which one a tool picks is decided by the exports map in uuid's package.json — a standard Node.js feature for pointing different environments (require, import, browsers) at different files from the same package name. The error trace above names dist/esm-browser/index.js specifically: on the uuid major version this question was filed against, that is what the exports map's default condition resolves to, so any resolver that reads the map at all without a more specific CommonJS condition can land there — which is why the file itself reads like it was meant for a <script> tag, not a Jest test.

Jest's own module resolver (jest-resolve) handled that exports map inconsistently for a long time — not reading it on some releases, reading it and landing on the wrong condition on others. One of the people who hit this exact error documented the workaround in the uuid repository, writing the fix as:

text
// jest.config.js
moduleNameMapper: {
  // Force module uuid to resolve with the CJS entry point, because Jest does not support package.json.exports. See https://github.com/uuidjs/uuid/issues/451
  "uuid": require.resolve('uuid'),
},

Jest tracked the gap as its own issue, "Support package exports in jest-resolve", now closed. Jest's maintainer confirmed on the uuid thread that "Jest 28 does support it" once that resolver work landed. The current Jest configuration docs describe the surviving knob, testEnvironmentOptions.customExportConditions, which controls which condition inside a package's exports map Jest follows — it does not change whether the map is read at all on a recent Jest. That distinction matters for anyone still debugging this on an older Jest: a customExportConditions tweak will not fix a resolver that pre-dates the exports support entirely, only the two config fixes below will.

So on an old Jest, or on a dependency chain where something still resolves to an old uuid, the resolver falls back to a file that happens to be an ES module, and Jest's CommonJS wrapper — which runs every file inside a function(module, exports, require, __dirname, __filename, jest) { ... } wrapper — meets a bare export keyword it was never written to parse. That Unexpected token 'export' is JavaScript's own parser rejecting ESM syntax inside a CommonJS wrapper, not a Jest-specific error message.

Fix#

1. Force the CommonJS build with moduleNameMapper#

This is the fix from the accepted answer (score 135) and confirmed independently in a separate 43-vote answer on the same question:

js
// jest.config.js
module.exports = {
  moduleNameMapper: {
    uuid: require.resolve('uuid'),
  },
};

require.resolve('uuid') returns the path Node's own CommonJS resolver would pick for uuid — its main entry, not whatever the exports map would hand to an ESM-aware resolver. This works regardless of which uuid version is installed, and regardless of which part of the dependency tree pulled it in, because it rewrites every require('uuid')/import ... from 'uuid' Jest sees, including ones inside your dependencies' own node_modules.

A shorter form some projects use is moduleNameMapper: { "^uuid$": "uuid" } — functionally the same redirect, letting Jest's own module resolution (not the exports map) decide which file uuid means.

2. Or upgrade uuid to major version 9 or later#

Version 9.0.0 of uuid was published on the npm registry on 2022-09-05, changing the package's exports map so a plain CommonJS require() resolves to the right build without Jest needing to read the map correctly at all. Jest's maintainer closed the loop on the uuid issue thread, writing: "Note that moduleNameMapper is not needed for Jest 29 and uuid v9." If your code imports uuid directly, bumping the dependency is generally enough — no Jest config change required — but later reports on the same GitHub thread, from April 2023, show the error can still surface on uuid major version 9 with a Jest 29 release, independently of any transitive dependency; if it does, apply the moduleNameMapper fix from step 1 as well:

bash
npm install uuid

Check the installed major version afterwards:

bash
npm ls uuid

3. If the error survives both fixes, a transitive dependency is still on the old major#

Upgrading your own package.json entry for uuid does not touch the copy a different dependency bundles internally. On the same GitHub thread, a user (mbrookson) noted they still needed the workaround because packages such as next-auth and some @aws-sdk modules were "still reliant on" the old major release months after version 9 shipped. If the npm ls uuid output above shows more than one resolved version, the moduleNameMapper fix from step 1 is the one that covers every copy at once — it rewrites the module name itself, not a specific file path, so it catches the uuid a dependency three levels down requires too.

Verify the fix#

Re-run the previously failing suite. The stack trace that named esm-browser/index.js should be gone; if uuid is still involved anywhere in the trace, it will now point at a .js file under dist/ (the CommonJS build) rather than dist/esm-browser/. If the suite passes but a different package produces the same Unexpected token 'export' shape, the fix is the same moduleNameMapper pattern applied to that package's name instead — the Jest resolver gap was never specific to uuid, only widely hit through it because so many other packages depend on it transitively.

If npm ls uuid printed more than one version earlier, run it again after the fix: it will still list every resolved copy in the tree (upgrading or mapping uuid does not collapse duplicate installs into one), but the test run itself no longer cares, because moduleNameMapper redirects every one of those copies to the same CommonJS file regardless of which package.json asked for it. A leftover old uuid under a dependency's own node_modules is then a disk-size or npm audit concern, not a test-breaking one.

One email a month — the latest explained errors

What the site published that month — mostly Next.js, Supabase and Node errors, explained. You confirm by email; unsubscribe any time in one click.

If switching your own package.json to "type": "module" is what triggered test failures, the symptom is usually a different error — ReferenceError: jest is not defined rather than this SyntaxError — because Jest stops auto-injecting its globals under native ESM. That is covered separately in Fix: ReferenceError: jest is not defined (ESM tests). A Vite app that reads import.meta.env hits another member of the same family under Jest, SyntaxError: Cannot use 'import.meta' outside a module, because the transformer compiles the source to CommonJS; the Jest and Vite import.meta fix moves those reads into one module that Jest maps to a stand-in, or runs the tests with Vitest.

Projects doing a gradual CommonJS-to-ESM interop change also run into ERR_IMPORT_ASSERTION_TYPE_MISSING when importing JSON modules on current Node.js releases, and a CJS/ESM default-export mismatch on the TypeScript side shows up as TS2305: Module Has No Exported Member — a different parser (tsc, not Jest) rejecting the same kind of interop gap for a different reason.

These three errors share one root cause family — a package or a test runner disagreeing about whether a module is CommonJS or ESM — but each needs its own fix, because the tool that rejects the file (Jest's runtime wrapper, Node's loader, or the TypeScript compiler) is different in each case.

Frequently Asked Questions

|

Have more questions? Contact us

Still stuck? Paste your error.

Paste the error message or stack trace — we check it against every fix and tool on the site.

Matching happens in your browser. When you search, a cleaned error signature is sent so we can spot errors we do not cover yet — never the raw text, your IP address or your e-mail. Do not paste passwords or keys. Details

Written by

Mahdi Ben Rhouma
Mahdi Ben Rhouma

Full-Stack Dev — Next.js & Supabase

Solo developer building SaaS products with Next.js and Supabase. Writing about production patterns the official docs skip.

Remote