@nx/jest 23.0.0-beta.20 → 23.0.0-beta.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. package/dist/src/executors/jest/jest.impl.js +0 -10
  2. package/dist/src/executors/jest/schema.d.ts +0 -7
  3. package/dist/src/executors/jest/schema.json +0 -5
  4. package/dist/src/generators/configuration/configuration.js +2 -5
  5. package/dist/src/generators/configuration/lib/ensure-dependencies.js +1 -1
  6. package/dist/src/generators/configuration/schema.d.ts +0 -4
  7. package/dist/src/generators/configuration/schema.json +0 -6
  8. package/dist/src/generators/convert-to-inferred/convert-to-inferred.js +2 -0
  9. package/dist/src/generators/init/init.js +3 -5
  10. package/dist/src/generators/init/schema.json +1 -1
  11. package/dist/src/migrations/update-23-0-0/migrate-jest-configuration-skip-setup-file.d.ts +2 -0
  12. package/dist/src/migrations/update-23-0-0/migrate-jest-configuration-skip-setup-file.js +62 -0
  13. package/dist/src/migrations/update-23-0-0/migrate-jest-configuration-skip-setup-file.md +91 -0
  14. package/dist/src/migrations/update-23-0-0/migrate-jest-executor-setup-file.d.ts +2 -0
  15. package/dist/src/migrations/update-23-0-0/migrate-jest-executor-setup-file.js +476 -0
  16. package/dist/src/migrations/update-23-0-0/migrate-jest-executor-setup-file.md +169 -0
  17. package/dist/src/utils/assert-supported-jest-version.d.ts +2 -0
  18. package/dist/src/utils/assert-supported-jest-version.js +11 -0
  19. package/dist/src/utils/versions.d.ts +13 -2
  20. package/dist/src/utils/versions.js +26 -46
  21. package/migrations.json +29 -8
  22. package/package.json +16 -4
  23. package/dist/src/utils/version-utils.d.ts +0 -2
  24. package/dist/src/utils/version-utils.js +0 -18
@@ -42,7 +42,6 @@ function getExtraArgs(options, schema) {
42
42
  return extraArgs;
43
43
  }
