@nx/jest 19.4.0-beta.0 → 19.4.0-beta.2

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.
package/generators.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "init": {
6
6
  "factory": "./src/generators/init/init#jestInitGeneratorInternal",
7
7
  "schema": "./src/generators/init/schema.json",
8
- "description": "Initialize the `@nrwl/jest` plugin.",
8
+ "description": "Initialize the `@nx/jest` plugin.",
9
9
  "aliases": ["ng-add"],
10
10
  "hidden": true
11
11
  },
@@ -14,6 +14,11 @@
14
14
  "schema": "./src/generators/configuration/schema.json",
15
15
  "description": "Add Jest configuration to a project.",
16
16
  "hidden": true
17
+ },
18
+ "convert-to-inferred": {
19
+ "factory": "./src/generators/convert-to-inferred/convert-to-inferred",
20
+ "schema": "./src/generators/convert-to-inferred/schema.json",
21
+ "description": "Convert existing Jest project(s) using `@nx/jest:jest` executor to use `@nx/jest/plugin`."
17
22
  }
18
23
  }
19
24
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nx/jest",
3
- "version": "19.4.0-beta.0",
3
+ "version": "19.4.0-beta.2",
4
4
  "private": false,
5
5
  "description": "The Nx Plugin for Jest contains executors and generators allowing your workspace to use the powerful Jest testing capabilities.",
