diff --git a/packages/jest/index.ts b/packages/jest/index.ts index 19f5482a69b27..cbc98ef8234c5 100644 --- a/packages/jest/index.ts +++ b/packages/jest/index.ts @@ -13,5 +13,6 @@ export { jestConfigObjectAst } from './src/utils/config/functions'; export { jestInitGenerator } from './src/generators/init/init'; export { getJestProjects, + getJestProjectsAsync, getNestedJestProjects, } from './src/utils/config/get-jest-projects'; diff --git a/packages/jest/package.json b/packages/jest/package.json index ed042ccb001a5..edab430a6ca8f 100644 --- a/packages/jest/package.json +++ b/packages/jest/package.json @@ -36,6 +36,8 @@ "dependencies": { "@jest/reporters": "^29.4.1", "@jest/test-result": "^29.4.1", + "@nx/devkit": "file:../devkit", + "@nx/js": "file:../js", "@phenomnomnominal/tsquery": "~5.0.1", "chalk": "^4.1.0", "identity-obj-proxy": "3.0.0", @@ -45,8 +47,7 @@ "minimatch": "9.0.3", "resolve.exports": "1.1.0", "tslib": "^2.3.0", - "@nx/devkit": "file:../devkit", - "@nx/js": "file:../js" + "yargs-parser": "21.1.1" }, "publishConfig": { "access": "public" diff --git a/packages/jest/src/generators/configuration/lib/__snapshots__/create-jest-config.spec.ts.snap b/packages/jest/src/generators/configuration/lib/__snapshots__/create-jest-config.spec.ts.snap index e8fda7f077f70..45bec790ca796 100644 --- a/packages/jest/src/generators/configuration/lib/__snapshots__/create-jest-config.spec.ts.snap +++ b/packages/jest/src/generators/configuration/lib/__snapshots__/create-jest-config.spec.ts.snap @@ -1,11 +1,11 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`createJestConfig should generate files 1`] = ` -"import { getJestProjects } from '@nx/jest'; +"import { getJestProjectsAsync } from '@nx/jest'; -export default { -projects: getJestProjects() -};" +export default async () => ({ +projects: await getJestProjectsAsync() +});" `; exports[`createJestConfig should generate files 2`] = ` @@ -15,11 +15,11 @@ module.exports = { ...nxPreset }" `; exports[`createJestConfig should generate files with --js flag 1`] = ` -"const { getJestProjects } = require('@nx/jest'); +"const { getJestProjectsAsync } = require('@nx/jest'); -module.exports = { -projects: getJestProjects() -};" +module.exports = async () => ({ +projects: await getJestProjectsAsync() +});" `; exports[`createJestConfig should generate files with --js flag 2`] = ` diff --git a/packages/jest/src/generators/configuration/lib/create-jest-config.spec.ts b/packages/jest/src/generators/configuration/lib/create-jest-config.spec.ts index 3646ecdc9933a..e866287528f41 100644 --- a/packages/jest/src/generators/configuration/lib/create-jest-config.spec.ts +++ b/packages/jest/src/generators/configuration/lib/create-jest-config.spec.ts @@ -162,11 +162,11 @@ export default { " `); expect(tree.read('jest.config.ts', 'utf-8')) - .toEqual(`import { getJestProjects } from '@nx/jest'; + .toEqual(`import { getJestProjectsAsync } from '@nx/jest'; -export default { -projects: getJestProjects() -};`); +export default async () => ({ +projects: await getJestProjectsAsync() +});`); expect(readProjectConfiguration(tree, 'my-project').targets.test) .toMatchInlineSnapshot(` { @@ -214,11 +214,11 @@ module.exports = { expect(tree.exists('jest.config.app.js')).toBeTruthy(); expect(tree.read('jest.config.js', 'utf-8')) - .toEqual(`const { getJestProjects } = require('@nx/jest'); + .toEqual(`const { getJestProjectsAsync } = require('@nx/jest'); -module.exports = { -projects: getJestProjects() -};`); +module.exports = async () => ({ +projects: await getJestProjectsAsync() +});`); }); }); }); diff --git a/packages/jest/src/generators/configuration/lib/create-jest-config.ts b/packages/jest/src/generators/configuration/lib/create-jest-config.ts index 739f646bbc7a5..07f106f263deb 100644 --- a/packages/jest/src/generators/configuration/lib/create-jest-config.ts +++ b/packages/jest/src/generators/configuration/lib/create-jest-config.ts @@ -129,16 +129,16 @@ export async function createJestConfig( function generateGlobalConfig(tree: Tree, isJS: boolean) { const contents = isJS ? stripIndents` - const { getJestProjects } = require('@nx/jest'); + const { getJestProjectsAsync } = require('@nx/jest'); - module.exports = { - projects: getJestProjects() - };` + module.exports = async () => ({ + projects: await getJestProjectsAsync() + });` : stripIndents` - import { getJestProjects } from '@nx/jest'; + import { getJestProjectsAsync } from '@nx/jest'; - export default { - projects: getJestProjects() - };`; + export default async () => ({ + projects: await getJestProjectsAsync() + });`; tree.write(`jest.config.${isJS ? 'js' : 'ts'}`, contents); } diff --git a/packages/jest/src/generators/configuration/lib/update-jestconfig.ts b/packages/jest/src/generators/configuration/lib/update-jestconfig.ts index f9eef9fa6943e..0fae25ceb72ad 100644 --- a/packages/jest/src/generators/configuration/lib/update-jestconfig.ts +++ b/packages/jest/src/generators/configuration/lib/update-jestconfig.ts @@ -5,8 +5,15 @@ import { readProjectConfiguration, Tree } from '@nx/devkit'; function isUsingUtilityFunction(host: Tree) { const rootConfig = findRootJestConfig(host); + if (!rootConfig) { + return false; + } + + const rootConfigContent = host.read(rootConfig, 'utf-8'); + return ( - rootConfig && host.read(rootConfig).toString().includes('getJestProjects()') + rootConfigContent.includes('getJestProjects()') || + rootConfigContent.includes('getJestProjectsAsync()') ); } diff --git a/packages/jest/src/plugins/plugin.spec.ts b/packages/jest/src/plugins/plugin.spec.ts index 2eb0db233d4b0..8595e35b320c2 100644 --- a/packages/jest/src/plugins/plugin.spec.ts +++ b/packages/jest/src/plugins/plugin.spec.ts @@ -11,6 +11,7 @@ describe('@nx/jest/plugin', () => { beforeEach(async () => { tempFs = new TempFs('test'); + process.chdir(tempFs.tempDir); context = { nxJsonConfiguration: { namedInputs: { diff --git a/packages/jest/src/plugins/plugin.ts b/packages/jest/src/plugins/plugin.ts index f72090b4f44ea..b181e0bfed8d1 100644 --- a/packages/jest/src/plugins/plugin.ts +++ b/packages/jest/src/plugins/plugin.ts @@ -12,7 +12,7 @@ import { dirname, join, relative, resolve } from 'path'; import { readTargetDefaultsForTarget } from 'nx/src/project-graph/utils/project-configuration-utils'; import { getNamedInputs } from '@nx/devkit/src/utils/get-named-inputs'; -import { existsSync, readdirSync } from 'fs'; +import { existsSync, readdirSync, readFileSync } from 'fs'; import { readConfig } from 'jest-config'; import { projectGraphCacheDirectory } from 'nx/src/utils/cache-directory'; import { calculateHashForCreateNodes } from '@nx/devkit/src/utils/calculate-hash-for-create-nodes'; @@ -82,6 +82,17 @@ export const createNodes: CreateNodes = [ } } + const jestConfigContent = readFileSync( + resolve(context.workspaceRoot, configFilePath), + 'utf-8' + ); + if (jestConfigContent.includes('getJestProjectsAsync()')) { + // The `getJestProjectsAsync` function uses the project graph, which leads to a + // circular dependency. We can skip this since it's no intended to be used for + // an Nx project. + return {}; + } + options = normalizeOptions(options); const hash = calculateHashForCreateNodes(projectRoot, options, context); diff --git a/packages/jest/src/utils/config/get-jest-projects.spec.ts b/packages/jest/src/utils/config/get-jest-projects.spec.ts index 6cc89ea246cd3..626b82b0b9968 100644 --- a/packages/jest/src/utils/config/get-jest-projects.spec.ts +++ b/packages/jest/src/utils/config/get-jest-projects.spec.ts @@ -1,6 +1,11 @@ -import { getJestProjects } from './get-jest-projects'; +import type { + ProjectConfiguration, + ProjectGraph, + WorkspaceJsonConfiguration, +} from '@nx/devkit'; +import * as devkit from '@nx/devkit'; import * as Workspace from 'nx/src/project-graph/file-utils'; -import type { WorkspaceJsonConfiguration } from '@nx/devkit'; +import { getJestProjects, getJestProjectsAsync } from './get-jest-projects'; describe('getJestProjects', () => { test('single project', () => { @@ -180,3 +185,315 @@ describe('getJestProjects', () => { expect(getJestProjects()).toEqual(expectedResults); }); }); + +describe('getJestProjectsAsync', () => { + let projectGraph: ProjectGraph; + + function addProject(name: string, config: ProjectConfiguration): void { + projectGraph.nodes[name] = { name, type: 'app', data: config }; + } + + beforeEach(() => { + projectGraph = { nodes: {}, dependencies: {} }; + jest + .spyOn(devkit, 'createProjectGraphAsync') + .mockReturnValue(Promise.resolve(projectGraph)); + }); + + test('single project', async () => { + addProject('test-1', { + root: 'blah', + targets: { + test: { + executor: '@nx/jest:jest', + options: { + jestConfig: 'test/jest/config/location/jest.config.js', + }, + }, + }, + }); + const expectedResults = [ + '/test/jest/config/location/jest.config.js', + ]; + expect(await getJestProjectsAsync()).toEqual(expectedResults); + }); + + test('custom target name', async () => { + addProject('test-1', { + root: 'blah', + targets: { + 'test-with-jest': { + executor: '@nx/jest:jest', + options: { + jestConfig: 'test/jest/config/location/jest.config.js', + }, + }, + }, + }); + const expectedResults = [ + '/test/jest/config/location/jest.config.js', + ]; + expect(await getJestProjectsAsync()).toEqual(expectedResults); + }); + + test('root project', async () => { + addProject('test-1', { + root: '.', + targets: { + test: { + executor: '@nx/jest:jest', + options: { + jestConfig: 'jest.config.app.js', + }, + }, + }, + }); + const expectedResults = ['/jest.config.app.js']; + expect(await getJestProjectsAsync()).toEqual(expectedResults); + }); + + test('configuration set with unique jestConfig', async () => { + addProject('test-1', { + root: 'blah', + targets: { + 'test-with-jest': { + executor: '@nx/jest:jest', + options: { + jestConfig: 'test/jest/config/location/jest.config.js', + }, + configurations: { + prod: { + jestConfig: 'configuration-specific/jest.config.js', + }, + }, + }, + }, + }); + const expectedResults = [ + '/test/jest/config/location/jest.config.js', + '/configuration-specific/jest.config.js', + ]; + expect(await getJestProjectsAsync()).toEqual(expectedResults); + }); + + test('configuration, set with same jestConfig on configuration', async () => { + addProject('test', { + root: 'blah', + targets: { + 'test-with-jest': { + executor: '@nx/jest:jest', + options: { + jestConfig: 'test/jest/config/location/jest.config.js', + }, + configurations: { + prod: { + jestConfig: 'test/jest/config/location/jest.config.js', + }, + }, + }, + }, + }); + const expectedResults = [ + '/test/jest/config/location/jest.config.js', + ]; + expect(await getJestProjectsAsync()).toEqual(expectedResults); + }); + + test('other projects and targets that do not use the nrwl jest test runner', async () => { + addProject('otherTarget', { + root: 'test', + targets: { + test: { + executor: 'something else', + options: {}, + }, + }, + }); + addProject('test', { + root: 'blah', + targets: { + 'test-with-jest': { + executor: 'something else', + options: { + jestConfig: 'something random', + }, + configurations: { + prod: { + jestConfig: 'configuration-specific/jest.config.js', + }, + }, + }, + }, + }); + const expectedResults = []; + expect(await getJestProjectsAsync()).toEqual(expectedResults); + }); + + test.each` + command + ${'jest'} + ${'npx jest'} + ${'yarn jest'} + ${'pnpm jest'} + ${'pnpm dlx jest'} + ${'echo "foo" && jest'} + ${'echo "foo" && npx jest'} + ${'jest && echo "foo"'} + ${'npx jest && echo "foo"'} + ${'echo "foo" && jest && echo "bar"'} + ${'echo "foo" && npx jest && echo "bar"'} + `( + 'targets with nx:run-commands executor running "$command"', + async ({ command }) => { + addProject('test-1', { + root: 'projects/test-1', + targets: { + test: { + executor: 'nx:run-commands', + options: { command }, + }, + }, + }); + const expectedResults = ['/projects/test-1']; + expect(await getJestProjectsAsync()).toEqual(expectedResults); + } + ); + + test.each` + command + ${'jest'} + ${'npx jest'} + ${'yarn jest'} + ${'pnpm jest'} + ${'pnpm dlx jest'} + ${'echo "foo" && jest'} + ${'echo "foo" && npx jest'} + ${'jest && echo "foo"'} + ${'npx jest && echo "foo"'} + ${'echo "foo" && jest && echo "bar"'} + ${'echo "foo" && npx jest && echo "bar"'} + `( + 'targets with nx:run-commands executor using "commands" option and running "$command"', + async ({ command }) => { + addProject('test-1', { + root: 'projects/test-1', + targets: { + test: { + executor: 'nx:run-commands', + options: { commands: [command] }, + }, + }, + }); + const expectedResults = ['/projects/test-1']; + expect(await getJestProjectsAsync()).toEqual(expectedResults); + } + ); + + test.each` + command + ${'jest'} + ${'npx jest'} + ${'yarn jest'} + ${'pnpm jest'} + ${'pnpm dlx jest'} + ${'echo "foo" && jest'} + ${'echo "foo" && npx jest'} + ${'jest && echo "foo"'} + ${'npx jest && echo "foo"'} + ${'echo "foo" && jest && echo "bar"'} + ${'echo "foo" && npx jest && echo "bar"'} + `( + 'targets with nx:run-commands executor using "commands" option using the object notation and running "$command"', + async ({ command }) => { + addProject('test-1', { + root: 'projects/test-1', + targets: { + test: { + executor: 'nx:run-commands', + options: { commands: [{ command }] }, + }, + }, + }); + const expectedResults = ['/projects/test-1']; + expect(await getJestProjectsAsync()).toEqual(expectedResults); + } + ); + + test.each` + command | cwd + ${'jest --config projects/test-1/jest.config.ts'} | ${'.'} + ${'npx jest --config projects/test-1/jest.config.ts'} | ${'.'} + ${'jest --config jest.config.ts'} | ${undefined} + ${'npx jest --config jest.config.ts'} | ${undefined} + ${'jest --config jest.config.ts'} | ${'projects/test-1'} + ${'npx jest --config jest.config.ts'} | ${'projects/test-1'} + ${'echo "foo" && jest --config jest.config.ts'} | ${undefined} + ${'echo "foo" && npx jest --config jest.config.ts'} | ${undefined} + ${'jest --config jest.config.ts && echo "foo"'} | ${undefined} + ${'npx jest --config jest.config.ts && echo "foo"'} | ${undefined} + ${'echo "foo" && jest --config jest.config.ts && echo "bar"'} | ${undefined} + ${'echo "foo" && npx jest --config jest.config.ts && echo "bar"'} | ${undefined} + `( + 'targets with nx:run-commands executor running "$command" at "$cwd"', + async ({ command, cwd }) => { + addProject('test-1', { + root: 'projects/test-1', + targets: { + test: { + executor: 'nx:run-commands', + options: { command, cwd }, + }, + }, + }); + const expectedResults = ['/projects/test-1/jest.config.ts']; + expect(await getJestProjectsAsync()).toEqual(expectedResults); + } + ); + + test('targets with nx:run-commands executor with a command with multiple "jest" runs', async () => { + addProject('test-1', { + root: 'projects/test-1', + targets: { + test: { + executor: 'nx:run-commands', + options: { + command: 'jest && jest --config jest1.config.ts', + }, + }, + }, + }); + const expectedResults = [ + '/projects/test-1', + '/projects/test-1/jest1.config.ts', + ]; + expect(await getJestProjectsAsync()).toEqual(expectedResults); + }); + + test('projects with targets using both executors', async () => { + addProject('test-1', { + root: 'projects/test-1', + targets: { + test: { + executor: '@nx/jest:jest', + options: { + jestConfig: 'projects/test-1/jest.config.js', + }, + }, + }, + }); + addProject('test-2', { + root: 'projects/test-2', + targets: { + test: { + executor: 'nx:run-commands', + options: { command: 'jest' }, + }, + }, + }); + const expectedResults = [ + '/projects/test-1/jest.config.js', + '/projects/test-2', + ]; + expect(await getJestProjectsAsync()).toEqual(expectedResults); + }); +}); diff --git a/packages/jest/src/utils/config/get-jest-projects.ts b/packages/jest/src/utils/config/get-jest-projects.ts index 910e6f6bec7be..6f08f9575cb3d 100644 --- a/packages/jest/src/utils/config/get-jest-projects.ts +++ b/packages/jest/src/utils/config/get-jest-projects.ts @@ -1,6 +1,8 @@ -import { join } from 'path'; import type { ProjectsConfigurations } from '@nx/devkit'; +import { createProjectGraphAsync, type TargetConfiguration } from '@nx/devkit'; import { readWorkspaceConfig } from 'nx/src/project-graph/file-utils'; +import { join } from 'path'; +import * as yargs from 'yargs-parser'; function getJestConfigProjectPath(projectJestConfigPath: string): string { return join('', projectJestConfigPath); @@ -10,7 +12,8 @@ function getJestConfigProjectPath(projectJestConfigPath: string): string { * Get a list of paths to all the jest config files * using the Nx Jest executor. * - * This is used to configure Jest multi-project support. + * This is used to configure Jest multi-project support. To support projects + * using inferred targets @see getJestProjectsAsync * * To add a project not using the Nx Jest executor: * export default { @@ -68,3 +71,146 @@ export function getNestedJestProjects() { const allProjects = getJestProjects(); return ['/node_modules/']; } + +/** + * Get a list of paths to all the jest config files + * using the Nx Jest executor and `@nx/run:commands` + * running `jest`. + * + * This is used to configure Jest multi-project support. + * + * To add a project not using the Nx Jest executor: + * export default async () => ({ + * projects: [...(await getJestProjectsAsync()), '/path/to/jest.config.ts']; + * }); + * + **/ +export async function getJestProjectsAsync() { + const graph = await createProjectGraphAsync({ + exitOnError: false, + resetDaemonClient: true, + }); + const jestConfigurations = new Set(); + for (const node of Object.values(graph.nodes)) { + const projectConfig = node.data; + if (!projectConfig.targets) { + continue; + } + for (const targetConfiguration of Object.values(projectConfig.targets)) { + if ( + targetConfiguration.executor === '@nx/jest:jest' || + targetConfiguration.executor === '@nrwl/jest:jest' + ) { + collectJestConfigFromJestExecutor( + targetConfiguration, + jestConfigurations + ); + } else if (targetConfiguration.executor === 'nx:run-commands') { + collectJestConfigFromRunCommandsExecutor( + targetConfiguration, + projectConfig.root, + jestConfigurations + ); + } + } + } + + return Array.from(jestConfigurations); +} + +function collectJestConfigFromJestExecutor( + targetConfiguration: TargetConfiguration, + jestConfigurations: Set +): void { + if (targetConfiguration.options?.jestConfig) { + jestConfigurations.add( + getJestConfigProjectPath(targetConfiguration.options.jestConfig) + ); + } + if (targetConfiguration.configurations) { + for (const configurationObject of Object.values( + targetConfiguration.configurations + )) { + if (configurationObject.jestConfig) { + jestConfigurations.add( + getJestConfigProjectPath(configurationObject.jestConfig) + ); + } + } + } +} + +function collectJestConfigFromRunCommandsExecutor( + targetConfiguration: TargetConfiguration, + projectRoot: string, + jestConfigurations: Set +): void { + if (targetConfiguration.options?.command) { + collectJestConfigFromCommand( + targetConfiguration.options.command, + targetConfiguration.options.cwd ?? projectRoot, + jestConfigurations + ); + } else if (targetConfiguration.options?.commands) { + for (const command of targetConfiguration.options.commands) { + const commandScript = + typeof command === 'string' ? command : command.command; + collectJestConfigFromCommand( + commandScript, + targetConfiguration.options.cwd ?? projectRoot, + jestConfigurations + ); + } + } + + if (targetConfiguration.configurations) { + for (const configurationObject of Object.values( + targetConfiguration.configurations + )) { + if (configurationObject.command) { + collectJestConfigFromCommand( + configurationObject.command, + configurationObject.cwd ?? projectRoot, + jestConfigurations + ); + } else if (configurationObject.commands) { + for (const command of configurationObject.commands) { + const commandScript = + typeof command === 'string' ? command : command.command; + collectJestConfigFromCommand( + commandScript, + configurationObject.cwd ?? projectRoot, + jestConfigurations + ); + } + } + } + } +} + +function collectJestConfigFromCommand( + command: string, + cwd: string, + jestConfigurations: Set +) { + const jestCommandRegex = + /(?<=^|&)(?:[^&\r\n\s]* )*jest(?: [^&\r\n\s]*)*(?=$|&)/g; + const matches = command.match(jestCommandRegex); + if (!matches) { + return; + } + + for (const match of matches) { + const parsed = yargs(match, { + configuration: { 'strip-dashed': true }, + string: ['config'], + }); + if (parsed.config) { + jestConfigurations.add( + getJestConfigProjectPath(join(cwd, parsed.config)) + ); + } else { + jestConfigurations.add(getJestConfigProjectPath(cwd)); + } + } +} diff --git a/packages/workspace/src/generators/move/lib/update-jest-config.spec.ts b/packages/workspace/src/generators/move/lib/update-jest-config.spec.ts index c376cad926415..ea04c6875ea6c 100644 --- a/packages/workspace/src/generators/move/lib/update-jest-config.spec.ts +++ b/packages/workspace/src/generators/move/lib/update-jest-config.spec.ts @@ -66,7 +66,7 @@ describe('updateJestConfig', () => { expect(jestConfigAfter).toContain( `coverageDirectory: '../coverage/my-destination'` ); - expect(rootJestConfigAfter).toContain('getJestProjects()'); + expect(rootJestConfigAfter).toContain('getJestProjectsAsync()'); }); it('should update the name and dir correctly when moving to a nested dir', async () => { @@ -142,7 +142,7 @@ describe('updateJestConfig', () => { expect(jestConfigAfter).toContain( `coverageDirectory: '../coverage/other/test/dir/my-destination'` ); - expect(rootJestConfigAfter).toContain('getJestProjects()'); + expect(rootJestConfigAfter).toContain('getJestProjectsAsync()'); }); it('updates the root config if not using `getJestProjects()`', async () => { diff --git a/packages/workspace/src/generators/move/lib/update-jest-config.ts b/packages/workspace/src/generators/move/lib/update-jest-config.ts index d087d35a89d26..c5a76a0a52f3f 100644 --- a/packages/workspace/src/generators/move/lib/update-jest-config.ts +++ b/packages/workspace/src/generators/move/lib/update-jest-config.ts @@ -1,6 +1,7 @@ import { ProjectConfiguration, Tree } from '@nx/devkit'; import * as path from 'path'; import { NormalizedSchema } from '../schema'; +import { findRootJestConfig } from '../../utils/jest-config'; /** * Updates the project name and coverage folder in the jest.config.js if it exists @@ -58,9 +59,9 @@ export function updateJestConfig( } // update root jest.config.ts - const rootJestConfigPath = '/jest.config.ts'; + const rootJestConfigPath = findRootJestConfig(tree); - if (!tree.exists(rootJestConfigPath)) { + if (!rootJestConfigPath || !tree.exists(rootJestConfigPath)) { return; } @@ -68,7 +69,8 @@ export function updateJestConfig( const oldRootJestConfigContent = tree.read(rootJestConfigPath, 'utf-8'); const usingJestProjects = - oldRootJestConfigContent.includes('getJestProjects()'); + oldRootJestConfigContent.includes('getJestProjects()') || + oldRootJestConfigContent.includes('getJestProjectsAsync()'); const newRootJestConfigContent = oldRootJestConfigContent.replace( findProject, diff --git a/packages/workspace/src/generators/remove/lib/update-jest-config.ts b/packages/workspace/src/generators/remove/lib/update-jest-config.ts index 89e1f0e0f53e5..1cf5aef33a8f5 100644 --- a/packages/workspace/src/generators/remove/lib/update-jest-config.ts +++ b/packages/workspace/src/generators/remove/lib/update-jest-config.ts @@ -14,11 +14,22 @@ import type { } from 'typescript'; import { join } from 'path'; import { ensureTypescript } from '../../../utilities/typescript'; +import { findRootJestConfig } from '../../utils/jest-config'; let tsModule: typeof import('typescript'); function isUsingUtilityFunction(host: Tree) { - return host.read('jest.config.ts').toString().includes('getJestProjects()'); + const rootConfigPath = findRootJestConfig(host); + if (!rootConfigPath) { + return false; + } + + const rootConfig = host.read(rootConfigPath, 'utf-8'); + + return ( + rootConfig.includes('getJestProjects()') || + rootConfig.includes('getJestProjectsAsync()') + ); } /** @@ -27,7 +38,12 @@ function isUsingUtilityFunction(host: Tree) { * in that case we do not need to edit it to remove it **/ function isMonorepoConfig(tree: Tree) { - return tree.read('jest.config.ts', 'utf-8').includes('projects:'); + const rootConfigPath = findRootJestConfig(tree); + if (!rootConfigPath) { + return false; + } + + return tree.read(rootConfigPath, 'utf-8').includes('projects:'); } /** @@ -50,8 +66,10 @@ export function updateJestConfig( } = tsModule; const projectToRemove = schema.projectName; + const rootConfigPath = findRootJestConfig(tree); + if ( - !tree.exists('jest.config.ts') || + !tree.exists(rootConfigPath) || !tree.exists(join(projectConfig.root, 'jest.config.ts')) || isUsingUtilityFunction(tree) || !isMonorepoConfig(tree) @@ -59,9 +77,9 @@ export function updateJestConfig( return; } - const contents = tree.read('jest.config.ts', 'utf-8'); + const contents = tree.read(rootConfigPath, 'utf-8'); const sourceFile = createSourceFile( - 'jest.config.ts', + rootConfigPath, contents, ScriptTarget.Latest ); @@ -104,7 +122,7 @@ export function updateJestConfig( : project.getStart(sourceFile); tree.write( - 'jest.config.ts', + rootConfigPath, applyChangesToString(contents, [ { type: ChangeType.Delete, diff --git a/packages/workspace/src/generators/utils/jest-config.ts b/packages/workspace/src/generators/utils/jest-config.ts new file mode 100644 index 0000000000000..91ac15c1a2479 --- /dev/null +++ b/packages/workspace/src/generators/utils/jest-config.ts @@ -0,0 +1,13 @@ +import type { Tree } from '@nx/devkit'; + +export function findRootJestConfig(tree: Tree): string | null { + if (tree.exists('jest.config.js')) { + return 'jest.config.js'; + } + + if (tree.exists('jest.config.ts')) { + return 'jest.config.ts'; + } + + return null; +}