44
44
  async function parseJestConfig(options, context, multiProjects = false) {
45
- let jestConfig;
46
45
  // support passing extra args to jest cli supporting 3rd party plugins
47
46
  // like 'jest-runner-groups' --group arg
48
47
  const extraArgs = getExtraArgs(options, schema_json_1.default);
@@ -84,15 +83,6 @@ async function parseJestConfig(options, context, multiProjects = false) {
84
83
  };
85
84
  if (!multiProjects) {
86
85
  options.jestConfig = path.resolve(context.root, options.jestConfig);
87
- jestConfig = (await (0, jest_config_1.readConfig)(config, options.jestConfig)).projectConfig;
88
- }
89
- // for backwards compatibility
90
- if (options.setupFile && !multiProjects) {
91
- const setupFilesAfterEnvSet = new Set([
92
- ...(jestConfig.setupFilesAfterEnv ?? []),
93
- path.resolve(context.root, options.setupFile),
94
- ]);
95
- config.setupFilesAfterEnv = Array.from(setupFilesAfterEnvSet);
96
86
  }
97
87
  if (options.testFile) {
98
88
  config._.push(options.testFile);
@@ -37,11 +37,4 @@ export interface JestExecutorOptions {
37
37
  watchAll?: boolean;
38
38
  testLocationInResults?: boolean;
39
39
  testTimeout?: number;
40
-
41
- /**
42
- * @deprecated Use the `setupFilesAfterEnv` option in the Jest configuration
43
- * file instead. See https://jestjs.io/docs/configuration#setupfilesafterenv-array.
44
- * It will be removed in Nx v22.
45
- */
46
- setupFile?: string;
47
40
  }
@@ -66,11 +66,6 @@
66
66
  "x-completion-type": "file",
67
67
  "x-completion-glob": "tsconfig.*.json"
68
68
  },
69
- "setupFile": {
70
- "description": "The name of a setup file used by Jest.",
71
- "type": "string",
72
- "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."
73
- },
74
69
  "bail": {
75
70
  "alias": "b",
76
71
  "description": "Exit the test suite immediately after `n` number of failing tests. (https://jestjs.io/docs/cli#--bail)",
@@ -8,6 +8,7 @@ const js_1 = require("@nx/js");
8
8
  const internal_2 = require("@nx/js/internal");
9
9
  const config_file_1 = require("../../utils/config/config-file");
10
10
  const init_1 = require("../init/init");
11
+ const assert_supported_jest_version_1 = require("../../utils/assert-supported-jest-version");
11
12
  const deprecation_1 = require("../../utils/deprecation");
12
13
  const check_for_test_target_1 = require("./lib/check-for-test-target");
13
14
  const create_files_1 = require("./lib/create-files");
@@ -20,7 +21,6 @@ const schemaDefaults = {
20
21
  setupFile: 'none',
21
22
  babelJest: false,
22
23
  supportTsx: false,
23
- skipSetupFile: false,
24
24
  skipSerializers: false,
25
25
  testEnvironment: 'jsdom',
26
26
  };
@@ -42,10 +42,6 @@ function normalizeOptions(tree, options) {
42
42
  ['swc', 'babel'].includes(options.compiler)) {
43
43
  options.skipSerializers = true;
44
44
  }
45
- if (options.skipSetupFile) {
46
- // setupFile is always 'none'
47
- options.setupFile = schemaDefaults.setupFile;
48
- }
49
45
  const project = (0, devkit_1.readProjectConfiguration)(tree, options.project);
50
46
  return {
51
47
  ...schemaDefaults,
@@ -59,6 +55,7 @@ function configurationGenerator(tree, schema) {
59
55
  return configurationGeneratorInternal(tree, { addPlugin: false, ...schema });
60
56
  }
61
57
  async function configurationGeneratorInternal(tree, schema) {
58
+ (0, assert_supported_jest_version_1.assertSupportedJestVersion)(tree);
62
59
  const options = normalizeOptions(tree, schema);
63
60
  // we'll only add the vscode recommended extension if the jest preset does
64
61
  // not exist, which most likely means this is a first run, in the cases it's
@@ -32,5 +32,5 @@ function ensureDependencies(tree, options) {
32
32
  else if (options.compiler === 'swc') {
33
33
  devDeps['@swc/jest'] = swcJestVersion;
34
34
  }
35
- return (0, devkit_1.addDependenciesToPackageJson)(tree, dependencies, devDeps, undefined, options.keepExistingVersions);
35
+ return (0, devkit_1.addDependenciesToPackageJson)(tree, dependencies, devDeps, undefined, options.keepExistingVersions ?? true);
36
36
  }
@@ -26,10 +26,6 @@ export interface JestProjectSchema {
26
26
  * @deprecated Use the `compiler` option instead. It will be removed in Nx v22.
27
27
  */
28
28
  babelJest?: boolean;
29
- /**
30
- * @deprecated Use the `setupFile` option instead. It will be removed in Nx v22.
31
- */
32
- skipSetupFile?: boolean;
33
29
  keepExistingVersions?: boolean;
34
30
  }
35
31
 
@@ -14,12 +14,6 @@
14
14
  },
15
15
  "x-priority": "important"
16
16
  },
17
- "skipSetupFile": {
18
- "type": "boolean",
19
- "description": "Skips the setup file required for angular.",
20
- "default": false,
21
- "x-deprecated": "Use the `setupFile` option instead. It will be removed in Nx v22."
22
- },
23
17
  "setupFile": {
24
18
  "type": "string",
25
19
  "enum": ["none", "angular", "web-components"],
@@ -7,8 +7,10 @@ const jest_config_1 = require("jest-config");
7
7
  const node_path_1 = require("node:path");
8
8
  const plugin_1 = require("../../plugins/plugin");
9
9
  const config_file_1 = require("../../utils/config/config-file");
10
+ const assert_supported_jest_version_1 = require("../../utils/assert-supported-jest-version");
10
11
  const versions_1 = require("../../utils/versions");
11
12
  async function convertToInferred(tree, options) {
13
+ (0, assert_supported_jest_version_1.assertSupportedJestVersion)(tree);
12
14
  const projectGraph = await (0, devkit_1.createProjectGraphAsync)();
13
15
  const migratedProjects = await (0, internal_1.migrateProjectExecutorsToPlugin)(tree, projectGraph, '@nx/jest/plugin', plugin_1.createNodesV2, { targetName: 'test' }, [
14
16
  {
@@ -6,6 +6,7 @@ const internal_1 = require("@nx/devkit/internal");
6
6
  const devkit_1 = require("@nx/devkit");
7
7
  const plugin_1 = require("../../plugins/plugin");
8
8
  const config_file_1 = require("../../utils/config/config-file");
9
+ const assert_supported_jest_version_1 = require("../../utils/assert-supported-jest-version");
9
10
  const versions_1 = require("../../utils/versions");
10
11
  function updateProductionFileSet(tree) {
11
12
  const nxJson = (0, devkit_1.readNxJson)(tree);
@@ -94,16 +95,13 @@ function updateDependencies(tree, options) {
94
95
  return (0, devkit_1.addDependenciesToPackageJson)(tree, {}, {
95
96
  '@nx/jest': nxVersion,
96
97
  jest: jestVersion,
97
- }, undefined, options.keepExistingVersions);
98
+ }, undefined, options.keepExistingVersions ?? true);
98
99
  }
99
100
  function jestInitGenerator(tree, options) {
100
101
  return jestInitGeneratorInternal(tree, { addPlugin: false, ...options });
101
102
  }
102
103
  async function jestInitGeneratorInternal(tree, options) {
103
- const installedJestVersion = (0, versions_1.getInstalledJestVersion)(tree);
104
- if (installedJestVersion) {
105
- (0, versions_1.validateInstalledJestVersion)(tree);
106
- }
104
+ (0, assert_supported_jest_version_1.assertSupportedJestVersion)(tree);
107
105
  const nxJson = (0, devkit_1.readNxJson)(tree);
108
106
  const addPluginDefault = process.env.NX_ADD_PLUGINS !== 'false' &&
109
107
  nxJson.useInferencePlugins !== false;
@@ -22,7 +22,7 @@
22
22
  "type": "boolean",
23
23
  "x-priority": "internal",
24
24
  "description": "Keep existing dependencies versions",
25
- "default": false
25
+ "default": true
26
26
  },
27
27
  "updatePackageScripts": {
28
28
  "type": "boolean",
@@ -0,0 +1,2 @@
1
+ import { type Tree } from '@nx/devkit';
2
+ export default function (tree: Tree): Promise<void>;
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = default_1;
4
+ const devkit_1 = require("@nx/devkit");
5
+ const GENERATOR_NAME = '@nx/jest:configuration';
6
+ const PACKAGE = '@nx/jest';
7
+ const GENERATOR = 'configuration';
8
+ const SKIP_SETUP_FILE = 'skipSetupFile';
9
+ const SETUP_FILE = 'setupFile';
10
+ // Migrate the deprecated `skipSetupFile` option of `@nx/jest:configuration`
11
+ // generator defaults. `skipSetupFile: true` was equivalent to setting
12
+ // `setupFile: 'none'`, so rewrite to that. `skipSetupFile: false` was a no-op
13
+ // — drop it. Run on both `nx.json` `generators` and per-project
14
+ // `project.json` `generators`, in both flat (`@nx/jest:configuration`) and
15
+ // nested (`@nx/jest` → `configuration`) forms.
16
+ async function default_1(tree) {
17
+ const nxJson = (0, devkit_1.readNxJson)(tree);
18
+ if (nxJson?.generators && transformGenerators(nxJson.generators)) {
19
+ (0, devkit_1.updateNxJson)(tree, nxJson);
20
+ }
21
+ for (const [projectName, projectConfig] of (0, devkit_1.getProjects)(tree)) {
22
+ if (projectConfig.generators &&
23
+ transformGenerators(projectConfig.generators)) {
24
+ (0, devkit_1.updateProjectConfiguration)(tree, projectName, projectConfig);
25
+ }
26
+ }
27
+ await (0, devkit_1.formatFiles)(tree);
28
+ }
29
+ function transformGenerators(generators) {
30
+ let changed = false;
31
+ if (transformDefaults(generators[GENERATOR_NAME])) {
32
+ if (Object.keys(generators[GENERATOR_NAME]).length === 0) {
33
+ delete generators[GENERATOR_NAME];
34
+ }
35
+ changed = true;
36
+ }
37
+ const nested = generators[PACKAGE];
38
+ if (nested &&
39
+ typeof nested === 'object' &&
40
+ transformDefaults(nested[GENERATOR])) {
41
+ if (nested[GENERATOR] && Object.keys(nested[GENERATOR]).length === 0) {
42
+ delete nested[GENERATOR];
43
+ }
44
+ if (Object.keys(nested).length === 0) {
45
+ delete generators[PACKAGE];
46
+ }
47
+ changed = true;
48
+ }
49
+ return changed;
50
+ }
51
+ function transformDefaults(defaults) {
52
+ if (!defaults || typeof defaults !== 'object')
53
+ return false;
54
+ if (!(SKIP_SETUP_FILE in defaults))
55
+ return false;
56
+ if (defaults[SKIP_SETUP_FILE] === true &&
57
+ defaults[SETUP_FILE] === undefined) {
58
+ defaults[SETUP_FILE] = 'none';
59
+ }
60
+ delete defaults[SKIP_SETUP_FILE];
61
+ return true;
62
+ }
@@ -0,0 +1,91 @@
1
+ #### Migrate `skipSetupFile` Generator Default to `setupFile`
2
+
3
+ Migrates the previously deprecated `skipSetupFile` option of the `@nx/jest:configuration` generator. When set as a default in `nx.json` `generators` or per-project `project.json` `generators`, it is rewritten as follows:
4
+
5
+ - `skipSetupFile: true` becomes `setupFile: 'none'` (preserving the original behavior of skipping the setup file). Existing `setupFile` values are left untouched.
6
+ - `skipSetupFile: false` is dropped (it was a no-op).
7
+
8
+ Both flat (`@nx/jest:configuration`) and nested (`@nx/jest` → `configuration`) forms are handled.
9
+
10
+ #### Examples
11
+
12
+ Rewrite a `nx.json` generator default:
13
+
14
+ ##### Before
15
+
16
+ ```json title="nx.json" {4}
17
+ {
18
+ "generators": {
19
+ "@nx/jest:configuration": {
20
+ "skipSetupFile": true
21
+ }
22
+ }
23
+ }
24
+ ```
25
+
26
+ ##### After
27
+
28
+ ```json title="nx.json"
29
+ {
30
+ "generators": {
31
+ "@nx/jest:configuration": {
32
+ "setupFile": "none"
33
+ }
34
+ }
35
+ }
36
+ ```
37
+
38
+ Drop the option when set to `false`:
39
+
40
+ ##### Before
41
+
42
+ ```json title="nx.json" {4}
43
+ {
44
+ "generators": {
45
+ "@nx/jest:configuration": {
46
+ "skipSetupFile": false,
47
+ "testEnvironment": "jsdom"
48
+ }
49
+ }
50
+ }
51
+ ```
52
+
53
+ ##### After
54
+
55
+ ```json title="nx.json"
56
+ {
57
+ "generators": {
58
+ "@nx/jest:configuration": {
59
+ "testEnvironment": "jsdom"
60
+ }
61
+ }
62
+ }
63
+ ```
64
+
65
+ Rewrite a per-project generator default:
66
+
67
+ ##### Before
68
+
69
+ ```json title="apps/myapp/project.json" {4}
70
+ {
71
+ "generators": {
72
+ "@nx/jest:configuration": {
73
+ "skipSetupFile": true
74
+ }
75
+ }
76
+ }
77
+ ```
78
+
79
+ ##### After
80
+
81
+ ```json title="apps/myapp/project.json"
82
+ {
83
+ "generators": {
84
+ "@nx/jest:configuration": {
85
+ "setupFile": "none"
86
+ }
87
+ }
88
+ }
89
+ ```
90
+
91
+ The nested form (`@nx/jest` → `configuration`) is handled the same way.
@@ -0,0 +1,2 @@
1
+ import { type Tree } from '@nx/devkit';
2
+ export default function (tree: Tree): Promise<void | (() => void)>;
@@ -0,0 +1,476 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = default_1;
4
+ const internal_1 = require("@nx/devkit/internal");
5
+ const devkit_1 = require("@nx/devkit");
6
+ const internal_2 = require("@nx/js/internal");
7
+ const devkit_internals_1 = require("nx/src/devkit-internals");
8
+ const project_configuration_utils_1 = require("nx/src/project-graph/utils/project-configuration-utils");
9
+ const path_1 = require("path");
10
+ const functions_1 = require("../../utils/config/functions");
11
+ const EXECUTOR_TO_MIGRATE = '@nx/jest:jest';
12
+ const ROOT_DIR_TOKEN = '<rootDir>';
13
+ const SETUP_FILES_AFTER_ENV = 'setupFilesAfterEnv';
14
+ const SETUP_FILE = 'setupFile';
15
+ const JEST_CONFIG = 'jestConfig';
16
+ const ROOT_DIR = 'rootDir';
17
+ let tsModule;
18
+ async function default_1(tree) {
19
+ const warnLists = {
20
+ unparseable: [],
21
+ nonLiteralRootDir: [],
22
+ sharedConfigConflict: [],
23
+ passthroughCollision: [],
24
+ configurationOnly: [],
25
+ noResolvableJestConfig: [],
26
+ };
27
+ // configPath -> the setupFile string the AST rewrite committed for it.
28
+ const rewrittenJestConfigs = new Map();
29
+ // `${project}::${target}` -> base setupFile snapshotted before mutation.
30
+ const baseSetupFiles = new Map();
31
+ // Per-project ProjectConfiguration cache so multi-target / multi-configuration
32
+ // projects don't re-read + re-write project.json on every callback iteration.
33
+ const projectConfigCache = new Map();
34
+ const dirtyProjects = new Set();
35
+ const nxJson = (0, devkit_1.readNxJson)(tree);
36
+ (0, internal_1.forEachExecutorOptions)(tree, EXECUTOR_TO_MIGRATE, (snapshotOptions, project, target, configuration) => {
37
+ if (configuration === undefined &&
38
+ snapshotOptions.setupFile !== undefined) {
39
+ baseSetupFiles.set(`${project}::${target}`, snapshotOptions.setupFile);
40
+ }
41
+ });
42
+ (0, internal_1.forEachExecutorOptions)(tree, EXECUTOR_TO_MIGRATE, (options, project, target, configuration) => {
43
+ if (options.setupFile === undefined) {
44
+ // Configuration passthrough that inherits the base setupFile would
45
+ // silently override the migrated value at runtime — warn so the user
46
+ // can consolidate.
47
+ if (configuration !== undefined &&
48
+ options.setupFilesAfterEnv !== undefined &&
49
+ baseSetupFiles.has(`${project}::${target}`)) {
50
+ warnLists.passthroughCollision.push(formatLocation({ project, target, configuration }));
51
+ return;
52
+ }
53
+ // Base target inheriting `setupFile` from `nx.json` `targetDefaults`
54
+ // (no own value). The defaults strip-pass would otherwise leave the
55
+ // jest config with no setup file at runtime — migrate now using the
56
+ // inherited value.
57
+ if (configuration === undefined) {
58
+ migrateInheritedSetupFile(tree, project, target, nxJson, projectConfigCache, rewrittenJestConfigs, warnLists);
59
+ }
60
+ return;
61
+ }
62
+ const projectConfiguration = getProjectConfig(tree, project, projectConfigCache);
63
+ const targetOptions = projectConfiguration.targets[target]?.options;
64
+ const baseSetupFile = baseSetupFiles.get(`${project}::${target}`);
65
+ const jestConfigPath = resolveJestConfigPath(options.jestConfig, targetOptions?.jestConfig, projectConfiguration.root, target, nxJson);
66
+ const location = {
67
+ project,
68
+ target,
69
+ configuration,
70
+ jestConfig: jestConfigPath,
71
+ };
72
+ const hasPassthroughInScope = options.setupFilesAfterEnv !== undefined ||
73
+ (configuration !== undefined &&
74
+ targetOptions?.setupFilesAfterEnv !== undefined);
75
+ if (hasPassthroughInScope) {
76
+ warnLists.passthroughCollision.push(formatLocation(location));
77
+ }
78
+ else if (configuration !== undefined &&
79
+ baseSetupFile !== options.setupFile) {
80
+ // Configuration's setupFile diverges from base. When the configuration
81
+ // also overrides `jestConfig` (separate file), we can write the setup
82
+ // file there without leaking to the base run. Otherwise the only safe
83
+ // move is to bail: writing into a shared jest config would make the
84
+ // configuration's setup file run for every invocation.
85
+ if (options.jestConfig !== undefined && jestConfigPath) {
86
+ migrateOneJestConfig(tree, jestConfigPath, options.setupFile, location, rewrittenJestConfigs, warnLists);
87
+ }
88
+ else {
89
+ warnLists.configurationOnly.push(formatLocation(location));
90
+ }
91
+ }
92
+ else if (!jestConfigPath) {
93
+ warnLists.noResolvableJestConfig.push(formatLocation(location));
94
+ }
95
+ else {
96
+ migrateOneJestConfig(tree, jestConfigPath, options.setupFile, location, rewrittenJestConfigs, warnLists);
97
+ if (configuration === undefined) {
98
+ migrateInheritingConfigurations(tree, projectConfiguration, target, options.setupFile, jestConfigPath, location, rewrittenJestConfigs, warnLists);
99
+ }
100
+ }
101
+ if (configuration) {
102
+ stripFromConfiguration(projectConfiguration.targets[target], configuration);
103
+ }
104
+ else {
105
+ stripFromOptions(projectConfiguration.targets[target]);
106
+ }
107
+ dirtyProjects.add(project);
108
+ });
109
+ for (const project of dirtyProjects) {
110
+ (0, devkit_1.updateProjectConfiguration)(tree, project, projectConfigCache.get(project));
111
+ }
112
+ const nxJsonHadSetupFile = stripSetupFileFromNxJson(tree, nxJson);
113
+ await (0, devkit_1.formatFiles)(tree);
114
+ return buildFollowUp(warnLists, nxJsonHadSetupFile);
115
+ }
116
+ function getProjectConfig(tree, project, cache) {
117
+ let cached = cache.get(project);
118
+ if (!cached) {
119
+ cached = (0, devkit_1.readProjectConfiguration)(tree, project);
120
+ cache.set(project, cached);
121
+ }
122
+ return cached;
123
+ }
124
+ function migrateInheritingConfigurations(tree, projectConfiguration, target, baseSetupFile, baseJestConfigPath, baseLocation, rewrittenJestConfigs, warnLists) {
125
+ const configurations = projectConfiguration.targets[target]?.configurations ?? {};
126
+ for (const [configName, rawConfigOptions] of Object.entries(configurations)) {
127
+ const configOptions = rawConfigOptions;
128
+ if (configOptions.setupFile !== undefined)
129
+ continue;
130
+ if (configOptions.jestConfig === undefined)
131
+ continue;
132
+ const configJestConfigPath = expandWorkspaceRelativePath(configOptions.jestConfig, projectConfiguration.root);
133
+ if (configJestConfigPath === baseJestConfigPath)
134
+ continue;
135
+ migrateOneJestConfig(tree, configJestConfigPath, baseSetupFile, {
136
+ ...baseLocation,
137
+ configuration: configName,
138
+ jestConfig: configJestConfigPath,
139
+ }, rewrittenJestConfigs, warnLists);
140
+ }
141
+ }
142
+ function migrateOneJestConfig(tree, jestConfigPath, setupFile, location, rewrittenJestConfigs, warnLists) {
143
+ const previouslyMigrated = rewrittenJestConfigs.get(jestConfigPath);
144
+ if (previouslyMigrated !== undefined) {
145
+ if (previouslyMigrated !== setupFile) {
146
+ warnLists.sharedConfigConflict.push(formatLocation(location));
147
+ }
148
+ return;
149
+ }
150
+ const result = pushSetupFileIntoJestConfig(tree, jestConfigPath, setupFile);
151
+ switch (result) {
152
+ case 'written':
153
+ case 'already-present':
154
+ rewrittenJestConfigs.set(jestConfigPath, setupFile);
155
+ break;
156
+ case 'custom-root-dir-non-literal':
157
+ warnLists.nonLiteralRootDir.push(formatLocation(location));
158
+ break;
159
+ case 'unparseable':
160
+ warnLists.unparseable.push(formatLocation(location));
161
+ break;
162
+ }
163
+ }
164
+ // For a base target that doesn't declare its own `setupFile` but inherits
165
+ // one from `nx.json` `targetDefaults`, migrate the inherited value into the
166
+ // project's jest config — otherwise stripping `setupFile` from `nx.json`
167
+ // would silently drop the setup file for every inheriting target at runtime.
168
+ function migrateInheritedSetupFile(tree, project, target, nxJson, projectConfigCache, rewrittenJestConfigs, warnLists) {
169
+ if (!nxJson?.targetDefaults)
170
+ return;
171
+ const matched = (0, project_configuration_utils_1.readTargetDefaultsForTarget)(target, nxJson.targetDefaults, EXECUTOR_TO_MIGRATE);
172
+ const inheritedSetupFile = matched?.options?.[SETUP_FILE];
173
+ if (inheritedSetupFile === undefined)
174
+ return;
175
+ const projectConfiguration = getProjectConfig(tree, project, projectConfigCache);
176
+ const targetOptions = projectConfiguration.targets[target]?.options;
177
+ const expandedSetupFile = expandWorkspaceRelativePath(inheritedSetupFile, projectConfiguration.root);
178
+ const jestConfigPath = resolveJestConfigPath(undefined, targetOptions?.jestConfig, projectConfiguration.root, target, nxJson);
179
+ const location = {
180
+ project,
181
+ target,
182
+ jestConfig: jestConfigPath,
183
+ };
184
+ if (!jestConfigPath) {
185
+ warnLists.noResolvableJestConfig.push(formatLocation(location));
186
+ return;
187
+ }
188
+ migrateOneJestConfig(tree, jestConfigPath, expandedSetupFile, location, rewrittenJestConfigs, warnLists);
189
+ }
190
+ function stripSetupFileFromNxJson(tree, nxJson) {
191
+ if (!nxJson?.targetDefaults)
192
+ return false;
193
+ let changed = false;
194
+ let hadSetupFile = false;
195
+ for (const [targetOrExecutor, targetConfig] of Object.entries(nxJson.targetDefaults)) {
196
+ if (targetOrExecutor !== EXECUTOR_TO_MIGRATE &&
197
+ targetConfig.executor !== EXECUTOR_TO_MIGRATE) {
198
+ continue;
199
+ }
200
+ if (targetConfig.options?.setupFile !== undefined) {
201
+ hadSetupFile = true;
202
+ changed = true;
203
+ delete targetConfig.options.setupFile;
204
+ if (!Object.keys(targetConfig.options).length) {
205
+ delete targetConfig.options;
206
+ }
207
+ }
208
+ for (const config of Object.keys(targetConfig.configurations ?? {})) {
209
+ if (targetConfig.configurations[config]?.setupFile !== undefined) {
210
+ hadSetupFile = true;
211
+ changed = true;
212
+ delete targetConfig.configurations[config].setupFile;
213
+ if (!Object.keys(targetConfig.configurations[config]).length &&
214
+ (!targetConfig.defaultConfiguration ||
215
+ targetConfig.defaultConfiguration !== config)) {
216
+ delete targetConfig.configurations[config];
217
+ }
218
+ }
219
+ }
220
+ if (targetConfig.configurations &&
221
+ !Object.keys(targetConfig.configurations).length) {
222
+ delete targetConfig.configurations;
223
+ }
224
+ if (!Object.keys(targetConfig).length ||
225
+ (Object.keys(targetConfig).length === 1 &&
226
+ Object.keys(targetConfig)[0] === 'executor')) {
227
+ delete nxJson.targetDefaults[targetOrExecutor];
228
+ }
229
+ }
230
+ if (!Object.keys(nxJson.targetDefaults).length) {
231
+ delete nxJson.targetDefaults;
232
+ }
233
+ if (changed)
234
+ (0, devkit_1.updateNxJson)(tree, nxJson);
235
+ return hadSetupFile;
236
+ }
237
+ function buildFollowUp(warnLists, nxJsonHadSetupFile) {
238
+ const hasWarnings = warnLists.unparseable.length > 0 ||
239
+ warnLists.nonLiteralRootDir.length > 0 ||
240
+ warnLists.sharedConfigConflict.length > 0 ||
241
+ warnLists.passthroughCollision.length > 0 ||
242
+ warnLists.configurationOnly.length > 0 ||
243
+ warnLists.noResolvableJestConfig.length > 0 ||
244
+ nxJsonHadSetupFile;
245
+ if (!hasWarnings)
246
+ return;
247
+ return () => {
248
+ warn(warnLists.unparseable, 'The deprecated `setupFile` option of `@nx/jest:jest` was removed from the following targets, ' +
249
+ 'but the corresponding Jest config could not be parsed automatically. Add the setup file path ' +
250
+ `manually to \`${SETUP_FILES_AFTER_ENV}\` in each Jest config:`);
251
+ warn(warnLists.nonLiteralRootDir, 'The deprecated `setupFile` option of `@nx/jest:jest` was removed from the following targets, ' +
252
+ 'but their Jest config sets `rootDir` to a non-literal value (e.g. a function call or ' +
253
+ 'imported variable) so the path could not be migrated automatically. Add the setup file path ' +
254
+ `to \`${SETUP_FILES_AFTER_ENV}\` in each Jest config using the correct \`rootDir\`-relative path:`);
255
+ warn(warnLists.sharedConfigConflict, 'The following targets reuse a Jest config that another target already migrated with a ' +
256
+ `different \`setupFile\`. Their \`setupFile\` was removed but not added to \`${SETUP_FILES_AFTER_ENV}\`, ` +
257
+ 'since per-target setup files require separate Jest configs. Either give each target its own ' +
258
+ 'Jest config or merge the setup files in the shared config:');
259
+ warn(warnLists.passthroughCollision, 'The following targets had both `setupFile` (now removed) and a `setupFilesAfterEnv` option in ' +
260
+ 'the same scope. Pre-migration the executor was overriding the passthrough silently; ' +
261
+ 'post-migration the passthrough wins, which may change behavior. Consolidate the setup files ' +
262
+ `manually under \`${SETUP_FILES_AFTER_ENV}\` in either the target options or the Jest config:`);
263
+ warn(warnLists.configurationOnly, 'The following targets declared `setupFile` only under a named configuration (or with a value ' +
264
+ "different from the base target's `setupFile`). Pre-migration that setup file ran only when " +
265
+ 'the configuration was selected. Configuration-scoped setup files cannot be expressed in a ' +
266
+ 'shared Jest config without leaking to the base run, so the option was removed without being ' +
267
+ 'migrated. Add the setup file to a configuration-scoped Jest config or guard it via ' +
268
+ '`process.env.NX_TASK_TARGET_CONFIGURATION`:');
269
+ warn(warnLists.noResolvableJestConfig, 'The following targets had a `setupFile` option but no resolvable `jestConfig` (neither in the ' +
270
+ 'target options nor in `nx.json` target defaults). The deprecated option was removed; add the ' +
271
+ `setup file path to \`${SETUP_FILES_AFTER_ENV}\` in the Jest config you intend the target to use:`);
272
+ if (nxJsonHadSetupFile) {
273
+ devkit_1.logger.warn('Removed the deprecated `setupFile` option from the `@nx/jest:jest` target defaults in `nx.json`. ' +
274
+ "If you relied on this default, add the setup file path to each project's Jest config under " +
275
+ `\`${SETUP_FILES_AFTER_ENV}\`.`);
276
+ }
277
+ };
278
+ }
279
+ function warn(items, header) {
280
+ if (items.length === 0)
281
+ return;
282
+ devkit_1.logger.warn(`${header}\n${items.map((p) => ` - ${p}`).join('\n')}`);
283
+ }
284
+ function formatLocation(loc) {
285
+ const targetRef = loc.configuration
286
+ ? `${loc.target}:${loc.configuration}`
287
+ : loc.target;
288
+ const configPart = loc.jestConfig ? ` (${loc.jestConfig})` : '';
289
+ return `${loc.project} -> ${targetRef}${configPart}`;
290
+ }
291
+ function stripFromOptions(target) {
292
+ delete target.options.setupFile;
293
+ if (!Object.keys(target.options).length) {
294
+ delete target.options;
295
+ }
296
+ }
297
+ function stripFromConfiguration(target, configuration) {
298
+ delete target.configurations[configuration].setupFile;
299
+ if (!Object.keys(target.configurations[configuration]).length &&
300
+ (!target.defaultConfiguration ||
301
+ target.defaultConfiguration !== configuration)) {
302
+ delete target.configurations[configuration];
303
+ }
304
+ if (!Object.keys(target.configurations).length) {
305
+ delete target.configurations;
306
+ }
307
+ }
308
+ // Falls through `callbackOptions → targetOptions → nx.json defaults`,
309
+ // expanding `{projectRoot}` / `{workspaceRoot}` tokens via the canonical
310
+ // `interpolate` helper. Defaults lookup uses `readTargetDefaultsForTarget`
311
+ // for the canonical executor-key → target-name → glob-match precedence.
312
+ function resolveJestConfigPath(callbackJestConfig, targetJestConfig, projectRoot, target, nxJson) {
313
+ const explicit = callbackJestConfig ?? targetJestConfig;
314
+ if (explicit)
315
+ return expandWorkspaceRelativePath(explicit, projectRoot);
316
+ if (!nxJson?.targetDefaults)
317
+ return undefined;
318
+ const matched = (0, project_configuration_utils_1.readTargetDefaultsForTarget)(target, nxJson.targetDefaults, EXECUTOR_TO_MIGRATE);
319
+ const fromDefaults = matched?.options?.[JEST_CONFIG];
320
+ if (fromDefaults)
321
+ return expandWorkspaceRelativePath(fromDefaults, projectRoot);
322
+ return undefined;
323
+ }
324
+ function expandWorkspaceRelativePath(value, projectRoot) {
325
+ return path_1.posix.normalize((0, devkit_internals_1.interpolate)(value, {
326
+ projectRoot: projectRoot || '.',
327
+ workspaceRoot: '.',
328
+ }));
329
+ }
330
+ function pushSetupFileIntoJestConfig(tree, jestConfigPath, setupFileFromOptions) {
331
+ if (!tree.exists(jestConfigPath))
332
+ return 'unparseable';
333
+ const content = tree.read(jestConfigPath, 'utf-8');
334
+ if (!content)
335
+ return 'unparseable';
336
+ if (!tsModule) {
337
+ tsModule = (0, internal_2.ensureTypescript)();
338
+ }
339
+ let configObject;
340
+ try {
341
+ configObject = (0, functions_1.jestConfigObjectAst)(content);
342
+ }
343
+ catch {
344
+ return 'unparseable';
345
+ }
346
+ const configDir = path_1.posix.dirname(jestConfigPath);
347
+ const rootDirInfo = computeEffectiveRootDir(configDir, configObject);
348
+ if (rootDirInfo.kind === 'non-literal') {
349
+ return 'custom-root-dir-non-literal';
350
+ }
351
+ const effectiveRootDir = rootDirInfo.absolute;
352
+ const setupFileWithRootDir = toRootDirRelative(effectiveRootDir, setupFileFromOptions);
353
+ const properties = configObject.properties;
354
+ const existingIndex = properties.findIndex((p) => tsModule.isPropertyAssignment(p) &&
355
+ getPropertyName(p) === SETUP_FILES_AFTER_ENV);
356
+ const spreadIndices = properties
357
+ .map((p, i) => (tsModule.isSpreadAssignment(p) ? i : -1))
358
+ .filter((i) => i >= 0);
359
+ if (existingIndex >= 0) {
360
+ if (spreadIndices.some((i) => i > existingIndex)) {
361
+ return 'unparseable';
362
+ }
363
+ const existing = properties[existingIndex];
364
+ if (!tsModule.isArrayLiteralExpression(existing.initializer)) {
365
+ return 'unparseable';
366
+ }
367
+ const arr = existing.initializer;
368
+ const newPathResolved = resolveJestPath(setupFileWithRootDir, configDir, effectiveRootDir);
369
+ const alreadyPresent = arr.elements.some((e) => tsModule.isStringLiteral(e) &&
370
+ resolveJestPath(e.text, configDir, effectiveRootDir) === newPathResolved);
371
+ if (alreadyPresent)
372
+ return 'already-present';
373
+ const insertPos = arr.getEnd() - 1; // position of `]`
374
+ const hasElements = arr.elements.length > 0;
375
+ let newContent;
376
+ if (hasElements) {
377
+ const lastElement = arr.elements[arr.elements.length - 1];
378
+ const between = content.slice(lastElement.getEnd(), insertPos);
379
+ const hasTrailingComma = /,/.test(between);
380
+ const sep = hasTrailingComma ? ' ' : ', ';
381
+ newContent =
382
+ content.slice(0, insertPos) +
383
+ `${sep}'${setupFileWithRootDir}'` +
384
+ content.slice(insertPos);
385
+ }
386
+ else {
387
+ newContent =
388
+ content.slice(0, insertPos) +
389
+ `'${setupFileWithRootDir}'` +
390
+ content.slice(insertPos);
391
+ }
392
+ tree.write(jestConfigPath, newContent);
393
+ return 'written';
394
+ }
395
+ // Object-spread "last wins": mirror it with a nullish-coalescing fallback
396
+ // ordered last-spread-first so the emitted property resolves to the same
397
+ // array the runtime would have seen pre-migration. `as any` lets the chain
398
+ // compile when the spread source's type omits `setupFilesAfterEnv`.
399
+ const spreadExpressions = properties
400
+ .filter((p) => tsModule.isSpreadAssignment(p))
401
+ .map((p) => p.expression.getText());
402
+ const isTs = /\.(c|m)?tsx?$/.test(jestConfigPath);
403
+ const wrap = (expr) => isTs
404
+ ? `((${expr}) as any)?.${SETUP_FILES_AFTER_ENV}`
405
+ : `(${expr})?.${SETUP_FILES_AFTER_ENV}`;
406
+ let spreadElement;
407
+ if (spreadExpressions.length === 1) {
408
+ spreadElement = `...${wrap(spreadExpressions[0])} ?? []`;
409
+ }
410
+ else if (spreadExpressions.length > 1) {
411
+ const fallbacks = [...spreadExpressions].reverse().map(wrap);
412
+ spreadElement = `...(${fallbacks.join(' ?? ')} ?? [])`;
413
+ }
414
+ const arrayLiteral = spreadElement
415
+ ? `[${spreadElement}, '${setupFileWithRootDir}']`
416
+ : `['${setupFileWithRootDir}']`;
417
+ const newProp = `${SETUP_FILES_AFTER_ENV}: ${arrayLiteral}`;
418
+ const insertPos = configObject.getEnd() - 1; // position of `}`
419
+ const hasProps = properties.length > 0;
420
+ let insertion;
421
+ if (hasProps) {
422
+ const lastProp = properties[properties.length - 1];
423
+ const between = content.slice(lastProp.getEnd(), insertPos);
424
+ const hasTrailingComma = /,/.test(between);
425
+ insertion = hasTrailingComma ? ` ${newProp},` : `, ${newProp}`;
426
+ }
427
+ else {
428
+ insertion = newProp;
429
+ }
430
+ const newContent = content.slice(0, insertPos) + insertion + content.slice(insertPos);
431
+ tree.write(jestConfigPath, newContent);
432
+ return 'written';
433
+ }
434
+ function computeEffectiveRootDir(configDir, configObject) {
435
+ const rootDirNode = configObject.properties.find((p) => tsModule.isPropertyAssignment(p) && getPropertyName(p) === ROOT_DIR);
436
+ if (!rootDirNode) {
437
+ return { kind: 'static', absolute: configDir };
438
+ }
439
+ const initializer = rootDirNode.initializer;
440
+ if (tsModule.isStringLiteral(initializer) ||
441
+ tsModule.isNoSubstitutionTemplateLiteral(initializer)) {
442
+ const value = initializer.text;
443
+ if (path_1.posix.isAbsolute(value))
444
+ return { kind: 'non-literal' };
445
+ return {
446
+ kind: 'static',
447
+ absolute: path_1.posix.normalize(path_1.posix.join(configDir, value)),
448
+ };
449
+ }
450
+ return { kind: 'non-literal' };
451
+ }
452
+ function getPropertyName(p) {
453
+ if (tsModule.isIdentifier(p.name) ||
454
+ tsModule.isStringLiteral(p.name) ||
455
+ tsModule.isNoSubstitutionTemplateLiteral(p.name)) {
456
+ return p.name.text;
457
+ }
458
+ return undefined;
459
+ }
460
+ function toRootDirRelative(effectiveRootDir, workspacePath) {
461
+ return `${ROOT_DIR_TOKEN}/${path_1.posix.relative(effectiveRootDir, workspacePath)}`;
462
+ }
463
+ // Normalize a `setupFilesAfterEnv` entry to a workspace-root-relative path
464
+ // for dedup. Handles `<rootDir>/...`, `./...`, and absolute strings.
465
+ function resolveJestPath(rawValue, configDir, rootDir) {
466
+ if (rawValue.startsWith(ROOT_DIR_TOKEN)) {
467
+ const rest = rawValue.slice(ROOT_DIR_TOKEN.length).replace(/^\/+/, '');
468
+ return path_1.posix.normalize(path_1.posix.join(rootDir, rest));
469
+ }
470
+ if (path_1.posix.isAbsolute(rawValue))
471
+ return rawValue;
472
+ if (rawValue.startsWith('./') || rawValue.startsWith('../')) {
473
+ return path_1.posix.normalize(path_1.posix.join(configDir, rawValue));
474
+ }
475
+ return rawValue;
476
+ }
@@ -0,0 +1,169 @@
1
+ #### Migrate `setupFile` Option to `setupFilesAfterEnv`
2
+
3
+ Migrates the previously deprecated `setupFile` option of the `@nx/jest:jest` executor. The setup file path is appended to the `setupFilesAfterEnv` array in the project's Jest configuration (using `<rootDir>/...` form), and the deprecated option is removed from `project.json` and `nx.json` target defaults.
4
+
5
+ If the Jest configuration cannot be parsed automatically (e.g. it exports a factory function or assigns `setupFilesAfterEnv` to a non-array value), the deprecated option is still removed and a warning is logged listing the affected projects so the setup file path can be moved manually.
6
+
7
+ #### Examples
8
+
9
+ Push the setup file into the project's Jest configuration and remove the option from `project.json`:
10
+
11
+ ##### Before
12
+
13
+ ```json title="apps/myapp/project.json" {7}
14
+ {
15
+ "targets": {
16
+ "test": {
17
+ "executor": "@nx/jest:jest",
18
+ "options": {
19
+ "jestConfig": "apps/myapp/jest.config.ts",
20
+ "setupFile": "apps/myapp/src/test-setup.ts"
21
+ }
22
+ }
23
+ }
24
+ }
25
+ ```
26
+
27
+ ```ts title="apps/myapp/jest.config.ts"
28
+ export default {
29
+ displayName: 'myapp',
30
+ };
31
+ ```
32
+
33
+ ##### After
34
+
35
+ ```json title="apps/myapp/project.json"
36
+ {
37
+ "targets": {
38
+ "test": {
39
+ "executor": "@nx/jest:jest",
40
+ "options": {
41
+ "jestConfig": "apps/myapp/jest.config.ts"
42
+ }
43
+ }
44
+ }
45
+ }
46
+ ```
47
+
48
+ ```ts title="apps/myapp/jest.config.ts"
49
+ export default {
50
+ displayName: 'myapp',
51
+ setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
52
+ };
53
+ ```
54
+
55
+ Append to an existing `setupFilesAfterEnv` array:
56
+
57
+ ##### Before
58
+
59
+ ```json title="apps/myapp/project.json" {7}
60
+ {
61
+ "targets": {
62
+ "test": {
63
+ "executor": "@nx/jest:jest",
64
+ "options": {
65
+ "jestConfig": "apps/myapp/jest.config.ts",
66
+ "setupFile": "apps/myapp/src/test-setup.ts"
67
+ }
68
+ }
69
+ }
70
+ }
71
+ ```
72
+
73
+ ```ts title="apps/myapp/jest.config.ts"
74
+ export default {
75
+ displayName: 'myapp',
76
+ setupFilesAfterEnv: ['<rootDir>/src/existing-setup.ts'],
77
+ };
78
+ ```
79
+
80
+ ##### After
81
+
82
+ ```json title="apps/myapp/project.json"
83
+ {
84
+ "targets": {
85
+ "test": {
86
+ "executor": "@nx/jest:jest",
87
+ "options": {
88
+ "jestConfig": "apps/myapp/jest.config.ts"
89
+ }
90
+ }
91
+ }
92
+ }
93
+ ```
94
+
95
+ ```ts title="apps/myapp/jest.config.ts"
96
+ export default {
97
+ displayName: 'myapp',
98
+ setupFilesAfterEnv: [
99
+ '<rootDir>/src/existing-setup.ts',
100
+ '<rootDir>/src/test-setup.ts',
101
+ ],
102
+ };
103
+ ```
104
+
105
+ Remove the option from a target default using the `@nx/jest:jest` executor:
106
+
107
+ ##### Before
108
+
109
+ ```json title="nx.json" {7}
110
+ {
111
+ "targetDefaults": {
112
+ "test": {
113
+ "executor": "@nx/jest:jest",
114
+ "options": {
115
+ "jestConfig": "{projectRoot}/jest.config.ts",
116
+ "setupFile": "{projectRoot}/src/test-setup.ts"
117
+ }
118
+ }
119
+ }
120
+ }
121
+ ```
122
+
123
+ ##### After
124
+
125
+ ```json title="nx.json"
126
+ {
127
+ "targetDefaults": {
128
+ "test": {
129
+ "executor": "@nx/jest:jest",
130
+ "options": {
131
+ "jestConfig": "{projectRoot}/jest.config.ts"
132
+ }
133
+ }
134
+ }
135
+ }
136
+ ```
137
+
138
+ Per-project paths don't make sense as workspace defaults, so the option is removed without rewriting individual project Jest configs. A warning is logged so the setup file path can be added to each project's Jest config manually if needed.
139
+
140
+ Remove the option from a target default using the `@nx/jest:jest` executor as the key:
141
+
142
+ ##### Before
143
+
144
+ ```json title="nx.json" {6}
145
+ {
146
+ "targetDefaults": {
147
+ "@nx/jest:jest": {
148
+ "options": {
149
+ "jestConfig": "{projectRoot}/jest.config.ts",
150
+ "setupFile": "{projectRoot}/src/test-setup.ts"
151
+ }
152
+ }
153
+ }
154
+ }
155
+ ```
156
+
157
+ ##### After
158
+
159
+ ```json title="nx.json"
160
+ {
161
+ "targetDefaults": {
162
+ "@nx/jest:jest": {
163
+ "options": {
164
+ "jestConfig": "{projectRoot}/jest.config.ts"
165
+ }
166
+ }
167
+ }
168
+ }
169
+ ```
@@ -0,0 +1,2 @@
1
+ import { type Tree } from '@nx/devkit';
2
+ export declare function assertSupportedJestVersion(tree: Tree): void;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.assertSupportedJestVersion = assertSupportedJestVersion;
4
+ const internal_1 = require("@nx/devkit/internal");
5
+ const versions_1 = require("./versions");
6
+ function assertSupportedJestVersion(tree) {
7
+ (0, internal_1.assertSupportedPackageVersion)(tree, 'jest', versions_1.minSupportedJestVersion);
8
+ // ts-jest installs on an independent versioning train from jest, so its
9
+ // floor is asserted separately.
10
+ (0, internal_1.assertSupportedPackageVersion)(tree, 'ts-jest', versions_1.minSupportedTsJestVersion);
11
+ }
@@ -1,4 +1,6 @@
1
1
  import { type Tree } from '@nx/devkit';
2
+ export declare const minSupportedJestVersion = "29.0.0";
3
+ export declare const minSupportedTsJestVersion = "29.0.0";
2
4
  export declare const latestVersions: {
3
5
  nxVersion: any;
4
6
  jestVersion: string;
@@ -17,12 +19,21 @@ export type VersionMap = {
17
19
  [key in SupportedVersions]: Record<PackageVersionNames, string>;
18
20
  };
19
21
  export declare const versionMap: VersionMap;
20
- export declare function versions(tree: Tree): any;
22
+ export declare function versions(tree: Tree): {
23
+ nxVersion: any;
24
+ jestVersion: string;
25
+ babelJestVersion: string;
26
+ jestTypesVersion: string;
27
+ tsJestVersion: string;
28
+ tslibVersion: string;
29
+ swcJestVersion: string;
30
+ typesNodeVersion: string;
31
+ tsNodeVersion: string;
32
+ };
21
33
  export declare function getInstalledJestVersion(tree?: Tree): string | null;
22
34
  export declare function getInstalledJestVersionInfo(tree?: Tree): {
23
35
  version: string | null;
24
36
  major: number | null;
25
37
  };
26
38
  export declare function getInstalledJestMajorVersion(tree?: Tree): number | null;
27
- export declare function validateInstalledJestVersion(tree?: Tree): void;
28
39
  export {};
@@ -1,15 +1,17 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.versionMap = exports.latestVersions = void 0;
3
+ exports.versionMap = exports.latestVersions = exports.minSupportedTsJestVersion = exports.minSupportedJestVersion = void 0;
4
4
  exports.versions = versions;
5
5
  exports.getInstalledJestVersion = getInstalledJestVersion;
6
6
  exports.getInstalledJestVersionInfo = getInstalledJestVersionInfo;
7
7
  exports.getInstalledJestMajorVersion = getInstalledJestMajorVersion;
8
- exports.validateInstalledJestVersion = validateInstalledJestVersion;
9
8
  const devkit_1 = require("@nx/devkit");
9
+ const internal_1 = require("@nx/devkit/internal");
10
10
  const path_1 = require("path");
11
11
  const semver_1 = require("semver");
12
12
  const nxVersion = require((0, path_1.join)('@nx/jest', 'package.json')).version;
13
+ exports.minSupportedJestVersion = '29.0.0';
14
+ exports.minSupportedTsJestVersion = '29.0.0';
13
15
  // Jest is pinned to 30.3.x because jest-runtime@30.4.0 added a call to
14
16
  // `_moduleMocker.clearMocksOnScope()`, which doesn't exist on the
15
17
  // jest-mock@29 ModuleMocker that React Native's preset still feeds in
@@ -20,6 +22,9 @@ exports.latestVersions = {
20
22
  jestVersion: '~30.3.0',
21
23
  babelJestVersion: '~30.3.0',
22
24
  jestTypesVersion: '~30.0.0',
25
+ // ts-jest has not shipped a v30 — 29.x is its latest line and its peers
26
+ // already span jest 29 and 30 (`jest: '^29.0.0 || ^30.0.0'`), so a jest-30
27
+ // workspace correctly stays on ts-jest 29.x.
23
28
  tsJestVersion: '^29.4.0',
24
29
  tslibVersion: '^2.3.0',
25
30
  swcJestVersion: '~0.2.38',
@@ -27,8 +32,6 @@ exports.latestVersions = {
27
32
  tsNodeVersion: '10.9.1',
28
33
  };
29
34
  const supportedMajorVersions = [29, 30];
30
- const minSupportedMajorVersion = Math.min(...supportedMajorVersions);
31
- const currentMajorVersion = Math.max(...supportedMajorVersions);
32
35
  exports.versionMap = {
33
36
  29: {
34
37
  nxVersion,
@@ -49,22 +52,28 @@ function versions(tree) {
49
52
  return exports.latestVersions;
50
53
  }
51
54
  const jestMajorVersion = (0, semver_1.major)(installedJestVersion);
52
- if (exports.versionMap[jestMajorVersion]) {
53
- return exports.versionMap[jestMajorVersion];
54
- }
55
- const backwardCompatibleVersions = supportedMajorVersions.slice(0, -1);
56
- throw new Error(`You're currently using an unsupported Jest version: ${installedJestVersion}. Supported major versions are ${backwardCompatibleVersions.join(', ')} and ${currentMajorVersion}.`);
55
+ return exports.versionMap[jestMajorVersion] ?? exports.latestVersions;
57
56
  }
58
57
  function getInstalledJestVersion(tree) {
58
+ if (!tree) {
59
+ // Fall back to `@nx/jest`'s own jest when the workspace cwd can't resolve
60
+ // one (e.g. inferred-plugin createNodes runs against an empty workspace).
61
+ return (0, internal_1.getInstalledPackageVersion)('jest') ?? getJestVersionFromRequire();
62
+ }
63
+ const installedVersion = (0, devkit_1.getDependencyVersionFromPackageJson)(tree, 'jest');
64
+ if (!installedVersion) {
65
+ return null;
66
+ }
67
+ if (installedVersion === 'latest' || installedVersion === 'next') {
68
+ return ((0, semver_1.clean)(exports.latestVersions.jestVersion) ??
69
+ (0, semver_1.coerce)(exports.latestVersions.jestVersion)?.version ??
70
+ null);
71
+ }
72
+ return (0, semver_1.clean)(installedVersion) ?? (0, semver_1.coerce)(installedVersion)?.version ?? null;
73
+ }
74
+ function getJestVersionFromRequire() {
59
75
  try {
60
- let version;
61
- if (tree) {
62
- version = getJestVersionFromTree(tree);
63
- }
64
- else {
65
- version = getJestVersionFromFileSystem();
66
- }
67
- return version;
76
+ return require('jest').getVersion();
68
77
  }
69
78
  catch {
70
79
  return null;
@@ -80,32 +89,3 @@ function getInstalledJestMajorVersion(tree) {
80
89
  const installedJestVersion = getInstalledJestVersion(tree);
81
90
  return installedJestVersion ? (0, semver_1.major)(installedJestVersion) : null;
82
91
  }
83
- function validateInstalledJestVersion(tree) {
84
- const { version, major } = getInstalledJestVersionInfo(tree);
85
- if (!version) {
86
- return;
87
- }
88
- if (major < minSupportedMajorVersion || major > currentMajorVersion) {
89
- const backwardCompatibleVersions = supportedMajorVersions.slice(0, -1);
90
- throw new Error(`You're currently using an unsupported Jest version: ${version}. Supported major versions are ${backwardCompatibleVersions.join(', ')} and ${currentMajorVersion}.`);
91
- }
92
- }
93
- function getJestVersionFromTree(tree) {
94
- const installedVersion = (0, devkit_1.getDependencyVersionFromPackageJson)(tree, 'jest');
95
- if (!installedVersion) {
96
- return null;
97
- }
98
- if (installedVersion === 'latest' || installedVersion === 'next') {
99
- return ((0, semver_1.clean)(exports.latestVersions.jestVersion) ??
100
- (0, semver_1.coerce)(exports.latestVersions.jestVersion)?.version);
101
- }
102
- return (0, semver_1.clean)(installedVersion) ?? (0, semver_1.coerce)(installedVersion)?.version;
103
- }
104
- function getJestVersionFromFileSystem() {
105
- try {
106
- const { getVersion } = require('jest');
107
- return getVersion();
108
- }
109
- catch { }
110
- return null;
111
- }
package/migrations.json CHANGED
@@ -4,23 +4,27 @@
4
4
  "cli": "nx",
5
5
  "version": "20.0.0-beta.5",
6
6
  "description": "Replace usage of `getJestProjects` with `getJestProjectsAsync`.",
7
- "implementation": "./dist/src/migrations/update-20-0-0/replace-getJestProjects-with-getJestProjectsAsync"
7
+ "implementation": "./dist/src/migrations/update-20-0-0/replace-getJestProjects-with-getJestProjectsAsync",
8
+ "documentation": "./dist/src/migrations/update-20-0-0/replace-getJestProjects-with-getJestProjectsAsync.md"
8
9
  },
9
10
  "replace-getJestProjects-with-getJestProjectsAsync-v21": {
10
11
  "cli": "nx",
11
12
  "version": "21.0.0-beta.9",
12
13
  "description": "Replace usage of `getJestProjects` with `getJestProjectsAsync`.",
13
- "implementation": "./dist/src/migrations/update-21-0-0/replace-getJestProjects-with-getJestProjectsAsync"
14
+ "implementation": "./dist/src/migrations/update-21-0-0/replace-getJestProjects-with-getJestProjectsAsync",
15
+ "documentation": "./dist/src/migrations/update-21-0-0/replace-getJestProjects-with-getJestProjectsAsync.md"
14
16
  },
15
17
  "remove-tsconfig-option-from-jest-executor": {
16
18
  "version": "21.0.0-beta.10",
17
19
  "description": "Remove the previously deprecated and unused `tsConfig` option from the `@nx/jest:jest` executor.",
18
- "implementation": "./dist/src/migrations/update-21-0-0/remove-tsconfig-option-from-jest-executor"
20
+ "implementation": "./dist/src/migrations/update-21-0-0/remove-tsconfig-option-from-jest-executor",
21
+ "documentation": "./dist/src/migrations/update-21-0-0/remove-tsconfig-option-from-jest-executor.md"
19
22
  },
20
23
  "rename-test-path-pattern": {
21
24
  "version": "21.3.0-beta.3",
22
25
  "description": "Rename the CLI option `testPathPattern` to `testPathPatterns`.",
23
- "implementation": "./dist/src/migrations/update-21-3-0/rename-test-path-pattern"
26
+ "implementation": "./dist/src/migrations/update-21-3-0/rename-test-path-pattern",
27
+ "documentation": "./dist/src/migrations/update-21-3-0/rename-test-path-pattern.md"
24
28
  },
25
29
  "replace-removed-matcher-aliases": {
26
30
  "version": "21.3.0-beta.3",
@@ -28,12 +32,14 @@
28
32
  "jest": ">=30.0.0"
29
33
  },
30
34
  "description": "Replace removed matcher aliases in Jest v30 with their corresponding matcher",
31
- "implementation": "./dist/src/migrations/update-21-3-0/replace-removed-matcher-aliases"
35
+ "implementation": "./dist/src/migrations/update-21-3-0/replace-removed-matcher-aliases",
36
+ "documentation": "./dist/src/migrations/update-21-3-0/replace-removed-matcher-aliases.md"
32
37
  },
33
38
  "convert-jest-config-to-cjs": {
34
39
  "version": "22.2.0-beta.2",
35
40
  "description": "Convert jest.config.ts files from ESM to CJS syntax (export default -> module.exports, import -> require) for projects using CommonJS resolution to ensure correct loading under Node.js type-stripping.",
36
- "implementation": "./dist/src/migrations/update-22-2-0/convert-jest-config-to-cjs"
41
+ "implementation": "./dist/src/migrations/update-22-2-0/convert-jest-config-to-cjs",
42
+ "documentation": "./dist/src/migrations/update-22-2-0/convert-jest-config-to-cjs.md"
37
43
  },
38
44
  "replace-removed-matcher-aliases-v22-3": {
39
45
  "version": "22.3.2-beta.0",
@@ -41,7 +47,8 @@
41
47
  "jest": ">=30.0.0"
42
48
  },
43
49
  "description": "Replace removed matcher aliases in Jest v30 with their corresponding matcher",
44
- "implementation": "./dist/src/migrations/update-21-3-0/replace-removed-matcher-aliases"
50
+ "implementation": "./dist/src/migrations/update-21-3-0/replace-removed-matcher-aliases",
51
+ "documentation": "./dist/src/migrations/update-21-3-0/replace-removed-matcher-aliases.md"
45
52
  },
46
53
  "update-snapshot-guide-link": {
47
54
  "version": "23.0.0-beta.10",
@@ -49,7 +56,8 @@
49
56
  "jest": ">=30.0.0"
50
57
  },
51
58
  "description": "Update the Jest snapshot guide link in `.snap` files from the legacy `https://goo.gl/fbAQLP` URL to `https://jestjs.io/docs/snapshot-testing`, which Jest v30 now requires.",
52
- "implementation": "./dist/src/migrations/update-23-0-0/update-snapshot-guide-link"
59
+ "implementation": "./dist/src/migrations/update-23-0-0/update-snapshot-guide-link",
60
+ "documentation": "./dist/src/migrations/update-23-0-0/update-snapshot-guide-link.md"
53
61
  },
54
62
  "rewrite-jest-internal-subpath-imports": {
55
63
  "version": "23.0.0-beta.16",
@@ -60,6 +68,16 @@
60
68
  "version": "23.0.0-beta.16",
61
69
  "description": "Replaces the removed `jestProjectGenerator` export from `@nx/jest` with its replacement `configurationGenerator`.",
62
70
  "implementation": "./dist/src/migrations/update-23-0-0/rewrite-jest-project-generator"
71
+ },
72
+ "migrate-jest-executor-setup-file": {
73
+ "version": "23.0.0-beta.22",
74
+ "description": "Migrate the deprecated `setupFile` option of the `@nx/jest:jest` executor: push the file path into `setupFilesAfterEnv` in the project's Jest config and remove the option from `project.json` and `nx.json` target defaults.",
75
+ "implementation": "./dist/src/migrations/update-23-0-0/migrate-jest-executor-setup-file"
76
+ },
77
+ "migrate-jest-configuration-skip-setup-file": {
78
+ "version": "23.0.0-beta.22",
79
+ "description": "Migrate the deprecated `skipSetupFile` option of the `@nx/jest:configuration` generator stored as a default in `nx.json` or per-project `project.json` to `setupFile: 'none'` (when `true`) or remove it (when `false`).",
80
+ "implementation": "./dist/src/migrations/update-23-0-0/migrate-jest-configuration-skip-setup-file"
63
81
  }
64
82
  },
65
83
  "packageJsonUpdates": {
@@ -69,6 +87,9 @@
69
87
  "incompatibleWith": {
70
88
  "@angular-devkit/build-angular": "< 21.0.0"
71
89
  },
90
+ "requires": {
91
+ "jest": ">=29.0.0 <30.0.0"
92
+ },
72
93
  "packages": {
73
94
  "jest": {
74
95
  "version": "~30.0.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nx/jest",
3
- "version": "23.0.0-beta.20",
3
+ "version": "23.0.0-beta.22",
4
4
  "private": false,
5
5
  "type": "commonjs",
6
6
  "files": [
@@ -90,6 +90,18 @@
90
90
  "default": "./dist/internal.js"
91
91
  }
92
92
  },
93
+ "peerDependencies": {
94
+ "jest": "^29.0.0 || ^30.0.0",
95
+ "ts-jest": "^29.0.0"
96
+ },
97
+ "peerDependenciesMeta": {
98
+ "jest": {
99
+ "optional": true
100
+ },
101
+ "ts-jest": {
102
+ "optional": true
103
+ }
104
+ },
93
105
  "dependencies": {
94
106
  "@jest/reporters": "^30.0.2",
95
107
  "@jest/test-result": "^30.0.2",
@@ -104,11 +116,11 @@
104
116
  "semver": "^7.6.3",
105
117
  "tslib": "^2.3.0",
106
118
  "yargs-parser": "21.1.1",
107
- "@nx/devkit": "23.0.0-beta.20",
108
- "@nx/js": "23.0.0-beta.20"
119
+ "@nx/devkit": "23.0.0-beta.22",
120
+ "@nx/js": "23.0.0-beta.22"
109
121
  },
110
122
  "devDependencies": {
111
- "nx": "23.0.0-beta.20"
123
+ "nx": "23.0.0-beta.22"
112
124
  },
113
125
  "publishConfig": {
114
126
  "access": "public"
@@ -1,2 +0,0 @@
1
- export declare function getInstalledJestVersion(): string | null;
2
- export declare function getInstalledJestMajorVersion(): number | null;
@@ -1,18 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getInstalledJestVersion = getInstalledJestVersion;
4
- exports.getInstalledJestMajorVersion = getInstalledJestMajorVersion;
5
- const jest_1 = require("jest");
6
- const semver_1 = require("semver");
7
- function getInstalledJestVersion() {
8
- try {
9
- return (0, jest_1.getVersion)();
10
- }
11
- catch {
12
- return null;
13
- }
14
- }
15
- function getInstalledJestMajorVersion() {
16
- const installedVersion = getInstalledJestVersion();
17
- return installedVersion ? (0, semver_1.major)(installedVersion) : null;
18
- }