6
6
  "repository": {
@@ -37,8 +37,8 @@
37
37
  "dependencies": {
38
38
  "@jest/reporters": "^29.4.1",
39
39
  "@jest/test-result": "^29.4.1",
40
- "@nx/devkit": "19.4.0-beta.0",
41
- "@nx/js": "19.4.0-beta.0",
40
+ "@nx/devkit": "19.4.0-beta.2",
41
+ "@nx/js": "19.4.0-beta.2",
42
42
  "@phenomnomnominal/tsquery": "~5.0.1",
43
43
  "chalk": "^4.1.0",
44
44
  "identity-obj-proxy": "3.0.0",
@@ -49,7 +49,7 @@
49
49
  "resolve.exports": "1.1.0",
50
50
  "tslib": "^2.3.0",
51
51
  "yargs-parser": "21.1.1",
52
- "@nrwl/jest": "19.4.0-beta.0"
52
+ "@nrwl/jest": "19.4.0-beta.2"
53
53
  },
54
54
  "publishConfig": {
55
55
  "access": "public"
@@ -0,0 +1,7 @@
1
+ import { type Tree } from '@nx/devkit';
2
+ interface Schema {
3
+ project?: string;
4
+ skipFormat?: boolean;
5
+ }
6
+ export declare function convertToInferred(tree: Tree, options: Schema): Promise<void>;
7
+ export default convertToInferred;
@@ -0,0 +1,207 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.convertToInferred = void 0;
4
+ const devkit_1 = require("@nx/devkit");
5
+ const executor_to_plugin_migrator_1 = require("@nx/devkit/src/generators/plugin-migrations/executor-to-plugin-migrator");
6
+ const plugin_migration_utils_1 = require("@nx/devkit/src/generators/plugin-migrations/plugin-migration-utils");
7
+ const jest_config_1 = require("jest-config");
8
+ const node_path_1 = require("node:path");
9
+ const plugin_1 = require("../../plugins/plugin");
10
+ const config_file_1 = require("../../utils/config/config-file");
11
+ async function convertToInferred(tree, options) {
12
+ const projectGraph = await (0, devkit_1.createProjectGraphAsync)();
13
+ const migratedProjects = await (0, executor_to_plugin_migrator_1.migrateProjectExecutorsToPlugin)(tree, projectGraph, '@nx/jest/plugin', plugin_1.createNodesV2, { targetName: 'test' }, [
14
+ {
15
+ executors: ['@nx/jest:jest', '@nrwl/jest:jest'],
16
+ postTargetTransformer,
17
+ targetPluginOptionMapper: (targetName) => ({ targetName }),
18
+ },
19
+ ], options.project);
20
+ if (migratedProjects.size === 0) {
21
+ throw new Error('Could not find any targets to migrate.');
22
+ }
23
+ if (!options.skipFormat) {
24
+ await (0, devkit_1.formatFiles)(tree);
25
+ }
26
+ }
27
+ exports.convertToInferred = convertToInferred;
28
+ async function postTargetTransformer(target, tree, projectDetails, inferredTarget) {
29
+ const jestConfigPath = config_file_1.jestConfigExtensions
30
+ .map((ext) => `jest.config.${ext}`)
31
+ .find((configFileName) => tree.exists(node_path_1.posix.join(projectDetails.root, configFileName)));
32
+ if (target.options) {
33
+ await updateOptionsObject(target.options, projectDetails.root, tree.root, jestConfigPath);
34
+ }
35
+ if (target.configurations) {
36
+ for (const [configName, config] of Object.entries(target.configurations)) {
37
+ await updateConfigurationObject(config, projectDetails.root, tree.root, jestConfigPath);
38
+ if (!Object.keys(config).length) {
39
+ delete target.configurations[configName];
40
+ }
41
+ }
42
+ if (!Object.keys(target.configurations).length) {
43
+ delete target.defaultConfiguration;
44
+ delete target.configurations;
45
+ }
46
+ if ('defaultConfiguration' in target &&
47
+ !target.configurations?.[target.defaultConfiguration]) {
48
+ delete target.defaultConfiguration;
49
+ }
50
+ }
51
+ if (target.outputs) {
52
+ (0, plugin_migration_utils_1.processTargetOutputs)(target, [], inferredTarget, {
53
+ projectName: projectDetails.projectName,
54
+ projectRoot: projectDetails.root,
55
+ });
56
+ }
57
+ return target;
58
+ }
59
+ exports.default = convertToInferred;
60
+ async function updateOptionsObject(targetOptions, projectRoot, workspaceRoot, defaultJestConfigPath) {
61
+ const jestConfigPath = targetOptions.jestConfig ?? defaultJestConfigPath;
62
+ // inferred targets are only identified after known files that Jest would
63
+ // pick up, so we can safely remove the config options
64
+ delete targetOptions.jestConfig;
65
+ delete targetOptions.config;
66
+ await updateOptions(targetOptions, projectRoot, workspaceRoot, jestConfigPath);
67
+ }
68
+ async function updateConfigurationObject(targetOptions, projectRoot, workspaceRoot, defaultJestConfigPath) {
69
+ const jestConfigPath = targetOptions.jestConfig ?? defaultJestConfigPath;
70
+ if (targetOptions.jestConfig) {
71
+ targetOptions.config = (0, plugin_migration_utils_1.toProjectRelativePath)(targetOptions.jestConfig, projectRoot);
72
+ delete targetOptions.jestConfig;
73
+ }
74
+ else if (targetOptions.config) {
75
+ targetOptions.config = (0, plugin_migration_utils_1.toProjectRelativePath)(targetOptions.config, projectRoot);
76
+ }
77
+ await updateOptions(targetOptions, projectRoot, workspaceRoot, jestConfigPath);
78
+ }
79
+ async function updateOptions(targetOptions, projectRoot, workspaceRoot, jestConfigPath) {
80
+ // deprecated and unused
81
+ delete targetOptions.tsConfig;
82
+ if ('codeCoverage' in targetOptions) {
83
+ targetOptions.coverage = targetOptions.codeCoverage;
84
+ delete targetOptions.codeCoverage;
85
+ }
86
+ const testPathPatterns = [];
87
+ if ('testFile' in targetOptions) {
88
+ testPathPatterns.push(toProjectRelativeRegexPath(targetOptions.testFile, projectRoot));
89
+ delete targetOptions.testFile;
90
+ }
91
+ if ('testPathPattern' in targetOptions) {
92
+ testPathPatterns.push(...targetOptions.testPathPattern.map((pattern) => toProjectRelativeRegexPath(pattern, projectRoot)));
93
+ }
94
+ if (testPathPatterns.length > 1) {
95
+ targetOptions.testPathPattern = `\"${testPathPatterns.join('|')}\"`;
96
+ }
97
+ else if (testPathPatterns.length === 1) {
98
+ targetOptions.testPathPattern = testPathPatterns[0];
99
+ }
100
+ if ('testPathIgnorePatterns' in targetOptions) {
101
+ if (targetOptions.testPathIgnorePatterns.length > 1) {
102
+ targetOptions.testPathIgnorePatterns = `\"${targetOptions.testPathIgnorePatterns
103
+ .map((pattern) => toProjectRelativeRegexPath(pattern, projectRoot))
104
+ .join('|')}\"`;
105
+ }
106
+ else if (targetOptions.testPathIgnorePatterns.length === 1) {
107
+ targetOptions.testPathIgnorePatterns = toProjectRelativeRegexPath(targetOptions.testPathIgnorePatterns[0], projectRoot);
108
+ }
109
+ }
110
+ if ('testMatch' in targetOptions) {
111
+ targetOptions.testMatch = targetOptions.testMatch
112
+ .map((pattern) => `"${toProjectRelativeGlobPath(pattern, projectRoot)}"`)
113
+ .join(' ');
114
+ }
115
+ if ('findRelatedTests' in targetOptions) {
116
+ // the executor accepts a comma-separated string, while jest accepts a space-separated string
117
+ const parsedSourceFiles = targetOptions.findRelatedTests
118
+ .split(',')
119
+ .map((s) => (0, plugin_migration_utils_1.toProjectRelativePath)(s.trim(), projectRoot))
120
+ .join(' ');
121
+ targetOptions.args = [`--findRelatedTests ${parsedSourceFiles}`];
122
+ delete targetOptions.findRelatedTests;
123
+ }
124
+ if ('setupFile' in targetOptions) {
125
+ const setupFiles = await processSetupFiles(targetOptions.setupFile, targetOptions.setupFilesAfterEnv, projectRoot, workspaceRoot, jestConfigPath);
126
+ if (setupFiles.length > 1) {
127
+ targetOptions.setupFilesAfterEnv = setupFiles
128
+ .map((sf) => `"${sf}"`)
129
+ .join(' ');
130
+ }
131
+ else if (setupFiles.length === 1) {
132
+ targetOptions.setupFilesAfterEnv = setupFiles[0];
133
+ }
134
+ else {
135
+ // if there are no setup files, it means they are already defined in the
136
+ // jest config, so we can remove the option
137
+ delete targetOptions.setupFilesAfterEnv;
138
+ }
139
+ delete targetOptions.setupFile;
140
+ }
141
+ if ('outputFile' in targetOptions) {
142
+ // update the output file to be relative to the project root
143
+ targetOptions.outputFile = (0, plugin_migration_utils_1.toProjectRelativePath)(targetOptions.outputFile, projectRoot);
144
+ }
145
+ if ('coverageDirectory' in targetOptions) {
146
+ // update the coverage directory to be relative to the project root
147
+ targetOptions.coverageDirectory = (0, plugin_migration_utils_1.toProjectRelativePath)(targetOptions.coverageDirectory, projectRoot);
148
+ }
149
+ }
150
+ async function processSetupFiles(setupFile, setupFilesAfterEnv, projectRoot, workspaceRoot, jestConfigPath) {
151
+ // the jest executor merges the setupFile with the setupFilesAfterEnv, so
152
+ // to keep the task working as before we resolve the setupFilesAfterEnv
153
+ // from the options or the jest config and add the setupFile to it
154
+ // https://github.com/nrwl/nx/blob/bdd3375256613340899f649eb800d22abcc9f507/packages/jest/src/executors/jest/jest.impl.ts#L107-L113
155
+ const configSetupFilesAfterEnv = [];
156
+ if (jestConfigPath) {
157
+ const jestConfig = await (0, jest_config_1.readConfig)({ setupFilesAfterEnv }, (0, node_path_1.join)(workspaceRoot, jestConfigPath));
158
+ if (jestConfig.projectConfig.setupFilesAfterEnv) {
159
+ configSetupFilesAfterEnv.push(...jestConfig.projectConfig.setupFilesAfterEnv.map((file) => (0, plugin_migration_utils_1.toProjectRelativePath)(file, projectRoot)));
160
+ }
161
+ }
162
+ if (!configSetupFilesAfterEnv.length) {
163
+ return [(0, plugin_migration_utils_1.toProjectRelativePath)(setupFile, projectRoot)];
164
+ }
165
+ if (isSetupFileInConfig(configSetupFilesAfterEnv, setupFile, projectRoot, workspaceRoot)) {
166
+ // the setupFile is already included in the setupFilesAfterEnv
167
+ return [];
168
+ }
169
+ return [
170
+ ...configSetupFilesAfterEnv,
171
+ (0, plugin_migration_utils_1.toProjectRelativePath)(setupFile, projectRoot),
172
+ ];
173
+ }
174
+ function isSetupFileInConfig(setupFilesAfterEnv, setupFile, projectRoot, workspaceRoot) {
175
+ const normalizePath = (f) => f.startsWith('<rootDir>')
176
+ ? node_path_1.posix.join(workspaceRoot, projectRoot, f.slice('<rootDir>'.length))
177
+ : node_path_1.posix.join(workspaceRoot, projectRoot, f);
178
+ const normalizedSetupFiles = new Set(setupFilesAfterEnv.map(normalizePath));
179
+ return normalizedSetupFiles.has(normalizePath((0, plugin_migration_utils_1.toProjectRelativePath)(setupFile, projectRoot)));
180
+ }
181
+ function toProjectRelativeRegexPath(path, projectRoot) {
182
+ if (projectRoot === '.') {
183
+ // workspace and project root are the same, keep the path as is
184
+ return path;
185
+ }
186
+ const normalizedRoot = (0, node_path_1.normalize)(projectRoot);
187
+ if (new RegExp(`^(?:\\.[\\/\\\\])?${normalizedRoot}(?:[\\/\\\\])?$`).test(path)) {
188
+ // path includes everything inside project root
189
+ return '.*';
190
+ }
191
+ const normalizedPath = (0, node_path_1.normalize)(path);
192
+ const startWithProjectRootRegex = new RegExp(`^(?:\\.[\\/\\\\])?${normalizedRoot}[\\/\\\\]`);
193
+ return startWithProjectRootRegex.test(normalizedPath)
194
+ ? normalizedPath.replace(startWithProjectRootRegex, '')
195
+ : path;
196
+ }
197
+ function toProjectRelativeGlobPath(path, projectRoot) {
198
+ if (projectRoot === '.') {
199
+ // workspace and project root are the same, keep the path as is
200
+ return path;
201
+ }
202
+ // globs use forward slashes, so we make sure to normalize the path
203
+ const normalizedRoot = node_path_1.posix.normalize(projectRoot);
204
+ return path
205
+ .replace(new RegExp(`\/${normalizedRoot}\/`), '/')
206
+ .replace(/\*\*\/\*\*/g, '**');
207
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "$schema": "https://json-schema.org/schema",
3
+ "$id": "NxJestConvertToInferred",
4
+ "description": "Convert existing Jest project(s) using `@nx/jest:jest` executor to use `@nx/jest/plugin`.",
5
+ "title": "Convert Jest project from executor to plugin",
6
+ "type": "object",
7
+ "properties": {
8
+ "project": {
9
+ "type": "string",
10
+ "description": "The project to convert from using the `@nx/jest:jest` executor to use `@nx/jest/plugin`. If not provided, all projects using the `@nx/jest:jest` executor will be converted.",
11
+ "x-priority": "important"
12
+ },
13
+ "skipFormat": {
14
+ "type": "boolean",
15
+ "description": "Whether to format files.",
16
+ "default": false
17
+ }
18
+ }
19
+ }
@@ -144,6 +144,7 @@ async function buildJestTargets(configFilePath, projectRoot, options, context) {
144
144
  metadata: {
145
145
  technologies: ['jest'],
146
146
  description: 'Run Jest Tests in CI',
147
+ nonAtomizedTarget: options.targetName,
147
148
  },
148
149
  };
149
150
  targetGroup.push(options.ciTargetName);
@@ -162,6 +163,7 @@ async function buildJestTargets(configFilePath, projectRoot, options, context) {
162
163
  metadata: {
163
164
  technologies: ['jest'],
164
165
  description: `Run Jest Tests in ${relativePath}`,
166
+ nonAtomizedTarget: options.targetName,
165
167
  },
166
168
  };
167
169
  targetGroup.push(targetName);