feat(testing): remove tsConfig option from the @nx/jest:jest executor (#30888)

Removes the previously deprecated and unused `tsConfig` option from the
`@nx/jest:jest` executor.

BREAKING CHANGE: The previously deprecated and unused `tsConfig` option
from the `@nx/jest:jest` executor was removed.
This commit is contained in:
Leosvel Pérez Espinosa 2025-04-29 17:08:59 +02:00 committed by GitHub
parent c8d89e2f2a
commit 9ae691ede8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 549 additions and 30 deletions

View File

@ -2219,6 +2219,16 @@
}
},
"migrations": {
"/nx-api/jest/migrations/remove-tsconfig-option-from-jest-executor": {
"description": "Remove the previously deprecated and unused `tsConfig` option from the `@nx/jest:jest` executor.",
"file": "generated/packages/jest/migrations/remove-tsconfig-option-from-jest-executor.json",
"hidden": false,
"name": "remove-tsconfig-option-from-jest-executor",
"version": "21.0.0-beta.10",
"originalFilePath": "/packages/jest",
"path": "/nx-api/jest/migrations/remove-tsconfig-option-from-jest-executor",
"type": "migration"
},
"/nx-api/jest/migrations/replace-getJestProjects-with-getJestProjectsAsync-v21": {
"description": "Replace usage of `getJestProjects` with `getJestProjectsAsync`.",
"file": "generated/packages/jest/migrations/replace-getJestProjects-with-getJestProjectsAsync-v21.json",

View File

@ -2202,6 +2202,16 @@
}
],
"migrations": [
{
"description": "Remove the previously deprecated and unused `tsConfig` option from the `@nx/jest:jest` executor.",
"file": "generated/packages/jest/migrations/remove-tsconfig-option-from-jest-executor.json",
"hidden": false,
"name": "remove-tsconfig-option-from-jest-executor",
"version": "21.0.0-beta.10",
"originalFilePath": "/packages/jest",
"path": "jest/migrations/remove-tsconfig-option-from-jest-executor",
"type": "migration"
},
{
"description": "Replace usage of `getJestProjects` with `getJestProjectsAsync`.",
"file": "generated/packages/jest/migrations/replace-getJestProjects-with-getJestProjectsAsync-v21.json",

View File

@ -54,15 +54,10 @@
"type": "string",
"$default": { "$source": "argv", "index": 0 }
},
"tsConfig": {
"description": "The name of the Typescript configuration file.",
"type": "string",
"x-deprecated": "Use the ts-jest configuration options in the jest config file instead."
},
"setupFile": {
"description": "The name of a setup file used by Jest.",
"type": "string",
"x-deprecated": "Use the setupFilesAfterEnv option in the jest config file. https://jestjs.io/docs/en/configuration#setupfilesafterenv-array"
"x-deprecated": "Use the `setupFilesAfterEnv` option in the Jest configuration file instead. See https://jestjs.io/docs/configuration#setupfilesafterenv-array. It will be removed in Nx v22."
},
"bail": {
"alias": "b",

View File

@ -19,7 +19,7 @@
"type": "boolean",
"description": "Skips the setup file required for angular.",
"default": false,
"x-deprecated": "Use `--setup-file` instead."
"x-deprecated": "Use the `setupFile` option instead. It will be removed in Nx v22."
},
"setupFile": {
"type": "string",
@ -56,7 +56,7 @@
"type": "boolean",
"alias": "babel-jest",
"description": "Use `babel-jest` instead of `ts-jest`.",
"x-deprecated": "Use `--compiler=babel` instead.",
"x-deprecated": "Use the `compiler` option instead. It will be removed in Nx v22.",
"default": false
},
"skipFormat": {

View File

@ -0,0 +1,12 @@
{
"name": "remove-tsconfig-option-from-jest-executor",
"version": "21.0.0-beta.10",
"description": "Remove the previously deprecated and unused `tsConfig` option from the `@nx/jest:jest` executor.",
"implementation": "/packages/jest/src/migrations/update-21-0-0/remove-tsconfig-option-from-jest-executor.ts",
"aliases": [],
"hidden": false,
"path": "/packages/jest",
"schema": null,
"type": "migration",
"examplesFile": "#### Remove `tsConfig` Option from Jest Executor\n\nRemoves the previously deprecated and unused `tsConfig` option from the `@nx/jest:jest` executor configuration in all projects.\n\n#### Examples\n\nRemove the option from the project configuration:\n\n{% tabs %}\n{% tab label=\"Before\" %}\n\n```json {% fileName=\"apps/myapp/project.json\" highlightLines=[7] %}\n{\n \"targets\": {\n \"test\": {\n \"executor\": \"@nx/jest:jest\",\n \"options\": {\n \"jestConfig\": \"apps/myapp/jest.config.ts\",\n \"tsConfig\": \"apps/myapp/tsconfig.spec.json\"\n }\n }\n }\n}\n```\n\n{% /tab %}\n\n{% tab label=\"After\" %}\n\n```json {% fileName=\"apps/myapp/project.json\" %}\n{\n \"targets\": {\n \"test\": {\n \"executor\": \"@nx/jest:jest\",\n \"options\": {\n \"jestConfig\": \"apps/myapp/jest.config.ts\"\n }\n }\n }\n}\n```\n\n{% /tab %}\n{% /tabs %}\n\nRemove the option from a target default using the `@nx/jest:jest` executor:\n\n{% tabs %}\n{% tab label=\"Before\" %}\n\n```json {% fileName=\"nx.json\" highlightLines=[7] %}\n{\n \"targetDefaults\": {\n \"test\": {\n \"executor\": \"@nx/jest:jest\",\n \"options\": {\n \"jestConfig\": \"{projectRoot}/jest.config.ts\",\n \"tsConfig\": \"{projectRoot}/tsconfig.spec.json\"\n }\n }\n }\n}\n```\n\n{% /tab %}\n\n{% tab label=\"After\" %}\n\n```json {% fileName=\"nx.json\" %}\n{\n \"targetDefaults\": {\n \"test\": {\n \"executor\": \"@nx/jest:jest\",\n \"options\": {\n \"jestConfig\": \"{projectRoot}/jest.config.ts\"\n }\n }\n }\n}\n```\n\n{% /tab %}\n{% /tabs %}\n\nRemove the option from a target default using the `@nx/jest:jest` executor as the key:\n\n{% tabs %}\n{% tab label=\"Before\" %}\n\n```json {% fileName=\"nx.json\" highlightLines=[6] %}\n{\n \"targetDefaults\": {\n \"@nx/jest:jest\": {\n \"options\": {\n \"jestConfig\": \"{projectRoot}/jest.config.ts\",\n \"tsConfig\": \"{projectRoot}/tsconfig.spec.json\"\n }\n }\n }\n}\n```\n\n{% /tab %}\n\n{% tab label=\"After\" %}\n\n```json {% fileName=\"nx.json\" %}\n{\n \"targetDefaults\": {\n \"@nx/jest:jest\": {\n \"options\": {\n \"jestConfig\": \"{projectRoot}/jest.config.ts\"\n }\n }\n }\n}\n```\n\n{% /tab %}\n{% /tabs %}\n"
}

View File

@ -2,7 +2,9 @@ import { configurationGenerator } from './src/generators/configuration/configura
export { configurationGenerator };
// Exported for backwards compatibility in case a plugin is using the old name.
/** @deprecated Use `configurationGenerator` instead. */
/**
* @deprecated Use `configurationGenerator` instead. It will be removed in Nx v22.
*/
export const jestProjectGenerator = configurationGenerator;
export {

View File

@ -11,6 +11,11 @@
"version": "21.0.0-beta.9",
"description": "Replace usage of `getJestProjects` with `getJestProjectsAsync`.",
"implementation": "./src/migrations/update-21-0-0/replace-getJestProjects-with-getJestProjectsAsync"
},
"remove-tsconfig-option-from-jest-executor": {
"version": "21.0.0-beta.10",
"description": "Remove the previously deprecated and unused `tsConfig` option from the `@nx/jest:jest` executor.",
"implementation": "./src/migrations/update-21-0-0/remove-tsconfig-option-from-jest-executor"
}
},
"packageJsonUpdates": {

View File

@ -6,10 +6,6 @@ export interface JestExecutorOptions {
detectLeaks?: boolean;
jestConfig: string;
testFile?: string;
/**
* @deprecated set in the jest config file instead
**/
setupFile?: string;
bail?: boolean | number;
ci?: boolean;
color?: boolean;
@ -41,4 +37,11 @@ export interface JestExecutorOptions {
watchAll?: boolean;
testLocationInResults?: boolean;
testTimeout?: number;
/**
* @deprecated Use the `setupFilesAfterEnv` option in the Jest configuration
* file instead. See https://jestjs.io/docs/configuration#setupfilesafterenv-array.
* It will be removed in Nx v22.
*/
setupFile?: string;
}

View File

@ -59,15 +59,10 @@
"index": 0
}
},
"tsConfig": {
"description": "The name of the Typescript configuration file.",
"type": "string",
"x-deprecated": "Use the ts-jest configuration options in the jest config file instead."
},
"setupFile": {
"description": "The name of a setup file used by Jest.",
"type": "string",
"x-deprecated": "Use the setupFilesAfterEnv option in the jest config file. https://jestjs.io/docs/en/configuration#setupfilesafterenv-array"
"x-deprecated": "Use the `setupFilesAfterEnv` option in the Jest configuration file instead. See https://jestjs.io/docs/configuration#setupfilesafterenv-array. It will be removed in Nx v22."
},
"bail": {
"alias": "b",

View File

@ -2,10 +2,6 @@ export interface JestProjectSchema {
project: string;
targetName?: string;
supportTsx?: boolean;
/**
* @deprecated use setupFile instead
*/
skipSetupFile?: boolean;
setupFile?:
| 'angular'
| 'web-components'
@ -14,12 +10,7 @@ export interface JestProjectSchema {
| 'none';
skipSerializers?: boolean;
testEnvironment?: 'node' | 'jsdom' | 'none';
/**
* @deprecated use compiler: "babel" instead
*/
babelJest?: boolean;
skipFormat?: boolean;
addPlugin?: boolean;
compiler?: 'tsc' | 'babel' | 'swc';
skipPackageJson?: boolean;
@ -30,6 +21,15 @@ export interface JestProjectSchema {
* @internal
*/
addExplicitTargets?: boolean;
/**
* @deprecated Use the `compiler` option instead. It will be removed in Nx v22.
*/
babelJest?: boolean;
/**
* @deprecated Use the `setupFile` option instead. It will be removed in Nx v22.
*/
skipSetupFile?: boolean;
}
export type NormalizedJestProjectSchema = JestProjectSchema & {

View File

@ -18,7 +18,7 @@
"type": "boolean",
"description": "Skips the setup file required for angular.",
"default": false,
"x-deprecated": "Use `--setup-file` instead."
"x-deprecated": "Use the `setupFile` option instead. It will be removed in Nx v22."
},
"setupFile": {
"type": "string",
@ -55,7 +55,7 @@
"type": "boolean",
"alias": "babel-jest",
"description": "Use `babel-jest` instead of `ts-jest`.",
"x-deprecated": "Use `--compiler=babel` instead.",
"x-deprecated": "Use the `compiler` option instead. It will be removed in Nx v22.",
"default": false
},
"skipFormat": {

View File

@ -0,0 +1,120 @@
#### Remove `tsConfig` Option from Jest Executor
Removes the previously deprecated and unused `tsConfig` option from the `@nx/jest:jest` executor configuration in all projects.
#### Examples
Remove the option from the project configuration:
{% tabs %}
{% tab label="Before" %}
```json {% fileName="apps/myapp/project.json" highlightLines=[7] %}
{
"targets": {
"test": {
"executor": "@nx/jest:jest",
"options": {
"jestConfig": "apps/myapp/jest.config.ts",
"tsConfig": "apps/myapp/tsconfig.spec.json"
}
}
}
}
```
{% /tab %}
{% tab label="After" %}
```json {% fileName="apps/myapp/project.json" %}
{
"targets": {
"test": {
"executor": "@nx/jest:jest",
"options": {
"jestConfig": "apps/myapp/jest.config.ts"
}
}
}
}
```
{% /tab %}
{% /tabs %}
Remove the option from a target default using the `@nx/jest:jest` executor:
{% tabs %}
{% tab label="Before" %}
```json {% fileName="nx.json" highlightLines=[7] %}
{
"targetDefaults": {
"test": {
"executor": "@nx/jest:jest",
"options": {
"jestConfig": "{projectRoot}/jest.config.ts",
"tsConfig": "{projectRoot}/tsconfig.spec.json"
}
}
}
}
```
{% /tab %}
{% tab label="After" %}
```json {% fileName="nx.json" %}
{
"targetDefaults": {
"test": {
"executor": "@nx/jest:jest",
"options": {
"jestConfig": "{projectRoot}/jest.config.ts"
}
}
}
}
```
{% /tab %}
{% /tabs %}
Remove the option from a target default using the `@nx/jest:jest` executor as the key:
{% tabs %}
{% tab label="Before" %}
```json {% fileName="nx.json" highlightLines=[6] %}
{
"targetDefaults": {
"@nx/jest:jest": {
"options": {
"jestConfig": "{projectRoot}/jest.config.ts",
"tsConfig": "{projectRoot}/tsconfig.spec.json"
}
}
}
}
```
{% /tab %}
{% tab label="After" %}
```json {% fileName="nx.json" %}
{
"targetDefaults": {
"@nx/jest:jest": {
"options": {
"jestConfig": "{projectRoot}/jest.config.ts"
}
}
}
}
```
{% /tab %}
{% /tabs %}

View File

@ -0,0 +1,265 @@
import 'nx/src/internal-testing-utils/mock-project-graph';
import {
addProjectConfiguration,
readJson,
readProjectConfiguration,
updateJson,
type NxJsonConfiguration,
type ProjectConfiguration,
type Tree,
} from '@nx/devkit';
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import migration from './remove-tsconfig-option-from-jest-executor';
describe('remove-tsconfig-option-from-jest-executor', () => {
let tree: Tree;
beforeEach(() => {
tree = createTreeWithEmptyWorkspace();
});
it('should remove tsConfig from default options', async () => {
addProjectConfiguration(tree, 'app1', {
root: 'apps/app1',
sourceRoot: 'apps/app1/src',
projectType: 'application',
targets: {
test: {
executor: '@nx/jest:jest',
options: {
tsConfig: 'apps/app1/tsconfig.json',
jestConfig: 'apps/app1/jest.config.ts',
},
},
},
});
await migration(tree);
const updatedConfig = readProjectConfiguration(tree, 'app1');
expect(updatedConfig.targets.test.options).toStrictEqual({
jestConfig: 'apps/app1/jest.config.ts',
});
});
it('should remove tsConfig from configurations', async () => {
addProjectConfiguration(tree, 'app1', {
root: 'apps/app1',
sourceRoot: 'apps/app1/src',
projectType: 'application',
targets: {
test: {
executor: '@nx/jest:jest',
options: {
jestConfig: 'apps/app1/jest.config.ts',
},
configurations: {
production: {
tsConfig: 'apps/app1/tsconfig.prod.json',
codeCoverage: true,
},
},
},
},
});
await migration(tree);
const updatedConfig = readProjectConfiguration(tree, 'app1');
expect(updatedConfig.targets.test.configurations.production).toStrictEqual({
codeCoverage: true,
});
});
it('should handle projects without the deprecated options', async () => {
const intialProjectConfig: ProjectConfiguration = {
root: 'apps/app1',
sourceRoot: 'apps/app1/src',
projectType: 'application',
targets: {
test: {
executor: '@nx/jest:jest',
options: {
jestConfig: 'apps/app1/jest.config.ts',
},
},
},
};
addProjectConfiguration(tree, 'app1', intialProjectConfig);
await migration(tree);
const updatedConfig = readProjectConfiguration(tree, 'app1');
expect(updatedConfig.targets.test).toStrictEqual(
intialProjectConfig.targets.test
);
});
it('should handle projects with multiple targets using jest executor', async () => {
addProjectConfiguration(tree, 'app1', {
root: 'apps/app1',
sourceRoot: 'apps/app1/src',
projectType: 'application',
targets: {
test: {
executor: '@nx/jest:jest',
options: {
jestConfig: 'apps/app1/jest.config.ts',
tsConfig: 'apps/app1/tsconfig.json',
},
},
test2: {
executor: '@nx/jest:jest',
options: {
jestConfig: 'apps/app1/jest.config.ts',
tsConfig: 'apps/app1/tsconfig.json',
},
},
},
});
await migration(tree);
const updatedConfig = readProjectConfiguration(tree, 'app1');
expect(updatedConfig.targets.test.options).toStrictEqual({
jestConfig: 'apps/app1/jest.config.ts',
});
expect(updatedConfig.targets.test2.options).toStrictEqual({
jestConfig: 'apps/app1/jest.config.ts',
});
});
it('should remove tsConfig option in nx.json target defaults for a target with the jest executor', async () => {
updateJson<NxJsonConfiguration>(tree, 'nx.json', (json) => {
json.targetDefaults ??= {};
json.targetDefaults.test = {
executor: '@nx/jest:jest',
options: {
jestConfig: '{projectRoot}/jest.config.ts',
tsConfig: '{projectRoot}/tsconfig.json',
},
configurations: {
production: {
tsConfig: '{projectRoot}/tsconfig.prod.json',
codeCoverage: true,
},
},
};
return json;
});
await migration(tree);
const nxJson = readJson<NxJsonConfiguration>(tree, 'nx.json');
expect(nxJson.targetDefaults.test.options).toStrictEqual({
jestConfig: '{projectRoot}/jest.config.ts',
});
expect(nxJson.targetDefaults.test.configurations.production).toStrictEqual({
codeCoverage: true,
});
});
it('should remove tsConfig option in nx.json target defaults for the jest executor', async () => {
updateJson<NxJsonConfiguration>(tree, 'nx.json', (json) => {
json.targetDefaults ??= {};
json.targetDefaults['@nx/jest:jest'] = {
options: {
jestConfig: '{projectRoot}/jest.config.ts',
tsConfig: '{projectRoot}/tsconfig.json',
},
configurations: {
production: {
tsConfig: '{projectRoot}/tsconfig.prod.json',
codeCoverage: true,
},
},
};
return json;
});
await migration(tree);
const nxJson = readJson<NxJsonConfiguration>(tree, 'nx.json');
expect(nxJson.targetDefaults['@nx/jest:jest'].options).toStrictEqual({
jestConfig: '{projectRoot}/jest.config.ts',
});
expect(
nxJson.targetDefaults['@nx/jest:jest'].configurations.production
).toStrictEqual({
codeCoverage: true,
});
});
it('should remove empty options and configurations objects from project configuration', async () => {
addProjectConfiguration(tree, 'app1', {
root: 'apps/app1',
sourceRoot: 'apps/app1/src',
projectType: 'application',
targets: {
test: {
executor: '@nx/jest:jest',
options: {
tsConfig: 'apps/app1/tsconfig.json',
},
configurations: {
production: {
tsConfig: 'apps/app1/tsconfig.prod.json',
},
},
},
},
});
await migration(tree);
const updatedConfig = readProjectConfiguration(tree, 'app1');
expect(updatedConfig.targets.test.options).toBeUndefined();
expect(updatedConfig.targets.test.configurations).toBeUndefined();
});
it('should remove empty targetDefault object from nx.json when using a target name as the key', async () => {
updateJson<NxJsonConfiguration>(tree, 'nx.json', (json) => {
json.targetDefaults = {};
json.targetDefaults.test = {
executor: '@nx/jest:jest',
options: {
tsConfig: '{projectRoot}/tsconfig.json',
},
configurations: {
production: {
tsConfig: '{projectRoot}/tsconfig.prod.json',
},
},
};
return json;
});
await migration(tree);
const nxJson = readJson<NxJsonConfiguration>(tree, 'nx.json');
expect(nxJson.targetDefaults).toBeUndefined();
});
it('should remove empty targetDefault object from nx.json when using the jest executor as the key', async () => {
updateJson<NxJsonConfiguration>(tree, 'nx.json', (json) => {
json.targetDefaults = {};
json.targetDefaults['@nx/jest:jest'] = {
options: {
tsConfig: '{projectRoot}/tsconfig.json',
},
configurations: {
production: {
tsConfig: '{projectRoot}/tsconfig.prod.json',
},
},
};
return json;
});
await migration(tree);
const nxJson = readJson<NxJsonConfiguration>(tree, 'nx.json');
expect(nxJson.targetDefaults).toBeUndefined();
});
});

View File

@ -0,0 +1,102 @@
import {
formatFiles,
readNxJson,
readProjectConfiguration,
type TargetConfiguration,
type Tree,
updateNxJson,
updateProjectConfiguration,
} from '@nx/devkit';
import { forEachExecutorOptions } from '@nx/devkit/src/generators/executor-options-utils';
const EXECUTOR_TO_MIGRATE = '@nx/jest:jest';
export default async function (tree: Tree) {
// update options from project configs
forEachExecutorOptions<{ tsConfig?: string }>(
tree,
EXECUTOR_TO_MIGRATE,
(options, project, target, configuration) => {
if (options.tsConfig === undefined) {
return;
}
const projectConfiguration = readProjectConfiguration(tree, project);
if (configuration) {
updateConfiguration(
projectConfiguration.targets[target],
configuration
);
} else {
updateOptions(projectConfiguration.targets[target]);
}
updateProjectConfiguration(tree, project, projectConfiguration);
}
);
// update options from nx.json target defaults
const nxJson = readNxJson(tree);
if (nxJson.targetDefaults) {
for (const [targetOrExecutor, targetConfig] of Object.entries(
nxJson.targetDefaults
)) {
if (
targetOrExecutor !== EXECUTOR_TO_MIGRATE &&
targetConfig.executor !== EXECUTOR_TO_MIGRATE
) {
continue;
}
if (targetConfig.options) {
updateOptions(targetConfig);
}
Object.keys(targetConfig.configurations ?? {}).forEach((config) => {
updateConfiguration(targetConfig, config);
});
if (
!Object.keys(targetConfig).length ||
(Object.keys(targetConfig).length === 1 &&
Object.keys(targetConfig)[0] === 'executor')
) {
delete nxJson.targetDefaults[targetOrExecutor];
}
if (!Object.keys(nxJson.targetDefaults).length) {
delete nxJson.targetDefaults;
}
}
updateNxJson(tree, nxJson);
}
await formatFiles(tree);
}
function updateOptions(target: TargetConfiguration) {
delete target.options.tsConfig;
if (!Object.keys(target.options).length) {
delete target.options;
}
}
function updateConfiguration(
target: TargetConfiguration,
configuration: string
) {
delete target.configurations[configuration].tsConfig;
if (
!Object.keys(target.configurations[configuration]).length &&
(!target.defaultConfiguration ||
target.defaultConfiguration !== configuration)
) {
delete target.configurations[configuration];
}
if (!Object.keys(target.configurations).length) {
delete target.configurations;
}
}