diff --git a/packages/jest/package.json b/packages/jest/package.json index 1cab03672cae4..d033696e22ec8 100644 --- a/packages/jest/package.json +++ b/packages/jest/package.json @@ -35,18 +35,19 @@ "migrations": "./migrations.json" }, "dependencies": { - "@jest/reporters": "^29.4.1", - "@jest/test-result": "^29.4.1", + "@jest/reporters": "^29.4.1 || ^30.0.0-alpha.6", + "@jest/test-result": "^29.4.1 || ^30.0.0-alpha.6", "@nx/devkit": "file:../devkit", "@nx/js": "file:../js", "@phenomnomnominal/tsquery": "~5.0.1", "chalk": "^4.1.0", "identity-obj-proxy": "3.0.0", - "jest-config": "^29.4.1", - "jest-resolve": "^29.4.1", - "jest-util": "^29.4.1", + "jest-config": "^29.4.1 || ^30.0.0-alpha.6", + "jest-resolve": "^29.4.1 || ^30.0.0-alpha.6", + "jest-util": "^29.4.1 || ^30.0.0-alpha.6", "minimatch": "9.0.3", "resolve.exports": "1.1.0", + "semver": "^7.5.3", "tslib": "^2.3.0", "yargs-parser": "21.1.1" }, diff --git a/packages/jest/plugins/resolver.ts b/packages/jest/plugins/resolver.ts index 9cdd26d2fb036..33c8b0303d1df 100644 --- a/packages/jest/plugins/resolver.ts +++ b/packages/jest/plugins/resolver.ts @@ -1,16 +1,6 @@ import { dirname, extname, join, resolve } from 'path'; import { resolve as resolveExports } from 'resolve.exports'; -import type defaultResolver from 'jest-resolve/build/defaultResolver'; - -interface ResolveOptions { - rootDir: string; - basedir: string; - paths: string[]; - moduleDirectory: string[]; - browser: boolean; - extensions: string[]; - defaultResolver: typeof defaultResolver; -} +import type { ResolverOptions } from 'jest-resolve'; let compilerSetup; let ts; @@ -38,7 +28,7 @@ function getCompilerSetup(rootDir: string) { return { compilerOptions, host }; } -module.exports = function (path: string, options: ResolveOptions) { +module.exports = function (path: string, options: ResolverOptions) { const ext = extname(path); if (ext === '.css' || ext === '.scss' || ext === '.sass' || ext === '.less') { return require.resolve('identity-obj-proxy'); diff --git a/packages/jest/src/plugins/plugin.ts b/packages/jest/src/plugins/plugin.ts index e82eb2114de25..0cdf6b9aad2d6 100644 --- a/packages/jest/src/plugins/plugin.ts +++ b/packages/jest/src/plugins/plugin.ts @@ -1,3 +1,4 @@ +import type { Config } from '@jest/types'; import { CreateNodes, CreateNodesContext, @@ -13,22 +14,20 @@ import { TargetConfiguration, writeJsonFile, } from '@nx/devkit'; -import { dirname, isAbsolute, join, relative, resolve } from 'path'; - -import { getNamedInputs } from '@nx/devkit/src/utils/get-named-inputs'; -import { existsSync, readdirSync, readFileSync } from 'fs'; -import { readConfig, replaceRootDirInPath } from 'jest-config'; -import jestResolve from 'jest-resolve'; -import { workspaceDataDirectory } from 'nx/src/utils/cache-directory'; import { calculateHashForCreateNodes } from '@nx/devkit/src/utils/calculate-hash-for-create-nodes'; import { clearRequireCache, loadConfigFile, } from '@nx/devkit/src/utils/config-utils'; -import { getGlobPatternsFromPackageManagerWorkspaces } from 'nx/src/plugins/package-json'; -import { combineGlobPatterns } from 'nx/src/utils/globs'; +import { getNamedInputs } from '@nx/devkit/src/utils/get-named-inputs'; +import { existsSync, readdirSync, readFileSync } from 'fs'; import { minimatch } from 'minimatch'; import { hashObject } from 'nx/src/devkit-internals'; +import { getGlobPatternsFromPackageManagerWorkspaces } from 'nx/src/plugins/package-json'; +import { workspaceDataDirectory } from 'nx/src/utils/cache-directory'; +import { combineGlobPatterns } from 'nx/src/utils/globs'; +import { dirname, isAbsolute, join, relative, resolve } from 'path'; +import { getInstalledJestMajorVersion } from '../utils/version-utils'; const pmc = getPackageManagerCommand(); @@ -167,6 +166,9 @@ async function buildJestTargets( } const rawConfig = await loadConfigFile(absConfigFilePath); + + const { readConfig } = + requireJestUtil('jest-config'); const config = await readConfig( { _: [], @@ -211,23 +213,24 @@ async function buildJestTargets( let metadata: ProjectConfiguration['metadata']; if (options?.ciTargetName) { - // Resolve the version of `jest-runtime` that `jest` is using. - const jestPath = require.resolve('jest'); - const jest = require(jestPath) as typeof import('jest'); - // nx-ignore-next-line - const { default: Runtime } = require(require.resolve('jest-runtime', { - paths: [dirname(jestPath)], + const { default: Runtime } = // nx-ignore-next-line - })) as typeof import('jest-runtime'); + requireJestUtil('jest-runtime'); const jestContext = await Runtime.createContext(config.projectConfig, { maxWorkers: 1, watchman: false, }); + const jest = require('jest') as typeof import('jest'); const source = new jest.SearchSource(jestContext); - const specs = await source.getTestPaths(config.globalConfig); + const jestVersion = getInstalledJestMajorVersion()!; + const specs = + jestVersion >= 30 + ? // @ts-expect-error Jest 30+ expects the project config as the second argument + await source.getTestPaths(config.globalConfig, config.projectConfig) + : await source.getTestPaths(config.globalConfig); const testPaths = new Set(specs.tests.map(({ path }) => path)); @@ -343,11 +346,15 @@ function resolvePresetInput( return null; } + const { replaceRootDirInPath } = + requireJestUtil('jest-config'); let presetPath = replaceRootDirInPath(projectRoot, presetValue); const isNpmPackage = !presetValue.startsWith('.') && !isAbsolute(presetPath); presetPath = presetPath.startsWith('.') ? presetPath : join(presetPath, 'jest-preset'); + const { default: jestResolve } = + requireJestUtil('jest-resolve'); const presetModule = jestResolve.findNodeModule(presetPath, { basedir: projectRoot, extensions: ['.json', '.js', '.cjs', '.mjs'], @@ -369,7 +376,7 @@ function resolvePresetInput( function getOutputs( projectRoot: string, - { globalConfig }: Awaited>, + { globalConfig }: { globalConfig: Config.GlobalConfig }, context: CreateNodesContext ): string[] { function getOutput(path: string): string { @@ -403,3 +410,13 @@ function normalizeOptions(options: JestPluginOptions): JestPluginOptions { options.targetName ??= 'test'; return options; } + +let jestPath: string; +/** + * Resolves a jest util package version that `jest` is using. + */ +function requireJestUtil(packageName: string): T { + jestPath ??= require.resolve('jest'); + + return require(require.resolve(packageName, { paths: [dirname(jestPath)] })); +} diff --git a/packages/jest/src/utils/version-utils.ts b/packages/jest/src/utils/version-utils.ts new file mode 100644 index 0000000000000..da6633f3fa678 --- /dev/null +++ b/packages/jest/src/utils/version-utils.ts @@ -0,0 +1,15 @@ +import { major } from 'semver'; + +export function getInstalledJestVersion(): string | null { + try { + return require('jest/package.json').version; + } catch { + return null; + } +} + +export function getInstalledJestMajorVersion(): number | null { + const installedVersion = getInstalledJestVersion(); + + return installedVersion ? major(installedVersion) : null; +}