pretty-format
30.5.1 · 1 Sep 2026MIT304.4M / weekRepositoryHomepagenpmAtom feed
Where each line stands
Plan an upgradeLatest releases
All 142 releases- 30.x · latest line
30.5.1
1 Sep 2026jest-config: Don't warn about global-only options in the config that supplies the global config - the root config a project resolves to, or the first entry of
--projectswhen no root config is passedjest-config, jest-types: Stop accepting
reporters,coverageReporters,workerIdleMemoryLimit,cwdandrunnerOptionsin a project config - they were silently ignored, and now warn like the other global-only optionsjest-config, jest-validate: Warn about
maxWorkersandcoverageThresholdin a project config instead of dropping them without a word
Fixes
jest-config: Don't warn about global-only options in the config that supplies the global config - the root config a project resolves to, or the first entry of
--projectswhen no root config is passedjest-config, jest-types: Stop accepting
reporters,coverageReporters,workerIdleMemoryLimit,cwdandrunnerOptionsin a project config - they were silently ignored, and now warn like the other global-only optionsjest-config, jest-validate: Warn about
maxWorkersandcoverageThresholdin a project config instead of dropping them without a wordjest-resolve: Match
moduleNameMapperpatterns against the specifier as written again (reverting #16390)jest-runtime: Resolve package
importsspecifiers like#depunder ESM again
30.5.0
28 Aug 2026- breaking
jest-haste-map: Match watched files on a full extension, so
moduleFileExtensions: ['js']no longer acceptsfoo.mjs - deprecated
@jest/source-map: Deprecate
getCallsitein favour ofSourceMapSupport#getCallsite babel-plugin-istanbulis updated to v8
On a personal note: King Harald V of Norway passed away this morning. He ascended the throne 35 years ago, two months before I was born. This release is dedicated to his memory. Hvil i fred 🇳🇴
This is a big release. It touches
jest-runtime,jest-resolveandjest-haste-mapin many places, and with this many changes there might be regressions 😬. If your suite behaves differently after upgrading, please open an issue.Highlights
whenCalledWithMock functions can now configure return values per argument list, contributed by @timkindberg (#16053):
const fn = jest.fn(); fn.whenCalledWith('apple').mockReturnValue('red'); fn.whenCalledWith('banana').mockReturnValue('yellow'); fn.whenCalledWith(expect.any(Number)).mockReturnValue('numeric'); fn('apple'); // 'red' fn('banana'); // 'yellow' fn(42); // 'numeric' fn('grape'); // undefinedThe returned object is a real
Mock, somockReturnValueOnce,mockResolvedValue,mockImplementationetc. all chain here too. Argument slots accept literals or any asymmetric matcher, with the same equality semantics astoHaveBeenCalledWith(). Calls that match nothing fall through to the base mock. See the Mock Functions docs for matching and precedence details.Describe-level retries
jest.retryTimes()can now retry a wholedescribeblock instead of a single test, contributed by @soltonigiri (#16322). Each attempt reruns the block'sbeforeAll/afterAllhooks, child tests and nested describes, which helps when tests in a block depend on shared state:describe('workflow', () => { jest.retryTimes(3, {entireDescribe: true}); test('first step', () => {}); test('second step', () => {}); // a failure retries the entire block });New file watcher
The non-watchman path of
jest-haste-mapis rewritten.@parcel/watcherreplaces the homegrownNodeWatcherandFSEventsWatcher(#16188), andfdirreplaces the hand-rolled directory recursion in the crawler (#16187). A batch of fixes also makes watching and indexing survive locked files on Windows, watchman failures, and duplicate manual mocks (#16295, #16358, #16355, #16360).If you can, please run your suite with
--no-watchman(in and out of watch mode) to exercise the new crawler and watchers, and report anything odd 👍Long-requested dependency updates
babel-plugin-istanbulis updated to v8globis updated to v13source-map-supportis replaced with an implementation in@jest/source-map(#16327). This fixes a memory leak wheresource-map-support's process-global caches pinned every finished test file's sandbox. Source maps also survive environment teardown,--testLocationInResultslooks up the right column, and scheme-prefixed sources likewebpack:///are kept as-is.
Performance
Warm module resolution costs about a third of what it did, and repeated work is gone from the resolution hot path (#16373, #16371).
Per-
requireoverhead injest-runtimeis cut: module ID resolution is skipped when no mock can apply, and core modules answer before the manual-mock probe (#16376).jest-snapshotloads babel, semver and synckit lazily, so every test process loads ~200 fewer modules (#16387).jest-haste-mapcaches the watchman socket path, so warm runs spawn no watchman processes (#16386), and reuses cached metadata for duplicated haste names instead of re-parsing them on every startup (#16351).
More new APIs
Snapshot failures expose the
.snapfile path asmatcherResult.snapshotPathinfailureDetails(#16374).--collectTestsexpandstest.each/describe.eachcases and reports per-status counts plus a summary line that matches a real run (#16259).AggregateErrorinner errors and nestedcausechains render with code frames and per-level indentation, and are included in--jsonoutput,retryReasonsand reporter annotations (#16316).
ESM
A long list of fixes brings
jest-runtime's module system closer to Node's:Automocking and manual
__mocks__files now apply to ESM on Node 24.9+. Static imports, dynamicimport()andrequire()of an ESM file generate an automock from the real module's namespace instead of failing with "Attempting to import a mock without a factory" (#16391).Many
jest.unstable_mockModulegaps are closed: mocks apply when the mocked file isrequire()d directly, async factories run once and fail the import instead of crashing the worker 😅, and mocks made insidejest.isolateModulesstay scoped to that block (#16389, #16365).require(esm), CJS/ESM interop,import.meta,data:URIs and error ordering now match Node in dozens of edge cases. See the Fixes section for the full list (#16364 through #16391).The
module-syncexport condition resolves, so packages that expose their ESM entry point forrequire()load the same file Node picks (#16336).
The intentional divergences from Node's module system are now documented on the ECMAScript Modules page (#16368).
Resolver
The default resolver honors Node's
--preserve-symlinks/NODE_PRESERVE_SYMLINKS(#16260).moduleNameMapperapplies to both spellings of core module specifiers (fsandnode:fs), and builtins that only exist prefixed (node:sqlite,node:test) resolve asynchronously (#16390, #16388).A user resolver can be written as an ES module (#16332).
On top of the highlights there are ~60 fixes across
jest-circus,jest-haste-map,jest-config,jest-mockand the transform cache keys. See the full changelog below 😀Features
@jest/expect-utils, jest-mock: Add
mockFn.whenCalledWith(...args)for configuring return values per argument list, with first-class asymmetric-matcher support@jest/expect-utils: Export
AsymmetricMatcherandFunctionParameterstypes (previously private toexpect)[jest-circus, jest-core, jest-jasmine2, jest-test-result, jest-types]--collectTestsnow expandstest.each/describe.eachcases and reports per-status counts (skipped/todo via the newwouldRunflag for selected tests) plus a summary line that match a real run, including under--testNamePatternand.only/fdescribefocus on both the circus and jasmine2 runners[jest-circus, jest-environment, jest-runtime, jest-types]Add describe-level retries viajest.retryTimes(..., {entireDescribe: true})[jest-circus, jest-message-util, jest-reporters, jest-types]AddretryMessagestoAssertionResultand exportformatErrorStack, so the retry log renders nestedcauseandAggregateErrorsections with code frames instead of serialized[cause]:/[errors]:markersjest-circus, jest-types: Add
unhandledErrorsDetailedtoCircus.RunResult, so an unhandled rejection reports itscausechain andAggregateErrorentries with code frames instead of a pre-serialized stackjest-haste-map: Replace
NodeWatcherandFSEventsWatcherwith@parcel/watcherfor the non-watchman watch pathjest-resolve: Bump
unrs-resolverto 1.12.1, removejest-pnp-resolverand unnecessary checksjest-resolve: Honor Node's
--preserve-symlinks/NODE_PRESERVE_SYMLINKSin the default resolver by passingsymlinks: falsetounrs-resolverjest-runtime: Apply automocking and manual
__mocks__files to synchronously evaluable ESM graphs on Node 24.9+ - static imports, dynamicimport()andrequire()of an ESM file now generate an automock from the real module's namespace instead of failing with "Attempting to import a mock without a factory". Graphs that need async evaluation (top-level await) or an async-only resolver or transformer still throwjest-runtime: Route
process.getBuiltinModulethrough the sandbox, so it returns the sandboxprocessand the hookednode:moduleinstead of the host'sjest-runtime: Throw an actionable error from
module.register()andmodule.registerHooks()inside a test - the hooks attached to the loader running Jest itself, never saw the sandboxed requires they were meant for, and stayed registered for every later test file in the workerjest-runtime: Surface resolution and import-attribute errors in an ESM graph before executing any of its CJS dependencies on Node 24.9+, matching Node's run-nothing-on-a-broken-graph behavior; the legacy loader on older versions keeps its linking-time execution order
jest-runtime: Throw
ERR_SOURCE_PHASE_NOT_DEFINEDwith an actionable message forimport sourceandimport.source(), instead of failing at instantiation with V8's bare "Source phase import object is not defined"jest-runtime: Emit the JSON-without-import-attribute deprecation warning once per test file instead of once per worker, so it is no longer silently swallowed for every file after the first
jest-runtime: Set
import.meta.maintotruein the test file andfalsein every module it loads, matching Node 24+jest-runtime: Resolve the
module-syncexport condition, so a package that exposes its ESM entry point forrequire()loads the same file Node wouldjest-snapshot: Add external snapshot paths to custom reporter failure details
Fixes
jest-console, jest-reporters:
CustomConsolenow buffers console output soTestResult.consoleis populated for reporters whenverboseis enabled, whileGitHubActionsReporteravoids replaying buffered output in verbose mode[expect, jest-message-util, jest-pattern, jest-regex-util, jest-util]Revertnode:protocol imports to restore webpack/browser-bundle compatibilityexpect: Widen
toMatchObjectandobjectContainingparameter type fromRecord<string, unknown>toobjectso class instances are acceptedjest-circus: Call a generator test body with the shared test context, so
thismatches what a regular test function receivesjest-circus: Capture the error listeners of the parent process instead of the in-sandbox
process, so listeners registered before the test file survive teardown and sandbox listeners no longer leak onto the parentjest-circus: Clear
currentlyRunningTestafter skipped and todo testsjest-circus: Prevent late
done()callbacks from affecting later test or hook invocationsjest-circus, jest-jasmine2: Honor
--expandwhen formattingnode:assertfailures, instead of always collapsing the diffjest-circus, jest-jasmine2, jest-message-util: Serialize the inner errors of an
AggregateErrorintofailureMessages,retryReasonsandunhandledErrors, so--jsonoutput and reporter annotations include themjest-circus, jest-snapshot: Keep snapshot state and counts correct when a test retries
@jest/create-cache-key-function: Include the caller support flags in the generated key, so a transformer that emits ESM or CJS based on them no longer shares one cache entry between the two
@jest/create-cache-key-function: Include the stringified project config in the generated key, so editing a transformer's own settings invalidates what it cached
@jest/transform: Include the caller support flags in a transform's cache key, so a file transformed both as ESM and as CJS no longer serves one shape's output for the other
jest-config: Add missing
findRelatedTests,outputFile, andreplnameentries toValidConfigso they no longer trigger spurious "Unknown option" warningsjest-config: Use
--configfor the global config when multiple--projectsare specifiedjest-core: Serialize
bigintvalues in--jsonand--outputFileoutput as their literal form (4n), instead of failing the run withTypeError: Do not know how to serialize a BigIntjest-core: Do not report a
CustomGCasync resource (used by N-API addons such as napi-rs for per-isolate GC bookkeeping) as an open handle, since it isnapi_unref'd by the addon and can never keep the event loop alivejest-each: Keep a
$&,$`,$'or$$inside a%pparam value out of the replacement, so the title shows the value instead of the text around itjest-each: Interpolate a
bigintinto a%jtitle as its literal form ("4n") at any depth, instead of throwingTypeError: Do not know how to serialize a BigIntwhile collecting the testsjest-environment, jest-runtime: Bind
sandboxInjectedGlobalsto the right values wheninjectGlobalsisfalse, instead of shifting every one of them by a positionjest-environment-node, jest-util: Only warn about a conflicting
globalsCleanupmode when one was explicitly configured, and follow the mode that is actually in effectjest-environment-node, jest-util: Stop resolving lazy globals when setting up an environment, so Node 26's builtin module globals are no longer loaded (and no longer emit their deprecation warnings) for every test file
jest-haste-map: Keep watch mode alive when an outside process briefly makes a file unreadable on Windows, instead of tearing the watcher down on
EPERMjest-haste-map: Keep indexing when an outside process holds a file open on Windows, instead of failing the whole crawl on
EPERMjest-haste-map: Keep a duplicated manual mock resolving when the file it pointed at is deleted in watch mode
jest-haste-map: Shut the worker farm down when a duplicate manual mock aborts the build under
throwOnModuleCollisionjest-haste-map: Attach the watchman client's
errorlistener before the first command, so a watchman failure falls back to the node crawler instead of crashing on an unhandlederrorevent, and always end the clientjest-haste-map: Stop delivering watch events after
WatchmanWatcheris closed, and route its warnings through the configured consolejest-haste-map: Restore the nested
duplicatesindex correctly inModuleMap.fromJSON, so a haste collision reported inside a test worker raisesDuplicateHasteCandidatesErrorinstead of aTypeError- breaking
jest-haste-map: Match watched files on a full extension, so
moduleFileExtensions: ['js']no longer acceptsfoo.mjs jest-haste-map: Delimit the fields that make up the haste map cache key, so two different option sets cannot hash to the same cache file
jest-message-util: Print the inner errors of an
AggregateErrorthrown inside a testjest-message-util: Indent nested
causeandAggregateErrorsections of a test failure by one level per depth, so the nesting is legible instead of rendering flatjest-message-util: Color stack traces line by line so blank lines stay blank
jest-message-util: Detect Jest's own frames without assuming the checkout directory's name, and cover
@jest/*packages, so stack traces and code frames point at user codejest-mock:
mockResolvedValue/mockRejectedValuenow see all overload return types, so a Promise-returning overload survives even when a later overload returns a non-Promise (e.g.pg.Client['end'])@jest-environment/jsdom-abstract: Make
@types/jsdoma peer dependencyjest-mock: Remove the leftover own accessor descriptor when restoring a
spyOnof an inherited getter or setter, so the instance keeps reflecting the prototypejest-resolve: Include
extensionsToTreatAsEsmin theshouldLoadAsEsmcache key, so projects with different extension lists don't read each other's answersjest-resolve: Make
getModuleIDAsyncbuild and cachedata:URI module IDs the same way asgetModuleIDjest-resolve: Keep the
node:prefix when resolving a core module asynchronously, so a builtin that only exists prefixed (node:sea,node:sqlite,node:test,node:test/reporters) resolves instead of failing as a missing bare packagejest-resolve: Look up manual mocks for
node:protocol specifiers under the unprefixed name they are stored asjest-resolve: Apply
moduleNameMapperconsistently to both spellings of core module specifiers (fsvsnode:fs)jest-resolve: Keep virtual and ordinary mock module IDs isolated across test files
jest-resolve: Guard missing
require.resolve.pathsjest-resolve, jest-config, jest-runner: Support a user resolver written as an ES module
jest-resolve, jest-runtime: Throw the CJS parse error for ESM syntax in a
"type": "commonjs"package or a.cjsfile instead of loading it as ESM, matching Node@jest/source-map: Keep source map sources that name a scheme, such as
webpack:///, instead of resolving them into a path that does not exist@jest/source-map: Look up
--testLocationInResultspositions at the right column, and keep a mapping to the first column instead of discarding it@jest/source-map: Warn when a source map cannot be parsed, instead of silently leaving its frames untranslated
jest-runner, @jest/source-map: Keep a source-mapped stack for an error thrown after the test environment was torn down
jest-runtime, @jest/source-map: Keep source maps past teardown and past the next test file's
install, so a stack from a file no earlier stack mentioned still points at the original sourcejest-runtime: Report that no coverage was collected when
getAllV8CoverageInfoCopyis called afterteardown, instead of returning an empty resultjest-runtime: Cache a CJS module's parsed exports before walking its re-exports, so two modules that re-export each other no longer overflow the stack when imported from ESM
jest-runtime: Keep a re-exported ES module's parse failure from marking the re-exporting CommonJS file as ESM, so
module.exports = require('./dep.mjs')loads instead of failing withmodule is not definedjest-runtime: Scope module mocks instantiated inside
jest.isolateModules/isolateModulesAsyncto that block, so a mock first imported there no longer outlives it - matching how CommonJS mocks already behavejest-runtime: Suspend module isolation while generating an automock, so loading the real module to read its shape no longer populates the isolated registry
jest-runtime: Check a cached ES module's status before
require()returns it, so a module whose evaluation threw rethrows that error and one left linked by a failed sibling is evaluated instead of returning uninitialized bindingsjest-runtime: Report the original
ERR_REQUIRE_ASYNC_MODULEwhen arequire()of a top-level-await graph is retried, instead of a spurious "concurrentimport()" errorjest-runtime: Throw the evaluation error when another caller's
import()of the same module failed while we awaited it, instead of resolving with the errored modulejest-runtime: Mark the result of
require()ing an ES module that has a default export with__esModule: truethrough a live-binding facade, and serve the same object fromrequire.cache, matching Nodejest-runtime: Provide a CommonJS module's exports under the
'module.exports'named export when imported from ESM, matching Node 23+jest-runtime: Give the test file itself a non-null
require.mainjest-runtime: Populate
module.childrenwith the modules a file loads, matching Nodejest-runtime: Provide
import.meta.resolveandimport.meta.jestindata:URI modules, accept any-case mediatype parameters, and use Node's error codes for invaliddata:URIsjest-runtime: Key ES modules by full URL, so query and fragment suffixes create the same module instances as Node and show up in
import.meta.urljest-runtime: Share modules between overlapping graphs when a CommonJS module
require()s an ES module mid-load, instead of evaluating shared dependencies twicejest-runtime: Throw
ERR_REQUIRE_CYCLE_MODULElike Node when a CommonJS modulerequire()s an ES module that is still being loaded, instead of evaluating the module a second timejest-runtime: Key builtin modules in the ESM registry by one canonical specifier
jest-runtime:
import.meta.resolve()for a builtin uses itsnode:specifierjest-runtime: Fall back to native ESM when a
.jsfile contains ESM syntax but has no"type":"module"markerjest-runtime: Allow
require()of ESM-marked files on Node < 24.9 via transform fallbackjest-runtime, @jest/transform: Surface actionable
ERR_REQUIRE_ESMerror for files with untransformed ESM syntax instead of the generic "unexpected token" messagejest-runtime: Support older test environments whose
moduleMockerdoes not implementclearMocksOnScopejest-runtime: Apply
jest.unstable_mockModulewhen the mocked file itself isrequire()d, not only when it is imported as a dependencyjest-runtime: Apply
jest.unstable_mockModuleto statically importeddata:URIs on Node 24.9+, matching dynamicimport()jest-runtime: Run an async
jest.unstable_mockModulefactory once per module instead of twice, and fail the import instead of crashing the worker when the factory rejectsjest-runtime: Hide a
require(esm)module that failed to evaluate fromrequire.cache, as Node does, instead of exposing a namespace with uninitialized bindingsjest-runtime: Strip the byte-order mark when importing a JSON module, matching
require()and Nodejest-runtime: Throw
ERR_REQUIRE_ASYNC_MODULEwhenrequire(esm)runs under an async-only custom resolver, instead of silently resolving with the default resolverjest-runtime: Parse imported JSON modules with the test realm's
JSON, so their objects passinstanceof Objectinside the test likerequire()d JSON doesjest-runtime: Accept every
file:URL string in the sandboxedmodule.createRequire, including one with alocalhostauthority, as Node doesjest-runtime: Point at
{virtual: true}whenjest.mockorjest.unstable_mockModuleis given a module that cannot be resolvedjest-reporters: Fix coverage report table formatting in CI/GitHub Actions environments where
process.stdout.columnsis undefined by falling back to theCOLUMNSenv var or80columns in CI, preserving existing behaviour in other non-TTY environmentsjest-runtime: Support CJS-in-ESM exports via
"module.exports"named exportsjest-snapshot: Keep a skipped or failed test's hinted snapshots, instead of reporting them obsolete
jest-util: Stop
globsToMatcherreusing a cached matcher compiled with different picomatch options, and keep itsdot: truedefault whendotis passed asundefinedpretty-format: Move the
react-isaliases into the@jestscope, so they cannot be shadowed by unrelated packages published under the alias names
Chore & Maintenance
- deprecated
@jest/source-map: Deprecate
getCallsitein favour ofSourceMapSupport#getCallsite
30.4.1
8 May 2026
Notes older than 90 days are read on the Releases tab.
Dependency tracking · free for 3 projects
Know when your own dependencies move
pretty-format published 5 releases in the last 12 months. inlinr folds the routine ones into a line and keeps the few that need you.
Advisories reach you
An email when a critical or high advisory affects a project, before the next install.
Upgrades checked first
Peer ranges, Node, ESM-only and types changes read before you bump.
Only what matters
Majors and breaking notes stay; patches and sibling bumps fold into one line.
Registry facts last checked 4 Nov 2026. Releases and advisories last read 4 Nov 2026.