Description
The replace-removed-matcher-aliases-v22-3 migration crashes when run on workspaces that use the @nx/jest:jest executor (via targetDefaults) instead of @nx/jest/plugin. The root cause is a mismatch between two migrations' preconditions.
Environment
- Node: v24.11.0 (type-stripping enabled by default →
.ts files evaluated as ESM)
- Nx: migrating from 21.5.3 → 22.5.2
- Package manager: pnpm 10.12.1
Crash Logs
Running pnpm exec nx migrate --run-migrations on monorepo.tools after migrating from 21.5.3 → 22.5.2:
Running migration @nx/jest: replace-removed-matcher-aliases-v22-3
NX Failed to run replace-removed-matcher-aliases-v22-3 from @nx/jest. This workspace is NOT up to date!
NX Jest: Failed to parse the TypeScript config file /Users/juri/nrwl/oss/monorepo.tools/libs/website/ui-typescript/jest.config.ts
ReferenceError: __dirname is not defined in ES module scope
Pass --verbose to see the stacktrace.
The failing jest config (libs/website/ui-typescript/jest.config.ts):
import { readFileSync } from 'fs';
const { exclude: _, ...swcJestConfig } = JSON.parse(
readFileSync(`${__dirname}/.swcrc`, 'utf-8')
);
if (swcJestConfig.swcrc === undefined) {
swcJestConfig.swcrc = false;
}
export default {
displayName: 'website-ui-typescript',
preset: '../../../jest.preset.js',
transform: {
'^.+\\.[tj]s$': ['@swc/jest', swcJestConfig],
},
moduleFileExtensions: ['ts', 'js', 'html'],
testEnvironment: 'node',
coverageDirectory: '../../../coverage/libs/website/ui-typescript',
};
Note: 8 of 9 migrations ran successfully. The earlier convert-jest-config-to-cjs migration reported "No changes were made" because it skipped this workspace entirely.
Reproduction
Run nx migrate latest followed by nx migrate --run-migrations on the monorepo.tools repo (which uses targetDefaults with @nx/jest:jest executor, not @nx/jest/plugin).
Root Cause
Two migrations interact poorly:
1. convert-jest-config-to-cjs (update-22-2-0)
This migration gates on @nx/jest/plugin being registered in nx.json:
if (!isJestPluginRegistered(tree)) return;
Workspaces using the older executor pattern (@nx/jest:jest in targetDefaults) skip this migration entirely. So jest.config.ts files with ESM syntax (import/export default) mixed with CJS globals (__dirname) are never converted.
2. replace-removed-matcher-aliases-v22-3 (update-21-3-0)
This migration unconditionally loads all jest.config.ts files via Jest's readConfig:
const config = await readConfig(
{ _: [], $0: undefined },
join(tree.root, jestConfigFile)
);
This evaluates the config at runtime. On Node 24 with type-stripping, the .ts file is treated as ESM, but __dirname is not available in ESM scope → ReferenceError: __dirname is not defined in ES module scope.
The mismatch
convert-jest-config-to-cjs: only runs if @nx/jest/plugin is registered
replace-removed-matcher-aliases: runs for all jest configs regardless
Possible Fixes
- Widen
convert-jest-config-to-cjs: Remove or relax the isJestPluginRegistered gate so it converts configs even for executor-based setups
- Add error handling in
replace-removed-matcher-aliases: Catch readConfig failures per config file and log a warning instead of crashing the entire migration
- Both (defensive approach)
Description
The
replace-removed-matcher-aliases-v22-3migration crashes when run on workspaces that use the@nx/jest:jestexecutor (viatargetDefaults) instead of@nx/jest/plugin. The root cause is a mismatch between two migrations' preconditions.Environment
.tsfiles evaluated as ESM)Crash Logs
Running
pnpm exec nx migrate --run-migrationson monorepo.tools after migrating from 21.5.3 → 22.5.2:The failing jest config (
libs/website/ui-typescript/jest.config.ts):Note: 8 of 9 migrations ran successfully. The earlier
convert-jest-config-to-cjsmigration reported "No changes were made" because it skipped this workspace entirely.Reproduction
Run
nx migrate latestfollowed bynx migrate --run-migrationson the monorepo.tools repo (which usestargetDefaultswith@nx/jest:jestexecutor, not@nx/jest/plugin).Root Cause
Two migrations interact poorly:
1.
convert-jest-config-to-cjs(update-22-2-0)This migration gates on
@nx/jest/pluginbeing registered innx.json:Workspaces using the older executor pattern (
@nx/jest:jestintargetDefaults) skip this migration entirely. Sojest.config.tsfiles with ESM syntax (import/export default) mixed with CJS globals (__dirname) are never converted.2.
replace-removed-matcher-aliases-v22-3(update-21-3-0)This migration unconditionally loads all
jest.config.tsfiles via Jest'sreadConfig:This evaluates the config at runtime. On Node 24 with type-stripping, the
.tsfile is treated as ESM, but__dirnameis not available in ESM scope →ReferenceError: __dirname is not defined in ES module scope.The mismatch
convert-jest-config-to-cjs: only runs if@nx/jest/pluginis registeredreplace-removed-matcher-aliases: runs for all jest configs regardlessPossible Fixes
convert-jest-config-to-cjs: Remove or relax theisJestPluginRegisteredgate so it converts configs even for executor-based setupsreplace-removed-matcher-aliases: CatchreadConfigfailures per config file and log a warning instead of crashing the entire migration