Skip to content

fix(jest): replace-removed-matcher-aliases migration crashes when convert-jest-config-to-cjs skips non-plugin workspaces #34593

@juristr

Description

@juristr

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

  1. Widen convert-jest-config-to-cjs: Remove or relax the isJestPluginRegistered gate so it converts configs even for executor-based setups
  2. Add error handling in replace-removed-matcher-aliases: Catch readConfig failures per config file and log a warning instead of crashing the entire migration
  3. Both (defensive approach)

Metadata

Metadata

Assignees

Labels

communityThis is a good first issue for contributingpriority: highHigh Priority (important issues which affect many people severely)scope: testing toolsIssues related to Cypress / Jest / Playwright / Vitest support in Nxtype: bug

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions